@aipack-ai/multi-agent 0.0.1 → 0.0.2

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
@@ -768,6 +768,24 @@ function isContextOverflow(message, contextWindow) {
768
768
  return true;
769
769
  }
770
770
  }
771
+ if (message.stopReason === "length" && message.content) {
772
+ const blocks = Array.isArray(message.content) ? message.content : null;
773
+ if (blocks) {
774
+ const hasMeaningfulText = blocks.some(
775
+ (b) => b.type === "text" && typeof b.text === "string" && b.text.trim().length > 0
776
+ );
777
+ const hasToolCall = blocks.some((b) => b.type === "toolCall");
778
+ const hasThinking = blocks.some((b) => b.type === "thinking");
779
+ if (!hasMeaningfulText && !hasToolCall && hasThinking) {
780
+ return true;
781
+ }
782
+ }
783
+ }
784
+ if (message.maxTokens && message.maxTokens > 0 && message.stopReason === "length" && message.usage) {
785
+ if (message.usage.output >= Math.floor(message.maxTokens * 0.95)) {
786
+ return true;
787
+ }
788
+ }
771
789
  return false;
772
790
  }
773
791
 
@@ -1863,12 +1881,14 @@ ${text}`,
1863
1881
  /**
1864
1882
  * 单回合模型调用 + 上下文溢出自动恢复闭环。
1865
1883
  *
1866
- * 检测(isContextOverflow,统一传入 model.contextWindow,覆盖显式错误 /
1867
- * 静默溢出 / 截断溢出三模式)→ 丢弃失败的 assistant 消息 → 截断会话历史
1868
- * → 同回合重试(不消耗回合数,上限 OVERFLOW_RECOVERY_LIMIT):
1884
+ * 检测(isContextOverflow,统一传入 model.contextWindow / content / maxTokens,
1885
+ * 覆盖显式错误 / 静默溢出 / 输入截断溢出 / 输出 thinking 耗尽 / 输出打满 五模式)
1886
+ * → 丢弃失败的 assistant 消息 → 截断会话历史 → 同回合重试(不消耗回合数,
1887
+ * 上限 OVERFLOW_RECOVERY_LIMIT):
1869
1888
  *
1870
- * - 显式错误 / 零产出截断溢出:丢弃错误消息后重试;流式路径吞掉可恢复的
1871
- * error chunk(消费者看不到瞬态错误),不可恢复时补发。
1889
+ * - 显式错误 / 零产出截断溢出 / thinking 耗尽溢出 / 输出打满溢出:丢弃失败
1890
+ * 消息后重试;流式路径吞掉可恢复的 error chunk(消费者看不到瞬态错误),
1891
+ * 不可恢复时补发。
1872
1892
  * - 静默溢出(stop + 有完整产出):保留回复,仅压缩旧上下文供后续轮次。
1873
1893
  * - 恢复耗尽或单请求超窗(无可丢弃):返回最后一次错误消息,维持旧行为。
1874
1894
  *
@@ -1877,14 +1897,22 @@ ${text}`,
1877
1897
  */
1878
1898
  async *modelTurnWithRecovery(compilation, signal, sessionKey, stream) {
1879
1899
  const contextWindow = this._model.contextWindow;
1900
+ const maxTokens = this._model.maxTokens;
1880
1901
  let recoveries = 0;
1902
+ const toProbe = (m) => ({
1903
+ stopReason: m.stopReason,
1904
+ errorMessage: m.errorMessage,
1905
+ usage: m.usage,
1906
+ content: m.content,
1907
+ maxTokens
1908
+ });
1881
1909
  while (true) {
1882
1910
  let assistant = null;
1883
1911
  let suppressed = null;
1884
1912
  for await (const event of this.streamModelEvents(compilation, signal, stream)) {
1885
1913
  if (event.type === "error") {
1886
1914
  const msg = event.message;
1887
- if (recoveries < OVERFLOW_RECOVERY_LIMIT && isContextOverflow(msg, contextWindow)) {
1915
+ if (recoveries < OVERFLOW_RECOVERY_LIMIT && isContextOverflow(toProbe(msg), contextWindow)) {
1888
1916
  suppressed = msg;
1889
1917
  continue;
1890
1918
  }
@@ -1899,13 +1927,22 @@ ${text}`,
1899
1927
  }
1900
1928
  const final = assistant ?? suppressed;
1901
1929
  if (!final) return null;
1902
- if (isContextOverflow(final, contextWindow)) {
1903
- const failed = final.stopReason === "error" || (final.usage?.output ?? 0) === 0;
1930
+ if (isContextOverflow(toProbe(final), contextWindow)) {
1931
+ const output = final.usage?.output ?? 0;
1932
+ const blocks = Array.isArray(final.content) ? final.content : null;
1933
+ const hasMeaningfulText = blocks?.some(
1934
+ (b) => b.type === "text" && "text" in b && typeof b.text === "string" && b.text.trim().length > 0
1935
+ );
1936
+ const hasToolCall = blocks?.some((b) => b.type === "toolCall");
1937
+ const thinkingOnly = blocks ? !hasMeaningfulText && !hasToolCall && blocks.some((b) => b.type === "thinking") : false;
1938
+ const outputFull = maxTokens > 0 && output >= Math.floor(maxTokens * 0.95);
1939
+ const failed = final.stopReason === "error" || output === 0 || thinkingOnly || outputFull;
1904
1940
  if (failed && recoveries < OVERFLOW_RECOVERY_LIMIT) {
1905
1941
  recoveries += 1;
1906
1942
  if (await this.recoverFromOverflow(compilation, recoveries, sessionKey, signal)) {
1943
+ const reason = thinkingOnly ? "thinking \u8017\u5C3D" : outputFull ? "output \u6253\u6EE1" : final.stopReason;
1907
1944
  console.warn(
1908
- `[Runtime] \u4E0A\u4E0B\u6587\u6EA2\u51FA\uFF0C\u5DF2\u538B\u7F29\u5386\u53F2\uFF08\u6458\u8981\u6216\u622A\u65AD\uFF09\u5E76\u540C\u56DE\u5408\u91CD\u8BD5\uFF08${recoveries}/${OVERFLOW_RECOVERY_LIMIT}\uFF09`
1945
+ `[Runtime] \u4E0A\u4E0B\u6587\u6EA2\u51FA\uFF08${reason}\uFF09\uFF0C\u5DF2\u538B\u7F29\u5386\u53F2\u5E76\u540C\u56DE\u5408\u91CD\u8BD5\uFF08${recoveries}/${OVERFLOW_RECOVERY_LIMIT}\uFF09`
1909
1946
  );
1910
1947
  await this.emitTelemetry("onRetry", {
1911
1948
  traceId: compilation.traceId,