@kenkaiiii/gg-ai 4.5.0 → 4.6.1

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.d.cts CHANGED
@@ -255,8 +255,9 @@ declare class StreamResult implements AsyncIterable<StreamEvent> {
255
255
  private resolveResponse;
256
256
  private rejectResponse;
257
257
  private resolveWait;
258
- constructor(generator: AsyncGenerator<StreamEvent, StreamResponse>);
258
+ constructor(generator: AsyncGenerator<StreamEvent, StreamResponse>, signal?: AbortSignal);
259
259
  private pump;
260
+ private _nextWithAbort;
260
261
  [Symbol.asyncIterator](): AsyncIterator<StreamEvent>;
261
262
  then<TResult1 = StreamResponse, TResult2 = never>(onfulfilled?: ((value: StreamResponse) => TResult1 | PromiseLike<TResult1>) | null, onrejected?: ((reason: unknown) => TResult2 | PromiseLike<TResult2>) | null): Promise<TResult1 | TResult2>;
262
263
  }
package/dist/index.d.ts CHANGED
@@ -255,8 +255,9 @@ declare class StreamResult implements AsyncIterable<StreamEvent> {
255
255
  private resolveResponse;
256
256
  private rejectResponse;
257
257
  private resolveWait;
258
- constructor(generator: AsyncGenerator<StreamEvent, StreamResponse>);
258
+ constructor(generator: AsyncGenerator<StreamEvent, StreamResponse>, signal?: AbortSignal);
259
259
  private pump;
260
+ private _nextWithAbort;
260
261
  [Symbol.asyncIterator](): AsyncIterator<StreamEvent>;
261
262
  then<TResult1 = StreamResponse, TResult2 = never>(onfulfilled?: ((value: StreamResponse) => TResult1 | PromiseLike<TResult1>) | null, onrejected?: ((reason: unknown) => TResult2 | PromiseLike<TResult2>) | null): Promise<TResult1 | TResult2>;
262
263
  }
package/dist/index.js CHANGED
@@ -151,7 +151,7 @@ function finaliseBySource(source, message, requestId, hint) {
151
151
  headline: message,
152
152
  source,
153
153
  message: "",
154
- guidance: hint ?? "Only Kimi, Gemini, and MiniMax can analyze video. Switch with /model.",
154
+ guidance: hint ?? "Only Kimi, Gemini, MiniMax, and MiMo-V2.5 can analyze video. Switch with /model.",
155
155
  ...requestId ? { requestId } : {}
156
156
  };
157
157
  case "ggcoder":
@@ -274,21 +274,21 @@ var StreamResult = class {
274
274
  resolveResponse;
275
275
  rejectResponse;
276
276
  resolveWait = null;
277
- constructor(generator) {
277
+ constructor(generator, signal) {
278
278
  this.response = new Promise((resolve, reject) => {
279
279
  this.resolveResponse = resolve;
280
280
  this.rejectResponse = reject;
281
281
  });
282
- this.pump(generator);
282
+ this.pump(generator, signal);
283
283
  }
284
- async pump(generator) {
284
+ async pump(generator, signal) {
285
285
  try {
286
- let next = await generator.next();
286
+ let next = await this._nextWithAbort(generator, signal);
287
287
  while (!next.done) {
288
288
  this.buffer.push(next.value);
289
289
  this.resolveWait?.();
290
290
  this.resolveWait = null;
291
- next = await generator.next();
291
+ next = await this._nextWithAbort(generator, signal);
292
292
  }
293
293
  this.done = true;
294
294
  this.resolveResponse(next.value);
@@ -303,6 +303,28 @@ var StreamResult = class {
303
303
  this.resolveWait = null;
304
304
  }
305
305
  }
306
+ async _nextWithAbort(generator, signal) {
307
+ if (!signal) {
308
+ return generator.next();
309
+ }
310
+ if (signal.aborted) {
311
+ return Promise.reject(new DOMException("Aborted", "AbortError"));
312
+ }
313
+ let onAbort;
314
+ const abortPromise = new Promise((_, reject) => {
315
+ onAbort = () => {
316
+ generator.return?.(void 0).catch(() => {
317
+ });
318
+ reject(new DOMException("Aborted", "AbortError"));
319
+ };
320
+ signal.addEventListener("abort", onAbort, { once: true });
321
+ });
322
+ try {
323
+ return await Promise.race([generator.next(), abortPromise]);
324
+ } finally {
325
+ if (onAbort) signal.removeEventListener("abort", onAbort);
326
+ }
327
+ }
306
328
  async *[Symbol.asyncIterator]() {
307
329
  let index = 0;
308
330
  while (true) {
@@ -794,13 +816,14 @@ function toOpenAIMessages(messages, options) {
794
816
  }
795
817
  if (msg.role === "tool") {
796
818
  const isMoonshot = options?.provider === "moonshot";
797
- const imageBlocks = [];
819
+ const followUpMediaBlocks = [];
820
+ let followUpHasVideo = false;
798
821
  for (const result of msg.content) {
799
822
  const text = toolResultText(result.content);
800
823
  const images = toolResultImages(result.content);
801
- const videos = isMoonshot ? toolResultVideos(result.content) : [];
824
+ const videos = toolResultVideos(result.content);
802
825
  const hasText = text.length > 0;
803
- if (videos.length > 0) {
826
+ if (isMoonshot && videos.length > 0) {
804
827
  const parts = [];
805
828
  if (hasText) parts.push({ type: "text", text });
806
829
  const videoParts = videos.map((v) => {
@@ -817,21 +840,31 @@ function toOpenAIMessages(messages, options) {
817
840
  out.push({
818
841
  role: "tool",
819
842
  tool_call_id: remapToolCallId(result.toolCallId, idMap),
820
- content: hasText ? text : "(see attached image)"
843
+ content: hasText ? text : "(see attached media)"
821
844
  });
822
845
  if (images.length > 0 && options?.supportsImages !== false) {
823
846
  for (const img of images) {
824
- imageBlocks.push({
847
+ followUpMediaBlocks.push({
825
848
  type: "image_url",
826
849
  image_url: { url: `data:${img.mediaType};base64,${img.data}` }
827
850
  });
828
851
  }
829
852
  }
853
+ if (!isMoonshot && videos.length > 0) {
854
+ for (const v of videos) {
855
+ followUpMediaBlocks.push({
856
+ type: "video_url",
857
+ video_url: { url: `data:${v.mediaType};base64,${v.data}` }
858
+ });
859
+ followUpHasVideo = true;
860
+ }
861
+ }
830
862
  }
831
- if (imageBlocks.length > 0) {
863
+ if (followUpMediaBlocks.length > 0) {
864
+ const label = followUpHasVideo ? "Attached media from tool result:" : "Attached image(s) from tool result:";
832
865
  out.push({
833
866
  role: "user",
834
- content: [{ type: "text", text: "Attached image(s) from tool result:" }, ...imageBlocks]
867
+ content: [{ type: "text", text: label }, ...followUpMediaBlocks]
835
868
  });
836
869
  }
837
870
  }
@@ -908,10 +941,10 @@ function createClient(options) {
908
941
  ...isOAuth ? { apiKey: null, authToken: options.apiKey } : { apiKey: options.apiKey },
909
942
  ...options.baseUrl ? { baseURL: options.baseUrl } : {},
910
943
  ...options.fetch ? { fetch: options.fetch } : {},
911
- // Allow SDK retries for connection-level failures (socket hang up, 500s,
912
- // connection refused). Our stall detection handles abort-initiated retries
913
- // separately SDK retries only fire on genuine transport errors.
914
- maxRetries: 2,
944
+ // Disable SDK retries the agent loop has its own stall/overload retry
945
+ // logic that surfaces errors properly. SDK retries on 429s can cause
946
+ // multi-minute hangs when the provider stops responding mid-retry.
947
+ maxRetries: 0,
915
948
  ...isOAuth ? {
916
949
  defaultHeaders: {
917
950
  // Anthropic's OAuth edge validates the claude-cli version. Callers
@@ -924,7 +957,7 @@ function createClient(options) {
924
957
  });
925
958
  }
926
959
  function streamAnthropic(options) {
927
- return new StreamResult(runStream(options));
960
+ return new StreamResult(runStream(options), options.signal);
928
961
  }
929
962
  async function* runStream(options) {
930
963
  const client = createClient(options);
@@ -1019,7 +1052,6 @@ async function* runStream(options) {
1019
1052
  throw toError(err);
1020
1053
  }
1021
1054
  }
1022
- const stream2 = client.messages.stream(params, requestOptions);
1023
1055
  const contentParts = [];
1024
1056
  const blocks = /* @__PURE__ */ new Map();
1025
1057
  let inputTokens = 0;
@@ -1028,8 +1060,14 @@ async function* runStream(options) {
1028
1060
  let cacheWrite;
1029
1061
  let stopReason = null;
1030
1062
  const keepalive = { type: "keepalive" };
1063
+ let receivedAnyEvent = false;
1031
1064
  try {
1065
+ const stream2 = await client.messages.create(
1066
+ params,
1067
+ requestOptions
1068
+ );
1032
1069
  for await (const event of stream2) {
1070
+ receivedAnyEvent = true;
1033
1071
  switch (event.type) {
1034
1072
  case "message_start": {
1035
1073
  const usage = event.message.usage;
@@ -1066,7 +1104,7 @@ async function* runStream(options) {
1066
1104
  accum.toolId = block.id;
1067
1105
  accum.toolName = block.name;
1068
1106
  accum.input = block.input;
1069
- } else if (block.type === "redacted_thinking") {
1107
+ } else if (block.type !== "text" && block.type !== "thinking") {
1070
1108
  accum.raw = block;
1071
1109
  }
1072
1110
  blocks.set(idx, accum);
@@ -1163,8 +1201,7 @@ async function* runStream(options) {
1163
1201
  contentParts.push({ type: "raw", data: accum.raw });
1164
1202
  yield keepalive;
1165
1203
  } else {
1166
- const msg = stream2.currentMessage;
1167
- const rawBlock = msg?.content[event.index];
1204
+ const rawBlock = accum.raw;
1168
1205
  if (rawBlock) {
1169
1206
  const blockType = rawBlock.type;
1170
1207
  if (blockType === "web_search_tool_result") {
@@ -1210,6 +1247,11 @@ async function* runStream(options) {
1210
1247
  } catch (err) {
1211
1248
  throw toError(err);
1212
1249
  }
1250
+ if (!receivedAnyEvent) {
1251
+ throw new ProviderError("anthropic", "Stream ended without producing any events.", {
1252
+ statusCode: 504
1253
+ });
1254
+ }
1213
1255
  const normalizedStop = normalizeAnthropicStopReason(stopReason);
1214
1256
  const response = {
1215
1257
  message: {
@@ -1501,7 +1543,7 @@ function createClient2(options) {
1501
1543
  });
1502
1544
  }
1503
1545
  function streamOpenAI(options) {
1504
- return new StreamResult(runStream2(options));
1546
+ return new StreamResult(runStream2(options), options.signal);
1505
1547
  }
1506
1548
  async function* runStream2(options) {
1507
1549
  const providerName = options.provider ?? "openai";
@@ -1593,51 +1635,62 @@ async function* runStream2(options) {
1593
1635
  let outputTokens = 0;
1594
1636
  let cacheRead = 0;
1595
1637
  let finishReason = null;
1596
- for await (const chunk of stream2) {
1597
- const choice = chunk.choices?.[0];
1598
- if (chunk.usage) {
1599
- ({ inputTokens, outputTokens, cacheRead } = extractOpenAIUsage(chunk.usage));
1600
- }
1601
- if (!choice) continue;
1602
- if (choice.finish_reason) {
1603
- finishReason = choice.finish_reason;
1604
- }
1605
- const delta = choice.delta;
1606
- const reasoningContent = delta.reasoning_content;
1607
- if (typeof reasoningContent === "string" && reasoningContent) {
1608
- thinkingAccum += reasoningContent;
1609
- if (options.thinking) {
1610
- yield { type: "thinking_delta", text: reasoningContent };
1638
+ let receivedAnyChunk = false;
1639
+ try {
1640
+ for await (const chunk of stream2) {
1641
+ receivedAnyChunk = true;
1642
+ const choice = chunk.choices?.[0];
1643
+ if (chunk.usage) {
1644
+ ({ inputTokens, outputTokens, cacheRead } = extractOpenAIUsage(chunk.usage));
1611
1645
  }
1612
- }
1613
- if (delta.content) {
1614
- textAccum += delta.content;
1615
- yield { type: "text_delta", text: delta.content };
1616
- }
1617
- if (delta.tool_calls) {
1618
- for (const tc of delta.tool_calls) {
1619
- let accum = toolCallAccum.get(tc.index);
1620
- if (!accum) {
1621
- accum = {
1622
- id: tc.id ?? "",
1623
- name: tc.function?.name ?? "",
1624
- argsJson: ""
1625
- };
1626
- toolCallAccum.set(tc.index, accum);
1646
+ if (!choice) continue;
1647
+ if (choice.finish_reason) {
1648
+ finishReason = choice.finish_reason;
1649
+ }
1650
+ const delta = choice.delta;
1651
+ const reasoningContent = delta.reasoning_content;
1652
+ if (typeof reasoningContent === "string" && reasoningContent) {
1653
+ thinkingAccum += reasoningContent;
1654
+ if (options.thinking) {
1655
+ yield { type: "thinking_delta", text: reasoningContent };
1627
1656
  }
1628
- if (tc.id) accum.id = tc.id;
1629
- if (tc.function?.name) accum.name = tc.function.name;
1630
- if (tc.function?.arguments) {
1631
- accum.argsJson += tc.function.arguments;
1632
- yield {
1633
- type: "toolcall_delta",
1634
- id: accum.id,
1635
- name: accum.name,
1636
- argsJson: tc.function.arguments
1637
- };
1657
+ }
1658
+ if (delta.content) {
1659
+ textAccum += delta.content;
1660
+ yield { type: "text_delta", text: delta.content };
1661
+ }
1662
+ if (delta.tool_calls) {
1663
+ for (const tc of delta.tool_calls) {
1664
+ let accum = toolCallAccum.get(tc.index);
1665
+ if (!accum) {
1666
+ accum = {
1667
+ id: tc.id ?? "",
1668
+ name: tc.function?.name ?? "",
1669
+ argsJson: ""
1670
+ };
1671
+ toolCallAccum.set(tc.index, accum);
1672
+ }
1673
+ if (tc.id) accum.id = tc.id;
1674
+ if (tc.function?.name) accum.name = tc.function.name;
1675
+ if (tc.function?.arguments) {
1676
+ accum.argsJson += tc.function.arguments;
1677
+ yield {
1678
+ type: "toolcall_delta",
1679
+ id: accum.id,
1680
+ name: accum.name,
1681
+ argsJson: tc.function.arguments
1682
+ };
1683
+ }
1638
1684
  }
1639
1685
  }
1640
1686
  }
1687
+ } catch (err) {
1688
+ throw toError2(err, providerName);
1689
+ }
1690
+ if (!receivedAnyChunk) {
1691
+ throw new ProviderError(providerName, "Stream ended without producing any chunks.", {
1692
+ statusCode: 504
1693
+ });
1641
1694
  }
1642
1695
  if (thinkingAccum) {
1643
1696
  contentParts.push({ type: "thinking", text: thinkingAccum });
@@ -1880,7 +1933,7 @@ function isVisibleOutputItem(itemType) {
1880
1933
  return itemType === "message";
1881
1934
  }
1882
1935
  function streamOpenAICodex(options) {
1883
- return new StreamResult(runStream3(options));
1936
+ return new StreamResult(runStream3(options), options.signal);
1884
1937
  }
1885
1938
  async function* runStream3(options) {
1886
1939
  const baseUrl = (options.baseUrl || DEFAULT_BASE_URL).replace(/\/+$/, "");
@@ -2210,10 +2263,16 @@ async function* parseSSE(body) {
2210
2263
  }
2211
2264
  }
2212
2265
  function remapCodexId(id, idMap) {
2213
- if (id.startsWith("fc_") || id.startsWith("fc-")) return id;
2214
2266
  const existing = idMap.get(id);
2215
2267
  if (existing) return existing;
2216
- const mapped = `fc_${id.replace(/^toolu_/, "")}`;
2268
+ const withPrefix = id.startsWith("fc_") || id.startsWith("fc-") ? id : `fc_${id.replace(/^toolu_/, "")}`;
2269
+ const sanitized = withPrefix.replace(/[^A-Za-z0-9_-]/g, "_");
2270
+ let mapped = sanitized;
2271
+ let suffix = 2;
2272
+ const used = new Set(idMap.values());
2273
+ while (used.has(mapped)) {
2274
+ mapped = `${sanitized}_${suffix++}`;
2275
+ }
2217
2276
  idMap.set(id, mapped);
2218
2277
  return mapped;
2219
2278
  }
@@ -2736,7 +2795,7 @@ async function fetchCodeAssistWithRetry(plan, options) {
2736
2795
  throw lastError ?? new ProviderError("gemini", "Gemini Code Assist request failed.");
2737
2796
  }
2738
2797
  function streamGemini(options) {
2739
- return new StreamResult(runStream4(options));
2798
+ return new StreamResult(runStream4(options), options.signal);
2740
2799
  }
2741
2800
  async function* runStream4(options) {
2742
2801
  const useStreaming = options.streaming !== false;
@@ -3192,7 +3251,7 @@ function registerPalsuProvider(config) {
3192
3251
  const stopReason = explicitStop ?? (hasToolCalls ? "tool_use" : "end_turn");
3193
3252
  return yield* simulateStream(message, stopReason, options.signal, cacheUsage);
3194
3253
  })();
3195
- return new StreamResult(gen);
3254
+ return new StreamResult(gen, options.signal);
3196
3255
  }
3197
3256
  });
3198
3257
  return handle;