@aipack-ai/multi-agent 0.0.1 → 1.0.0

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
@@ -345,6 +345,7 @@ var ExtensionManager = class {
345
345
  beforeRun: new AsyncSeriesWaterfallHook("beforeRun"),
346
346
  beforeTransform: new AsyncSeriesWaterfallHook("beforeTransform"),
347
347
  afterTransform: new AsyncSeriesWaterfallHook("afterTransform"),
348
+ beforeModelCall: new AsyncSeriesWaterfallHook("beforeModelCall"),
348
349
  beforeEmit: new AsyncSeriesHook("beforeEmit"),
349
350
  afterEmit: new AsyncSeriesHook("afterEmit"),
350
351
  done: new AsyncSeriesHook("done"),
@@ -768,6 +769,24 @@ function isContextOverflow(message, contextWindow) {
768
769
  return true;
769
770
  }
770
771
  }
772
+ if (message.stopReason === "length" && message.content) {
773
+ const blocks = Array.isArray(message.content) ? message.content : null;
774
+ if (blocks) {
775
+ const hasMeaningfulText = blocks.some(
776
+ (b) => b.type === "text" && typeof b.text === "string" && b.text.trim().length > 0
777
+ );
778
+ const hasToolCall = blocks.some((b) => b.type === "toolCall");
779
+ const hasThinking = blocks.some((b) => b.type === "thinking");
780
+ if (!hasMeaningfulText && !hasToolCall && hasThinking) {
781
+ return true;
782
+ }
783
+ }
784
+ }
785
+ if (message.maxTokens && message.maxTokens > 0 && message.stopReason === "length" && message.usage) {
786
+ if (message.usage.output >= Math.floor(message.maxTokens * 0.95)) {
787
+ return true;
788
+ }
789
+ }
771
790
  return false;
772
791
  }
773
792
 
@@ -1036,7 +1055,8 @@ var AgentRuntime = class _AgentRuntime {
1036
1055
  config: runtime._config,
1037
1056
  workspace: options.workspace ?? process.cwd(),
1038
1057
  sessionKey: runtime._sessionKey,
1039
- shared: /* @__PURE__ */ new Map()
1058
+ shared: /* @__PURE__ */ new Map(),
1059
+ runtime
1040
1060
  };
1041
1061
  runtime._extensionContext = ctx;
1042
1062
  runtime._extensions.applyAll(ctx);
@@ -1863,12 +1883,14 @@ ${text}`,
1863
1883
  /**
1864
1884
  * 单回合模型调用 + 上下文溢出自动恢复闭环。
1865
1885
  *
1866
- * 检测(isContextOverflow,统一传入 model.contextWindow,覆盖显式错误 /
1867
- * 静默溢出 / 截断溢出三模式)→ 丢弃失败的 assistant 消息 → 截断会话历史
1868
- * → 同回合重试(不消耗回合数,上限 OVERFLOW_RECOVERY_LIMIT):
1886
+ * 检测(isContextOverflow,统一传入 model.contextWindow / content / maxTokens,
1887
+ * 覆盖显式错误 / 静默溢出 / 输入截断溢出 / 输出 thinking 耗尽 / 输出打满 五模式)
1888
+ * → 丢弃失败的 assistant 消息 → 截断会话历史 → 同回合重试(不消耗回合数,
1889
+ * 上限 OVERFLOW_RECOVERY_LIMIT):
1869
1890
  *
1870
- * - 显式错误 / 零产出截断溢出:丢弃错误消息后重试;流式路径吞掉可恢复的
1871
- * error chunk(消费者看不到瞬态错误),不可恢复时补发。
1891
+ * - 显式错误 / 零产出截断溢出 / thinking 耗尽溢出 / 输出打满溢出:丢弃失败
1892
+ * 消息后重试;流式路径吞掉可恢复的 error chunk(消费者看不到瞬态错误),
1893
+ * 不可恢复时补发。
1872
1894
  * - 静默溢出(stop + 有完整产出):保留回复,仅压缩旧上下文供后续轮次。
1873
1895
  * - 恢复耗尽或单请求超窗(无可丢弃):返回最后一次错误消息,维持旧行为。
1874
1896
  *
@@ -1877,14 +1899,22 @@ ${text}`,
1877
1899
  */
1878
1900
  async *modelTurnWithRecovery(compilation, signal, sessionKey, stream) {
1879
1901
  const contextWindow = this._model.contextWindow;
1902
+ const maxTokens = this._model.maxTokens;
1880
1903
  let recoveries = 0;
1904
+ const toProbe = (m) => ({
1905
+ stopReason: m.stopReason,
1906
+ errorMessage: m.errorMessage,
1907
+ usage: m.usage,
1908
+ content: m.content,
1909
+ maxTokens
1910
+ });
1881
1911
  while (true) {
1882
1912
  let assistant = null;
1883
1913
  let suppressed = null;
1884
1914
  for await (const event of this.streamModelEvents(compilation, signal, stream)) {
1885
1915
  if (event.type === "error") {
1886
1916
  const msg = event.message;
1887
- if (recoveries < OVERFLOW_RECOVERY_LIMIT && isContextOverflow(msg, contextWindow)) {
1917
+ if (recoveries < OVERFLOW_RECOVERY_LIMIT && isContextOverflow(toProbe(msg), contextWindow)) {
1888
1918
  suppressed = msg;
1889
1919
  continue;
1890
1920
  }
@@ -1899,13 +1929,22 @@ ${text}`,
1899
1929
  }
1900
1930
  const final = assistant ?? suppressed;
1901
1931
  if (!final) return null;
1902
- if (isContextOverflow(final, contextWindow)) {
1903
- const failed = final.stopReason === "error" || (final.usage?.output ?? 0) === 0;
1932
+ if (isContextOverflow(toProbe(final), contextWindow)) {
1933
+ const output = final.usage?.output ?? 0;
1934
+ const blocks = Array.isArray(final.content) ? final.content : null;
1935
+ const hasMeaningfulText = blocks?.some(
1936
+ (b) => b.type === "text" && "text" in b && typeof b.text === "string" && b.text.trim().length > 0
1937
+ );
1938
+ const hasToolCall = blocks?.some((b) => b.type === "toolCall");
1939
+ const thinkingOnly = blocks ? !hasMeaningfulText && !hasToolCall && blocks.some((b) => b.type === "thinking") : false;
1940
+ const outputFull = maxTokens > 0 && output >= Math.floor(maxTokens * 0.95);
1941
+ const failed = final.stopReason === "error" || output === 0 || thinkingOnly || outputFull;
1904
1942
  if (failed && recoveries < OVERFLOW_RECOVERY_LIMIT) {
1905
1943
  recoveries += 1;
1906
1944
  if (await this.recoverFromOverflow(compilation, recoveries, sessionKey, signal)) {
1945
+ const reason = thinkingOnly ? "thinking \u8017\u5C3D" : outputFull ? "output \u6253\u6EE1" : final.stopReason;
1907
1946
  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`
1947
+ `[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
1948
  );
1910
1949
  await this.emitTelemetry("onRetry", {
1911
1950
  traceId: compilation.traceId,
@@ -2215,7 +2254,10 @@ ${text}`,
2215
2254
  });
2216
2255
  };
2217
2256
  try {
2218
- for await (const event of this._streamFn(this._model, this.buildContext(compilation.messages), options)) {
2257
+ const context = await this._hooks.beforeModelCall.promise(
2258
+ this.buildContext(compilation.messages)
2259
+ );
2260
+ for await (const event of this._streamFn(this._model, context, options)) {
2219
2261
  if (stream && event.type === "text_delta" && ttftAt === void 0) {
2220
2262
  ttftAt = Date.now();
2221
2263
  }