@aipack-ai/multi-agent 1.1.6 → 1.1.8

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
@@ -57,6 +57,10 @@ function createSharedContext(init) {
57
57
  meta: init?.meta ?? {}
58
58
  };
59
59
  }
60
+ function storeOriginalInput(ctx, input) {
61
+ const inputText = typeof input === "string" ? input : input.message;
62
+ ctx.blackboard.set("__original_input__", inputText);
63
+ }
60
64
 
61
65
  // ../agent/core/types.ts
62
66
  function extractText(content) {
@@ -74,7 +78,50 @@ function createEmptyUsage() {
74
78
  return { input: 0, output: 0, total: 0 };
75
79
  }
76
80
 
81
+ // ../agent/core/tokens.ts
82
+ function estimateTextTokens(text) {
83
+ if (!text) return 0;
84
+ return Math.ceil(text.length / 4);
85
+ }
86
+ function estimateMessageTokens(message) {
87
+ const content = message.content;
88
+ if (typeof content === "string") return estimateTextTokens(content);
89
+ try {
90
+ return Math.ceil(JSON.stringify(content ?? []).length / 4);
91
+ } catch {
92
+ return 0;
93
+ }
94
+ }
95
+
96
+ // ../agent/core/errors.ts
97
+ var AgentErrorCategory = {
98
+ RETRYABLE: "retryable",
99
+ TIMEOUT: "timeout",
100
+ AUTH: "auth",
101
+ CONTEXT_OVERFLOW: "context-overflow",
102
+ RATE_LIMIT: "rate-limit",
103
+ INVALID_REQUEST: "invalid-request",
104
+ UNKNOWN: "unknown"
105
+ };
106
+
77
107
  // ../agent/core/tapable.ts
108
+ var tapFailurePolicy = "log";
109
+ var tapErrorHandler;
110
+ function setTapErrorHandler(handler) {
111
+ tapErrorHandler = handler;
112
+ }
113
+ function handleTapError(hook, tap, err) {
114
+ if (tapErrorHandler) {
115
+ try {
116
+ tapErrorHandler({ hook, tap: tap.name, error: err });
117
+ } catch {
118
+ }
119
+ }
120
+ if (tapFailurePolicy === "throw") throw err;
121
+ if (tapFailurePolicy === "log") {
122
+ console.warn(`[aipack] extension tap "${tap.name}" on hook "${hook}" \u5931\u8D25:`, err);
123
+ }
124
+ }
78
125
  var AsyncSeriesHook = class {
79
126
  constructor(name) {
80
127
  this.name = name;
@@ -90,6 +137,7 @@ var AsyncSeriesHook = class {
90
137
  try {
91
138
  await tap.fn(...args);
92
139
  } catch (err) {
140
+ handleTapError(this.name, tap, err);
93
141
  }
94
142
  }
95
143
  }
@@ -116,6 +164,7 @@ var AsyncSeriesWaterfallHook = class {
116
164
  try {
117
165
  current = await tap.fn(current, ...rest);
118
166
  } catch (err) {
167
+ handleTapError(this.name, tap, err);
119
168
  }
120
169
  }
121
170
  return current;
@@ -602,15 +651,6 @@ function resourcesToMessages(resources) {
602
651
  import { Type } from "@sinclair/typebox";
603
652
 
604
653
  // ../agent/ai/errors.ts
605
- var AgentErrorCategory = {
606
- RETRYABLE: "retryable",
607
- TIMEOUT: "timeout",
608
- AUTH: "auth",
609
- CONTEXT_OVERFLOW: "context-overflow",
610
- RATE_LIMIT: "rate-limit",
611
- INVALID_REQUEST: "invalid-request",
612
- UNKNOWN: "unknown"
613
- };
614
654
  var RETRYABLE_CATEGORIES = /* @__PURE__ */ new Set([
615
655
  AgentErrorCategory.RETRYABLE,
616
656
  AgentErrorCategory.RATE_LIMIT
@@ -798,62 +838,7 @@ function isContextOverflow(message, contextWindow) {
798
838
  return false;
799
839
  }
800
840
 
801
- // ../agent/transformer/index.ts
802
- function ensureToolPairing(messages) {
803
- const toolCallIds = /* @__PURE__ */ new Set();
804
- const toolResultIds = /* @__PURE__ */ new Set();
805
- for (const msg of messages) {
806
- if (msg.role === "assistant") {
807
- const content = msg.content;
808
- if (Array.isArray(content)) {
809
- for (const tc of extractToolCalls(content)) {
810
- toolCallIds.add(tc.id);
811
- }
812
- }
813
- }
814
- if (msg.role === "toolResult") {
815
- toolResultIds.add(msg.toolCallId);
816
- }
817
- }
818
- const orphanedCalls = /* @__PURE__ */ new Set();
819
- const orphanedResults = /* @__PURE__ */ new Set();
820
- for (const id of toolCallIds) {
821
- if (!toolResultIds.has(id)) orphanedCalls.add(id);
822
- }
823
- for (const id of toolResultIds) {
824
- if (!toolCallIds.has(id)) orphanedResults.add(id);
825
- }
826
- if (orphanedCalls.size === 0 && orphanedResults.size === 0) {
827
- return messages;
828
- }
829
- const result = [];
830
- for (const msg of messages) {
831
- if (msg.role === "assistant") {
832
- const content = msg.content;
833
- if (Array.isArray(content)) {
834
- const filteredContent = content.filter((block) => {
835
- if (block.type === "toolCall") {
836
- return !orphanedCalls.has(block.id);
837
- }
838
- return true;
839
- });
840
- if (filteredContent.length === 0 && content.length > 0) continue;
841
- result.push({ ...msg, content: filteredContent });
842
- } else {
843
- result.push(msg);
844
- }
845
- } else if (msg.role === "toolResult") {
846
- if (!orphanedResults.has(msg.toolCallId)) {
847
- result.push(msg);
848
- }
849
- } else {
850
- result.push(msg);
851
- }
852
- }
853
- return result;
854
- }
855
-
856
- // ../agent/runtime/index.ts
841
+ // ../agent/runtime/shared.ts
857
842
  import { randomUUID } from "crypto";
858
843
  function newTraceId() {
859
844
  return `${Date.now().toString(36)}-${randomUUID()}`;
@@ -925,19 +910,25 @@ function buildImageContent(media) {
925
910
  }
926
911
  return { type: "image", mimeType: "image/url", data: media };
927
912
  }
928
- var OVERFLOW_RECOVERY_LIMIT = 2;
929
- function estimateMessageTokens(message) {
930
- const content = message.content;
931
- if (typeof content === "string") return Math.ceil(content.length / 4);
932
- try {
933
- return Math.ceil(JSON.stringify(content ?? []).length / 4);
934
- } catch {
935
- return 0;
913
+ function buildUserMessage(request) {
914
+ if (request.media && request.media.length > 0) {
915
+ const blocks = [
916
+ createTextContent(request.message),
917
+ ...request.media.filter(Boolean).map(buildImageContent)
918
+ ];
919
+ return {
920
+ role: "user",
921
+ content: blocks,
922
+ timestamp: Date.now()
923
+ };
936
924
  }
925
+ return {
926
+ role: "user",
927
+ content: request.message,
928
+ timestamp: Date.now()
929
+ };
937
930
  }
938
- function estimateTextTokens(text) {
939
- return Math.ceil(text.length / 4);
940
- }
931
+ var OVERFLOW_RECOVERY_LIMIT = 2;
941
932
  var COMPACTION_SUMMARY_BUDGET_RATIO = 0.6;
942
933
  var COMPACTION_LINE_CLAMP = 4e3;
943
934
  var DEFAULT_COMPACTION_PROMPT = [
@@ -988,150 +979,146 @@ function messageToSummaryLine(msg) {
988
979
  }
989
980
  }
990
981
  }
991
- var AgentRuntime = class _AgentRuntime {
992
- _config;
993
- _extensions;
994
- _hooks;
995
- /** 上下文转换器列表,按数组顺序链式执行(上一个输出作为下一个输入) */
996
- _transformers;
997
- _model;
998
- _streamFn;
999
- _systemPrompt;
1000
- _thinkingLevel;
1001
- _globalTools = /* @__PURE__ */ new Map();
1002
- /** 多会话状态表:key = sessionKey。模型/工具/扩展/转换器等资源跨会话共享 */
1003
- _sessions;
1004
- /** 默认会话 key(常量 'default';请求未指定 sessionKey 时路由到此会话) */
1005
- _sessionKey;
1006
- /** 内存会话状态表 LRU 上限 */
1007
- _maxSessions;
1008
- _sessionStorage;
1009
- /** Extension 应用时的上下文(shared Map 供 ToolCallContext 引用) */
1010
- _extensionContext;
1011
- _maxTurns;
1012
- _toolTimeoutMs;
1013
- _parallelToolCalls;
1014
- _contextBudgetRatio;
1015
- /** 内置摘要压缩配置(未配置 = 保持旧行为,仅硬截断兜底) */
1016
- _compaction;
1017
- _telemetry;
1018
- /** 框架级工具权限策略(未配置 → 放行,向后兼容) */
1019
- _permissionPolicy;
1020
- /** 审批管理器(pending 决策挂起等待外部批准;未配置 → pending 视为 deny) */
1021
- _approvals;
1022
- /** 审批等待超时(毫秒) */
1023
- _approvalTimeoutMs;
1024
- /** traceId 生成器(测试可注入确定性 id) */
1025
- _traceIdGenerator;
1026
- constructor(options) {
1027
- this._config = options.config ?? {};
1028
- this._extensions = new ExtensionManager();
1029
- this._hooks = this._extensions.getHooks();
1030
- this._transformers = [...options.transformers ?? []];
1031
- this._model = options.model ?? {
1032
- id: "unknown",
1033
- name: "unknown",
1034
- provider: "unknown",
1035
- contextWindow: 128e3,
1036
- maxTokens: 8192,
1037
- reasoning: false
1038
- };
1039
- this._streamFn = options.streamFn ?? (async function* () {
1040
- throw new Error("streamFn \u672A\u8BBE\u7F6E\uFF0C\u8BF7\u901A\u8FC7 setStreamFn() \u6216 RuntimeOptions.streamFn \u63D0\u4F9B");
1041
- });
1042
- this._systemPrompt = options.systemPrompt ?? "";
1043
- this._thinkingLevel = options.thinkingLevel ?? "off";
1044
- this._sessionStorage = options.sessionStorage;
1045
- this._maxTurns = options.maxTurns ?? 50;
1046
- this._toolTimeoutMs = options.toolTimeoutMs ?? 12e4;
1047
- this._parallelToolCalls = options.parallelToolCalls ?? true;
1048
- this._contextBudgetRatio = options.contextBudgetRatio ?? 0.8;
1049
- this._compaction = options.compaction;
1050
- this._telemetry = options.telemetry;
1051
- this._permissionPolicy = options.permissionPolicy;
1052
- this._approvals = options.approvals;
1053
- this._approvalTimeoutMs = options.approvalTimeoutMs ?? 3e5;
1054
- this._traceIdGenerator = options.traceIdGenerator;
1055
- if (options.tools) {
1056
- for (const tool of options.tools) {
1057
- this._globalTools.set(tool.name, tool);
982
+ function buildResult(compilation) {
983
+ const messages = compilation.messages;
984
+ let content = "";
985
+ let stopReason = "completed";
986
+ let error;
987
+ const toolsUsed = [];
988
+ const usage = {};
989
+ for (const msg of messages) {
990
+ if (msg.role === "assistant") {
991
+ const assistant = msg;
992
+ content = extractText(assistant.content);
993
+ stopReason = assistant.stopReason ?? "completed";
994
+ error = assistant.errorMessage;
995
+ if (assistant.usage) {
996
+ usage.input = (usage.input ?? 0) + assistant.usage.input;
997
+ usage.output = (usage.output ?? 0) + assistant.usage.output;
998
+ usage.total = (usage.total ?? 0) + assistant.usage.total;
999
+ if (assistant.usage.cacheRead) usage.cacheRead = (usage.cacheRead ?? 0) + assistant.usage.cacheRead;
1000
+ if (assistant.usage.cacheWrite) usage.cacheWrite = (usage.cacheWrite ?? 0) + assistant.usage.cacheWrite;
1058
1001
  }
1059
1002
  }
1060
- this._sessionKey = "default";
1061
- this._maxSessions = options.maxSessions ?? DEFAULT_MAX_SESSIONS;
1062
- this._sessions = /* @__PURE__ */ new Map([[this._sessionKey, createSessionState()]]);
1063
- }
1064
- // ─── 静态工厂 ───────────────────────────────────────────────────
1065
- static create(options = {}) {
1066
- const runtime = new _AgentRuntime(options);
1067
- if (options.extensions) {
1068
- runtime._extensions.registerAll(options.extensions);
1003
+ if (msg.role === "toolResult") {
1004
+ const toolMsg = msg;
1005
+ if (!toolsUsed.includes(toolMsg.toolName)) {
1006
+ toolsUsed.push(toolMsg.toolName);
1007
+ }
1069
1008
  }
1070
- const ctx = {
1071
- config: runtime._config,
1072
- workspace: options.workspace ?? process.cwd(),
1073
- sessionKey: runtime._sessionKey,
1074
- shared: /* @__PURE__ */ new Map(),
1075
- runtime
1076
- };
1077
- runtime._extensionContext = ctx;
1078
- runtime._extensions.applyAll(ctx);
1079
- return runtime;
1080
1009
  }
1081
- // ─── Runtime 接口实现 ───────────────────────────────────────────
1082
- get config() {
1083
- return this._config;
1010
+ const resources = messagesToResources(messages);
1011
+ const builder = new ResultBuilder().content(content).toolsUsed(toolsUsed).usage(usage).stopReason(stopReason).error(error).resources(resources).metadata("traceId", compilation.traceId);
1012
+ if (compilation.terminateReason) {
1013
+ builder.stopReason("terminated").metadata("terminateReason", compilation.terminateReason);
1084
1014
  }
1085
- get extensions() {
1086
- return this._extensions;
1015
+ if (compilation.maxTurnsExhausted) {
1016
+ builder.stopReason("max_turns").metadata("maxTurns", true);
1087
1017
  }
1088
- get hooks() {
1089
- return this._hooks;
1018
+ return builder.build();
1019
+ }
1020
+ function streamEventToChunk(event) {
1021
+ switch (event.type) {
1022
+ case "text_delta":
1023
+ return { type: "text", content: event.delta };
1024
+ case "thinking_delta":
1025
+ return { type: "thinking", content: event.delta };
1026
+ case "error":
1027
+ return {
1028
+ type: "error",
1029
+ content: event.message.errorMessage || "\u6A21\u578B\u8C03\u7528\u51FA\u9519"
1030
+ };
1031
+ default:
1032
+ return null;
1090
1033
  }
1091
- // ─── 工具/模型/流管理 ───────────────────────────────────────────
1092
- registerTool(tool) {
1093
- if (this._globalTools.has(tool.name)) {
1094
- console.warn(`[Runtime] \u5DE5\u5177 "${tool.name}" \u5DF2\u5B58\u5728\uFF0C\u5C06\u88AB\u8986\u76D6`);
1095
- }
1096
- this._globalTools.set(tool.name, tool);
1097
- return this;
1034
+ }
1035
+
1036
+ // ../agent/runtime/session-store.ts
1037
+ var SessionStore = class {
1038
+ /** 多会话状态表:key = sessionKey。模型/工具/扩展/转换器等资源跨会话共享 */
1039
+ _sessions;
1040
+ /** 内存会话状态表 LRU 上限 */
1041
+ _maxSessions;
1042
+ /** 默认会话 key(常量 'default';请求未指定 sessionKey 时路由到此会话) */
1043
+ defaultKey;
1044
+ constructor(defaultKey, maxSessions = DEFAULT_MAX_SESSIONS) {
1045
+ this.defaultKey = defaultKey;
1046
+ this._maxSessions = maxSessions;
1047
+ this._sessions = /* @__PURE__ */ new Map();
1048
+ this._sessions.set(defaultKey, createSessionState());
1098
1049
  }
1099
- registerTools(tools) {
1100
- for (const tool of tools) {
1101
- this.registerTool(tool);
1050
+ /**
1051
+ * 获取(懒创建)会话状态。同一 Runtime 下不同 sessionKey 的消息历史、
1052
+ * 串行队列、abort 控制相互独立;共享模型/工具/扩展/转换器。
1053
+ * 超过 maxSessions 时淘汰最久未用的非活动会话(仅清内存态,不删存储)。
1054
+ */
1055
+ ensure(key) {
1056
+ let session = this._sessions.get(key);
1057
+ if (session) {
1058
+ this._sessions.delete(key);
1059
+ this._sessions.set(key, session);
1060
+ return session;
1102
1061
  }
1103
- return this;
1104
- }
1105
- unregisterTool(name) {
1106
- return this._globalTools.delete(name);
1107
- }
1108
- setModel(model) {
1109
- this._model = model;
1110
- return this;
1062
+ session = createSessionState();
1063
+ this._sessions.set(key, session);
1064
+ this.evictIdleSessions();
1065
+ return session;
1111
1066
  }
1112
- setSystemPrompt(prompt) {
1113
- this._systemPrompt = prompt;
1114
- return this;
1067
+ /** 只读访问(不创建、不刷新 LRU;会话不存在返回 undefined) */
1068
+ peek(key) {
1069
+ return this._sessions.get(key);
1115
1070
  }
1116
- setThinkingLevel(level) {
1117
- this._thinkingLevel = level;
1118
- return this;
1071
+ /** LRU 淘汰:仅淘汰非活动(未运行、未排队)的最久未用会话 */
1072
+ evictIdleSessions() {
1073
+ if (this._sessions.size <= this._maxSessions) return;
1074
+ for (const [key, session] of this._sessions) {
1075
+ if (this._sessions.size <= this._maxSessions) break;
1076
+ if (session.isStreaming || session.lockHeld) continue;
1077
+ if (session.idleResolvers.length > 0) {
1078
+ const resolvers = session.idleResolvers.splice(0);
1079
+ for (const resolve of resolvers) resolve();
1080
+ }
1081
+ this._sessions.delete(key);
1082
+ }
1119
1083
  }
1120
- setStreamFn(fn) {
1121
- this._streamFn = fn;
1122
- return this;
1084
+ /** 当前活跃的会话 key 列表(含默认会话) */
1085
+ keys() {
1086
+ return Array.from(this._sessions.keys());
1123
1087
  }
1124
- registerExtension(extension) {
1125
- this._extensions.register(extension);
1126
- return this;
1088
+ /** 某会话是否存在(内存中) */
1089
+ has(key) {
1090
+ return this._sessions.has(key);
1127
1091
  }
1128
- useTransformer(transformer) {
1129
- this._transformers.push(transformer);
1130
- return this;
1092
+ /**
1093
+ * 获取同一会话的执行锁:返回 release 函数,调用后释放。
1094
+ * 同一 sessionKey 的 run/stream 会串行执行,避免消息数组交错、
1095
+ * abortController 互相覆盖、hydrate 竞态。
1096
+ */
1097
+ async acquire(session) {
1098
+ let release;
1099
+ const prev = session.queue;
1100
+ session.queue = new Promise((resolve) => {
1101
+ release = () => resolve();
1102
+ });
1103
+ await prev;
1104
+ session.lockHeld = true;
1105
+ return () => {
1106
+ session.lockHeld = false;
1107
+ release();
1108
+ };
1131
1109
  }
1132
- /** 获取指定会话的消息列表(默认会话;会话不存在返回空数组) */
1133
- getMessages(sessionKey) {
1134
- const session = this._sessions.get(sessionKey ?? this._sessionKey);
1110
+ /** 标记会话空闲并唤醒所有 waitForIdle 等待者 */
1111
+ markIdle(session) {
1112
+ session.isStreaming = false;
1113
+ session.abortController = null;
1114
+ if (session.idleResolvers.length > 0) {
1115
+ const resolvers = session.idleResolvers.splice(0);
1116
+ for (const resolve of resolvers) resolve();
1117
+ }
1118
+ }
1119
+ /** 指定会话的消息列表深拷贝(避免外部直接修改会话内部状态;不存在返回空数组) */
1120
+ getMessagesCopy(key) {
1121
+ const session = this._sessions.get(key);
1135
1122
  if (!session) return [];
1136
1123
  const messages = session.messages;
1137
1124
  try {
@@ -1140,372 +1127,253 @@ var AgentRuntime = class _AgentRuntime {
1140
1127
  return JSON.parse(JSON.stringify(messages));
1141
1128
  }
1142
1129
  }
1143
- // ─── 核心运行逻辑 ───────────────────────────────────────────────
1144
- /** 解析请求路由的会话 key:request.sessionKey ?? 默认会话 key */
1145
- resolveSessionKey(request) {
1146
- return request.sessionKey ?? this._sessionKey;
1130
+ /**
1131
+ * 指定会话消息的只读视图(高频轮询用):仅浅拷贝数组(O(n) 引用,
1132
+ * 不做 structuredClone 深拷贝),消息对象与内部共享——调用方不得修改,
1133
+ * 需要可变副本时用 getMessagesCopy。
1134
+ */
1135
+ peekMessages(key) {
1136
+ return this._sessions.get(key)?.messages.slice() ?? [];
1137
+ }
1138
+ /** 等待指定会话空闲(基于 promise,无轮询;会话不存在或非运行中直接返回) */
1139
+ async waitForIdle(key, timeoutMs) {
1140
+ const session = this._sessions.get(key);
1141
+ if (!session || !session.isStreaming) return;
1142
+ if (timeoutMs === void 0) {
1143
+ await new Promise((resolve) => {
1144
+ session.idleResolvers.push(resolve);
1145
+ });
1146
+ return;
1147
+ }
1148
+ await new Promise((resolve, reject) => {
1149
+ const resolver = () => {
1150
+ clearTimeout(timer);
1151
+ resolve();
1152
+ };
1153
+ const timer = setTimeout(() => {
1154
+ const i = session.idleResolvers.indexOf(resolver);
1155
+ if (i >= 0) session.idleResolvers.splice(i, 1);
1156
+ reject(new Error(
1157
+ `[Runtime] waitForIdle \u8D85\u65F6\uFF08${timeoutMs}ms\uFF09: ${key}`
1158
+ ));
1159
+ }, timeoutMs);
1160
+ session.idleResolvers.push(resolver);
1161
+ });
1162
+ }
1163
+ /** 清除指定会话消息(仅内存;下次 run 会从存储恢复) */
1164
+ clearMessages(key) {
1165
+ const session = this._sessions.get(key);
1166
+ if (!session) return;
1167
+ session.messages = [];
1168
+ session.hydrated = false;
1147
1169
  }
1148
1170
  /**
1149
- * 获取(懒创建)会话状态。同一 Runtime 下不同 sessionKey 的消息历史、
1150
- * 串行队列、abort 控制相互独立;共享模型/工具/扩展/转换器。
1151
- * 超过 _maxSessions 时淘汰最久未用的非活动会话(仅清内存态,不删存储)。
1171
+ * 删除指定会话(仅内存态):等待在途任务完成后移除,返回被移除的会话。
1152
1172
  */
1153
- getSession(key) {
1154
- let session = this._sessions.get(key);
1173
+ async remove(key) {
1174
+ const session = this._sessions.get(key);
1155
1175
  if (session) {
1176
+ await session.queue;
1156
1177
  this._sessions.delete(key);
1157
- this._sessions.set(key, session);
1158
- return session;
1159
1178
  }
1160
- session = createSessionState();
1161
- this._sessions.set(key, session);
1162
- this.evictIdleSessions();
1163
1179
  return session;
1164
1180
  }
1165
- /** LRU 淘汰:仅淘汰非活动(未运行、未排队)的最久未用会话 */
1166
- evictIdleSessions() {
1167
- if (this._sessions.size <= this._maxSessions) return;
1168
- for (const [key, session] of this._sessions) {
1169
- if (this._sessions.size <= this._maxSessions) break;
1170
- if (session.isStreaming || session.lockHeld) continue;
1171
- this._sessions.delete(key);
1181
+ /** 关闭用:等待所有会话的在途任务完成后清空状态表 */
1182
+ async drainAndClear() {
1183
+ await Promise.allSettled(Array.from(this._sessions.values(), (s) => s.queue));
1184
+ for (const session of this._sessions.values()) {
1185
+ session.messages = [];
1186
+ session.hydrated = false;
1172
1187
  }
1188
+ this._sessions.clear();
1173
1189
  }
1174
- /** 当前活跃的会话 key 列表(含默认会话) */
1175
- getSessionKeys() {
1176
- return Array.from(this._sessions.keys());
1190
+ };
1191
+
1192
+ // ../agent/runtime/session-persistence.ts
1193
+ var SessionPersistence = class {
1194
+ constructor(_storage, _defaultModel, _store) {
1195
+ this._storage = _storage;
1196
+ this._defaultModel = _defaultModel;
1197
+ this._store = _store;
1198
+ }
1199
+ _storage;
1200
+ _defaultModel;
1201
+ _store;
1202
+ get storage() {
1203
+ return this._storage;
1177
1204
  }
1178
- /** 某会话是否存在(内存中) */
1179
- hasSession(sessionKey) {
1180
- return this._sessions.has(sessionKey);
1205
+ /** 从存储懒加载会话(串行化后无竞态,每个会话仅恢复一次) */
1206
+ async hydrate(sessionKey, session) {
1207
+ if (!this._storage) return;
1208
+ if (session.hydrated) return;
1209
+ session.hydrated = true;
1210
+ const stored = await this._storage.load(sessionKey);
1211
+ if (!stored) return;
1212
+ session.messages = stored.messages;
1213
+ session.createdAt = stored.createdAt;
1181
1214
  }
1182
- async run(request) {
1183
- const validation = validateRequest(request);
1184
- if (!validation.valid) {
1185
- const invalidResult = new ResultBuilder().error(`\u8BF7\u6C42\u6821\u9A8C\u5931\u8D25: ${validation.errors.join("; ")}`).build();
1186
- await this.emitTelemetry("onRunEnd", {
1187
- traceId: this.newTraceId(),
1188
- sessionKey: this.resolveSessionKey(request),
1189
- request,
1190
- durationMs: 0,
1191
- activeMs: 0,
1192
- queuedMs: 0,
1193
- turnCount: 0,
1194
- result: invalidResult,
1195
- success: false,
1196
- errorClass: "validation",
1197
- tokens: { input: 0, output: 0 }
1198
- });
1199
- return invalidResult;
1200
- }
1201
- const finalRequest = normalizeRequest(request);
1202
- const sessionKey = this.resolveSessionKey(finalRequest);
1203
- const traceId = this.newTraceId();
1204
- const queuedAt = Date.now();
1205
- await this.emitTelemetry("onRunStart", {
1206
- traceId,
1207
- sessionKey,
1208
- request: finalRequest,
1209
- queuedAt
1210
- });
1211
- const session = this.getSession(sessionKey);
1212
- const release = await this.acquire(session);
1213
- const queuedMs = Date.now() - queuedAt;
1215
+ /** 整体保存指定会话(ephemeral 跳过;失败不影响运行结果) */
1216
+ async persist(sessionKey, session) {
1217
+ if (!this._storage) return;
1218
+ const stored = {
1219
+ key: sessionKey,
1220
+ version: SESSION_VERSION,
1221
+ messages: session.messages,
1222
+ model: this.deriveModel(session.messages),
1223
+ usage: this.sumUsage(session.messages),
1224
+ createdAt: session.createdAt,
1225
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
1226
+ };
1227
+ await this._storage.save(sessionKey, stored);
1228
+ }
1229
+ /**
1230
+ * 实时持久化指定会话:每轮 assistant 回复/工具结果完成后调用,
1231
+ * 让运行中的会话随时可被持久化数据观测到。ephemeral 跳过;
1232
+ * 存储失败仅告警,不影响对话循环继续。
1233
+ */
1234
+ async persistSafe(request, sessionKey) {
1235
+ if (request.ephemeral || !this._storage) return;
1214
1236
  try {
1215
- return await this.runWithStorageLock(
1216
- finalRequest,
1217
- sessionKey,
1218
- () => this._run(finalRequest, sessionKey, session, traceId, queuedMs)
1219
- );
1220
- } finally {
1221
- release();
1237
+ const session = this._store.peek(sessionKey);
1238
+ if (!session) return;
1239
+ await this.persist(sessionKey, session);
1240
+ } catch (err) {
1241
+ console.warn("[Runtime] \u4F1A\u8BDD\u6301\u4E45\u5316\u5931\u8D25:", err?.message);
1222
1242
  }
1223
1243
  }
1224
- async *stream(request) {
1225
- const validation = validateRequest(request);
1226
- if (!validation.valid) {
1227
- const message = `\u8BF7\u6C42\u6821\u9A8C\u5931\u8D25: ${validation.errors.join("; ")}`;
1228
- await this.emitTelemetry("onRunEnd", {
1229
- traceId: this.newTraceId(),
1230
- sessionKey: this.resolveSessionKey(request),
1231
- request,
1232
- durationMs: 0,
1233
- activeMs: 0,
1234
- queuedMs: 0,
1235
- turnCount: 0,
1236
- result: new ResultBuilder().error(message).build(),
1237
- success: false,
1238
- errorClass: "validation",
1239
- tokens: { input: 0, output: 0 }
1240
- });
1241
- yield { type: "error", content: message };
1242
- yield { type: "done" };
1243
- return;
1244
+ /** 从消息中推导最后使用的模型 */
1245
+ deriveModel(messages) {
1246
+ for (let i = messages.length - 1; i >= 0; i--) {
1247
+ const msg = messages[i];
1248
+ if (msg.role === "assistant" && msg.model) {
1249
+ return {
1250
+ provider: msg.provider ?? this._defaultModel().provider,
1251
+ modelId: msg.model
1252
+ };
1253
+ }
1244
1254
  }
1245
- const finalRequest = normalizeRequest(request);
1246
- const sessionKey = this.resolveSessionKey(finalRequest);
1247
- const traceId = this.newTraceId();
1248
- const queuedAt = Date.now();
1249
- await this.emitTelemetry("onRunStart", {
1250
- traceId,
1251
- sessionKey,
1252
- request: finalRequest,
1253
- queuedAt
1254
- });
1255
- const session = this.getSession(sessionKey);
1256
- const release = await this.acquire(session);
1257
- const queuedMs = Date.now() - queuedAt;
1258
- try {
1259
- yield* this.streamWithStorageLock(
1260
- finalRequest,
1261
- sessionKey,
1262
- () => this._stream(finalRequest, sessionKey, session, traceId, queuedMs)
1263
- );
1264
- } finally {
1265
- release();
1255
+ return null;
1256
+ }
1257
+ /** 汇总所有 assistant 消息的 token 用量(含 cache) */
1258
+ sumUsage(messages) {
1259
+ const usage = createEmptyUsage();
1260
+ for (const msg of messages) {
1261
+ if (msg.role === "assistant") {
1262
+ const u = msg.usage;
1263
+ if (u) {
1264
+ usage.input += u.input;
1265
+ usage.output += u.output;
1266
+ usage.total += u.total;
1267
+ usage.cacheRead = (usage.cacheRead ?? 0) + (u.cacheRead ?? 0);
1268
+ usage.cacheWrite = (usage.cacheWrite ?? 0) + (u.cacheWrite ?? 0);
1269
+ usage.reasoning = (usage.reasoning ?? 0) + (u.reasoning ?? 0);
1270
+ }
1271
+ }
1266
1272
  }
1273
+ return usage;
1267
1274
  }
1268
1275
  /**
1269
1276
  * 非 ephemeral 请求在"读(load)-改(run)-写(save)"全程持有存储级锁,
1270
1277
  * 防止多进程并发写同一会话导致 last-write-wins 丢消息。
1271
1278
  * ephemeral / 无锁支持 / 无存储时直接执行。
1272
1279
  */
1273
- async runWithStorageLock(request, sessionKey, fn) {
1274
- const storage = this._sessionStorage;
1275
- if (request.ephemeral || !storage?.withLock) return fn();
1276
- return storage.withLock(sessionKey, fn);
1280
+ async withStorageLock(request, sessionKey, fn) {
1281
+ if (request.ephemeral || !this._storage?.withLock) return fn();
1282
+ return this._storage.withLock(sessionKey, fn);
1277
1283
  }
1278
1284
  /** 流式版本:无法用回调包住生成器,改用手动锁(acquire/release) */
1279
1285
  async *streamWithStorageLock(request, sessionKey, gen) {
1280
- const storage = this._sessionStorage;
1281
- if (request.ephemeral || !storage?.acquireLock) {
1286
+ if (request.ephemeral || !this._storage?.acquireLock) {
1282
1287
  yield* gen();
1283
1288
  return;
1284
1289
  }
1285
- const lock = await storage.acquireLock(sessionKey);
1290
+ const lock = await this._storage.acquireLock(sessionKey);
1286
1291
  try {
1287
1292
  yield* gen();
1288
1293
  } finally {
1289
1294
  await lock.release();
1290
1295
  }
1291
1296
  }
1292
- async _run(request, sessionKey, session, traceId, queuedMs) {
1293
- const activeStartedAt = Date.now();
1294
- await this._hooks.beforeInitialize.promise(request);
1295
- await this._hooks.afterInitialize.promise(request);
1296
- const finalRequest = await this._hooks.beforeRun.promise(request);
1297
- if (!finalRequest.ephemeral) {
1298
- await this.hydrateSession(sessionKey, session);
1299
- }
1300
- const compilation = this.createCompilation(finalRequest, sessionKey, session, traceId);
1301
- compilation.messages.push(this.buildUserMessage(finalRequest));
1297
+ };
1298
+
1299
+ // ../agent/runtime/telemetry.ts
1300
+ var RuntimeTelemetry = class {
1301
+ constructor(_telemetry, _traceIdGenerator) {
1302
+ this._telemetry = _telemetry;
1303
+ this._traceIdGenerator = _traceIdGenerator;
1304
+ }
1305
+ _telemetry;
1306
+ _traceIdGenerator;
1307
+ /** traceId 生成:优先用注入的生成器(测试可确定性) */
1308
+ newTraceId() {
1309
+ return this._traceIdGenerator ? this._traceIdGenerator() : newTraceId();
1310
+ }
1311
+ /**
1312
+ * 触发遥测回调。全可选、失败不阻断主流程。
1313
+ */
1314
+ async emit(event, info) {
1315
+ const fn = this._telemetry?.[event];
1316
+ if (!fn) return;
1302
1317
  try {
1303
- await this.runLoop(compilation, finalRequest, session);
1304
- const result = this.buildResult(compilation);
1305
- await this._hooks.beforeEmit.promise(result);
1306
- await this._hooks.afterEmit.promise(result);
1307
- await this._hooks.done.promise(result, finalRequest);
1308
- compilation.completed = true;
1309
- await this.emitRunEnd(finalRequest, sessionKey, compilation, result, queuedMs, activeStartedAt);
1310
- return result;
1318
+ await Promise.resolve(fn(info));
1311
1319
  } catch (err) {
1312
- const error = err;
1313
- if (error?.name !== "AbortError") {
1314
- console.error("[Runtime] \u8FD0\u884C\u5931\u8D25:", error?.stack ?? error);
1315
- }
1316
- await this._hooks.failed.promise(error, finalRequest);
1317
- const result = new ResultBuilder().error(error.message).build();
1318
- await this.emitRunEnd(finalRequest, sessionKey, compilation, result, queuedMs, activeStartedAt);
1319
- return result;
1320
- } finally {
1321
- await this.persistSessionSafe(finalRequest, sessionKey);
1322
- }
1323
- }
1324
- async *_stream(request, sessionKey, session, traceId, queuedMs) {
1325
- const activeStartedAt = Date.now();
1326
- await this._hooks.beforeInitialize.promise(request);
1327
- await this._hooks.afterInitialize.promise(request);
1328
- const finalRequest = await this._hooks.beforeRun.promise(request);
1329
- if (!finalRequest.ephemeral) {
1330
- await this.hydrateSession(sessionKey, session);
1331
- }
1332
- const compilation = this.createCompilation(finalRequest, sessionKey, session, traceId);
1333
- compilation.messages.push(this.buildUserMessage(finalRequest));
1334
- try {
1335
- for await (const chunk of this.runLoopStream(compilation, finalRequest, session)) {
1336
- yield chunk;
1337
- }
1338
- const result = this.buildResult(compilation);
1339
- await this._hooks.beforeEmit.promise(result);
1340
- await this._hooks.afterEmit.promise(result);
1341
- await this._hooks.done.promise(result, finalRequest);
1342
- yield { type: "done", result };
1343
- await this.emitRunEnd(finalRequest, sessionKey, compilation, result, queuedMs, activeStartedAt);
1344
- } catch (err) {
1345
- const error = err;
1346
- if (error?.name !== "AbortError") {
1347
- console.error("[Runtime] \u6D41\u5F0F\u8FD0\u884C\u5931\u8D25:", error?.stack ?? error);
1348
- }
1349
- await this._hooks.failed.promise(error, finalRequest);
1350
- const result = new ResultBuilder().error(error.message).build();
1351
- yield { type: "error", content: error.message };
1352
- yield { type: "done", result };
1353
- await this.emitRunEnd(finalRequest, sessionKey, compilation, result, queuedMs, activeStartedAt);
1354
- } finally {
1355
- await this.persistSessionSafe(finalRequest, sessionKey);
1356
- }
1357
- }
1358
- createCompilation(request, sessionKey, session, traceId) {
1359
- return {
1360
- request,
1361
- graph: createTaskGraph(),
1362
- resources: [],
1363
- messages: (session ?? this.getSession(sessionKey ?? this._sessionKey)).messages,
1364
- completed: false,
1365
- traceId: traceId ?? this.newTraceId()
1366
- };
1367
- }
1368
- async close() {
1369
- await Promise.allSettled(Array.from(this._sessions.values(), (s) => s.queue));
1370
- for (const session of this._sessions.values()) {
1371
- session.messages = [];
1372
- session.hydrated = false;
1320
+ console.warn(`[aipack] telemetry "${String(event)}" \u4E0A\u62A5\u5931\u8D25:`, err);
1373
1321
  }
1374
- this._sessions.clear();
1375
- this._extensions.clear();
1376
- this._transformers = [];
1377
1322
  }
1378
- // ─── 对话循环(同步) ───────────────────────────────────────────
1379
- async runLoop(compilation, request, session) {
1380
- session.isStreaming = true;
1381
- session.abortController = new AbortController();
1382
- const sessionKey = this.resolveSessionKey(request);
1383
- try {
1384
- let maxTurns = this._maxTurns;
1385
- let turnCount = 0;
1386
- while (maxTurns-- > 0) {
1387
- turnCount += 1;
1388
- await this.transformMessages(compilation, sessionKey);
1389
- await this.maybeCompactByThreshold(
1390
- compilation,
1391
- sessionKey,
1392
- session.abortController.signal
1393
- );
1394
- const assistantMessage = await this.streamModel(
1395
- compilation,
1396
- session.abortController.signal,
1397
- sessionKey
1398
- );
1399
- compilation.messages.push(assistantMessage);
1400
- await this.persistSessionSafe(request, sessionKey);
1401
- const toolCalls = extractToolCalls(assistantMessage.content);
1402
- if (toolCalls.length === 0) {
1403
- break;
1404
- }
1405
- const outcome = await this.executeToolCalls(
1406
- compilation,
1407
- toolCalls,
1408
- session.abortController.signal
1409
- );
1410
- await this.persistSessionSafe(request, sessionKey);
1411
- if (outcome.terminate) {
1412
- compilation.terminateReason = outcome.terminateReason ?? "terminated";
1413
- break;
1414
- }
1415
- }
1416
- if (maxTurns < 0) compilation.maxTurnsExhausted = true;
1417
- compilation.turnCount = turnCount;
1418
- } finally {
1419
- this.markIdle(session);
1420
- }
1323
+ /** 组装并上报 run 级完成事件(run/stream 内部统一调用) */
1324
+ async emitRunEnd(request, sessionKey, compilation, result, queuedMs, activeStartedAt, defaultModelId) {
1325
+ const activeMs = Date.now() - activeStartedAt;
1326
+ await this.emit("onRunEnd", {
1327
+ traceId: compilation.traceId,
1328
+ sessionKey,
1329
+ // 请求未显式指定 model 时补实际模型(模型排行按 run 级 requests 统计,缺省会落入 'unknown')
1330
+ request: request.model ? request : { ...request, model: defaultModelId },
1331
+ durationMs: activeMs + queuedMs,
1332
+ activeMs,
1333
+ queuedMs,
1334
+ turnCount: compilation.turnCount ?? 0,
1335
+ result,
1336
+ success: result.success,
1337
+ errorClass: this.runErrorClass(compilation),
1338
+ tokens: {
1339
+ input: result.usage.input ?? 0,
1340
+ output: result.usage.output ?? 0,
1341
+ cacheRead: result.usage.cacheRead,
1342
+ cacheWrite: result.usage.cacheWrite
1343
+ },
1344
+ ttftMs: compilation.ttftMs
1345
+ });
1421
1346
  }
1422
- // ─── 对话循环(流式) ───────────────────────────────────────────
1423
- async *runLoopStream(compilation, request, session) {
1424
- session.isStreaming = true;
1425
- session.abortController = new AbortController();
1426
- const sessionKey = this.resolveSessionKey(request);
1427
- try {
1428
- let maxTurns = this._maxTurns;
1429
- let turnCount = 0;
1430
- while (maxTurns-- > 0) {
1431
- turnCount += 1;
1432
- await this.transformMessages(compilation, sessionKey);
1433
- await this.maybeCompactByThreshold(
1434
- compilation,
1435
- sessionKey,
1436
- session.abortController.signal
1437
- );
1438
- const turn = this.modelTurnWithRecovery(
1439
- compilation,
1440
- session.abortController.signal,
1441
- sessionKey,
1442
- true
1443
- // stream 模式:同时统计首 token 延迟
1444
- );
1445
- let turnResult = await turn.next();
1446
- while (!turnResult.done) {
1447
- yield turnResult.value;
1448
- turnResult = await turn.next();
1449
- }
1450
- const assistantMessage = turnResult.value;
1451
- if (!assistantMessage) break;
1452
- compilation.messages.push(assistantMessage);
1453
- await this.persistSessionSafe(request, sessionKey);
1454
- const toolCalls = extractToolCalls(assistantMessage.content);
1455
- if (toolCalls.length === 0) {
1456
- break;
1457
- }
1458
- for (const toolCall of toolCalls) {
1459
- yield {
1460
- type: "tool_start",
1461
- toolName: toolCall.name,
1462
- toolCallId: toolCall.id
1463
- };
1464
- }
1465
- const outcome = await this.executeToolCalls(
1466
- compilation,
1467
- toolCalls,
1468
- session.abortController.signal
1469
- );
1470
- await this.persistSessionSafe(request, sessionKey);
1471
- for (let i = 0; i < toolCalls.length; i++) {
1472
- yield {
1473
- type: "tool_end",
1474
- toolName: toolCalls[i].name,
1475
- toolCallId: toolCalls[i].id,
1476
- isError: this.isErrorResult(outcome.results[i])
1477
- };
1478
- }
1479
- if (outcome.terminate) {
1480
- compilation.terminateReason = outcome.terminateReason ?? "terminated";
1481
- break;
1482
- }
1347
+ /** run 级错误分类:terminate → 'terminated';否则只看最后一条 assistant 消息(与 buildResult 的 result.success 同口径) */
1348
+ runErrorClass(compilation) {
1349
+ if (compilation.terminateReason) return "terminated";
1350
+ const messages = compilation.messages;
1351
+ for (let i = messages.length - 1; i >= 0; i--) {
1352
+ const m = messages[i];
1353
+ if (m.role === "assistant") {
1354
+ if (!m.errorMessage) return void 0;
1355
+ const cls = errorClassFromMessage(m.errorMessage);
1356
+ return cls ?? "unknown";
1483
1357
  }
1484
- if (maxTurns < 0) compilation.maxTurnsExhausted = true;
1485
- compilation.turnCount = turnCount;
1486
- } finally {
1487
- this.markIdle(session);
1488
1358
  }
1359
+ return void 0;
1489
1360
  }
1490
- // ─── 工具执行 ───────────────────────────────────────────────────
1491
- /** 同步循环:执行工具调用并将结果消息按原顺序追加 */
1492
- async executeToolCalls(compilation, toolCalls, signal) {
1493
- const outcome = await this.runTools(toolCalls, signal, compilation.request, compilation.traceId);
1494
- for (let i = 0; i < toolCalls.length; i++) {
1495
- compilation.messages.push(
1496
- this.buildToolResultMessage(toolCalls[i], outcome.results[i])
1497
- );
1498
- }
1499
- return outcome;
1361
+ };
1362
+
1363
+ // ../agent/runtime/tool-executor.ts
1364
+ var ToolExecutor = class {
1365
+ constructor(_d) {
1366
+ this._d = _d;
1500
1367
  }
1368
+ _d;
1501
1369
  /**
1502
1370
  * 执行一组工具调用:parallelToolCalls 为 true 时并行,否则串行。
1503
1371
  * 返回 ToolExecutionOutcome:任一工具请求 terminate 即终止整个 run;
1504
1372
  * 串行模式下 terminate 后剩余工具生成 skipped 结果以保持配对完整。
1505
1373
  */
1506
- async runTools(toolCalls, signal, request, traceId) {
1507
- const execute = (tc) => this.executeTool(tc, signal, request, traceId);
1508
- if (this._parallelToolCalls && toolCalls.length > 1) {
1374
+ async run(toolCalls, signal, request, traceId) {
1375
+ const execute = (tc) => this.execute(tc, signal, request, traceId);
1376
+ if (this._d.parallelToolCalls && toolCalls.length > 1) {
1509
1377
  const outcomes = await Promise.all(toolCalls.map(execute));
1510
1378
  const terminated = outcomes.find((o) => o.terminate);
1511
1379
  return {
@@ -1531,8 +1399,8 @@ var AgentRuntime = class _AgentRuntime {
1531
1399
  }
1532
1400
  return { results, terminate, terminateReason };
1533
1401
  }
1534
- async executeTool(toolCall, signal, request, traceId) {
1535
- const tool = this._globalTools.get(toolCall.name);
1402
+ async execute(toolCall, signal, request, traceId) {
1403
+ const tool = this._d.getTools().get(toolCall.name);
1536
1404
  if (!tool) {
1537
1405
  return {
1538
1406
  result: {
@@ -1546,27 +1414,43 @@ var AgentRuntime = class _AgentRuntime {
1546
1414
  if (tool.prepareArguments) {
1547
1415
  args = tool.prepareArguments(toolCall.arguments);
1548
1416
  }
1549
- if (this._permissionPolicy) {
1417
+ if (!this._d.permissionPolicy) {
1418
+ if (!this._d.permissionFailOpen) {
1419
+ const reason = `no permissionPolicy configured (fail-closed by default). Configure RuntimeOptions.permissionPolicy (e.g. createAllowAllPolicy()) or explicitly set permissionFailOpen: true to allow unrestricted tool execution`;
1420
+ await this._d.telemetry.emit("onPermissionDenied", {
1421
+ traceId,
1422
+ sessionKey: request.sessionKey ?? this._d.defaultSessionKey,
1423
+ toolName: toolCall.name,
1424
+ permissions: tool.permissions ?? [],
1425
+ args,
1426
+ reason: `permission denied for tool "${toolCall.name}": ${reason}`
1427
+ });
1428
+ return {
1429
+ result: this.makeBlockedResult(`permission denied for tool "${toolCall.name}": ${reason}`),
1430
+ terminate: false
1431
+ };
1432
+ }
1433
+ } else {
1550
1434
  const permissionReq = {
1551
1435
  toolName: toolCall.name,
1552
1436
  permissions: tool.permissions ?? [],
1553
1437
  args,
1554
- sessionKey: request.sessionKey ?? this._sessionKey,
1438
+ sessionKey: request.sessionKey ?? this._d.defaultSessionKey,
1555
1439
  request,
1556
- shared: this._extensionContext?.shared ?? /* @__PURE__ */ new Map()
1440
+ shared: this._d.getShared() ?? /* @__PURE__ */ new Map()
1557
1441
  };
1558
- const decision = await this._permissionPolicy.check(permissionReq);
1442
+ const decision = await this._d.permissionPolicy.check(permissionReq);
1559
1443
  let allowed = decision === "allow";
1560
1444
  if (decision === "confirm") {
1561
- allowed = this._permissionPolicy.confirm ? await this._permissionPolicy.confirm(permissionReq) : false;
1445
+ allowed = this._d.permissionPolicy.confirm ? await this._d.permissionPolicy.confirm(permissionReq) : false;
1562
1446
  }
1563
1447
  if (decision === "pending") {
1564
- if (this._approvals) {
1565
- const approval = this._approvals.create(permissionReq, {
1566
- timeoutMs: this._approvalTimeoutMs,
1448
+ if (this._d.approvals) {
1449
+ const approval = this._d.approvals.create(permissionReq, {
1450
+ timeoutMs: this._d.approvalTimeoutMs,
1567
1451
  signal
1568
1452
  });
1569
- await this.emitTelemetry("onApprovalPending", {
1453
+ await this._d.telemetry.emit("onApprovalPending", {
1570
1454
  traceId,
1571
1455
  sessionKey: permissionReq.sessionKey,
1572
1456
  approvalId: approval.id,
@@ -1575,9 +1459,9 @@ var AgentRuntime = class _AgentRuntime {
1575
1459
  args,
1576
1460
  expiresAt: approval.expiresAt
1577
1461
  });
1578
- const outcome = await this._approvals.wait(approval);
1462
+ const outcome = await this._d.approvals.wait(approval);
1579
1463
  allowed = outcome.status === "approved";
1580
- await this.emitTelemetry("onApprovalResolved", {
1464
+ await this._d.telemetry.emit("onApprovalResolved", {
1581
1465
  traceId,
1582
1466
  sessionKey: permissionReq.sessionKey,
1583
1467
  approvalId: approval.id,
@@ -1591,7 +1475,7 @@ var AgentRuntime = class _AgentRuntime {
1591
1475
  }
1592
1476
  if (!allowed) {
1593
1477
  const reason = `permission denied by policy for tool "${toolCall.name}"`;
1594
- await this.emitTelemetry("onPermissionDenied", {
1478
+ await this._d.telemetry.emit("onPermissionDenied", {
1595
1479
  traceId,
1596
1480
  sessionKey: permissionReq.sessionKey,
1597
1481
  toolName: toolCall.name,
@@ -1605,10 +1489,10 @@ var AgentRuntime = class _AgentRuntime {
1605
1489
  };
1606
1490
  }
1607
1491
  }
1608
- const { signal: timedSignal, clear } = withTimeoutSignal(signal, this._toolTimeoutMs);
1492
+ const { signal: timedSignal, clear } = withTimeoutSignal(signal, this._d.toolTimeoutMs);
1609
1493
  try {
1610
1494
  const beforeCtx = this.buildToolCallContext(toolCall, tool, args, request, timedSignal);
1611
- const before = await this._hooks.beforeToolCall.promise(
1495
+ const before = await this._d.hooks.beforeToolCall.promise(
1612
1496
  { block: false, terminate: false, args },
1613
1497
  beforeCtx
1614
1498
  );
@@ -1640,10 +1524,10 @@ var AgentRuntime = class _AgentRuntime {
1640
1524
  };
1641
1525
  }
1642
1526
  const status = toolResultStatus(result);
1643
- await this.emitTelemetry("onToolCall", {
1527
+ await this._d.telemetry.emit("onToolCall", {
1644
1528
  traceId,
1645
1529
  spanId: newSpanId(),
1646
- sessionKey: request.sessionKey ?? this._sessionKey,
1530
+ sessionKey: request.sessionKey ?? this._d.defaultSessionKey,
1647
1531
  toolName: toolCall.name,
1648
1532
  args,
1649
1533
  durationMs: Date.now() - toolStartedAt,
@@ -1653,7 +1537,7 @@ var AgentRuntime = class _AgentRuntime {
1653
1537
  errorClass: status === "error" ? errorClassFromMessage(String(result.details?.error ?? "")) ?? "tool_error" : void 0
1654
1538
  });
1655
1539
  const afterCtx = this.buildToolCallContext(toolCall, tool, args, request, timedSignal);
1656
- const after = await this._hooks.afterToolCall.promise(
1540
+ const after = await this._d.hooks.afterToolCall.promise(
1657
1541
  { result, terminate: false },
1658
1542
  afterCtx
1659
1543
  );
@@ -1689,13 +1573,14 @@ var AgentRuntime = class _AgentRuntime {
1689
1573
  toolCall,
1690
1574
  tool,
1691
1575
  args,
1692
- sessionKey: request.sessionKey ?? this._sessionKey,
1576
+ sessionKey: request.sessionKey ?? this._d.defaultSessionKey,
1693
1577
  request,
1694
- shared: this._extensionContext?.shared ?? /* @__PURE__ */ new Map(),
1578
+ shared: this._d.getShared() ?? /* @__PURE__ */ new Map(),
1695
1579
  signal
1696
1580
  };
1697
1581
  }
1698
- buildToolResultMessage(toolCall, result) {
1582
+ /** 工具结果 → toolResult 消息(写入会话历史) */
1583
+ buildResultMessage(toolCall, result) {
1699
1584
  return {
1700
1585
  role: "toolResult",
1701
1586
  content: result.content,
@@ -1705,294 +1590,69 @@ var AgentRuntime = class _AgentRuntime {
1705
1590
  timestamp: Date.now()
1706
1591
  };
1707
1592
  }
1708
- buildUserMessage(request) {
1709
- if (request.media && request.media.length > 0) {
1710
- const blocks = [
1711
- createTextContent(request.message),
1712
- ...request.media.filter(Boolean).map(buildImageContent)
1713
- ];
1714
- return {
1715
- role: "user",
1716
- content: blocks,
1717
- timestamp: Date.now()
1718
- };
1593
+ };
1594
+
1595
+ // ../agent/transformer/index.ts
1596
+ function ensureToolPairing(messages) {
1597
+ const toolCallIds = /* @__PURE__ */ new Set();
1598
+ const toolResultIds = /* @__PURE__ */ new Set();
1599
+ for (const msg of messages) {
1600
+ if (msg.role === "assistant") {
1601
+ const content = msg.content;
1602
+ if (Array.isArray(content)) {
1603
+ for (const tc of extractToolCalls(content)) {
1604
+ toolCallIds.add(tc.id);
1605
+ }
1606
+ }
1719
1607
  }
1720
- return {
1721
- role: "user",
1722
- content: request.message,
1723
- timestamp: Date.now()
1724
- };
1725
- }
1726
- // ─── 内部方法 ───────────────────────────────────────────────────
1727
- /**
1728
- * 获取同一会话的执行锁:返回 release 函数,调用后释放。
1729
- * 同一 sessionKey 的 run/stream 会串行执行,避免消息数组交错、
1730
- * abortController 互相覆盖、hydrate 竞态。
1731
- */
1732
- async acquire(session) {
1733
- let release;
1734
- const prev = session.queue;
1735
- session.queue = new Promise((resolve) => {
1736
- release = () => resolve();
1737
- });
1738
- await prev;
1739
- session.lockHeld = true;
1740
- return () => {
1741
- session.lockHeld = false;
1742
- release();
1743
- };
1744
- }
1745
- /** 标记会话空闲并唤醒所有 waitForIdle 等待者 */
1746
- markIdle(session) {
1747
- session.isStreaming = false;
1748
- session.abortController = null;
1749
- if (session.idleResolvers.length > 0) {
1750
- const resolvers = session.idleResolvers.splice(0);
1751
- for (const resolve of resolvers) resolve();
1608
+ if (msg.role === "toolResult") {
1609
+ toolResultIds.add(msg.toolCallId);
1752
1610
  }
1753
1611
  }
1754
- // ─── 会话持久化 ─────────────────────────────────────────────────
1755
- /** 从存储懒加载会话(串行化后无竞态,每个会话仅恢复一次) */
1756
- async hydrateSession(sessionKey, session) {
1757
- if (!this._sessionStorage) return;
1758
- if (session.hydrated) return;
1759
- session.hydrated = true;
1760
- const stored = await this._sessionStorage.load(sessionKey);
1761
- if (!stored) return;
1762
- session.messages = stored.messages;
1763
- session.createdAt = stored.createdAt;
1612
+ const orphanedCalls = /* @__PURE__ */ new Set();
1613
+ const orphanedResults = /* @__PURE__ */ new Set();
1614
+ for (const id of toolCallIds) {
1615
+ if (!toolResultIds.has(id)) orphanedCalls.add(id);
1764
1616
  }
1765
- /** 整体保存指定会话(ephemeral 跳过;失败不影响运行结果) */
1766
- async persistSession(sessionKey, session) {
1767
- if (!this._sessionStorage) return;
1768
- const stored = {
1769
- key: sessionKey,
1770
- version: SESSION_VERSION,
1771
- messages: session.messages,
1772
- model: this.deriveModel(session.messages),
1773
- usage: this.sumUsage(session.messages),
1774
- createdAt: session.createdAt,
1775
- updatedAt: (/* @__PURE__ */ new Date()).toISOString()
1776
- };
1777
- await this._sessionStorage.save(sessionKey, stored);
1617
+ for (const id of toolResultIds) {
1618
+ if (!toolCallIds.has(id)) orphanedResults.add(id);
1778
1619
  }
1779
- /**
1780
- * 实时持久化指定会话:每轮 assistant 回复/工具结果完成后调用,
1781
- * 让运行中的会话随时可被持久化数据观测到。ephemeral 跳过;
1782
- * 存储失败仅告警,不影响对话循环继续。
1783
- */
1784
- async persistSessionSafe(request, sessionKey) {
1785
- if (request.ephemeral || !this._sessionStorage) return;
1786
- try {
1787
- const session = this._sessions.get(sessionKey);
1788
- if (!session) return;
1789
- await this.persistSession(sessionKey, session);
1790
- } catch (err) {
1791
- console.warn("[Runtime] \u4F1A\u8BDD\u6301\u4E45\u5316\u5931\u8D25:", err?.message);
1792
- }
1793
- }
1794
- /** 从消息中推导最后使用的模型 */
1795
- deriveModel(messages) {
1796
- for (let i = messages.length - 1; i >= 0; i--) {
1797
- const msg = messages[i];
1798
- if (msg.role === "assistant" && msg.model) {
1799
- return {
1800
- provider: msg.provider ?? this._model.provider,
1801
- modelId: msg.model
1802
- };
1803
- }
1804
- }
1805
- return null;
1806
- }
1807
- /** 汇总所有 assistant 消息的 token 用量(含 cache) */
1808
- sumUsage(messages) {
1809
- const usage = createEmptyUsage();
1810
- for (const msg of messages) {
1811
- if (msg.role === "assistant") {
1812
- const u = msg.usage;
1813
- if (u) {
1814
- usage.input += u.input;
1815
- usage.output += u.output;
1816
- usage.total += u.total;
1817
- usage.cacheRead = (usage.cacheRead ?? 0) + (u.cacheRead ?? 0);
1818
- usage.cacheWrite = (usage.cacheWrite ?? 0) + (u.cacheWrite ?? 0);
1819
- usage.reasoning = (usage.reasoning ?? 0) + (u.reasoning ?? 0);
1820
- }
1821
- }
1822
- }
1823
- return usage;
1824
- }
1825
- /** 链式执行上下文转换器(按数组顺序,上一个输出作为下一个输入) */
1826
- async transformMessages(compilation, sessionKey) {
1827
- if (this._transformers.length === 0) {
1828
- return;
1829
- }
1830
- let resources = messagesToResources(compilation.messages);
1831
- const context = {
1832
- graph: compilation.graph,
1833
- runtime: {
1834
- sessionKey,
1835
- turn: compilation.messages.length,
1836
- contextWindow: this._model.contextWindow,
1837
- maxTokens: this._model.maxTokens,
1838
- contextBudgetRatio: this._contextBudgetRatio
1839
- }
1840
- };
1841
- for (const transformer of this._transformers) {
1842
- try {
1843
- resources = await transformer.transform(resources, context);
1844
- } catch (err) {
1845
- console.warn(
1846
- `[Runtime] \u8F6C\u6362\u5668 "${transformer.name}" \u5931\u8D25\uFF0C\u5DF2\u8DF3\u8FC7:`,
1847
- err?.message ?? err
1848
- );
1849
- }
1850
- }
1851
- const messages = resourcesToMessages(resources);
1852
- compilation.messages.splice(0, compilation.messages.length, ...messages);
1853
- }
1854
- buildContext(messages) {
1855
- const tools = Array.from(this._globalTools.values());
1856
- return {
1857
- systemPrompt: this._systemPrompt,
1858
- // compactionSummary / stateSnapshot 为内部扩展 role,provider 适配层
1859
- // 仅支持 user/assistant/toolResult,发出前统一转为带标注的 user 消息
1860
- messages: messages.filter((m) => m.role !== "system").map((m) => {
1861
- const role = m.role;
1862
- if (role === "compactionSummary") return this.compactionSummaryToUser(m);
1863
- if (role === "stateSnapshot") return this.stateSnapshotToUser(m);
1864
- return m;
1865
- }),
1866
- tools: tools.length > 0 ? tools : void 0
1867
- };
1868
- }
1869
- /** compactionSummary 消息 → user 消息(所有 provider 均兼容 user role) */
1870
- compactionSummaryToUser(msg) {
1871
- const text = typeof msg.content === "string" ? msg.content : extractText(msg.content);
1872
- return {
1873
- role: "user",
1874
- content: `${COMPACTION_USER_PREFIX}
1875
- ${text}`,
1876
- timestamp: msg.timestamp
1877
- };
1878
- }
1879
- /** stateSnapshot 消息 → user 消息(所有 provider 均兼容 user role) */
1880
- stateSnapshotToUser(msg) {
1881
- const text = typeof msg.content === "string" ? msg.content : extractText(msg.content);
1882
- return {
1883
- role: "user",
1884
- content: `${STATE_SNAPSHOT_USER_PREFIX}
1885
- ${text}`,
1886
- timestamp: msg.timestamp
1887
- };
1888
- }
1889
- async streamModel(compilation, signal, sessionKey) {
1890
- const turn = this.modelTurnWithRecovery(compilation, signal, sessionKey, false);
1891
- let r = await turn.next();
1892
- while (!r.done) r = await turn.next();
1893
- return r.value ?? this.emptyAssistantMessage();
1894
- }
1895
- /** 空的 assistant 消息(流异常中断无 done/error 事件时的兜底,与旧 streamModel 行为一致) */
1896
- emptyAssistantMessage() {
1897
- return {
1898
- role: "assistant",
1899
- content: [],
1900
- stopReason: "stop",
1901
- usage: createEmptyUsage(),
1902
- model: this._model.id,
1903
- provider: this._model.provider,
1904
- timestamp: Date.now()
1905
- };
1620
+ if (orphanedCalls.size === 0 && orphanedResults.size === 0) {
1621
+ return messages;
1906
1622
  }
1907
- /**
1908
- * 单回合模型调用 + 上下文溢出自动恢复闭环。
1909
- *
1910
- * 检测(isContextOverflow,统一传入 model.contextWindow / content / maxTokens,
1911
- * 覆盖显式错误 / 静默溢出 / 输入截断溢出 / 输出 thinking 耗尽 / 输出打满 五模式)
1912
- * → 丢弃失败的 assistant 消息 → 截断会话历史 → 同回合重试(不消耗回合数,
1913
- * 上限 OVERFLOW_RECOVERY_LIMIT):
1914
- *
1915
- * - 显式错误 / 零产出截断溢出 / thinking 耗尽溢出 / 输出打满溢出:丢弃失败
1916
- * 消息后重试;流式路径吞掉可恢复的 error chunk(消费者看不到瞬态错误),
1917
- * 不可恢复时补发。
1918
- * - 静默溢出(stop + 有完整产出):保留回复,仅压缩旧上下文供后续轮次。
1919
- * - 恢复耗尽或单请求超窗(无可丢弃):返回最后一次错误消息,维持旧行为。
1920
- *
1921
- * 流式路径 yield 模型事件 chunk;非流式路径由 streamModel 消费(chunk 丢弃)。
1922
- * 返回最终 assistant 消息;无 done/error 事件时返回 null。
1923
- */
1924
- async *modelTurnWithRecovery(compilation, signal, sessionKey, stream) {
1925
- const contextWindow = this._model.contextWindow;
1926
- const maxTokens = this._model.maxTokens;
1927
- let recoveries = 0;
1928
- const toProbe = (m) => ({
1929
- stopReason: m.stopReason,
1930
- errorMessage: m.errorMessage,
1931
- usage: m.usage,
1932
- content: m.content,
1933
- maxTokens
1934
- });
1935
- while (true) {
1936
- let assistant = null;
1937
- let suppressed = null;
1938
- for await (const event of this.streamModelEvents(compilation, signal, stream)) {
1939
- if (event.type === "error") {
1940
- const msg = event.message;
1941
- if (recoveries < OVERFLOW_RECOVERY_LIMIT && isContextOverflow(toProbe(msg), contextWindow)) {
1942
- suppressed = msg;
1943
- continue;
1623
+ const result = [];
1624
+ for (const msg of messages) {
1625
+ if (msg.role === "assistant") {
1626
+ const content = msg.content;
1627
+ if (Array.isArray(content)) {
1628
+ const filteredContent = content.filter((block) => {
1629
+ if (block.type === "toolCall") {
1630
+ return !orphanedCalls.has(block.id);
1944
1631
  }
1945
- const chunk = this.streamEventToChunk(event);
1946
- if (chunk) yield chunk;
1947
- assistant = msg;
1948
- } else {
1949
- const chunk = this.streamEventToChunk(event);
1950
- if (chunk) yield chunk;
1951
- if (event.type === "done") assistant = event.message;
1952
- }
1632
+ return true;
1633
+ });
1634
+ if (filteredContent.length === 0 && content.length > 0) continue;
1635
+ result.push({ ...msg, content: filteredContent });
1636
+ } else {
1637
+ result.push(msg);
1953
1638
  }
1954
- const final = assistant ?? suppressed;
1955
- if (!final) return null;
1956
- if (isContextOverflow(toProbe(final), contextWindow)) {
1957
- const output = final.usage?.output ?? 0;
1958
- const blocks = Array.isArray(final.content) ? final.content : null;
1959
- const hasMeaningfulText = blocks?.some(
1960
- (b) => b.type === "text" && "text" in b && typeof b.text === "string" && b.text.trim().length > 0
1961
- );
1962
- const hasToolCall = blocks?.some((b) => b.type === "toolCall");
1963
- const thinkingOnly = blocks ? !hasMeaningfulText && !hasToolCall && blocks.some((b) => b.type === "thinking") : false;
1964
- const outputFull = maxTokens > 0 && output >= Math.floor(maxTokens * 0.95);
1965
- const failed = final.stopReason === "error" || output === 0 || thinkingOnly || outputFull;
1966
- if (failed && recoveries < OVERFLOW_RECOVERY_LIMIT) {
1967
- recoveries += 1;
1968
- if (await this.recoverFromOverflow(compilation, recoveries, sessionKey, signal)) {
1969
- const reason = thinkingOnly ? "thinking \u8017\u5C3D" : outputFull ? "output \u6253\u6EE1" : final.stopReason;
1970
- console.warn(
1971
- `[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`
1972
- );
1973
- await this.emitTelemetry("onRetry", {
1974
- traceId: compilation.traceId,
1975
- provider: this._model.provider,
1976
- modelId: this._model.id,
1977
- attempt: recoveries,
1978
- errorClass: "context-overflow",
1979
- delayMs: 0,
1980
- willRetry: true
1981
- });
1982
- continue;
1983
- }
1984
- if (suppressed) {
1985
- yield { type: "error", content: final.errorMessage || "\u6A21\u578B\u8C03\u7528\u51FA\u9519" };
1986
- }
1987
- return final;
1988
- }
1989
- if (failed) return final;
1990
- await this.recoverFromOverflow(compilation, 1, sessionKey, signal);
1991
- console.warn("[Runtime] \u68C0\u6D4B\u5230\u9759\u9ED8\u4E0A\u4E0B\u6587\u6EA2\u51FA\uFF08usage \u8D85\u7A97\uFF09\uFF0C\u5DF2\u538B\u7F29\u5386\u53F2\u6D88\u606F");
1639
+ } else if (msg.role === "toolResult") {
1640
+ if (!orphanedResults.has(msg.toolCallId)) {
1641
+ result.push(msg);
1992
1642
  }
1993
- return final;
1643
+ } else {
1644
+ result.push(msg);
1994
1645
  }
1995
1646
  }
1647
+ return result;
1648
+ }
1649
+
1650
+ // ../agent/runtime/compactor.ts
1651
+ var ContextCompactor = class {
1652
+ constructor(_d) {
1653
+ this._d = _d;
1654
+ }
1655
+ _d;
1996
1656
  /**
1997
1657
  * 计算溢出恢复的截断点:按 token 预算从最旧消息开始丢弃。
1998
1658
  *
@@ -2003,10 +1663,10 @@ ${text}`,
2003
1663
  * 最小集,单条请求即超窗,无法恢复。
2004
1664
  */
2005
1665
  computeOverflowSplit(messages, recovery) {
2006
- const contextWindow = this._model.contextWindow;
1666
+ const contextWindow = this._d.getModel().contextWindow;
2007
1667
  if (!contextWindow || contextWindow <= 0) return 0;
2008
1668
  const target = Math.max(
2009
- Math.floor(contextWindow * this._contextBudgetRatio * 0.5 ** recovery),
1669
+ Math.floor(contextWindow * this._d.contextBudgetRatio * 0.5 ** recovery),
2010
1670
  1
2011
1671
  );
2012
1672
  let total = 0;
@@ -2034,7 +1694,7 @@ ${text}`,
2034
1694
  const messages = compilation.messages;
2035
1695
  const split = this.computeOverflowSplit(messages, recovery);
2036
1696
  if (split <= 0) return false;
2037
- const useCompaction = !!this._compaction && this._compaction.enabled !== false && this._compaction.onOverflow !== false;
1697
+ const useCompaction = !!this._d.compaction && this._d.compaction.enabled !== false && this._d.compaction.onOverflow !== false;
2038
1698
  if (useCompaction) {
2039
1699
  await this.compactOrTruncate(
2040
1700
  messages,
@@ -2057,30 +1717,17 @@ ${text}`,
2057
1717
  * 其余部分摘要替换;摘要失败降级硬截断。
2058
1718
  */
2059
1719
  async maybeCompactByThreshold(compilation, sessionKey, signal) {
2060
- if (!this._compaction || this._compaction.enabled === false) return;
2061
- const contextWindow = this._model.contextWindow;
1720
+ if (!this._d.compaction || this._d.compaction.enabled === false) return;
1721
+ const contextWindow = this._d.getModel().contextWindow;
2062
1722
  if (!contextWindow || contextWindow <= 0) return;
2063
1723
  const messages = compilation.messages;
2064
1724
  const triggerTokens = Math.floor(
2065
- contextWindow * (this._compaction?.triggerRatio ?? this._contextBudgetRatio)
1725
+ contextWindow * (this._d.compaction?.triggerRatio ?? this._d.contextBudgetRatio)
2066
1726
  );
2067
1727
  let total = 0;
2068
1728
  for (const m of messages) total += estimateMessageTokens(m);
2069
1729
  if (total <= triggerTokens) return;
2070
- const targetTokens = Math.floor(
2071
- contextWindow * (this._compaction?.targetRatio ?? 0.5)
2072
- );
2073
- const keepTokens = Math.max(Math.floor(targetTokens / 2), 1);
2074
- let kept = 0;
2075
- let split = messages.length;
2076
- for (let i = messages.length - 1; i >= 0; i--) {
2077
- if (kept >= keepTokens) {
2078
- split = i + 1;
2079
- break;
2080
- }
2081
- kept += estimateMessageTokens(messages[i]);
2082
- split = i;
2083
- }
1730
+ const split = this.computeKeepSplit(messages, contextWindow);
2084
1731
  if (split <= 0 || split >= messages.length) return;
2085
1732
  const mode = await this.compactOrTruncate(
2086
1733
  messages,
@@ -2095,20 +1742,12 @@ ${text}`,
2095
1742
  );
2096
1743
  }
2097
1744
  /**
2098
- * 手动压缩指定会话(交互命令 /compact 等):跳过阈值判断,
2099
- * 直接将历史压缩至 targetRatio 保留量(复用 maybeCompactByThreshold
2100
- * 的保留段计算与 compactOrTruncate 执行路径),压缩后持久化。
1745
+ * 保留段计算:从尾部累计保留最新消息,保留量为压缩目标(contextWindow ×
1746
+ * targetRatio)的一半。返回被压缩段的结束下标;messages[0..split) 为被压缩段。
2101
1747
  */
2102
- async compact(sessionKey) {
2103
- if (!this._compaction || this._compaction.enabled === false) return null;
2104
- const key = sessionKey ?? this._sessionKey;
2105
- const session = this.getSession(key);
2106
- const messages = session.messages;
2107
- if (messages.length === 0) return null;
2108
- const contextWindow = this._model.contextWindow;
2109
- if (!contextWindow || contextWindow <= 0) return null;
1748
+ computeKeepSplit(messages, contextWindow) {
2110
1749
  const targetTokens = Math.floor(
2111
- contextWindow * (this._compaction.targetRatio ?? 0.5)
1750
+ contextWindow * (this._d.compaction?.targetRatio ?? 0.5)
2112
1751
  );
2113
1752
  const keepTokens = Math.max(Math.floor(targetTokens / 2), 1);
2114
1753
  let kept = 0;
@@ -2121,21 +1760,7 @@ ${text}`,
2121
1760
  kept += estimateMessageTokens(messages[i]);
2122
1761
  split = i;
2123
1762
  }
2124
- if (split <= 0 || split >= messages.length) return null;
2125
- const mode = await this.compactOrTruncate(
2126
- messages,
2127
- split,
2128
- key,
2129
- `manual-${Date.now().toString(36)}`,
2130
- new AbortController().signal,
2131
- "threshold"
2132
- );
2133
- try {
2134
- await this.persistSession(key, session);
2135
- } catch (err) {
2136
- console.warn("[Runtime] \u624B\u52A8\u538B\u7F29\u540E\u6301\u4E45\u5316\u5931\u8D25:", err?.message);
2137
- }
2138
- return mode;
1763
+ return split;
2139
1764
  }
2140
1765
  /**
2141
1766
  * 执行压缩:messages[0..split) 为被压缩段,原地替换。
@@ -2150,10 +1775,10 @@ ${text}`,
2150
1775
  const tokensBefore = messages.reduce((sum, m) => sum + estimateMessageTokens(m), 0);
2151
1776
  let mode = "truncate";
2152
1777
  let summaryText = "";
2153
- const summaryEnabled = !!this._compaction && this._compaction.enabled !== false;
1778
+ const summaryEnabled = !!this._d.compaction && this._d.compaction.enabled !== false;
2154
1779
  if (summaryEnabled && compacted.length > 0) {
2155
1780
  const summaryBudget = Math.floor(
2156
- this._model.contextWindow * COMPACTION_SUMMARY_BUDGET_RATIO
1781
+ this._d.getModel().contextWindow * COMPACTION_SUMMARY_BUDGET_RATIO
2157
1782
  );
2158
1783
  const inputText = compacted.map((m) => messageToSummaryLine(m)).filter((line) => line.length > 0).join("\n");
2159
1784
  if (inputText && estimateTextTokens(inputText) <= summaryBudget) {
@@ -2180,7 +1805,7 @@ ${text}`,
2180
1805
  messages.splice(0, messages.length, ...kept);
2181
1806
  tokensAfter = kept.reduce((sum, m) => sum + estimateMessageTokens(m), 0);
2182
1807
  }
2183
- await this.emitTelemetry("onCompaction", {
1808
+ await this._d.telemetry.emit("onCompaction", {
2184
1809
  traceId,
2185
1810
  sessionKey,
2186
1811
  mode,
@@ -2197,7 +1822,7 @@ ${text}`,
2197
1822
  * 由调用方降级硬截断——摘要失败不影响主流程。
2198
1823
  */
2199
1824
  async summarizeMessages(inputText, sessionKey, traceId, signal) {
2200
- const prompt = this._compaction?.prompt ?? DEFAULT_COMPACTION_PROMPT;
1825
+ const prompt = this._d.compaction?.prompt ?? DEFAULT_COMPACTION_PROMPT;
2201
1826
  const context = {
2202
1827
  systemPrompt: prompt,
2203
1828
  messages: [{ role: "user", content: inputText, timestamp: Date.now() }]
@@ -2207,7 +1832,7 @@ ${text}`,
2207
1832
  let usage;
2208
1833
  let failed = false;
2209
1834
  try {
2210
- for await (const event of this._streamFn(this._model, context, { signal })) {
1835
+ for await (const event of this._d.getStreamFn()(this._d.getModel(), context, { signal })) {
2211
1836
  if (event.type === "text_delta") {
2212
1837
  text += event.delta;
2213
1838
  } else if (event.type === "error") {
@@ -2225,11 +1850,11 @@ ${text}`,
2225
1850
  return null;
2226
1851
  }
2227
1852
  if (failed || !text.trim()) return null;
2228
- await this.emitTelemetry("onModelCall", {
1853
+ await this._d.telemetry.emit("onModelCall", {
2229
1854
  traceId,
2230
1855
  spanId: newSpanId(),
2231
1856
  sessionKey,
2232
- modelId: this._model.id,
1857
+ modelId: this._d.getModel().id,
2233
1858
  attempts: 1,
2234
1859
  inputTokens: usage?.input ?? estimateTextTokens(inputText),
2235
1860
  outputTokens: usage?.output ?? estimateTextTokens(text),
@@ -2246,30 +1871,148 @@ ${text}`,
2246
1871
  timestamp: Date.now()
2247
1872
  };
2248
1873
  }
2249
- /**
2250
- * 统一模型调用埋点生成器:run()(streamModel)与 stream()(runLoopStream)两路径共用。
2251
- * 职责:模型调用计时、spanId、attempts 累计、onRetry 转发、onModelCall 上报
2252
- * (含 tokens / cost / errorClass / ttft)。
2253
- */
2254
- async *streamModelEvents(compilation, signal, stream) {
2255
- const sessionKey = this.resolveSessionKey(compilation.request);
2256
- const modelStartedAt = Date.now();
1874
+ };
1875
+
1876
+ // ../agent/runtime/model-turn.ts
1877
+ var ModelTurnRunner = class {
1878
+ constructor(_d) {
1879
+ this._d = _d;
1880
+ }
1881
+ _d;
1882
+ async streamModel(compilation, signal, sessionKey) {
1883
+ const turn = this.modelTurnWithRecovery(compilation, signal, sessionKey, false);
1884
+ let r = await turn.next();
1885
+ while (!r.done) r = await turn.next();
1886
+ return r.value ?? this.emptyAssistantMessage();
1887
+ }
1888
+ /** 空的 assistant 消息(流异常中断无 done/error 事件时的兜底) */
1889
+ emptyAssistantMessage() {
1890
+ const model = this._d.getModel();
1891
+ return {
1892
+ role: "assistant",
1893
+ content: [],
1894
+ stopReason: "stop",
1895
+ usage: createEmptyUsage(),
1896
+ model: model.id,
1897
+ provider: model.provider,
1898
+ timestamp: Date.now()
1899
+ };
1900
+ }
1901
+ /**
1902
+ * 单回合模型调用 + 上下文溢出自动恢复闭环。
1903
+ *
1904
+ * 检测(isContextOverflow,统一传入 model.contextWindow / content / maxTokens,
1905
+ * 覆盖显式错误 / 静默溢出 / 输入截断溢出 / 输出 thinking 耗尽 / 输出打满 五模式)
1906
+ * → 丢弃失败的 assistant 消息 → 截断会话历史 → 同回合重试(不消耗回合数,
1907
+ * 上限 OVERFLOW_RECOVERY_LIMIT):
1908
+ *
1909
+ * - 显式错误 / 零产出截断溢出 / thinking 耗尽溢出 / 输出打满溢出:丢弃失败
1910
+ * 消息后重试;流式路径吞掉可恢复的 error chunk(消费者看不到瞬态错误),
1911
+ * 不可恢复时补发。
1912
+ * - 静默溢出(stop + 有完整产出):保留回复,仅压缩旧上下文供后续轮次。
1913
+ * - 恢复耗尽或单请求超窗(无可丢弃):返回最后一次错误消息,维持旧行为。
1914
+ *
1915
+ * 流式路径 yield 模型事件 chunk;非流式路径由 streamModel 消费(chunk 丢弃)。
1916
+ * 返回最终 assistant 消息;无 done/error 事件时返回 null。
1917
+ */
1918
+ async *modelTurnWithRecovery(compilation, signal, sessionKey, stream) {
1919
+ const model = this._d.getModel();
1920
+ const contextWindow = model.contextWindow;
1921
+ const maxTokens = model.maxTokens;
1922
+ let recoveries = 0;
1923
+ const toProbe = (m) => ({
1924
+ stopReason: m.stopReason,
1925
+ errorMessage: m.errorMessage,
1926
+ usage: m.usage,
1927
+ content: m.content,
1928
+ maxTokens
1929
+ });
1930
+ while (true) {
1931
+ let assistant = null;
1932
+ let suppressed = null;
1933
+ for await (const event of this.streamModelEvents(compilation, signal, stream)) {
1934
+ if (event.type === "error") {
1935
+ const msg = event.message;
1936
+ if (recoveries < OVERFLOW_RECOVERY_LIMIT && isContextOverflow(toProbe(msg), contextWindow)) {
1937
+ suppressed = msg;
1938
+ continue;
1939
+ }
1940
+ const chunk = streamEventToChunk(event);
1941
+ if (chunk) yield chunk;
1942
+ assistant = msg;
1943
+ } else {
1944
+ const chunk = streamEventToChunk(event);
1945
+ if (chunk) yield chunk;
1946
+ if (event.type === "done") assistant = event.message;
1947
+ }
1948
+ }
1949
+ const final = assistant ?? suppressed;
1950
+ if (!final) return null;
1951
+ if (isContextOverflow(toProbe(final), contextWindow)) {
1952
+ const output = final.usage?.output ?? 0;
1953
+ const blocks = Array.isArray(final.content) ? final.content : null;
1954
+ const hasMeaningfulText = blocks?.some(
1955
+ (b) => b.type === "text" && "text" in b && typeof b.text === "string" && b.text.trim().length > 0
1956
+ );
1957
+ const hasToolCall = blocks?.some((b) => b.type === "toolCall");
1958
+ const thinkingOnly = blocks ? !hasMeaningfulText && !hasToolCall && blocks.some((b) => b.type === "thinking") : false;
1959
+ const outputFull = maxTokens > 0 && output >= Math.floor(maxTokens * 0.95);
1960
+ const failed = final.stopReason === "error" || output === 0 || thinkingOnly || outputFull;
1961
+ if (failed && recoveries < OVERFLOW_RECOVERY_LIMIT) {
1962
+ recoveries += 1;
1963
+ if (await this._d.compactor.recoverFromOverflow(compilation, recoveries, sessionKey, signal)) {
1964
+ const reason = thinkingOnly ? "thinking \u8017\u5C3D" : outputFull ? "output \u6253\u6EE1" : final.stopReason;
1965
+ console.warn(
1966
+ `[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`
1967
+ );
1968
+ await this._d.telemetry.emit("onRetry", {
1969
+ traceId: compilation.traceId,
1970
+ provider: model.provider,
1971
+ modelId: model.id,
1972
+ attempt: recoveries,
1973
+ errorClass: "context-overflow",
1974
+ delayMs: 0,
1975
+ willRetry: true
1976
+ });
1977
+ continue;
1978
+ }
1979
+ if (suppressed) {
1980
+ yield { type: "error", content: final.errorMessage || "\u6A21\u578B\u8C03\u7528\u51FA\u9519" };
1981
+ }
1982
+ return final;
1983
+ }
1984
+ if (failed) return final;
1985
+ await this._d.compactor.recoverFromOverflow(compilation, 1, sessionKey, signal);
1986
+ console.warn("[Runtime] \u68C0\u6D4B\u5230\u9759\u9ED8\u4E0A\u4E0B\u6587\u6EA2\u51FA\uFF08usage \u8D85\u7A97\uFF09\uFF0C\u5DF2\u538B\u7F29\u5386\u53F2\u6D88\u606F");
1987
+ }
1988
+ return final;
1989
+ }
1990
+ }
1991
+ /**
1992
+ * 统一模型调用埋点生成器:run()(streamModel)与 stream()(runLoopStream)两路径共用。
1993
+ * 职责:模型调用计时、spanId、attempts 累计、onRetry 转发、onModelCall 上报
1994
+ * (含 tokens / cost / errorClass / ttft)。
1995
+ */
1996
+ async *streamModelEvents(compilation, signal, stream) {
1997
+ const model = this._d.getModel();
1998
+ const sessionKey = this._d.resolveSessionKey(compilation.request);
1999
+ const modelStartedAt = Date.now();
2257
2000
  const spanId = newSpanId();
2258
2001
  let attempts = 1;
2259
2002
  let ttftAt;
2260
2003
  let lastAssistant;
2261
2004
  const options = { signal };
2262
- if (this._thinkingLevel !== "off" && this._model.reasoning) {
2263
- options.reasoning = this._thinkingLevel;
2005
+ if (this._d.getThinkingLevel() !== "off" && model.reasoning) {
2006
+ options.reasoning = this._d.getThinkingLevel();
2264
2007
  }
2265
2008
  options.onRetryAttempt = (info) => {
2266
2009
  attempts += 1;
2267
- void this.emitTelemetry("onRetry", {
2010
+ void this._d.telemetry.emit("onRetry", {
2268
2011
  traceId: compilation.traceId,
2269
2012
  spanId,
2270
2013
  // P2:重试明细关联到本模型调用 span
2271
- provider: this._model.provider,
2272
- modelId: this._model.id,
2014
+ provider: model.provider,
2015
+ modelId: model.id,
2273
2016
  attempt: info.attempt,
2274
2017
  errorClass: classifyError(info.error),
2275
2018
  status: statusOfRetryError(info.error),
@@ -2278,10 +2021,10 @@ ${text}`,
2278
2021
  });
2279
2022
  };
2280
2023
  try {
2281
- const context = await this._hooks.beforeModelCall.promise(
2024
+ const context = await this._d.hooks.beforeModelCall.promise(
2282
2025
  this.buildContext(compilation.messages)
2283
2026
  );
2284
- for await (const event of this._streamFn(this._model, context, options)) {
2027
+ for await (const event of this._d.getStreamFn()(model, context, options)) {
2285
2028
  if (stream && event.type === "text_delta" && ttftAt === void 0) {
2286
2029
  ttftAt = Date.now();
2287
2030
  }
@@ -2293,11 +2036,11 @@ ${text}`,
2293
2036
  } finally {
2294
2037
  const assistant = lastAssistant;
2295
2038
  const errorClass = assistant?.errorMessage ? errorClassFromMessage(assistant.errorMessage) ?? "unknown" : void 0;
2296
- await this.emitTelemetry("onModelCall", {
2039
+ await this._d.telemetry.emit("onModelCall", {
2297
2040
  traceId: compilation.traceId,
2298
2041
  spanId,
2299
2042
  sessionKey,
2300
- modelId: this._model.id,
2043
+ modelId: model.id,
2301
2044
  attempts,
2302
2045
  inputTokens: assistant?.usage?.input ?? 0,
2303
2046
  outputTokens: assistant?.usage?.output ?? 0,
@@ -2312,164 +2055,718 @@ ${text}`,
2312
2055
  }
2313
2056
  }
2314
2057
  }
2058
+ buildContext(messages) {
2059
+ const tools = Array.from(this._d.getTools().values());
2060
+ return {
2061
+ systemPrompt: this._d.getSystemPrompt(),
2062
+ // compactionSummary / stateSnapshot 为内部扩展 role,provider 适配层
2063
+ // 仅支持 user/assistant/toolResult,发出前统一转为带标注的 user 消息
2064
+ messages: messages.filter((m) => m.role !== "system").map((m) => {
2065
+ const role = m.role;
2066
+ if (role === "compactionSummary") return this.compactionSummaryToUser(m);
2067
+ if (role === "stateSnapshot") return this.stateSnapshotToUser(m);
2068
+ return m;
2069
+ }),
2070
+ tools: tools.length > 0 ? tools : void 0
2071
+ };
2072
+ }
2073
+ /** compactionSummary 消息 → user 消息(所有 provider 均兼容 user role) */
2074
+ compactionSummaryToUser(msg) {
2075
+ const text = typeof msg.content === "string" ? msg.content : extractText(msg.content);
2076
+ return {
2077
+ role: "user",
2078
+ content: `${COMPACTION_USER_PREFIX}
2079
+ ${text}`,
2080
+ timestamp: msg.timestamp
2081
+ };
2082
+ }
2083
+ /** stateSnapshot 消息 → user 消息(所有 provider 均兼容 user role) */
2084
+ stateSnapshotToUser(msg) {
2085
+ const text = typeof msg.content === "string" ? msg.content : extractText(msg.content);
2086
+ return {
2087
+ role: "user",
2088
+ content: `${STATE_SNAPSHOT_USER_PREFIX}
2089
+ ${text}`,
2090
+ timestamp: msg.timestamp
2091
+ };
2092
+ }
2093
+ };
2094
+
2095
+ // ../agent/runtime/loops.ts
2096
+ var RunLoops = class {
2097
+ constructor(_d) {
2098
+ this._d = _d;
2099
+ }
2100
+ _d;
2101
+ async run(request, sessionKey, session, traceId, queuedMs) {
2102
+ const activeStartedAt = Date.now();
2103
+ this._d.onSessionActivated(sessionKey);
2104
+ await this._d.hooks.beforeInitialize.promise(request);
2105
+ await this._d.hooks.afterInitialize.promise(request);
2106
+ const finalRequest = await this._d.hooks.beforeRun.promise(request);
2107
+ if (!finalRequest.ephemeral) {
2108
+ await this._d.persistence.hydrate(sessionKey, session);
2109
+ }
2110
+ const compilation = this._d.createCompilation(finalRequest, sessionKey, session, traceId);
2111
+ compilation.messages.push(this._d.buildUserMessage(finalRequest));
2112
+ try {
2113
+ await this.runLoop(compilation, finalRequest, session);
2114
+ const result = this._d.buildResult(compilation);
2115
+ await this._d.hooks.beforeEmit.promise(result);
2116
+ await this._d.hooks.afterEmit.promise(result);
2117
+ await this._d.hooks.done.promise(result, finalRequest);
2118
+ compilation.completed = true;
2119
+ await this.emitRunEnd(finalRequest, sessionKey, compilation, result, queuedMs, activeStartedAt);
2120
+ return result;
2121
+ } catch (err) {
2122
+ const error = err;
2123
+ const message = error?.message ?? String(err);
2124
+ if (error?.name !== "AbortError") {
2125
+ console.error("[Runtime] \u8FD0\u884C\u5931\u8D25:", error?.stack ?? message);
2126
+ }
2127
+ await this._d.hooks.failed.promise(error, finalRequest);
2128
+ const result = new ResultBuilder().error(message).build();
2129
+ await this.emitRunEnd(finalRequest, sessionKey, compilation, result, queuedMs, activeStartedAt);
2130
+ return result;
2131
+ } finally {
2132
+ await this._d.persistence.persistSafe(finalRequest, sessionKey);
2133
+ }
2134
+ }
2135
+ async *stream(request, sessionKey, session, traceId, queuedMs) {
2136
+ const activeStartedAt = Date.now();
2137
+ this._d.onSessionActivated(sessionKey);
2138
+ await this._d.hooks.beforeInitialize.promise(request);
2139
+ await this._d.hooks.afterInitialize.promise(request);
2140
+ const finalRequest = await this._d.hooks.beforeRun.promise(request);
2141
+ if (!finalRequest.ephemeral) {
2142
+ await this._d.persistence.hydrate(sessionKey, session);
2143
+ }
2144
+ const compilation = this._d.createCompilation(finalRequest, sessionKey, session, traceId);
2145
+ compilation.messages.push(this._d.buildUserMessage(finalRequest));
2146
+ try {
2147
+ for await (const chunk of this.runLoopStream(compilation, finalRequest, session)) {
2148
+ yield chunk;
2149
+ }
2150
+ const result = this._d.buildResult(compilation);
2151
+ await this._d.hooks.beforeEmit.promise(result);
2152
+ await this._d.hooks.afterEmit.promise(result);
2153
+ await this._d.hooks.done.promise(result, finalRequest);
2154
+ yield { type: "done", result };
2155
+ await this.emitRunEnd(finalRequest, sessionKey, compilation, result, queuedMs, activeStartedAt);
2156
+ } catch (err) {
2157
+ const error = err;
2158
+ const message = error?.message ?? String(err);
2159
+ if (error?.name !== "AbortError") {
2160
+ console.error("[Runtime] \u6D41\u5F0F\u8FD0\u884C\u5931\u8D25:", error?.stack ?? message);
2161
+ }
2162
+ await this._d.hooks.failed.promise(error, finalRequest);
2163
+ const result = new ResultBuilder().error(message).build();
2164
+ yield { type: "error", content: message };
2165
+ yield { type: "done", result };
2166
+ await this.emitRunEnd(finalRequest, sessionKey, compilation, result, queuedMs, activeStartedAt);
2167
+ } finally {
2168
+ await this._d.persistence.persistSafe(finalRequest, sessionKey);
2169
+ }
2170
+ }
2171
+ // ─── 对话循环(同步) ───────────────────────────────────────────
2172
+ async runLoop(compilation, request, session) {
2173
+ session.isStreaming = true;
2174
+ session.abortController = new AbortController();
2175
+ const sessionKey = this._d.resolveSessionKey(request);
2176
+ try {
2177
+ let maxTurns = this._d.getMaxTurns();
2178
+ let turnCount = 0;
2179
+ while (maxTurns-- > 0) {
2180
+ turnCount += 1;
2181
+ await this._d.transformMessages(compilation, sessionKey);
2182
+ await this._d.compactor.maybeCompactByThreshold(
2183
+ compilation,
2184
+ sessionKey,
2185
+ session.abortController.signal
2186
+ );
2187
+ const assistantMessage = await this._d.modelTurn.streamModel(
2188
+ compilation,
2189
+ session.abortController.signal,
2190
+ sessionKey
2191
+ );
2192
+ compilation.messages.push(assistantMessage);
2193
+ await this._d.persistence.persistSafe(request, sessionKey);
2194
+ const toolCalls = extractToolCalls(assistantMessage.content);
2195
+ if (toolCalls.length === 0) {
2196
+ break;
2197
+ }
2198
+ const outcome = await this.executeToolCalls(
2199
+ compilation,
2200
+ toolCalls,
2201
+ session.abortController.signal
2202
+ );
2203
+ await this._d.persistence.persistSafe(request, sessionKey);
2204
+ if (outcome.terminate) {
2205
+ compilation.terminateReason = outcome.terminateReason ?? "terminated";
2206
+ break;
2207
+ }
2208
+ }
2209
+ if (maxTurns < 0) compilation.maxTurnsExhausted = true;
2210
+ compilation.turnCount = turnCount;
2211
+ } finally {
2212
+ this._d.store.markIdle(session);
2213
+ }
2214
+ }
2215
+ // ─── 对话循环(流式) ───────────────────────────────────────────
2216
+ async *runLoopStream(compilation, request, session) {
2217
+ session.isStreaming = true;
2218
+ session.abortController = new AbortController();
2219
+ const sessionKey = this._d.resolveSessionKey(request);
2220
+ try {
2221
+ let maxTurns = this._d.getMaxTurns();
2222
+ let turnCount = 0;
2223
+ while (maxTurns-- > 0) {
2224
+ turnCount += 1;
2225
+ await this._d.transformMessages(compilation, sessionKey);
2226
+ await this._d.compactor.maybeCompactByThreshold(
2227
+ compilation,
2228
+ sessionKey,
2229
+ session.abortController.signal
2230
+ );
2231
+ const turn = this._d.modelTurn.modelTurnWithRecovery(
2232
+ compilation,
2233
+ session.abortController.signal,
2234
+ sessionKey,
2235
+ true
2236
+ // stream 模式:同时统计首 token 延迟
2237
+ );
2238
+ let turnResult = await turn.next();
2239
+ while (!turnResult.done) {
2240
+ yield turnResult.value;
2241
+ turnResult = await turn.next();
2242
+ }
2243
+ const assistantMessage = turnResult.value;
2244
+ if (!assistantMessage) break;
2245
+ compilation.messages.push(assistantMessage);
2246
+ await this._d.persistence.persistSafe(request, sessionKey);
2247
+ const toolCalls = extractToolCalls(assistantMessage.content);
2248
+ if (toolCalls.length === 0) {
2249
+ break;
2250
+ }
2251
+ for (const toolCall of toolCalls) {
2252
+ yield {
2253
+ type: "tool_start",
2254
+ toolName: toolCall.name,
2255
+ toolCallId: toolCall.id
2256
+ };
2257
+ }
2258
+ const outcome = await this.executeToolCalls(
2259
+ compilation,
2260
+ toolCalls,
2261
+ session.abortController.signal
2262
+ );
2263
+ await this._d.persistence.persistSafe(request, sessionKey);
2264
+ for (let i = 0; i < toolCalls.length; i++) {
2265
+ yield {
2266
+ type: "tool_end",
2267
+ toolName: toolCalls[i].name,
2268
+ toolCallId: toolCalls[i].id,
2269
+ isError: this._d.tools.isErrorResult(outcome.results[i])
2270
+ };
2271
+ }
2272
+ if (outcome.terminate) {
2273
+ compilation.terminateReason = outcome.terminateReason ?? "terminated";
2274
+ break;
2275
+ }
2276
+ }
2277
+ if (maxTurns < 0) compilation.maxTurnsExhausted = true;
2278
+ compilation.turnCount = turnCount;
2279
+ } finally {
2280
+ this._d.store.markIdle(session);
2281
+ }
2282
+ }
2283
+ /** 同步循环:执行工具调用并将结果消息按原顺序追加 */
2284
+ async executeToolCalls(compilation, toolCalls, signal) {
2285
+ const outcome = await this._d.tools.run(
2286
+ toolCalls,
2287
+ signal,
2288
+ compilation.request,
2289
+ compilation.traceId
2290
+ );
2291
+ for (let i = 0; i < toolCalls.length; i++) {
2292
+ compilation.messages.push(
2293
+ this._d.tools.buildResultMessage(toolCalls[i], outcome.results[i])
2294
+ );
2295
+ }
2296
+ return outcome;
2297
+ }
2298
+ /** run 级完成事件上报(补充默认模型 id) */
2299
+ async emitRunEnd(request, sessionKey, compilation, result, queuedMs, activeStartedAt) {
2300
+ await this._d.telemetry.emitRunEnd(
2301
+ request,
2302
+ sessionKey,
2303
+ compilation,
2304
+ result,
2305
+ queuedMs,
2306
+ activeStartedAt,
2307
+ this._d.getModel().id
2308
+ );
2309
+ }
2310
+ };
2311
+
2312
+ // ../agent/runtime/index.ts
2313
+ var AgentRuntime = class _AgentRuntime {
2314
+ _config;
2315
+ _extensions;
2316
+ _hooks;
2317
+ /** 上下文转换器列表,按数组顺序链式执行(上一个输出作为下一个输入) */
2318
+ _transformers;
2319
+ _model;
2320
+ _streamFn;
2321
+ _systemPrompt;
2322
+ _thinkingLevel;
2323
+ _globalTools = /* @__PURE__ */ new Map();
2324
+ /** 默认会话 key(常量 'default';请求未指定 sessionKey 时路由到此会话) */
2325
+ _sessionKey;
2326
+ _sessionStorage;
2327
+ /** Extension 应用时的上下文(shared Map 供 ToolCallContext 引用) */
2328
+ _extensionContext;
2329
+ _maxTurns;
2330
+ _toolTimeoutMs;
2331
+ _parallelToolCalls;
2332
+ _contextBudgetRatio;
2333
+ /** 内置摘要压缩配置(未配置 = 保持旧行为,仅硬截断兜底) */
2334
+ _compaction;
2335
+ _telemetry;
2336
+ /** 框架级工具权限策略(未配置 → 默认 fail-closed,见 permissionFailOpen) */
2337
+ _permissionPolicy;
2338
+ /** 未配置策略时是否显式放行工具执行(默认 false = 拒绝,安全姿态) */
2339
+ _permissionFailOpen;
2340
+ /** 审批管理器(pending 决策挂起等待外部批准;未配置 → pending 视为 deny) */
2341
+ _approvals;
2342
+ /** 审批等待超时(毫秒) */
2343
+ _approvalTimeoutMs;
2344
+ /** traceId 生成器(测试可注入确定性 id) */
2345
+ _traceIdGenerator;
2346
+ // ─── 协作模块(构造时组装,职责见文件头注释) ───────────────────
2347
+ _store;
2348
+ _persistence;
2349
+ _emitter;
2350
+ _tools;
2351
+ _compactor;
2352
+ _modelTurn;
2353
+ _loops;
2354
+ constructor(options) {
2355
+ this._config = options.config ?? {};
2356
+ this._extensions = new ExtensionManager();
2357
+ this._hooks = this._extensions.getHooks();
2358
+ this._transformers = [...options.transformers ?? []];
2359
+ this._model = options.model ?? {
2360
+ id: "unknown",
2361
+ name: "unknown",
2362
+ provider: "unknown",
2363
+ contextWindow: 128e3,
2364
+ maxTokens: 8192,
2365
+ reasoning: false
2366
+ };
2367
+ this._streamFn = options.streamFn ?? (async function* () {
2368
+ throw new Error("streamFn \u672A\u8BBE\u7F6E\uFF0C\u8BF7\u901A\u8FC7 setStreamFn() \u6216 RuntimeOptions.streamFn \u63D0\u4F9B");
2369
+ });
2370
+ this._systemPrompt = options.systemPrompt ?? "";
2371
+ this._thinkingLevel = options.thinkingLevel ?? "off";
2372
+ this._sessionStorage = options.sessionStorage;
2373
+ this._maxTurns = options.maxTurns ?? 50;
2374
+ this._toolTimeoutMs = options.toolTimeoutMs ?? 12e4;
2375
+ this._parallelToolCalls = options.parallelToolCalls ?? true;
2376
+ this._contextBudgetRatio = options.contextBudgetRatio ?? 0.8;
2377
+ this._compaction = options.compaction;
2378
+ this._telemetry = options.telemetry;
2379
+ this._permissionPolicy = options.permissionPolicy;
2380
+ this._permissionFailOpen = options.permissionFailOpen ?? false;
2381
+ this._approvals = options.approvals;
2382
+ this._approvalTimeoutMs = options.approvalTimeoutMs ?? 3e5;
2383
+ this._traceIdGenerator = options.traceIdGenerator;
2384
+ if (options.tools) {
2385
+ for (const tool of options.tools) {
2386
+ this._globalTools.set(tool.name, tool);
2387
+ }
2388
+ }
2389
+ this._sessionKey = "default";
2390
+ this._emitter = new RuntimeTelemetry(options.telemetry, options.traceIdGenerator);
2391
+ setTapErrorHandler((info) => {
2392
+ void this._emitter.emit("onHookError", {
2393
+ sessionKey: this._sessionKey,
2394
+ hook: info.hook,
2395
+ tap: info.tap,
2396
+ error: info.error
2397
+ });
2398
+ });
2399
+ this._store = new SessionStore(this._sessionKey, options.maxSessions);
2400
+ this._persistence = new SessionPersistence(
2401
+ this._sessionStorage,
2402
+ () => this._model,
2403
+ this._store
2404
+ );
2405
+ this._tools = new ToolExecutor({
2406
+ hooks: this._hooks,
2407
+ telemetry: this._emitter,
2408
+ permissionPolicy: this._permissionPolicy,
2409
+ permissionFailOpen: this._permissionFailOpen,
2410
+ approvals: this._approvals,
2411
+ approvalTimeoutMs: this._approvalTimeoutMs,
2412
+ toolTimeoutMs: this._toolTimeoutMs,
2413
+ parallelToolCalls: this._parallelToolCalls,
2414
+ defaultSessionKey: this._sessionKey,
2415
+ getTools: () => this._globalTools,
2416
+ getShared: () => this._extensionContext?.shared
2417
+ });
2418
+ this._compactor = new ContextCompactor({
2419
+ telemetry: this._emitter,
2420
+ getModel: () => this._model,
2421
+ getStreamFn: () => this._streamFn,
2422
+ contextBudgetRatio: this._contextBudgetRatio,
2423
+ compaction: this._compaction
2424
+ });
2425
+ this._modelTurn = new ModelTurnRunner({
2426
+ telemetry: this._emitter,
2427
+ compactor: this._compactor,
2428
+ hooks: this._hooks,
2429
+ getModel: () => this._model,
2430
+ getStreamFn: () => this._streamFn,
2431
+ getSystemPrompt: () => this._systemPrompt,
2432
+ getThinkingLevel: () => this._thinkingLevel,
2433
+ getTools: () => this._globalTools,
2434
+ resolveSessionKey: (request) => this.resolveSessionKey(request)
2435
+ });
2436
+ this._loops = new RunLoops({
2437
+ hooks: this._hooks,
2438
+ telemetry: this._emitter,
2439
+ store: this._store,
2440
+ persistence: this._persistence,
2441
+ tools: this._tools,
2442
+ compactor: this._compactor,
2443
+ modelTurn: this._modelTurn,
2444
+ getModel: () => this._model,
2445
+ getMaxTurns: () => this._maxTurns,
2446
+ resolveSessionKey: (request) => this.resolveSessionKey(request),
2447
+ onSessionActivated: (key) => this.syncExtensionSessionKey(key),
2448
+ createCompilation: (request, sessionKey, session, traceId) => this.createCompilation(request, sessionKey, session, traceId),
2449
+ buildUserMessage: (request) => buildUserMessage(request),
2450
+ buildResult: (compilation) => buildResult(compilation),
2451
+ transformMessages: (compilation, sessionKey) => this.transformMessages(compilation, sessionKey)
2452
+ });
2453
+ }
2454
+ // ─── 静态工厂 ───────────────────────────────────────────────────
2455
+ static create(options = {}) {
2456
+ const runtime = new _AgentRuntime(options);
2457
+ if (options.extensions) {
2458
+ runtime._extensions.registerAll(options.extensions);
2459
+ }
2460
+ const ctx = {
2461
+ config: runtime._config,
2462
+ workspace: options.workspace ?? process.cwd(),
2463
+ sessionKey: runtime._sessionKey,
2464
+ shared: /* @__PURE__ */ new Map(),
2465
+ runtime
2466
+ };
2467
+ runtime._extensionContext = ctx;
2468
+ runtime._extensions.applyAll(ctx);
2469
+ return runtime;
2470
+ }
2471
+ // ─── Runtime 接口实现 ───────────────────────────────────────────
2472
+ get config() {
2473
+ return this._config;
2474
+ }
2475
+ get extensions() {
2476
+ return this._extensions;
2477
+ }
2478
+ get hooks() {
2479
+ return this._hooks;
2480
+ }
2481
+ // ─── 工具/模型/流管理 ───────────────────────────────────────────
2482
+ registerTool(tool) {
2483
+ if (this._globalTools.has(tool.name)) {
2484
+ console.warn(`[Runtime] \u5DE5\u5177 "${tool.name}" \u5DF2\u5B58\u5728\uFF0C\u5C06\u88AB\u8986\u76D6`);
2485
+ }
2486
+ this._globalTools.set(tool.name, tool);
2487
+ return this;
2488
+ }
2489
+ registerTools(tools) {
2490
+ for (const tool of tools) {
2491
+ this.registerTool(tool);
2492
+ }
2493
+ return this;
2494
+ }
2495
+ unregisterTool(name) {
2496
+ return this._globalTools.delete(name);
2497
+ }
2498
+ setModel(model) {
2499
+ this._model = model;
2500
+ return this;
2501
+ }
2502
+ setSystemPrompt(prompt) {
2503
+ this._systemPrompt = prompt;
2504
+ return this;
2505
+ }
2506
+ setThinkingLevel(level) {
2507
+ this._thinkingLevel = level;
2508
+ return this;
2509
+ }
2510
+ setStreamFn(fn) {
2511
+ this._streamFn = fn;
2512
+ return this;
2513
+ }
2514
+ registerExtension(extension) {
2515
+ this._extensions.register(extension);
2516
+ return this;
2517
+ }
2518
+ useTransformer(transformer) {
2519
+ this._transformers.push(transformer);
2520
+ return this;
2521
+ }
2522
+ /**
2523
+ * 获取指定会话的消息列表(默认会话;会话不存在返回空数组)。
2524
+ * 同步方法:会话被 LRU 淘汰且配置了存储时,后台异步恢复(下次调用可读到),
2525
+ * 需确定性读取(如断言/导出)请用 loadMessages()。
2526
+ */
2527
+ getMessages(sessionKey) {
2528
+ const key = sessionKey ?? this._sessionKey;
2529
+ if (this._store.has(key)) {
2530
+ return this._store.getMessagesCopy(key);
2531
+ }
2532
+ if (this._sessionStorage) {
2533
+ const session = this._store.ensure(key);
2534
+ void this._persistence.hydrate(key, session).catch(() => {
2535
+ });
2536
+ }
2537
+ return [];
2538
+ }
2539
+ /** 异步读取指定会话的消息列表:内存未命中(LRU 淘汰)时等待存储恢复后返回 */
2540
+ async loadMessages(sessionKey) {
2541
+ const key = sessionKey ?? this._sessionKey;
2542
+ const existing = this._store.peek(key);
2543
+ if (existing && existing.hydrated) {
2544
+ return this._store.getMessagesCopy(key);
2545
+ }
2546
+ const session = existing ?? this._store.ensure(key);
2547
+ await this._persistence.hydrate(key, session);
2548
+ return this._store.getMessagesCopy(key);
2549
+ }
2315
2550
  /**
2316
- * 触发遥测回调。全可选、失败不阻断主流程。
2551
+ * 指定会话消息的只读视图(高频轮询用):浅拷贝数组、共享消息对象,
2552
+ * 避免每次 structuredClone 深拷贝整个会话历史;调用方不得修改内容。
2317
2553
  */
2318
- async emitTelemetry(event, info) {
2319
- const fn = this._telemetry?.[event];
2320
- if (!fn) return;
2554
+ peekMessages(sessionKey) {
2555
+ return this._store.peekMessages(sessionKey ?? this._sessionKey);
2556
+ }
2557
+ /**
2558
+ * 同步共享扩展上下文的会话键为本次 run/stream 的真实会话。
2559
+ * 注意:ExtensionContext 为跨会话共享单例,多会话并发时该值为最近激活的
2560
+ * 会话;需精确会话请用 ToolCallContext.sessionKey / Request.sessionKey。
2561
+ */
2562
+ syncExtensionSessionKey(sessionKey) {
2563
+ if (this._extensionContext && this._extensionContext.sessionKey !== sessionKey) {
2564
+ this._extensionContext.sessionKey = sessionKey;
2565
+ }
2566
+ }
2567
+ // ─── 核心运行逻辑 ───────────────────────────────────────────────
2568
+ /** 解析请求路由的会话 key:request.sessionKey ?? 默认会话 key */
2569
+ resolveSessionKey(request) {
2570
+ return request.sessionKey ?? this._sessionKey;
2571
+ }
2572
+ /** 当前活跃的会话 key 列表(含默认会话) */
2573
+ getSessionKeys() {
2574
+ return this._store.keys();
2575
+ }
2576
+ /** 某会话是否存在(内存中) */
2577
+ hasSession(sessionKey) {
2578
+ return this._store.has(sessionKey);
2579
+ }
2580
+ async run(request) {
2581
+ const validation = validateRequest(request);
2582
+ if (!validation.valid) {
2583
+ const invalidResult = new ResultBuilder().error(`\u8BF7\u6C42\u6821\u9A8C\u5931\u8D25: ${validation.errors.join("; ")}`).build();
2584
+ await this._emitter.emit("onRunEnd", {
2585
+ traceId: this._emitter.newTraceId(),
2586
+ sessionKey: this.resolveSessionKey(request),
2587
+ request,
2588
+ durationMs: 0,
2589
+ activeMs: 0,
2590
+ queuedMs: 0,
2591
+ turnCount: 0,
2592
+ result: invalidResult,
2593
+ success: false,
2594
+ errorClass: "validation",
2595
+ tokens: { input: 0, output: 0 }
2596
+ });
2597
+ return invalidResult;
2598
+ }
2599
+ const finalRequest = normalizeRequest(request);
2600
+ const sessionKey = this.resolveSessionKey(finalRequest);
2601
+ const traceId = this._emitter.newTraceId();
2602
+ const queuedAt = Date.now();
2603
+ await this._emitter.emit("onRunStart", {
2604
+ traceId,
2605
+ sessionKey,
2606
+ request: finalRequest,
2607
+ queuedAt
2608
+ });
2609
+ const session = this._store.ensure(sessionKey);
2610
+ const release = await this._store.acquire(session);
2611
+ const queuedMs = Date.now() - queuedAt;
2612
+ try {
2613
+ return await this._persistence.withStorageLock(
2614
+ finalRequest,
2615
+ sessionKey,
2616
+ () => this._loops.run(finalRequest, sessionKey, session, traceId, queuedMs)
2617
+ );
2618
+ } finally {
2619
+ release();
2620
+ }
2621
+ }
2622
+ async *stream(request) {
2623
+ const validation = validateRequest(request);
2624
+ if (!validation.valid) {
2625
+ const message = `\u8BF7\u6C42\u6821\u9A8C\u5931\u8D25: ${validation.errors.join("; ")}`;
2626
+ await this._emitter.emit("onRunEnd", {
2627
+ traceId: this._emitter.newTraceId(),
2628
+ sessionKey: this.resolveSessionKey(request),
2629
+ request,
2630
+ durationMs: 0,
2631
+ activeMs: 0,
2632
+ queuedMs: 0,
2633
+ turnCount: 0,
2634
+ result: new ResultBuilder().error(message).build(),
2635
+ success: false,
2636
+ errorClass: "validation",
2637
+ tokens: { input: 0, output: 0 }
2638
+ });
2639
+ yield { type: "error", content: message };
2640
+ yield { type: "done" };
2641
+ return;
2642
+ }
2643
+ const finalRequest = normalizeRequest(request);
2644
+ const sessionKey = this.resolveSessionKey(finalRequest);
2645
+ const traceId = this._emitter.newTraceId();
2646
+ const queuedAt = Date.now();
2647
+ await this._emitter.emit("onRunStart", {
2648
+ traceId,
2649
+ sessionKey,
2650
+ request: finalRequest,
2651
+ queuedAt
2652
+ });
2653
+ const session = this._store.ensure(sessionKey);
2654
+ const release = await this._store.acquire(session);
2655
+ const queuedMs = Date.now() - queuedAt;
2321
2656
  try {
2322
- await Promise.resolve(fn(info));
2323
- } catch (err) {
2324
- console.warn(`[aipack] telemetry "${String(event)}" \u4E0A\u62A5\u5931\u8D25:`, err);
2657
+ yield* this._persistence.streamWithStorageLock(
2658
+ finalRequest,
2659
+ sessionKey,
2660
+ () => this._loops.stream(finalRequest, sessionKey, session, traceId, queuedMs)
2661
+ );
2662
+ } finally {
2663
+ release();
2325
2664
  }
2326
2665
  }
2327
- /** traceId 生成:优先用注入的生成器(测试可确定性) */
2328
- newTraceId() {
2329
- return this._traceIdGenerator ? this._traceIdGenerator() : newTraceId();
2666
+ createCompilation(request, sessionKey, session, traceId) {
2667
+ return {
2668
+ request,
2669
+ graph: createTaskGraph(),
2670
+ resources: [],
2671
+ messages: (session ?? this._store.ensure(sessionKey ?? this._sessionKey)).messages,
2672
+ completed: false,
2673
+ traceId: traceId ?? this._emitter.newTraceId()
2674
+ };
2330
2675
  }
2331
- /** 组装并上报 run 级完成事件(_run/_stream 内部统一调用) */
2332
- async emitRunEnd(request, sessionKey, compilation, result, queuedMs, activeStartedAt) {
2333
- const activeMs = Date.now() - activeStartedAt;
2334
- await this.emitTelemetry("onRunEnd", {
2335
- traceId: compilation.traceId,
2336
- sessionKey,
2337
- // 请求未显式指定 model 时补实际模型(模型排行按 run 级 requests 统计,缺省会落入 'unknown')
2338
- request: request.model ? request : { ...request, model: this._model.id },
2339
- durationMs: activeMs + queuedMs,
2340
- activeMs,
2341
- queuedMs,
2342
- turnCount: compilation.turnCount ?? 0,
2343
- result,
2344
- success: result.success,
2345
- errorClass: this.runErrorClass(compilation),
2346
- tokens: {
2347
- input: result.usage.input ?? 0,
2348
- output: result.usage.output ?? 0,
2349
- cacheRead: result.usage.cacheRead,
2350
- cacheWrite: result.usage.cacheWrite
2351
- },
2352
- ttftMs: compilation.ttftMs
2353
- });
2676
+ async close() {
2677
+ await this._store.drainAndClear();
2678
+ this._extensions.clear();
2679
+ this._transformers = [];
2354
2680
  }
2355
- /** run 级错误分类:terminate → 'terminated';否则只看最后一条 assistant 消息(与 buildResult 的 result.success 同口径) */
2356
- runErrorClass(compilation) {
2357
- if (compilation.terminateReason) return "terminated";
2358
- const messages = compilation.messages;
2359
- for (let i = messages.length - 1; i >= 0; i--) {
2360
- const m = messages[i];
2361
- if (m.role === "assistant") {
2362
- if (!m.errorMessage) return void 0;
2363
- const cls = errorClassFromMessage(m.errorMessage);
2364
- return cls ?? "unknown";
2365
- }
2681
+ // ─── 上下文转换(链式执行,原地替换保持 session 引用) ──────────
2682
+ /** 链式执行上下文转换器(按数组顺序,上一个输出作为下一个输入) */
2683
+ async transformMessages(compilation, sessionKey) {
2684
+ if (this._transformers.length === 0) {
2685
+ return;
2366
2686
  }
2367
- return void 0;
2368
- }
2369
- buildResult(compilation) {
2370
- const messages = compilation.messages;
2371
- let content = "";
2372
- let stopReason = "completed";
2373
- let error;
2374
- const toolsUsed = [];
2375
- const usage = {};
2376
- for (const msg of messages) {
2377
- if (msg.role === "assistant") {
2378
- const assistant = msg;
2379
- content = extractText(assistant.content);
2380
- stopReason = assistant.stopReason ?? "completed";
2381
- error = assistant.errorMessage;
2382
- if (assistant.usage) {
2383
- usage.input = (usage.input ?? 0) + assistant.usage.input;
2384
- usage.output = (usage.output ?? 0) + assistant.usage.output;
2385
- usage.total = (usage.total ?? 0) + assistant.usage.total;
2386
- if (assistant.usage.cacheRead) usage.cacheRead = (usage.cacheRead ?? 0) + assistant.usage.cacheRead;
2387
- if (assistant.usage.cacheWrite) usage.cacheWrite = (usage.cacheWrite ?? 0) + assistant.usage.cacheWrite;
2388
- }
2687
+ let resources = messagesToResources(compilation.messages);
2688
+ const originalResources = resources;
2689
+ const context = {
2690
+ graph: compilation.graph,
2691
+ runtime: {
2692
+ sessionKey,
2693
+ turn: compilation.messages.length,
2694
+ contextWindow: this._model.contextWindow,
2695
+ maxTokens: this._model.maxTokens,
2696
+ contextBudgetRatio: this._contextBudgetRatio
2389
2697
  }
2390
- if (msg.role === "toolResult") {
2391
- const toolMsg = msg;
2392
- if (!toolsUsed.includes(toolMsg.toolName)) {
2393
- toolsUsed.push(toolMsg.toolName);
2394
- }
2698
+ };
2699
+ for (const transformer of this._transformers) {
2700
+ try {
2701
+ const next = await transformer.transform(resources, context);
2702
+ if (next !== resources) resources = next;
2703
+ } catch (err) {
2704
+ console.warn(
2705
+ `[Runtime] \u8F6C\u6362\u5668 "${transformer.name}" \u5931\u8D25\uFF0C\u5DF2\u8DF3\u8FC7:`,
2706
+ err?.message ?? err
2707
+ );
2395
2708
  }
2396
2709
  }
2397
- const resources = messagesToResources(messages);
2398
- const builder = new ResultBuilder().content(content).toolsUsed(toolsUsed).usage(usage).stopReason(stopReason).error(error).resources(resources).metadata("traceId", compilation.traceId);
2399
- if (compilation.terminateReason) {
2400
- builder.stopReason("terminated").metadata("terminateReason", compilation.terminateReason);
2401
- }
2402
- if (compilation.maxTurnsExhausted) {
2403
- builder.stopReason("max_turns").metadata("maxTurns", true);
2710
+ if (resources === originalResources) {
2711
+ return;
2404
2712
  }
2405
- return builder.build();
2713
+ const messages = resourcesToMessages(resources);
2714
+ compilation.messages.splice(0, compilation.messages.length, ...messages);
2406
2715
  }
2407
- streamEventToChunk(event) {
2408
- switch (event.type) {
2409
- case "text_delta":
2410
- return { type: "text", content: event.delta };
2411
- case "thinking_delta":
2412
- return { type: "thinking", content: event.delta };
2413
- case "error":
2414
- return {
2415
- type: "error",
2416
- content: event.message.errorMessage || "\u6A21\u578B\u8C03\u7528\u51FA\u9519"
2417
- };
2418
- default:
2419
- return null;
2716
+ // ─── 手动压缩(交互命令 /compact 等) ───────────────────────────
2717
+ /**
2718
+ * 手动压缩指定会话:跳过阈值判断,直接将历史压缩至 targetRatio
2719
+ * 保留量(复用 compactor 的保留段计算与 compactOrTruncate 执行路径),
2720
+ * 压缩后持久化。返回压缩模式;compaction 未启用或无可压缩段时返回 null。
2721
+ */
2722
+ async compact(sessionKey) {
2723
+ if (!this._compaction || this._compaction.enabled === false) return null;
2724
+ const key = sessionKey ?? this._sessionKey;
2725
+ const session = this._store.ensure(key);
2726
+ const messages = session.messages;
2727
+ if (messages.length === 0) return null;
2728
+ const contextWindow = this._model.contextWindow;
2729
+ if (!contextWindow || contextWindow <= 0) return null;
2730
+ const split = this._compactor.computeKeepSplit(messages, contextWindow);
2731
+ if (split <= 0 || split >= messages.length) return null;
2732
+ const mode = await this._compactor.compactOrTruncate(
2733
+ messages,
2734
+ split,
2735
+ key,
2736
+ `manual-${Date.now().toString(36)}`,
2737
+ new AbortController().signal,
2738
+ "threshold"
2739
+ );
2740
+ try {
2741
+ await this._persistence.persist(key, session);
2742
+ } catch (err) {
2743
+ console.warn("[Runtime] \u624B\u52A8\u538B\u7F29\u540E\u6301\u4E45\u5316\u5931\u8D25:", err?.message);
2420
2744
  }
2745
+ return mode;
2421
2746
  }
2422
2747
  // ─── 便捷方法 ───────────────────────────────────────────────────
2423
2748
  /** 终止指定会话的运行(默认会话;会话不存在为 no-op) */
2424
2749
  abort(sessionKey) {
2425
- this._sessions.get(sessionKey ?? this._sessionKey)?.abortController?.abort();
2750
+ this._store.peek(sessionKey ?? this._sessionKey)?.abortController?.abort();
2426
2751
  }
2427
2752
  /** 检查指定会话是否正在运行(默认会话;会话不存在返回 false) */
2428
2753
  isBusy(sessionKey) {
2429
- return this._sessions.get(sessionKey ?? this._sessionKey)?.isStreaming ?? false;
2754
+ return this._store.peek(sessionKey ?? this._sessionKey)?.isStreaming ?? false;
2430
2755
  }
2431
2756
  /** 等待指定会话空闲(默认会话;基于 promise,无轮询) */
2432
2757
  async waitForIdle(sessionKey, timeoutMs) {
2433
- const session = this._sessions.get(sessionKey ?? this._sessionKey);
2434
- if (!session || !session.isStreaming) return;
2435
- if (timeoutMs === void 0) {
2436
- await new Promise((resolve) => {
2437
- session.idleResolvers.push(resolve);
2438
- });
2439
- return;
2440
- }
2441
- await new Promise((resolve, reject) => {
2442
- const resolver = () => {
2443
- clearTimeout(timer);
2444
- resolve();
2445
- };
2446
- const timer = setTimeout(() => {
2447
- const i = session.idleResolvers.indexOf(resolver);
2448
- if (i >= 0) session.idleResolvers.splice(i, 1);
2449
- reject(new Error(
2450
- `[Runtime] waitForIdle \u8D85\u65F6\uFF08${timeoutMs}ms\uFF09: ${sessionKey ?? "default"}`
2451
- ));
2452
- }, timeoutMs);
2453
- session.idleResolvers.push(resolver);
2454
- });
2758
+ await this._store.waitForIdle(sessionKey ?? this._sessionKey, timeoutMs);
2455
2759
  }
2456
2760
  /** 清除指定会话消息(仅内存;下次 run 会从存储恢复) */
2457
2761
  clearSession(sessionKey) {
2458
- const session = this._sessions.get(sessionKey ?? this._sessionKey);
2459
- if (!session) return;
2460
- session.messages = [];
2461
- session.hydrated = false;
2762
+ this._store.clearMessages(sessionKey ?? this._sessionKey);
2462
2763
  }
2463
2764
  /** 删除指定会话(内存 + 存储),返回是否删除成功 */
2464
2765
  async deleteSession(sessionKey) {
2465
2766
  const key = sessionKey ?? this._sessionKey;
2466
- const session = this._sessions.get(key);
2467
- if (session) {
2468
- await session.queue;
2469
- this._sessions.delete(key);
2470
- }
2471
- if (!this._sessionStorage) return true;
2767
+ await this._store.remove(key);
2472
2768
  const storage = this._sessionStorage;
2769
+ if (!storage) return true;
2473
2770
  if (!storage.withLock) return storage.delete(key);
2474
2771
  return storage.withLock(key, () => storage.delete(key));
2475
2772
  }
@@ -2478,7 +2775,55 @@ function createRuntime(options) {
2478
2775
  return AgentRuntime.create(options);
2479
2776
  }
2480
2777
 
2778
+ // core/event-stream.ts
2779
+ function createEventStream(execute, sink) {
2780
+ const eventQueue = [];
2781
+ let resolveEvent = null;
2782
+ let done = false;
2783
+ const emit = (event) => {
2784
+ eventQueue.push(event);
2785
+ if (sink) {
2786
+ try {
2787
+ sink(event);
2788
+ } catch {
2789
+ }
2790
+ }
2791
+ resolveEvent?.();
2792
+ };
2793
+ const graphPromise = execute(emit).finally(() => {
2794
+ done = true;
2795
+ resolveEvent?.();
2796
+ });
2797
+ return (async function* () {
2798
+ while (!done || eventQueue.length > 0) {
2799
+ if (eventQueue.length > 0) {
2800
+ yield eventQueue.shift();
2801
+ } else {
2802
+ await new Promise((resolve) => {
2803
+ resolveEvent = resolve;
2804
+ });
2805
+ }
2806
+ }
2807
+ await graphPromise;
2808
+ })();
2809
+ }
2810
+
2481
2811
  // core/executor.ts
2812
+ var GraphAbortedError = class extends Error {
2813
+ constructor() {
2814
+ super("AgentGraph: \u6267\u884C\u5DF2\u88AB\u4E2D\u6B62");
2815
+ this.name = "GraphAbortedError";
2816
+ }
2817
+ };
2818
+ function toInputText(input) {
2819
+ return typeof input === "string" ? input : input.message;
2820
+ }
2821
+ var NodeTimeoutError = class extends Error {
2822
+ constructor(nodeId, timeoutMs) {
2823
+ super(`AgentGraph: \u8282\u70B9 "${nodeId}" \u6267\u884C\u8D85\u65F6\uFF08${timeoutMs}ms\uFF09`);
2824
+ this.name = "NodeTimeoutError";
2825
+ }
2826
+ };
2482
2827
  var runtimeCache = /* @__PURE__ */ new WeakMap();
2483
2828
  function ensureRuntime(node) {
2484
2829
  let rt = runtimeCache.get(node);
@@ -2491,19 +2836,52 @@ function ensureRuntime(node) {
2491
2836
  runtimeCache.set(node, rt);
2492
2837
  return rt;
2493
2838
  }
2494
- async function executeNode(node, input, ctx) {
2839
+ async function executeNodeOnce(node, req) {
2495
2840
  const runtime = ensureRuntime(node);
2841
+ if (node.timeoutMs != null && node.timeoutMs > 0) {
2842
+ let timer;
2843
+ try {
2844
+ return await Promise.race([
2845
+ runtime.run(req),
2846
+ new Promise((_, reject) => {
2847
+ timer = setTimeout(() => reject(new NodeTimeoutError(node.id, node.timeoutMs)), node.timeoutMs);
2848
+ })
2849
+ ]);
2850
+ } finally {
2851
+ if (timer !== void 0) clearTimeout(timer);
2852
+ }
2853
+ }
2854
+ return runtime.run(req);
2855
+ }
2856
+ async function executeNode(node, input, ctx) {
2496
2857
  let req;
2497
2858
  if (typeof input === "string") {
2498
2859
  req = createRequest(input, { sessionKey: `multi-agent:${node.id}` });
2499
2860
  } else {
2500
2861
  req = { ...input, sessionKey: input.sessionKey ?? `multi-agent:${node.id}` };
2501
2862
  }
2502
- const result = await runtime.run(req);
2863
+ const maxAttempts = Math.max(1, node.retry?.maxAttempts ?? 1);
2864
+ const backoffMs = node.retry?.backoffMs ?? 0;
2865
+ let lastErr;
2866
+ let succeeded;
2867
+ for (let attempt = 1; attempt <= maxAttempts; attempt++) {
2868
+ if (attempt > 1 && backoffMs > 0) {
2869
+ await new Promise((resolve) => setTimeout(resolve, backoffMs));
2870
+ }
2871
+ try {
2872
+ succeeded = await executeNodeOnce(node, req);
2873
+ break;
2874
+ } catch (err) {
2875
+ lastErr = err;
2876
+ }
2877
+ }
2878
+ if (succeeded === void 0) {
2879
+ throw lastErr;
2880
+ }
2503
2881
  if (node.outputMapping) {
2504
- node.outputMapping(result, ctx);
2882
+ node.outputMapping(succeeded, ctx);
2505
2883
  }
2506
- return result;
2884
+ return succeeded;
2507
2885
  }
2508
2886
  function resolveInput(edge, prevResult, ctx, node, originalInput) {
2509
2887
  if (edge?.transform) {
@@ -2530,12 +2908,22 @@ var GraphExecutor = class {
2530
2908
  entryId;
2531
2909
  finishCondition;
2532
2910
  abortController = new AbortController();
2911
+ /** 外部事件监听(on() API 的底层接线),事件会同步分发 */
2912
+ eventSink;
2913
+ /** 并行分支最大并发数(默认不限制) */
2914
+ concurrency;
2915
+ /** 单节点最大访问次数(环图防死循环) */
2916
+ maxVisitsPerNode;
2533
2917
  state = {
2534
2918
  nodeStates: /* @__PURE__ */ new Map(),
2535
2919
  nodeResults: /* @__PURE__ */ new Map(),
2536
2920
  stepsCompleted: 0,
2537
2921
  finished: false
2538
2922
  };
2923
+ constructor(opts) {
2924
+ this.concurrency = opts?.concurrency ?? Infinity;
2925
+ this.maxVisitsPerNode = opts?.maxVisitsPerNode ?? 10;
2926
+ }
2539
2927
  addNode(node) {
2540
2928
  this.nodes.set(node.id, node);
2541
2929
  this.state.nodeStates.set(node.id, "pending");
@@ -2553,12 +2941,42 @@ var GraphExecutor = class {
2553
2941
  this.finishCondition = condition;
2554
2942
  return this;
2555
2943
  }
2944
+ /** 注入外部事件监听(由 AgentGraph 实现的 on() 接线) */
2945
+ setEventSink(sink) {
2946
+ this.eventSink = sink;
2947
+ return this;
2948
+ }
2556
2949
  getState() {
2557
- return { ...this.state };
2950
+ return {
2951
+ ...this.state,
2952
+ nodeStates: new Map(this.state.nodeStates),
2953
+ nodeResults: new Map(this.state.nodeResults)
2954
+ };
2558
2955
  }
2559
2956
  abort() {
2560
2957
  this.abortController.abort();
2561
2958
  }
2959
+ /** 每次 run/stream 前重置中止信号,避免上一次的 abort 永久失效 */
2960
+ resetAbortController() {
2961
+ this.abortController = new AbortController();
2962
+ }
2963
+ /** 每次 run/stream 前重置执行状态,避免残留上一次的 nodeStates/结果 */
2964
+ resetState() {
2965
+ this.state = {
2966
+ nodeStates: /* @__PURE__ */ new Map(),
2967
+ nodeResults: /* @__PURE__ */ new Map(),
2968
+ stepsCompleted: 0,
2969
+ finished: false
2970
+ };
2971
+ for (const id of this.nodes.keys()) {
2972
+ this.state.nodeStates.set(id, "pending");
2973
+ }
2974
+ }
2975
+ throwIfAborted() {
2976
+ if (this.abortController.signal.aborted) {
2977
+ throw new GraphAbortedError();
2978
+ }
2979
+ }
2562
2980
  /** 同步执行图 */
2563
2981
  async run(input) {
2564
2982
  if (!this.entryId) {
@@ -2567,66 +2985,78 @@ var GraphExecutor = class {
2567
2985
  if (!this.nodes.has(this.entryId)) {
2568
2986
  throw new Error(`AgentGraph: \u5165\u53E3\u8282\u70B9 "${this.entryId}" \u4E0D\u5B58\u5728`);
2569
2987
  }
2988
+ this.resetAbortController();
2989
+ this.resetState();
2570
2990
  const ctx = createSharedContext({
2571
2991
  meta: { traceId: `ma-${Date.now()}`, startTime: Date.now() }
2572
2992
  });
2573
- const events = [];
2993
+ storeOriginalInput(ctx, input);
2994
+ const emit = this.eventSink ?? (() => {
2995
+ });
2574
2996
  try {
2575
- const result = await this.executeGraph(input, ctx, (event) => events.push(event));
2997
+ const result = await this.executeGraph(input, ctx, emit);
2998
+ emit({ type: "graph_done", result });
2576
2999
  return result;
2577
3000
  } catch (err) {
3001
+ const errorMsg = err instanceof Error ? err.message : String(err);
3002
+ this.state.error = errorMsg;
3003
+ if (err instanceof GraphAbortedError) {
3004
+ const lastCompleted = [...this.state.nodeResults.values()].at(-1);
3005
+ const abortedResult = {
3006
+ content: lastCompleted?.content ?? "",
3007
+ lastAgentId: this.state.currentAgentId ?? "",
3008
+ agentResults: new Map(this.state.nodeResults),
3009
+ totalUsage: {},
3010
+ stepsCompleted: this.state.stepsCompleted,
3011
+ stopReason: "aborted",
3012
+ context: ctx,
3013
+ success: false,
3014
+ error: err.message
3015
+ };
3016
+ emit({ type: "graph_error", error: err.message });
3017
+ return abortedResult;
3018
+ }
3019
+ emit({ type: "graph_error", error: errorMsg });
2578
3020
  return {
2579
3021
  content: "",
2580
3022
  lastAgentId: this.state.currentAgentId ?? "",
2581
- agentResults: this.state.nodeResults,
3023
+ agentResults: new Map(this.state.nodeResults),
2582
3024
  totalUsage: {},
2583
3025
  stepsCompleted: this.state.stepsCompleted,
2584
3026
  stopReason: "error",
2585
3027
  context: ctx,
2586
3028
  success: false,
2587
- error: err instanceof Error ? err.message : String(err)
3029
+ error: errorMsg
2588
3030
  };
2589
3031
  }
2590
3032
  }
2591
3033
  /** 流式执行图 */
2592
- async *stream(input) {
3034
+ stream(input) {
2593
3035
  if (!this.entryId) {
2594
3036
  throw new Error("AgentGraph: \u5165\u53E3\u8282\u70B9\u672A\u8BBE\u7F6E\uFF0C\u8BF7\u8C03\u7528 setEntry()");
2595
3037
  }
2596
3038
  if (!this.nodes.has(this.entryId)) {
2597
3039
  throw new Error(`AgentGraph: \u5165\u53E3\u8282\u70B9 "${this.entryId}" \u4E0D\u5B58\u5728`);
2598
3040
  }
3041
+ this.resetAbortController();
3042
+ this.resetState();
2599
3043
  const ctx = createSharedContext({
2600
3044
  meta: { traceId: `ma-${Date.now()}`, startTime: Date.now() }
2601
3045
  });
2602
- const eventQueue = [];
2603
- let resolveEvent = null;
2604
- let done = false;
2605
- const emit = (event) => {
2606
- eventQueue.push(event);
2607
- resolveEvent?.();
2608
- };
2609
- const graphPromise = this.executeGraph(input, ctx, emit).then(
2610
- (result) => {
2611
- emit({ type: "graph_done", result });
2612
- },
2613
- (err) => {
2614
- emit({ type: "graph_error", error: err instanceof Error ? err.message : String(err) });
2615
- }
2616
- ).finally(() => {
2617
- done = true;
2618
- resolveEvent?.();
2619
- });
2620
- while (!done || eventQueue.length > 0) {
2621
- if (eventQueue.length > 0) {
2622
- yield eventQueue.shift();
2623
- } else {
2624
- await new Promise((resolve) => {
2625
- resolveEvent = resolve;
2626
- });
2627
- }
2628
- }
2629
- await graphPromise;
3046
+ storeOriginalInput(ctx, input);
3047
+ return createEventStream(
3048
+ (emit) => this.executeGraph(input, ctx, emit).then(
3049
+ (result) => {
3050
+ emit({ type: "graph_done", result });
3051
+ },
3052
+ (err) => {
3053
+ const errorMsg = err instanceof Error ? err.message : String(err);
3054
+ this.state.error = errorMsg;
3055
+ emit({ type: "graph_error", error: errorMsg });
3056
+ }
3057
+ ),
3058
+ this.eventSink
3059
+ );
2630
3060
  }
2631
3061
  /** 核心执行逻辑 */
2632
3062
  async executeGraph(input, ctx, emit) {
@@ -2639,7 +3069,7 @@ var GraphExecutor = class {
2639
3069
  const totalUsage = {};
2640
3070
  this.state.currentAgentId = currentId;
2641
3071
  this.state.nodeStates.set(currentId, "running");
2642
- emit({ type: "agent_start", agentId: currentId, agentName: entryNode.name });
3072
+ emit({ type: "agent_start", agentId: currentId, agentName: entryNode.name, input: toInputText(currentInput) });
2643
3073
  try {
2644
3074
  lastResult = await executeNode(entryNode, currentInput, ctx);
2645
3075
  this.state.nodeStates.set(currentId, "completed");
@@ -2658,50 +3088,134 @@ var GraphExecutor = class {
2658
3088
  if (this.finishCondition?.(ctx)) {
2659
3089
  return this.buildResult(lastResult, lastAgentId, ctx, totalUsage, stepsCompleted, "finish_condition");
2660
3090
  }
2661
- const visited = /* @__PURE__ */ new Set();
3091
+ let frontier = [{ id: currentId, result: lastResult }];
2662
3092
  const visitCount = /* @__PURE__ */ new Map();
2663
- const MAX_VISITS_PER_NODE = 10;
2664
- while (lastResult) {
2665
- const nextEdges = findNextEdges(currentId, this.edges, lastResult, ctx);
2666
- if (nextEdges.length === 0) {
3093
+ let stopReason = "completed";
3094
+ while (frontier.length > 0) {
3095
+ this.throwIfAborted();
3096
+ const candidates = [];
3097
+ const seenTargets = /* @__PURE__ */ new Set();
3098
+ for (const from of frontier) {
3099
+ const nextEdges = findNextEdges(from.id, this.edges, from.result, ctx);
3100
+ for (const edge of nextEdges) {
3101
+ if (seenTargets.has(edge.to)) continue;
3102
+ seenTargets.add(edge.to);
3103
+ const nextNode = this.nodes.get(edge.to);
3104
+ if (!nextNode) {
3105
+ throw new Error(`AgentGraph: \u76EE\u6807\u8282\u70B9 "${edge.to}" \u4E0D\u5B58\u5728`);
3106
+ }
3107
+ candidates.push({ edge, from, node: nextNode });
3108
+ }
3109
+ }
3110
+ if (candidates.length === 0) {
2667
3111
  break;
2668
3112
  }
2669
- const edge = nextEdges[0];
2670
- const nextNode = this.nodes.get(edge.to);
2671
- if (!nextNode) {
2672
- throw new Error(`AgentGraph: \u76EE\u6807\u8282\u70B9 "${edge.to}" \u4E0D\u5B58\u5728`);
3113
+ const allowed = [];
3114
+ let truncated = false;
3115
+ for (const c of candidates) {
3116
+ const count = (visitCount.get(c.edge.to) ?? 0) + 1;
3117
+ if (count > this.maxVisitsPerNode) {
3118
+ truncated = true;
3119
+ continue;
3120
+ }
3121
+ visitCount.set(c.edge.to, count);
3122
+ allowed.push(c);
2673
3123
  }
2674
- const count = (visitCount.get(edge.to) ?? 0) + 1;
2675
- if (count > MAX_VISITS_PER_NODE) {
3124
+ if (allowed.length === 0) {
3125
+ if (truncated) stopReason = "max_visits_exceeded";
2676
3126
  break;
2677
3127
  }
2678
- visitCount.set(edge.to, count);
2679
- currentInput = resolveInput(edge, lastResult, ctx, nextNode, input);
2680
- currentId = edge.to;
2681
- this.state.currentAgentId = currentId;
2682
- this.state.nodeStates.set(currentId, "running");
2683
- emit({ type: "edge_traversed", from: edge.from, to: edge.to });
2684
- emit({ type: "agent_start", agentId: currentId, agentName: nextNode.name });
2685
- try {
2686
- lastResult = await executeNode(nextNode, currentInput, ctx);
2687
- this.state.nodeStates.set(currentId, "completed");
2688
- this.state.nodeResults.set(currentId, lastResult);
2689
- this.mergeUsage(totalUsage, lastResult.usage);
3128
+ const tasks = allowed.map((c) => ({
3129
+ node: c.node,
3130
+ input: resolveInput(c.edge, c.from.result, ctx, c.node, input),
3131
+ fromId: c.edge.from
3132
+ }));
3133
+ let waveResults;
3134
+ if (tasks.length === 1) {
3135
+ const t = tasks[0];
3136
+ this.state.currentAgentId = t.node.id;
3137
+ this.state.nodeStates.set(t.node.id, "running");
3138
+ emit({ type: "edge_traversed", from: t.fromId, to: t.node.id });
3139
+ emit({ type: "agent_start", agentId: t.node.id, agentName: t.node.name, input: toInputText(t.input) });
3140
+ let result;
3141
+ try {
3142
+ result = await executeNode(t.node, t.input, ctx);
3143
+ } catch (err) {
3144
+ const errorMsg = err instanceof Error ? err.message : String(err);
3145
+ this.state.nodeStates.set(t.node.id, "failed");
3146
+ emit({ type: "agent_error", agentId: t.node.id, agentName: t.node.name, error: errorMsg });
3147
+ throw err;
3148
+ }
3149
+ this.state.nodeStates.set(t.node.id, "completed");
3150
+ this.state.nodeResults.set(t.node.id, result);
3151
+ this.mergeUsage(totalUsage, result.usage);
2690
3152
  stepsCompleted++;
2691
3153
  this.state.stepsCompleted = stepsCompleted;
2692
- emit({ type: "agent_result", agentId: currentId, agentName: nextNode.name, result: lastResult });
3154
+ emit({ type: "agent_result", agentId: t.node.id, agentName: t.node.name, result });
3155
+ waveResults = [{ id: t.node.id, result }];
3156
+ } else {
3157
+ waveResults = await this.executeWave(tasks, ctx, emit, totalUsage);
3158
+ stepsCompleted += waveResults.length;
3159
+ this.state.stepsCompleted = stepsCompleted;
3160
+ }
3161
+ lastResult = waveResults[waveResults.length - 1].result;
3162
+ lastAgentId = waveResults[waveResults.length - 1].id;
3163
+ frontier = waveResults;
3164
+ if (this.finishCondition?.(ctx)) {
3165
+ return this.buildResult(lastResult, lastAgentId, ctx, totalUsage, stepsCompleted, "finish_condition");
3166
+ }
3167
+ }
3168
+ return this.buildResult(lastResult, lastAgentId, ctx, totalUsage, stepsCompleted, stopReason);
3169
+ }
3170
+ /** 并行执行一个 wave 的节点(fail-fast + concurrency 限流) */
3171
+ async executeWave(tasks, ctx, emit, totalUsage) {
3172
+ const results = [];
3173
+ emit({ type: "parallel_start", agentIds: tasks.map((t) => t.node.id) });
3174
+ const runOne = async (t) => {
3175
+ this.state.currentAgentId = t.node.id;
3176
+ this.state.nodeStates.set(t.node.id, "running");
3177
+ emit({ type: "edge_traversed", from: t.fromId, to: t.node.id });
3178
+ emit({ type: "agent_start", agentId: t.node.id, agentName: t.node.name, input: toInputText(t.input) });
3179
+ try {
3180
+ const result = await executeNode(t.node, t.input, ctx);
3181
+ this.state.nodeStates.set(t.node.id, "completed");
3182
+ this.state.nodeResults.set(t.node.id, result);
3183
+ this.mergeUsage(totalUsage, result.usage);
3184
+ results.push({ id: t.node.id, result });
3185
+ emit({ type: "agent_result", agentId: t.node.id, agentName: t.node.name, result });
2693
3186
  } catch (err) {
2694
3187
  const errorMsg = err instanceof Error ? err.message : String(err);
2695
- this.state.nodeStates.set(currentId, "failed");
2696
- emit({ type: "agent_error", agentId: currentId, agentName: nextNode.name, error: errorMsg });
3188
+ this.state.nodeStates.set(t.node.id, "failed");
3189
+ emit({ type: "agent_error", agentId: t.node.id, agentName: t.node.name, error: errorMsg });
2697
3190
  throw err;
2698
3191
  }
2699
- lastAgentId = currentId;
2700
- if (this.finishCondition?.(ctx)) {
2701
- return this.buildResult(lastResult, lastAgentId, ctx, totalUsage, stepsCompleted, "finish_condition");
3192
+ };
3193
+ if (this.concurrency >= tasks.length) {
3194
+ await Promise.all(tasks.map((t) => runOne(t)));
3195
+ } else {
3196
+ let index = 0;
3197
+ const executing = /* @__PURE__ */ new Set();
3198
+ const enqueue = () => {
3199
+ if (index >= tasks.length) return null;
3200
+ const i = index++;
3201
+ const p = runOne(tasks[i]).finally(() => {
3202
+ executing.delete(p);
3203
+ });
3204
+ executing.add(p);
3205
+ p.catch(() => {
3206
+ });
3207
+ return p;
3208
+ };
3209
+ for (let i = 0; i < this.concurrency && index < tasks.length; i++) {
3210
+ enqueue();
3211
+ }
3212
+ while (executing.size > 0) {
3213
+ await Promise.race(executing);
3214
+ enqueue();
2702
3215
  }
2703
3216
  }
2704
- return this.buildResult(lastResult, lastAgentId, ctx, totalUsage, stepsCompleted, "completed");
3217
+ emit({ type: "parallel_done", results: new Map(results.map((r) => [r.id, r.result])) });
3218
+ return results;
2705
3219
  }
2706
3220
  buildResult(lastResult, lastAgentId, ctx, totalUsage, stepsCompleted, stopReason) {
2707
3221
  this.state.finished = true;
@@ -2726,8 +3240,12 @@ var GraphExecutor = class {
2726
3240
 
2727
3241
  // core/graph.ts
2728
3242
  var AgentGraphImpl = class {
2729
- executor = new GraphExecutor();
2730
- eventListeners = /* @__PURE__ */ new Map();
3243
+ executor;
3244
+ /** on() 注册的监听器,按 MultiAgentEvent.type 分发 */
3245
+ bus = new SimpleEventBus();
3246
+ constructor(opts) {
3247
+ this.executor = new GraphExecutor(opts);
3248
+ }
2731
3249
  addNode(node) {
2732
3250
  this.executor.addNode(node);
2733
3251
  return this;
@@ -2745,9 +3263,11 @@ var AgentGraphImpl = class {
2745
3263
  return this;
2746
3264
  }
2747
3265
  async run(input) {
3266
+ this.executor.setEventSink((event) => this.bus.emit(event.type, event));
2748
3267
  return this.executor.run(input);
2749
3268
  }
2750
3269
  async *stream(input) {
3270
+ this.executor.setEventSink((event) => this.bus.emit(event.type, event));
2751
3271
  yield* this.executor.stream(input);
2752
3272
  }
2753
3273
  getState() {
@@ -2757,17 +3277,12 @@ var AgentGraphImpl = class {
2757
3277
  this.executor.abort();
2758
3278
  }
2759
3279
  on(event, listener) {
2760
- let set = this.eventListeners.get(event);
2761
- if (!set) {
2762
- set = /* @__PURE__ */ new Set();
2763
- this.eventListeners.set(event, set);
2764
- }
2765
- set.add(listener);
3280
+ this.bus.on(event, listener);
2766
3281
  return this;
2767
3282
  }
2768
3283
  };
2769
- function createAgentGraph() {
2770
- return new AgentGraphImpl();
3284
+ function createAgentGraph(opts) {
3285
+ return new AgentGraphImpl(opts);
2771
3286
  }
2772
3287
 
2773
3288
  // patterns/pipeline.ts
@@ -2805,14 +3320,19 @@ function createRouter(router, targets, opts) {
2805
3320
  }
2806
3321
  graph.setEntry(router.id);
2807
3322
  const targetIds = new Set(targets.map((t) => t.id));
3323
+ const defaultTarget = opts.defaultTarget && targetIds.has(opts.defaultTarget) ? opts.defaultTarget : void 0;
3324
+ const resolveTarget = (routerResult) => {
3325
+ const resolvedId = opts.resolve(routerResult);
3326
+ if (targetIds.has(resolvedId)) return resolvedId;
3327
+ return defaultTarget ?? resolvedId;
3328
+ };
2808
3329
  for (const target of targets) {
2809
3330
  graph.addEdge({
2810
3331
  from: router.id,
2811
3332
  to: target.id,
2812
- // 条件:从路由器结果中解析目标ID,匹配当前目标
3333
+ // 条件:从路由器结果中解析目标ID(含 defaultTarget 兜底),匹配当前目标
2813
3334
  condition: (routerResult, _ctx) => {
2814
- const resolvedId = opts.resolve(routerResult);
2815
- return resolvedId === target.id;
3335
+ return resolveTarget(routerResult) === target.id;
2816
3336
  },
2817
3337
  // 转换:决定传给目标Agent的输入
2818
3338
  transform: (_routerResult, _ctx) => {
@@ -2824,8 +3344,6 @@ function createRouter(router, targets, opts) {
2824
3344
  }
2825
3345
  });
2826
3346
  }
2827
- if (opts.defaultTarget && targetIds.has(opts.defaultTarget)) {
2828
- }
2829
3347
  return graph;
2830
3348
  }
2831
3349
 
@@ -2834,6 +3352,11 @@ var SupervisorExecutor = class {
2834
3352
  supervisorNode;
2835
3353
  workerNodes;
2836
3354
  opts;
3355
+ abortController = new AbortController();
3356
+ /** 外部事件监听(on() API 的底层接线) */
3357
+ eventSink;
3358
+ /** 最近一次 run 中失败的 Worker ID(onWorkerError='skip' 时使用) */
3359
+ failedAgents = [];
2837
3360
  state = {
2838
3361
  nodeStates: /* @__PURE__ */ new Map(),
2839
3362
  nodeResults: /* @__PURE__ */ new Map(),
@@ -2846,7 +3369,8 @@ var SupervisorExecutor = class {
2846
3369
  this.opts = {
2847
3370
  schedule: opts?.schedule ?? "parallel",
2848
3371
  concurrency: opts?.concurrency ?? Infinity,
2849
- passOriginalInput: opts?.passOriginalInput ?? true
3372
+ passOriginalInput: opts?.passOriginalInput ?? true,
3373
+ onWorkerError: opts?.onWorkerError ?? "fail-fast"
2850
3374
  };
2851
3375
  this.state.nodeStates.set(supervisor.id, "pending");
2852
3376
  for (const w of workers) {
@@ -2854,69 +3378,91 @@ var SupervisorExecutor = class {
2854
3378
  }
2855
3379
  }
2856
3380
  getState() {
2857
- return { ...this.state };
3381
+ return {
3382
+ ...this.state,
3383
+ nodeStates: new Map(this.state.nodeStates),
3384
+ nodeResults: new Map(this.state.nodeResults)
3385
+ };
3386
+ }
3387
+ /** 注入外部事件监听(由 AgentGraph 实现的 on() 接线) */
3388
+ setEventSink(sink) {
3389
+ this.eventSink = sink;
3390
+ return this;
3391
+ }
3392
+ abort() {
3393
+ this.abortController.abort();
3394
+ }
3395
+ /** 每次 run/stream 前重置中止信号、失败记录与执行状态 */
3396
+ resetRun() {
3397
+ this.abortController = new AbortController();
3398
+ this.failedAgents = [];
3399
+ this.state = {
3400
+ nodeStates: /* @__PURE__ */ new Map(),
3401
+ nodeResults: /* @__PURE__ */ new Map(),
3402
+ stepsCompleted: 0,
3403
+ finished: false
3404
+ };
3405
+ this.state.nodeStates.set(this.supervisorNode.id, "pending");
3406
+ for (const w of this.workerNodes) {
3407
+ this.state.nodeStates.set(w.id, "pending");
3408
+ }
3409
+ }
3410
+ throwIfAborted() {
3411
+ if (this.abortController.signal.aborted) {
3412
+ throw new GraphAbortedError();
3413
+ }
2858
3414
  }
2859
3415
  /** 执行 Supervisor 模式 */
2860
3416
  async run(input) {
3417
+ this.resetRun();
2861
3418
  const ctx = createSharedContext({
2862
3419
  meta: { traceId: `sv-${Date.now()}`, startTime: Date.now() }
2863
3420
  });
2864
3421
  if (this.opts.passOriginalInput) {
2865
- const inputText = typeof input === "string" ? input : input.message;
2866
- ctx.blackboard.set("__original_input__", inputText);
3422
+ storeOriginalInput(ctx, input);
2867
3423
  }
3424
+ const emit = this.eventSink ?? (() => {
3425
+ });
2868
3426
  try {
2869
- return await this.executeSupervisor(input, ctx, () => {
2870
- });
3427
+ const result = await this.executeSupervisor(input, ctx, emit);
3428
+ emit({ type: "graph_done", result });
3429
+ return result;
2871
3430
  } catch (err) {
2872
- return this.buildErrorResult(ctx, err);
3431
+ const result = this.buildErrorResult(ctx, err);
3432
+ emit({ type: "graph_error", error: result.error ?? "" });
3433
+ return result;
2873
3434
  }
2874
3435
  }
2875
3436
  /** 流式执行 Supervisor 模式 */
2876
- async *stream(input) {
3437
+ stream(input) {
3438
+ this.resetRun();
2877
3439
  const ctx = createSharedContext({
2878
3440
  meta: { traceId: `sv-${Date.now()}`, startTime: Date.now() }
2879
3441
  });
2880
3442
  if (this.opts.passOriginalInput) {
2881
- const inputText = typeof input === "string" ? input : input.message;
2882
- ctx.blackboard.set("__original_input__", inputText);
2883
- }
2884
- const eventQueue = [];
2885
- let resolveEvent = null;
2886
- let done = false;
2887
- const emit = (event) => {
2888
- eventQueue.push(event);
2889
- resolveEvent?.();
2890
- };
2891
- const graphPromise = this.executeSupervisor(input, ctx, emit).then(
2892
- (result) => {
2893
- emit({ type: "graph_done", result });
2894
- },
2895
- (err) => {
2896
- emit({ type: "graph_error", error: err instanceof Error ? err.message : String(err) });
2897
- }
2898
- ).finally(() => {
2899
- done = true;
2900
- resolveEvent?.();
2901
- });
2902
- while (!done || eventQueue.length > 0) {
2903
- if (eventQueue.length > 0) {
2904
- yield eventQueue.shift();
2905
- } else {
2906
- await new Promise((resolve) => {
2907
- resolveEvent = resolve;
2908
- });
2909
- }
3443
+ storeOriginalInput(ctx, input);
2910
3444
  }
2911
- await graphPromise;
3445
+ return createEventStream(
3446
+ (emit) => this.executeSupervisor(input, ctx, emit).then(
3447
+ (result) => {
3448
+ emit({ type: "graph_done", result });
3449
+ },
3450
+ (err) => {
3451
+ this.state.error = err instanceof Error ? err.message : String(err);
3452
+ emit({ type: "graph_error", error: this.state.error });
3453
+ }
3454
+ ),
3455
+ this.eventSink
3456
+ );
2912
3457
  }
2913
3458
  /** 核心执行逻辑 */
2914
3459
  async executeSupervisor(input, ctx, emit) {
2915
3460
  let stepsCompleted = 0;
2916
3461
  const totalUsage = {};
3462
+ this.throwIfAborted();
2917
3463
  this.state.currentAgentId = this.supervisorNode.id;
2918
3464
  this.state.nodeStates.set(this.supervisorNode.id, "running");
2919
- emit({ type: "agent_start", agentId: this.supervisorNode.id, agentName: this.supervisorNode.name });
3465
+ emit({ type: "agent_start", agentId: this.supervisorNode.id, agentName: this.supervisorNode.name, input: toInputText(input) });
2920
3466
  let supervisorResult;
2921
3467
  try {
2922
3468
  supervisorResult = await executeNode(this.supervisorNode, input, ctx);
@@ -2943,18 +3489,21 @@ var SupervisorExecutor = class {
2943
3489
  }
2944
3490
  stepsCompleted += workerResults.size;
2945
3491
  this.state.stepsCompleted = stepsCompleted;
2946
- const lastWorkerId = this.workerNodes[this.workerNodes.length - 1].id;
2947
- const finalResult = workerResults.get(lastWorkerId) ?? supervisorResult;
3492
+ const lastEntry = [...workerResults.entries()].at(-1);
3493
+ const finalResult = lastEntry?.[1] ?? supervisorResult;
3494
+ const finalAgentId = lastEntry?.[0] ?? this.supervisorNode.id;
3495
+ const hasFailures = this.failedAgents.length > 0;
2948
3496
  this.state.finished = true;
2949
3497
  return {
2950
3498
  content: finalResult.content,
2951
- lastAgentId: lastWorkerId,
3499
+ lastAgentId: finalAgentId,
2952
3500
  agentResults: new Map(this.state.nodeResults),
2953
3501
  totalUsage,
2954
3502
  stepsCompleted,
2955
- stopReason: "completed",
3503
+ stopReason: hasFailures ? "partial_failure" : "completed",
2956
3504
  context: ctx,
2957
- success: true
3505
+ success: true,
3506
+ ...hasFailures ? { failedAgents: [...this.failedAgents] } : {}
2958
3507
  };
2959
3508
  }
2960
3509
  /** 并行执行所有 Worker */
@@ -2962,16 +3511,28 @@ var SupervisorExecutor = class {
2962
3511
  const results = /* @__PURE__ */ new Map();
2963
3512
  const workers = this.workerNodes;
2964
3513
  const concurrency = this.opts.concurrency;
3514
+ const skip = this.opts.onWorkerError === "skip";
2965
3515
  emit({ type: "parallel_start", agentIds: workers.map((w) => w.id) });
2966
3516
  if (concurrency >= workers.length) {
2967
- const promises = workers.map(async (worker) => {
2968
- return this.executeWorker(worker, ctx, emit, totalUsage);
2969
- });
2970
- const settled = await Promise.allSettled(promises);
2971
- for (let i = 0; i < settled.length; i++) {
2972
- const s = settled[i];
2973
- if (s.status === "fulfilled") {
2974
- results.set(workers[i].id, s.value);
3517
+ if (!skip) {
3518
+ await Promise.all(
3519
+ workers.map(async (worker) => {
3520
+ const r = await this.executeWorker(worker, ctx, emit, totalUsage);
3521
+ results.set(worker.id, r);
3522
+ return r;
3523
+ })
3524
+ );
3525
+ } else {
3526
+ const settled = await Promise.allSettled(
3527
+ workers.map((worker) => this.executeWorker(worker, ctx, emit, totalUsage))
3528
+ );
3529
+ for (let i = 0; i < settled.length; i++) {
3530
+ const s = settled[i];
3531
+ if (s.status === "fulfilled") {
3532
+ results.set(workers[i].id, s.value);
3533
+ } else {
3534
+ this.failedAgents.push(workers[i].id);
3535
+ }
2975
3536
  }
2976
3537
  }
2977
3538
  } else {
@@ -2980,12 +3541,20 @@ var SupervisorExecutor = class {
2980
3541
  const enqueue = () => {
2981
3542
  if (index >= workers.length) return null;
2982
3543
  const worker = workers[index++];
2983
- const p = this.executeWorker(worker, ctx, emit, totalUsage).then((r) => {
2984
- results.set(worker.id, r);
3544
+ const p = this.executeWorker(worker, ctx, emit, totalUsage).then(
3545
+ (r) => {
3546
+ results.set(worker.id, r);
3547
+ },
3548
+ (err) => {
3549
+ if (!skip) throw err;
3550
+ this.failedAgents.push(worker.id);
3551
+ }
3552
+ ).finally(() => {
2985
3553
  executing.delete(p);
2986
- return r;
2987
3554
  });
2988
3555
  executing.add(p);
3556
+ p.catch(() => {
3557
+ });
2989
3558
  return p;
2990
3559
  };
2991
3560
  for (let i = 0; i < concurrency && index < workers.length; i++) {
@@ -3002,40 +3571,122 @@ var SupervisorExecutor = class {
3002
3571
  /** 顺序执行所有 Worker */
3003
3572
  async executeWorkersSequential(ctx, emit, totalUsage) {
3004
3573
  const results = /* @__PURE__ */ new Map();
3574
+ const skip = this.opts.onWorkerError === "skip";
3005
3575
  for (const worker of this.workerNodes) {
3006
- const result = await this.executeWorker(worker, ctx, emit, totalUsage);
3007
- results.set(worker.id, result);
3576
+ try {
3577
+ const result = await this.executeWorker(worker, ctx, emit, totalUsage);
3578
+ results.set(worker.id, result);
3579
+ } catch (err) {
3580
+ if (!skip) throw err;
3581
+ this.failedAgents.push(worker.id);
3582
+ }
3008
3583
  }
3009
3584
  return results;
3010
3585
  }
3011
- /** 自动调度:根据 inputMapping 依赖分层执行 */
3586
+ /** 自动调度:按依赖分层执行
3587
+ *
3588
+ * 依赖推导规则:
3589
+ * 1. 显式声明 dependsOn 的 worker:依赖指定的 worker(依赖必须都在 workers 列表中,否则报错)
3590
+ * 2. 未声明 dependsOn 但有 inputMapping 的 worker:隐式依赖所有"第 0 层"worker(保持向后兼容)
3591
+ * 3. 其余 worker(无 dependsOn 且无 inputMapping):第 0 层,首批并行
3592
+ *
3593
+ * 按拓扑分层:同层并行执行(受 concurrency 限制),层间按序执行;依赖成环时报错。
3594
+ */
3012
3595
  async executeWorkersAuto(ctx, emit, totalUsage) {
3013
3596
  const results = /* @__PURE__ */ new Map();
3014
- const noInputMapping = this.workerNodes.filter((w) => !w.inputMapping);
3015
- const withInputMapping = this.workerNodes.filter((w) => !!w.inputMapping);
3016
- if (noInputMapping.length > 0) {
3017
- emit({ type: "parallel_start", agentIds: noInputMapping.map((w) => w.id) });
3018
- const batchResults = await Promise.all(
3019
- noInputMapping.map((w) => this.executeWorker(w, ctx, emit, totalUsage))
3020
- );
3021
- for (let i = 0; i < noInputMapping.length; i++) {
3022
- results.set(noInputMapping[i].id, batchResults[i]);
3597
+ const skip = this.opts.onWorkerError === "skip";
3598
+ const workerIds = new Set(this.workerNodes.map((w) => w.id));
3599
+ const layer0 = this.workerNodes.filter((w) => !w.inputMapping && !w.dependsOn);
3600
+ const deps = /* @__PURE__ */ new Map();
3601
+ for (const w of this.workerNodes) {
3602
+ if (w.dependsOn) {
3603
+ for (const dep of w.dependsOn) {
3604
+ if (!workerIds.has(dep)) {
3605
+ throw new Error(`Supervisor: Worker "${w.id}" \u7684 dependsOn \u5F15\u7528\u4E86\u4E0D\u5B58\u5728\u7684 Worker "${dep}"`);
3606
+ }
3607
+ }
3608
+ deps.set(w.id, new Set(w.dependsOn));
3609
+ } else if (w.inputMapping) {
3610
+ deps.set(w.id, new Set(layer0.map((l) => l.id)));
3611
+ } else {
3612
+ deps.set(w.id, /* @__PURE__ */ new Set());
3023
3613
  }
3024
- emit({ type: "parallel_done", results: new Map(results) });
3025
3614
  }
3026
- for (const worker of withInputMapping) {
3027
- const result = await this.executeWorker(worker, ctx, emit, totalUsage);
3028
- results.set(worker.id, result);
3615
+ const remaining = new Map(deps);
3616
+ const layers = [];
3617
+ const doneIds = /* @__PURE__ */ new Set();
3618
+ while (remaining.size > 0) {
3619
+ const layer = [];
3620
+ for (const [id, depSet] of remaining) {
3621
+ if ([...depSet].every((d) => doneIds.has(d))) {
3622
+ layer.push(this.workerNodes.find((w) => w.id === id));
3623
+ }
3624
+ }
3625
+ if (layer.length === 0) {
3626
+ throw new Error(`Supervisor: auto \u8C03\u5EA6\u7684 Worker \u4F9D\u8D56\u5B58\u5728\u73AF\uFF08\u6D89\u53CA: ${[...remaining.keys()].join(", ")}\uFF09`);
3627
+ }
3628
+ for (const w of layer) {
3629
+ remaining.delete(w.id);
3630
+ doneIds.add(w.id);
3631
+ }
3632
+ layers.push(layer);
3633
+ }
3634
+ for (const layer of layers) {
3635
+ if (layer.length === 1) {
3636
+ try {
3637
+ const r = await this.executeWorker(layer[0], ctx, emit, totalUsage);
3638
+ results.set(layer[0].id, r);
3639
+ } catch (err) {
3640
+ if (!skip) throw err;
3641
+ this.failedAgents.push(layer[0].id);
3642
+ }
3643
+ continue;
3644
+ }
3645
+ emit({ type: "parallel_start", agentIds: layer.map((w) => w.id) });
3646
+ const runOne = async (worker) => {
3647
+ try {
3648
+ const r = await this.executeWorker(worker, ctx, emit, totalUsage);
3649
+ results.set(worker.id, r);
3650
+ } catch (err) {
3651
+ if (!skip) throw err;
3652
+ this.failedAgents.push(worker.id);
3653
+ }
3654
+ };
3655
+ if (this.opts.concurrency >= layer.length) {
3656
+ await Promise.all(layer.map((w) => runOne(w)));
3657
+ } else {
3658
+ let index = 0;
3659
+ const executing = /* @__PURE__ */ new Set();
3660
+ const enqueue = () => {
3661
+ if (index >= layer.length) return null;
3662
+ const i = index++;
3663
+ const p = runOne(layer[i]).finally(() => {
3664
+ executing.delete(p);
3665
+ });
3666
+ executing.add(p);
3667
+ p.catch(() => {
3668
+ });
3669
+ return p;
3670
+ };
3671
+ for (let i = 0; i < this.opts.concurrency && index < layer.length; i++) {
3672
+ enqueue();
3673
+ }
3674
+ while (executing.size > 0) {
3675
+ await Promise.race(executing);
3676
+ enqueue();
3677
+ }
3678
+ }
3679
+ emit({ type: "parallel_done", results: new Map(results) });
3029
3680
  }
3030
3681
  return results;
3031
3682
  }
3032
3683
  /** 执行单个 Worker */
3033
3684
  async executeWorker(worker, ctx, emit, totalUsage) {
3685
+ this.throwIfAborted();
3034
3686
  this.state.currentAgentId = worker.id;
3035
3687
  this.state.nodeStates.set(worker.id, "running");
3036
- emit({ type: "agent_start", agentId: worker.id, agentName: worker.name });
3688
+ let workerInput;
3037
3689
  try {
3038
- let workerInput;
3039
3690
  if (worker.inputMapping) {
3040
3691
  workerInput = worker.inputMapping(ctx);
3041
3692
  } else {
@@ -3043,6 +3694,14 @@ var SupervisorExecutor = class {
3043
3694
  const myTask = tasks?.find((t) => t.assignee === worker.id);
3044
3695
  workerInput = myTask ? myTask.task : ctx.blackboard.get("__original_input__") ?? "";
3045
3696
  }
3697
+ } catch (err) {
3698
+ const errorMsg = err instanceof Error ? err.message : String(err);
3699
+ this.state.nodeStates.set(worker.id, "failed");
3700
+ emit({ type: "agent_error", agentId: worker.id, agentName: worker.name, error: errorMsg });
3701
+ throw err;
3702
+ }
3703
+ emit({ type: "agent_start", agentId: worker.id, agentName: worker.name, input: toInputText(workerInput) });
3704
+ try {
3046
3705
  const result = await executeNode(worker, workerInput, ctx);
3047
3706
  this.state.nodeStates.set(worker.id, "completed");
3048
3707
  this.state.nodeResults.set(worker.id, result);
@@ -3059,16 +3718,21 @@ var SupervisorExecutor = class {
3059
3718
  }
3060
3719
  buildErrorResult(ctx, err) {
3061
3720
  this.state.finished = true;
3721
+ const aborted = err instanceof GraphAbortedError;
3722
+ const errorMsg = err instanceof Error ? err.message : String(err);
3723
+ this.state.error = errorMsg;
3724
+ const lastCompleted = [...this.state.nodeResults.values()].at(-1);
3062
3725
  return {
3063
- content: "",
3064
- lastAgentId: this.supervisorNode.id,
3726
+ content: aborted ? lastCompleted?.content ?? "" : "",
3727
+ lastAgentId: this.state.currentAgentId ?? this.supervisorNode.id,
3065
3728
  agentResults: new Map(this.state.nodeResults),
3066
3729
  totalUsage: {},
3067
3730
  stepsCompleted: this.state.stepsCompleted,
3068
- stopReason: "error",
3731
+ stopReason: aborted ? "aborted" : "error",
3069
3732
  context: ctx,
3070
3733
  success: false,
3071
- error: err instanceof Error ? err.message : String(err)
3734
+ error: errorMsg,
3735
+ ...this.failedAgents.length > 0 ? { failedAgents: [...this.failedAgents] } : {}
3072
3736
  };
3073
3737
  }
3074
3738
  mergeUsage(total, usage) {
@@ -3081,7 +3745,8 @@ var SupervisorExecutor = class {
3081
3745
  // patterns/supervisor.ts
3082
3746
  var SupervisorGraphImpl = class {
3083
3747
  executor;
3084
- eventListeners = /* @__PURE__ */ new Map();
3748
+ /** on() 注册的监听器,按 MultiAgentEvent.type 分发 */
3749
+ bus = new SimpleEventBus();
3085
3750
  constructor(supervisor, workers, opts) {
3086
3751
  this.executor = new SupervisorExecutor(supervisor, workers, opts);
3087
3752
  }
@@ -3099,23 +3764,21 @@ var SupervisorGraphImpl = class {
3099
3764
  throw new Error("Supervisor \u6A21\u5F0F\u4E0D\u652F\u6301 setFinish\uFF0C\u7EC8\u6B62\u7531 Supervisor \u5224\u5B9A");
3100
3765
  }
3101
3766
  async run(input) {
3767
+ this.executor.setEventSink((event) => this.bus.emit(event.type, event));
3102
3768
  return this.executor.run(input);
3103
3769
  }
3104
3770
  async *stream(input) {
3771
+ this.executor.setEventSink((event) => this.bus.emit(event.type, event));
3105
3772
  yield* this.executor.stream(input);
3106
3773
  }
3107
3774
  getState() {
3108
3775
  return this.executor.getState();
3109
3776
  }
3110
3777
  abort() {
3778
+ this.executor.abort();
3111
3779
  }
3112
3780
  on(event, listener) {
3113
- let set = this.eventListeners.get(event);
3114
- if (!set) {
3115
- set = /* @__PURE__ */ new Set();
3116
- this.eventListeners.set(event, set);
3117
- }
3118
- set.add(listener);
3781
+ this.bus.on(event, listener);
3119
3782
  return this;
3120
3783
  }
3121
3784
  };
@@ -3133,6 +3796,9 @@ var DebateExecutor = class {
3133
3796
  maxRounds;
3134
3797
  convergeWhen;
3135
3798
  feedbackTransform;
3799
+ abortController = new AbortController();
3800
+ /** 外部事件监听(on() API 的底层接线) */
3801
+ eventSink;
3136
3802
  state = {
3137
3803
  nodeStates: /* @__PURE__ */ new Map(),
3138
3804
  nodeResults: /* @__PURE__ */ new Map(),
@@ -3155,53 +3821,75 @@ ${proposerResult.content}`;
3155
3821
  this.state.nodeStates.set(reviewer.id, "pending");
3156
3822
  }
3157
3823
  getState() {
3158
- return { ...this.state };
3824
+ return {
3825
+ ...this.state,
3826
+ nodeStates: new Map(this.state.nodeStates),
3827
+ nodeResults: new Map(this.state.nodeResults)
3828
+ };
3829
+ }
3830
+ /** 注入外部事件监听(由 AgentGraph 实现的 on() 接线) */
3831
+ setEventSink(sink) {
3832
+ this.eventSink = sink;
3833
+ return this;
3834
+ }
3835
+ abort() {
3836
+ this.abortController.abort();
3837
+ }
3838
+ /** 每次 run/stream 前重置中止信号与执行状态 */
3839
+ resetAbortController() {
3840
+ this.abortController = new AbortController();
3841
+ this.state = {
3842
+ nodeStates: /* @__PURE__ */ new Map(),
3843
+ nodeResults: /* @__PURE__ */ new Map(),
3844
+ stepsCompleted: 0,
3845
+ finished: false
3846
+ };
3847
+ this.state.nodeStates.set(this.proposerNode.id, "pending");
3848
+ this.state.nodeStates.set(this.reviewerNode.id, "pending");
3849
+ }
3850
+ throwIfAborted() {
3851
+ if (this.abortController.signal.aborted) {
3852
+ throw new GraphAbortedError();
3853
+ }
3159
3854
  }
3160
3855
  /** 执行 Debate */
3161
3856
  async run(input) {
3857
+ this.resetAbortController();
3162
3858
  const ctx = createSharedContext({
3163
3859
  meta: { traceId: `debate-${Date.now()}`, startTime: Date.now() }
3164
3860
  });
3861
+ storeOriginalInput(ctx, input);
3862
+ const emit = this.eventSink ?? (() => {
3863
+ });
3165
3864
  try {
3166
- return await this.executeDebate(input, ctx, () => {
3167
- });
3865
+ const result = await this.executeDebate(input, ctx, emit);
3866
+ emit({ type: "graph_done", result });
3867
+ return result;
3168
3868
  } catch (err) {
3169
- return this.buildErrorResult(ctx, err);
3869
+ const result = this.buildErrorResult(ctx, err);
3870
+ emit({ type: "graph_error", error: result.error ?? "" });
3871
+ return result;
3170
3872
  }
3171
3873
  }
3172
3874
  /** 流式执行 Debate */
3173
- async *stream(input) {
3875
+ stream(input) {
3876
+ this.resetAbortController();
3174
3877
  const ctx = createSharedContext({
3175
3878
  meta: { traceId: `debate-${Date.now()}`, startTime: Date.now() }
3176
3879
  });
3177
- const eventQueue = [];
3178
- let resolveEvent = null;
3179
- let done = false;
3180
- const emit = (event) => {
3181
- eventQueue.push(event);
3182
- resolveEvent?.();
3183
- };
3184
- const graphPromise = this.executeDebate(input, ctx, emit).then(
3185
- (result) => {
3186
- emit({ type: "graph_done", result });
3187
- },
3188
- (err) => {
3189
- emit({ type: "graph_error", error: err instanceof Error ? err.message : String(err) });
3190
- }
3191
- ).finally(() => {
3192
- done = true;
3193
- resolveEvent?.();
3194
- });
3195
- while (!done || eventQueue.length > 0) {
3196
- if (eventQueue.length > 0) {
3197
- yield eventQueue.shift();
3198
- } else {
3199
- await new Promise((resolve) => {
3200
- resolveEvent = resolve;
3201
- });
3202
- }
3203
- }
3204
- await graphPromise;
3880
+ storeOriginalInput(ctx, input);
3881
+ return createEventStream(
3882
+ (emit) => this.executeDebate(input, ctx, emit).then(
3883
+ (result) => {
3884
+ emit({ type: "graph_done", result });
3885
+ },
3886
+ (err) => {
3887
+ this.state.error = err instanceof Error ? err.message : String(err);
3888
+ emit({ type: "graph_error", error: this.state.error });
3889
+ }
3890
+ ),
3891
+ this.eventSink
3892
+ );
3205
3893
  }
3206
3894
  /** 核心执行逻辑 */
3207
3895
  async executeDebate(input, ctx, emit) {
@@ -3213,10 +3901,11 @@ ${proposerResult.content}`;
3213
3901
  let convergeReason = "";
3214
3902
  let currentInput = input;
3215
3903
  for (let round = 1; round <= this.maxRounds; round++) {
3904
+ this.throwIfAborted();
3216
3905
  emit({ type: "round_start", round });
3217
3906
  this.state.currentAgentId = this.proposerNode.id;
3218
3907
  this.state.nodeStates.set(this.proposerNode.id, "running");
3219
- emit({ type: "agent_start", agentId: this.proposerNode.id, agentName: this.proposerNode.name });
3908
+ emit({ type: "agent_start", agentId: this.proposerNode.id, agentName: this.proposerNode.name, input: toInputText(currentInput) });
3220
3909
  try {
3221
3910
  lastProposerResult = await executeNode(this.proposerNode, currentInput, ctx);
3222
3911
  this.state.nodeStates.set(this.proposerNode.id, "completed");
@@ -3233,7 +3922,7 @@ ${proposerResult.content}`;
3233
3922
  }
3234
3923
  this.state.currentAgentId = this.reviewerNode.id;
3235
3924
  this.state.nodeStates.set(this.reviewerNode.id, "running");
3236
- emit({ type: "agent_start", agentId: this.reviewerNode.id, agentName: this.reviewerNode.name });
3925
+ emit({ type: "agent_start", agentId: this.reviewerNode.id, agentName: this.reviewerNode.name, input: lastProposerResult.content });
3237
3926
  try {
3238
3927
  lastReviewerResult = await executeNode(this.reviewerNode, lastProposerResult.content, ctx);
3239
3928
  this.state.nodeStates.set(this.reviewerNode.id, "completed");
@@ -3272,16 +3961,20 @@ ${proposerResult.content}`;
3272
3961
  }
3273
3962
  buildErrorResult(ctx, err) {
3274
3963
  this.state.finished = true;
3964
+ const aborted = err instanceof GraphAbortedError;
3965
+ const errorMsg = err instanceof Error ? err.message : String(err);
3966
+ this.state.error = errorMsg;
3967
+ const lastCompleted = [...this.state.nodeResults.values()].at(-1);
3275
3968
  return {
3276
- content: "",
3969
+ content: aborted ? lastCompleted?.content ?? "" : "",
3277
3970
  lastAgentId: this.proposerNode.id,
3278
3971
  agentResults: new Map(this.state.nodeResults),
3279
3972
  totalUsage: {},
3280
3973
  stepsCompleted: this.state.stepsCompleted,
3281
- stopReason: "error",
3974
+ stopReason: aborted ? "aborted" : "error",
3282
3975
  context: ctx,
3283
3976
  success: false,
3284
- error: err instanceof Error ? err.message : String(err)
3977
+ error: errorMsg
3285
3978
  };
3286
3979
  }
3287
3980
  mergeUsage(total, usage) {
@@ -3294,7 +3987,8 @@ ${proposerResult.content}`;
3294
3987
  // patterns/debate.ts
3295
3988
  var DebateGraphImpl = class {
3296
3989
  executor;
3297
- eventListeners = /* @__PURE__ */ new Map();
3990
+ /** on() 注册的监听器,按 MultiAgentEvent.type 分发 */
3991
+ bus = new SimpleEventBus();
3298
3992
  constructor(proposer, reviewer, opts) {
3299
3993
  this.executor = new DebateExecutor(proposer, reviewer, opts);
3300
3994
  }
@@ -3311,23 +4005,21 @@ var DebateGraphImpl = class {
3311
4005
  throw new Error("Debate \u6A21\u5F0F\u4E0D\u652F\u6301 setFinish");
3312
4006
  }
3313
4007
  async run(input) {
4008
+ this.executor.setEventSink((event) => this.bus.emit(event.type, event));
3314
4009
  return this.executor.run(input);
3315
4010
  }
3316
4011
  async *stream(input) {
4012
+ this.executor.setEventSink((event) => this.bus.emit(event.type, event));
3317
4013
  yield* this.executor.stream(input);
3318
4014
  }
3319
4015
  getState() {
3320
4016
  return this.executor.getState();
3321
4017
  }
3322
4018
  abort() {
4019
+ this.executor.abort();
3323
4020
  }
3324
4021
  on(event, listener) {
3325
- let set = this.eventListeners.get(event);
3326
- if (!set) {
3327
- set = /* @__PURE__ */ new Set();
3328
- this.eventListeners.set(event, set);
3329
- }
3330
- set.add(listener);
4022
+ this.bus.on(event, listener);
3331
4023
  return this;
3332
4024
  }
3333
4025
  };
@@ -3341,7 +4033,11 @@ var MapReduceExecutor = class {
3341
4033
  reducerNode;
3342
4034
  split;
3343
4035
  concurrency;
4036
+ onMapperError;
3344
4037
  reduceInputFormat;
4038
+ abortController = new AbortController();
4039
+ /** 外部事件监听(on() API 的底层接线) */
4040
+ eventSink;
3345
4041
  state = {
3346
4042
  nodeStates: /* @__PURE__ */ new Map(),
3347
4043
  nodeResults: /* @__PURE__ */ new Map(),
@@ -3353,6 +4049,7 @@ var MapReduceExecutor = class {
3353
4049
  this.reducerNode = reducer;
3354
4050
  this.split = opts.split;
3355
4051
  this.concurrency = opts.concurrency ?? Infinity;
4052
+ this.onMapperError = opts.onMapperError ?? "fail-fast";
3356
4053
  this.reduceInputFormat = opts.reduceInputFormat ?? ((mapperResults) => {
3357
4054
  const parts = [];
3358
4055
  for (const [idx, result] of mapperResults) {
@@ -3365,132 +4062,98 @@ ${result.content}`);
3365
4062
  this.state.nodeStates.set(reducer.id, "pending");
3366
4063
  }
3367
4064
  getState() {
3368
- return { ...this.state };
4065
+ return {
4066
+ ...this.state,
4067
+ nodeStates: new Map(this.state.nodeStates),
4068
+ nodeResults: new Map(this.state.nodeResults)
4069
+ };
3369
4070
  }
3370
- /** 执行 MapReduce */
3371
- async run(input) {
4071
+ /** 注入外部事件监听(由 AgentGraph 实现的 on() 接线) */
4072
+ setEventSink(sink) {
4073
+ this.eventSink = sink;
4074
+ return this;
4075
+ }
4076
+ abort() {
4077
+ this.abortController.abort();
4078
+ }
4079
+ /** 每次 run/stream 前重置中止信号与执行状态 */
4080
+ resetAbortController() {
4081
+ this.abortController = new AbortController();
4082
+ this.state = {
4083
+ nodeStates: /* @__PURE__ */ new Map(),
4084
+ nodeResults: /* @__PURE__ */ new Map(),
4085
+ stepsCompleted: 0,
4086
+ finished: false
4087
+ };
4088
+ this.state.nodeStates.set(this.mapperNode.id, "pending");
4089
+ this.state.nodeStates.set(this.reducerNode.id, "pending");
4090
+ }
4091
+ throwIfAborted() {
4092
+ if (this.abortController.signal.aborted) {
4093
+ throw new GraphAbortedError();
4094
+ }
4095
+ }
4096
+ /** 拆分输入并校验(每次 run/stream 只调用一次 split,避免副作用放大) */
4097
+ splitInput(input) {
3372
4098
  const inputText = typeof input === "string" ? input : input.message;
3373
4099
  const chunks = this.split(inputText);
3374
4100
  if (chunks.length === 0) {
3375
4101
  throw new Error("MapReduce: split \u51FD\u6570\u8FD4\u56DE\u4E86\u7A7A\u6570\u7EC4\uFF0C\u81F3\u5C11\u9700\u8981\u4E00\u4E2A\u5B50\u4EFB\u52A1");
3376
4102
  }
4103
+ return chunks;
4104
+ }
4105
+ /** 执行 MapReduce */
4106
+ async run(input) {
4107
+ this.resetAbortController();
4108
+ const chunks = this.splitInput(input);
3377
4109
  const ctx = createSharedContext({
3378
4110
  meta: { traceId: `mr-${Date.now()}`, startTime: Date.now() }
3379
4111
  });
4112
+ storeOriginalInput(ctx, input);
4113
+ const emit = this.eventSink ?? (() => {
4114
+ });
3380
4115
  try {
3381
- return await this.executeMapReduce(input, ctx, () => {
3382
- });
4116
+ const result = await this.executeMapReduce(chunks, ctx, emit);
4117
+ emit({ type: "graph_done", result });
4118
+ return result;
3383
4119
  } catch (err) {
3384
- return this.buildErrorResult(ctx, err);
4120
+ const result = this.buildErrorResult(ctx, err);
4121
+ emit({ type: "graph_error", error: result.error ?? "" });
4122
+ return result;
3385
4123
  }
3386
4124
  }
3387
4125
  /** 流式执行 MapReduce */
3388
- async *stream(input) {
3389
- const inputText = typeof input === "string" ? input : input.message;
3390
- const chunks = this.split(inputText);
3391
- if (chunks.length === 0) {
3392
- throw new Error("MapReduce: split \u51FD\u6570\u8FD4\u56DE\u4E86\u7A7A\u6570\u7EC4\uFF0C\u81F3\u5C11\u9700\u8981\u4E00\u4E2A\u5B50\u4EFB\u52A1");
3393
- }
4126
+ stream(input) {
4127
+ this.resetAbortController();
4128
+ const chunks = this.splitInput(input);
3394
4129
  const ctx = createSharedContext({
3395
4130
  meta: { traceId: `mr-${Date.now()}`, startTime: Date.now() }
3396
4131
  });
3397
- const eventQueue = [];
3398
- let resolveEvent = null;
3399
- let done = false;
3400
- const emit = (event) => {
3401
- eventQueue.push(event);
3402
- resolveEvent?.();
3403
- };
3404
- const graphPromise = this.executeMapReduce(input, ctx, emit).then(
3405
- (result) => {
3406
- emit({ type: "graph_done", result });
3407
- },
3408
- (err) => {
3409
- emit({ type: "graph_error", error: err instanceof Error ? err.message : String(err) });
3410
- }
3411
- ).finally(() => {
3412
- done = true;
3413
- resolveEvent?.();
3414
- });
3415
- while (!done || eventQueue.length > 0) {
3416
- if (eventQueue.length > 0) {
3417
- yield eventQueue.shift();
3418
- } else {
3419
- await new Promise((resolve) => {
3420
- resolveEvent = resolve;
3421
- });
3422
- }
3423
- }
3424
- await graphPromise;
4132
+ storeOriginalInput(ctx, input);
4133
+ return createEventStream(
4134
+ (emit) => this.executeMapReduce(chunks, ctx, emit).then(
4135
+ (result) => {
4136
+ emit({ type: "graph_done", result });
4137
+ },
4138
+ (err) => {
4139
+ this.state.error = err instanceof Error ? err.message : String(err);
4140
+ emit({ type: "graph_error", error: this.state.error });
4141
+ }
4142
+ ),
4143
+ this.eventSink
4144
+ );
3425
4145
  }
3426
4146
  /** 核心执行逻辑 */
3427
- async executeMapReduce(input, ctx, emit) {
3428
- let stepsCompleted = 0;
4147
+ async executeMapReduce(chunks, ctx, emit) {
3429
4148
  const totalUsage = {};
3430
- const inputText = typeof input === "string" ? input : input.message;
3431
- const chunks = this.split(inputText);
3432
- const mapperResults = /* @__PURE__ */ new Map();
4149
+ this.throwIfAborted();
3433
4150
  this.state.nodeStates.set(this.mapperNode.id, "running");
3434
- const mapperAgentIds = chunks.map((_, i) => `${this.mapperNode.id}_${i}`);
3435
- emit({ type: "parallel_start", agentIds: mapperAgentIds });
3436
- if (this.concurrency >= chunks.length) {
3437
- const promises = chunks.map(async (chunk, index) => {
3438
- emit({ type: "agent_start", agentId: `${this.mapperNode.id}_${index}`, agentName: `${this.mapperNode.name}#${index + 1}` });
3439
- try {
3440
- const result = await executeNode(this.mapperNode, chunk, ctx);
3441
- this.state.nodeResults.set(`${this.mapperNode.id}_${index}`, result);
3442
- this.mergeUsage(totalUsage, result.usage);
3443
- stepsCompleted++;
3444
- this.state.stepsCompleted = stepsCompleted;
3445
- emit({ type: "agent_result", agentId: `${this.mapperNode.id}_${index}`, agentName: `${this.mapperNode.name}#${index + 1}`, result });
3446
- return { index, result };
3447
- } catch (err) {
3448
- const errorMsg = err instanceof Error ? err.message : String(err);
3449
- emit({ type: "agent_error", agentId: `${this.mapperNode.id}_${index}`, agentName: `${this.mapperNode.name}#${index + 1}`, error: errorMsg });
3450
- throw err;
3451
- }
3452
- });
3453
- const settled = await Promise.allSettled(promises);
3454
- for (const s of settled) {
3455
- if (s.status === "fulfilled") {
3456
- mapperResults.set(s.value.index, s.value.result);
3457
- }
3458
- }
3459
- } else {
3460
- let index = 0;
3461
- const executing = /* @__PURE__ */ new Set();
3462
- const enqueue = () => {
3463
- if (index >= chunks.length) return null;
3464
- const currentIndex = index++;
3465
- const chunk = chunks[currentIndex];
3466
- const p = (async () => {
3467
- emit({ type: "agent_start", agentId: `${this.mapperNode.id}_${currentIndex}`, agentName: `${this.mapperNode.name}#${currentIndex + 1}` });
3468
- try {
3469
- const result = await executeNode(this.mapperNode, chunk, ctx);
3470
- mapperResults.set(currentIndex, result);
3471
- this.state.nodeResults.set(`${this.mapperNode.id}_${currentIndex}`, result);
3472
- this.mergeUsage(totalUsage, result.usage);
3473
- stepsCompleted++;
3474
- this.state.stepsCompleted = stepsCompleted;
3475
- emit({ type: "agent_result", agentId: `${this.mapperNode.id}_${currentIndex}`, agentName: `${this.mapperNode.name}#${currentIndex + 1}`, result });
3476
- } catch (err) {
3477
- const errorMsg = err instanceof Error ? err.message : String(err);
3478
- emit({ type: "agent_error", agentId: `${this.mapperNode.id}_${currentIndex}`, agentName: `${this.mapperNode.name}#${currentIndex + 1}`, error: errorMsg });
3479
- throw err;
3480
- }
3481
- })();
3482
- executing.add(p);
3483
- p.finally(() => executing.delete(p));
3484
- return p;
3485
- };
3486
- for (let i = 0; i < this.concurrency && index < chunks.length; i++) {
3487
- enqueue();
3488
- }
3489
- while (executing.size > 0) {
3490
- await Promise.race(executing);
3491
- enqueue();
3492
- }
3493
- }
4151
+ chunks.forEach((_, i) => {
4152
+ this.state.nodeStates.set(`${this.mapperNode.id}_${i}`, "pending");
4153
+ });
4154
+ const { results: mapperResults, failed } = await this.runMappers(chunks, ctx, emit, totalUsage);
4155
+ let stepsCompleted = mapperResults.size + failed.length;
4156
+ this.state.stepsCompleted = stepsCompleted;
3494
4157
  this.state.nodeStates.set(this.mapperNode.id, "completed");
3495
4158
  const mapperResultsForEmit = /* @__PURE__ */ new Map();
3496
4159
  for (const [idx, result] of mapperResults) {
@@ -3498,10 +4161,14 @@ ${result.content}`);
3498
4161
  }
3499
4162
  emit({ type: "parallel_done", results: mapperResultsForEmit });
3500
4163
  ctx.blackboard.set("mapper_results", mapperResults);
4164
+ if (failed.length > 0 && mapperResults.size === 0) {
4165
+ throw new Error(`MapReduce: \u6240\u6709 mapper \u5747\u5931\u8D25\uFF08${failed.length}/${chunks.length}\uFF09`);
4166
+ }
3501
4167
  const reduceInput = this.reduceInputFormat(mapperResults);
4168
+ this.throwIfAborted();
3502
4169
  this.state.currentAgentId = this.reducerNode.id;
3503
4170
  this.state.nodeStates.set(this.reducerNode.id, "running");
3504
- emit({ type: "agent_start", agentId: this.reducerNode.id, agentName: this.reducerNode.name });
4171
+ emit({ type: "agent_start", agentId: this.reducerNode.id, agentName: this.reducerNode.name, input: reduceInput });
3505
4172
  let reducerResult;
3506
4173
  try {
3507
4174
  reducerResult = await executeNode(this.reducerNode, reduceInput, ctx);
@@ -3518,29 +4185,94 @@ ${result.content}`);
3518
4185
  throw err;
3519
4186
  }
3520
4187
  this.state.finished = true;
4188
+ const hasFailures = failed.length > 0;
3521
4189
  return {
3522
4190
  content: reducerResult.content,
3523
4191
  lastAgentId: this.reducerNode.id,
3524
4192
  agentResults: new Map(this.state.nodeResults),
3525
4193
  totalUsage,
3526
4194
  stepsCompleted,
3527
- stopReason: "completed",
4195
+ stopReason: hasFailures ? "partial_failure" : "completed",
3528
4196
  context: ctx,
3529
- success: true
4197
+ success: true,
4198
+ ...hasFailures ? { failedAgents: failed } : {}
4199
+ };
4200
+ }
4201
+ /** 并行执行所有 mapper 子任务(统一处理并发限制与失败策略) */
4202
+ async runMappers(chunks, ctx, emit, totalUsage) {
4203
+ const results = /* @__PURE__ */ new Map();
4204
+ const failed = [];
4205
+ const skip = this.onMapperError === "skip";
4206
+ const mapperId = this.mapperNode.id;
4207
+ const mapperAgentIds = chunks.map((_, i) => `${mapperId}_${i}`);
4208
+ emit({ type: "parallel_start", agentIds: mapperAgentIds });
4209
+ const runOne = async (index) => {
4210
+ const agentId = `${mapperId}_${index}`;
4211
+ const agentName = `${this.mapperNode.name}#${index + 1}`;
4212
+ emit({ type: "agent_start", agentId, agentName, input: chunks[index] });
4213
+ this.state.nodeStates.set(agentId, "running");
4214
+ try {
4215
+ const result = await executeNode(this.mapperNode, chunks[index], ctx);
4216
+ this.state.nodeResults.set(agentId, result);
4217
+ this.state.nodeStates.set(agentId, "completed");
4218
+ this.mergeUsage(totalUsage, result.usage);
4219
+ results.set(index, result);
4220
+ emit({ type: "agent_result", agentId, agentName, result });
4221
+ } catch (err) {
4222
+ const errorMsg = err instanceof Error ? err.message : String(err);
4223
+ this.state.nodeStates.set(agentId, "failed");
4224
+ emit({ type: "agent_error", agentId, agentName, error: errorMsg });
4225
+ if (!skip) throw err;
4226
+ failed.push(agentId);
4227
+ }
3530
4228
  };
4229
+ if (this.concurrency >= chunks.length) {
4230
+ const promises = chunks.map((_, i) => runOne(i));
4231
+ if (!skip) {
4232
+ await Promise.all(promises);
4233
+ } else {
4234
+ await Promise.allSettled(promises);
4235
+ }
4236
+ } else {
4237
+ let index = 0;
4238
+ const executing = /* @__PURE__ */ new Set();
4239
+ const enqueue = () => {
4240
+ if (index >= chunks.length) return null;
4241
+ const i = index++;
4242
+ const p = runOne(i).finally(() => {
4243
+ executing.delete(p);
4244
+ });
4245
+ executing.add(p);
4246
+ p.catch(() => {
4247
+ });
4248
+ return p;
4249
+ };
4250
+ for (let i = 0; i < this.concurrency && index < chunks.length; i++) {
4251
+ enqueue();
4252
+ }
4253
+ while (executing.size > 0) {
4254
+ await Promise.race(executing);
4255
+ enqueue();
4256
+ }
4257
+ }
4258
+ return { results, failed };
3531
4259
  }
3532
4260
  buildErrorResult(ctx, err) {
3533
4261
  this.state.finished = true;
4262
+ const aborted = err instanceof GraphAbortedError;
4263
+ const errorMsg = err instanceof Error ? err.message : String(err);
4264
+ this.state.error = errorMsg;
4265
+ const lastCompleted = [...this.state.nodeResults.values()].at(-1);
3534
4266
  return {
3535
- content: "",
4267
+ content: aborted ? lastCompleted?.content ?? "" : "",
3536
4268
  lastAgentId: this.mapperNode.id,
3537
4269
  agentResults: new Map(this.state.nodeResults),
3538
4270
  totalUsage: {},
3539
4271
  stepsCompleted: this.state.stepsCompleted,
3540
- stopReason: "error",
4272
+ stopReason: aborted ? "aborted" : "error",
3541
4273
  context: ctx,
3542
4274
  success: false,
3543
- error: err instanceof Error ? err.message : String(err)
4275
+ error: errorMsg
3544
4276
  };
3545
4277
  }
3546
4278
  mergeUsage(total, usage) {
@@ -3553,7 +4285,8 @@ ${result.content}`);
3553
4285
  // patterns/map-reduce.ts
3554
4286
  var MapReduceGraphImpl = class {
3555
4287
  executor;
3556
- eventListeners = /* @__PURE__ */ new Map();
4288
+ /** on() 注册的监听器,按 MultiAgentEvent.type 分发 */
4289
+ bus = new SimpleEventBus();
3557
4290
  constructor(mapper, reducer, opts) {
3558
4291
  this.executor = new MapReduceExecutor(mapper, reducer, opts);
3559
4292
  }
@@ -3570,23 +4303,21 @@ var MapReduceGraphImpl = class {
3570
4303
  throw new Error("MapReduce \u6A21\u5F0F\u4E0D\u652F\u6301 setFinish");
3571
4304
  }
3572
4305
  async run(input) {
4306
+ this.executor.setEventSink((event) => this.bus.emit(event.type, event));
3573
4307
  return this.executor.run(input);
3574
4308
  }
3575
4309
  async *stream(input) {
4310
+ this.executor.setEventSink((event) => this.bus.emit(event.type, event));
3576
4311
  yield* this.executor.stream(input);
3577
4312
  }
3578
4313
  getState() {
3579
4314
  return this.executor.getState();
3580
4315
  }
3581
4316
  abort() {
4317
+ this.executor.abort();
3582
4318
  }
3583
4319
  on(event, listener) {
3584
- let set = this.eventListeners.get(event);
3585
- if (!set) {
3586
- set = /* @__PURE__ */ new Set();
3587
- this.eventListeners.set(event, set);
3588
- }
3589
- set.add(listener);
4320
+ this.bus.on(event, listener);
3590
4321
  return this;
3591
4322
  }
3592
4323
  };
@@ -4225,6 +4956,7 @@ var GraphDebugger = class {
4225
4956
  const graphStartTime = Date.now();
4226
4957
  const steps = [];
4227
4958
  let stepIndex = 0;
4959
+ let finalResult;
4228
4960
  for await (const event of this.graph.stream(input)) {
4229
4961
  if (event.type === "agent_start") {
4230
4962
  stepIndex++;
@@ -4234,7 +4966,7 @@ var GraphDebugger = class {
4234
4966
  agentName: event.agentName,
4235
4967
  startTime: Date.now(),
4236
4968
  duration: 0,
4237
- input: "",
4969
+ input: event.input ?? "",
4238
4970
  output: "",
4239
4971
  state: "running"
4240
4972
  });
@@ -4259,6 +4991,8 @@ var GraphDebugger = class {
4259
4991
  step.error = event.error;
4260
4992
  step.state = "failed";
4261
4993
  }
4994
+ } else if (event.type === "graph_done") {
4995
+ finalResult = event.result;
4262
4996
  }
4263
4997
  }
4264
4998
  const state = this.graph.getState();
@@ -4269,10 +5003,10 @@ var GraphDebugger = class {
4269
5003
  duration: graphDuration,
4270
5004
  steps,
4271
5005
  result: {
4272
- success: !state.error,
4273
- content: state.error ?? "",
4274
- stopReason: state.error ? "error" : "completed",
4275
- stepsCompleted: state.stepsCompleted
5006
+ success: finalResult ? finalResult.success : !state.error,
5007
+ content: finalResult?.content ?? "",
5008
+ stopReason: finalResult?.stopReason ?? (state.error ? "error" : "completed"),
5009
+ stepsCompleted: finalResult?.stepsCompleted ?? state.stepsCompleted
4276
5010
  }
4277
5011
  };
4278
5012
  }
@@ -4324,13 +5058,16 @@ function createDebugger(graph) {
4324
5058
  return new GraphDebugger(graph);
4325
5059
  }
4326
5060
  export {
5061
+ GraphAbortedError,
4327
5062
  GraphDebugger,
4328
5063
  MCPBridge,
5064
+ NodeTimeoutError,
4329
5065
  SimpleEventBus,
4330
5066
  SimpleToolRegistry,
4331
5067
  createAgentGraph,
4332
5068
  createDebate,
4333
5069
  createDebugger,
5070
+ createEventStream,
4334
5071
  createMCPBridge,
4335
5072
  createMapReduce,
4336
5073
  createMultiAgentMcpServerHost,