@downcity/agent 1.1.79 → 1.1.81

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.
Files changed (173) hide show
  1. package/README.md +1 -1
  2. package/bin/agent/local/Agent.d.ts +8 -121
  3. package/bin/agent/local/Agent.d.ts.map +1 -1
  4. package/bin/agent/local/Agent.js +63 -381
  5. package/bin/agent/local/Agent.js.map +1 -1
  6. package/bin/agent/local/AgentRuntimeFactory.d.ts +2 -2
  7. package/bin/agent/local/AgentRuntimeFactory.d.ts.map +1 -1
  8. package/bin/agent/local/AgentRuntimeFactory.js +2 -23
  9. package/bin/agent/local/AgentRuntimeFactory.js.map +1 -1
  10. package/bin/agent/local/services/AgentAssemblyService.d.ts +112 -0
  11. package/bin/agent/local/services/AgentAssemblyService.d.ts.map +1 -0
  12. package/bin/agent/local/services/AgentAssemblyService.js +135 -0
  13. package/bin/agent/local/services/AgentAssemblyService.js.map +1 -0
  14. package/bin/agent/local/services/AgentLifecycleService.d.ts +59 -0
  15. package/bin/agent/local/services/AgentLifecycleService.d.ts.map +1 -0
  16. package/bin/agent/local/services/AgentLifecycleService.js +136 -0
  17. package/bin/agent/local/services/AgentLifecycleService.js.map +1 -0
  18. package/bin/agent/local/services/AgentSessionManager.d.ts +106 -0
  19. package/bin/agent/local/services/AgentSessionManager.d.ts.map +1 -0
  20. package/bin/agent/local/services/AgentSessionManager.js +182 -0
  21. package/bin/agent/local/services/AgentSessionManager.js.map +1 -0
  22. package/bin/executor/Executor.d.ts +7 -24
  23. package/bin/executor/Executor.d.ts.map +1 -1
  24. package/bin/executor/Executor.js +73 -361
  25. package/bin/executor/Executor.js.map +1 -1
  26. package/bin/executor/SessionRunScope.d.ts +18 -34
  27. package/bin/executor/SessionRunScope.d.ts.map +1 -1
  28. package/bin/executor/SessionRunScope.js +42 -28
  29. package/bin/executor/SessionRunScope.js.map +1 -1
  30. package/bin/executor/composer/context/LocalSessionContextComposer.d.ts +5 -3
  31. package/bin/executor/composer/context/LocalSessionContextComposer.d.ts.map +1 -1
  32. package/bin/executor/composer/context/LocalSessionContextComposer.js +11 -18
  33. package/bin/executor/composer/context/LocalSessionContextComposer.js.map +1 -1
  34. package/bin/executor/composer/context/SessionContextComposer.d.ts +8 -3
  35. package/bin/executor/composer/context/SessionContextComposer.d.ts.map +1 -1
  36. package/bin/executor/composer/system/SessionSystemComposer.d.ts +2 -1
  37. package/bin/executor/composer/system/SessionSystemComposer.d.ts.map +1 -1
  38. package/bin/executor/composer/system/default/DefaultSessionSystemComposer.d.ts +2 -1
  39. package/bin/executor/composer/system/default/DefaultSessionSystemComposer.d.ts.map +1 -1
  40. package/bin/executor/composer/system/default/DefaultSessionSystemComposer.js +2 -4
  41. package/bin/executor/composer/system/default/DefaultSessionSystemComposer.js.map +1 -1
  42. package/bin/executor/core-engine/CoreEngineRunner.d.ts +62 -0
  43. package/bin/executor/core-engine/CoreEngineRunner.d.ts.map +1 -0
  44. package/bin/executor/core-engine/CoreEngineRunner.js +309 -0
  45. package/bin/executor/core-engine/CoreEngineRunner.js.map +1 -0
  46. package/bin/executor/core-engine/CoreEngineUiStreamCollector.d.ts +5 -0
  47. package/bin/executor/core-engine/CoreEngineUiStreamCollector.d.ts.map +1 -1
  48. package/bin/executor/core-engine/CoreEngineUiStreamCollector.js +2 -4
  49. package/bin/executor/core-engine/CoreEngineUiStreamCollector.js.map +1 -1
  50. package/bin/executor/services/ExecutorInflightService.d.ts +39 -0
  51. package/bin/executor/services/ExecutorInflightService.d.ts.map +1 -0
  52. package/bin/executor/services/ExecutorInflightService.js +75 -0
  53. package/bin/executor/services/ExecutorInflightService.js.map +1 -0
  54. package/bin/executor/services/ExecutorRecoveryPolicy.d.ts +103 -0
  55. package/bin/executor/services/ExecutorRecoveryPolicy.d.ts.map +1 -0
  56. package/bin/executor/services/ExecutorRecoveryPolicy.js +87 -0
  57. package/bin/executor/services/ExecutorRecoveryPolicy.js.map +1 -0
  58. package/bin/executor/tools/plugin/PluginToolBridge.d.ts +19 -0
  59. package/bin/executor/tools/plugin/PluginToolBridge.d.ts.map +1 -0
  60. package/bin/executor/tools/plugin/PluginToolBridge.js +143 -0
  61. package/bin/executor/tools/plugin/PluginToolBridge.js.map +1 -0
  62. package/bin/executor/tools/plugin/PluginToolDefinition.d.ts +32 -0
  63. package/bin/executor/tools/plugin/PluginToolDefinition.d.ts.map +1 -0
  64. package/bin/executor/tools/plugin/PluginToolDefinition.js +27 -0
  65. package/bin/executor/tools/plugin/PluginToolDefinition.js.map +1 -0
  66. package/bin/executor/tools/plugin/PluginToolSchemas.d.ts +14 -0
  67. package/bin/executor/tools/plugin/PluginToolSchemas.d.ts.map +1 -0
  68. package/bin/executor/tools/plugin/PluginToolSchemas.js +19 -0
  69. package/bin/executor/tools/plugin/PluginToolSchemas.js.map +1 -0
  70. package/bin/executor/tools/plugin/types/PluginTool.d.ts +39 -0
  71. package/bin/executor/tools/plugin/types/PluginTool.d.ts.map +1 -0
  72. package/bin/executor/tools/plugin/types/PluginTool.js +9 -0
  73. package/bin/executor/tools/plugin/types/PluginTool.js.map +1 -0
  74. package/bin/executor/tools/shell/ShellToolBridge.js +3 -3
  75. package/bin/executor/tools/shell/ShellToolBridge.js.map +1 -1
  76. package/bin/executor/types/SessionRun.d.ts +18 -0
  77. package/bin/executor/types/SessionRun.d.ts.map +1 -1
  78. package/bin/index.d.ts +10 -1
  79. package/bin/index.d.ts.map +1 -1
  80. package/bin/index.js +3 -0
  81. package/bin/index.js.map +1 -1
  82. package/bin/plugin/core/BasePlugin.d.ts +2 -2
  83. package/bin/plugin/core/BasePlugin.d.ts.map +1 -1
  84. package/bin/plugin/core/BasePlugin.js.map +1 -1
  85. package/bin/plugin/core/ImagePlugin.d.ts +56 -0
  86. package/bin/plugin/core/ImagePlugin.d.ts.map +1 -0
  87. package/bin/plugin/core/ImagePlugin.js +109 -0
  88. package/bin/plugin/core/ImagePlugin.js.map +1 -0
  89. package/bin/session/Session.d.ts +14 -83
  90. package/bin/session/Session.d.ts.map +1 -1
  91. package/bin/session/Session.js +139 -362
  92. package/bin/session/Session.js.map +1 -1
  93. package/bin/session/SessionSystemBuilder.d.ts +2 -1
  94. package/bin/session/SessionSystemBuilder.d.ts.map +1 -1
  95. package/bin/session/SessionSystemBuilder.js +2 -3
  96. package/bin/session/SessionSystemBuilder.js.map +1 -1
  97. package/bin/session/services/SessionStateService.d.ts +132 -0
  98. package/bin/session/services/SessionStateService.d.ts.map +1 -0
  99. package/bin/session/services/SessionStateService.js +242 -0
  100. package/bin/session/services/SessionStateService.js.map +1 -0
  101. package/bin/session/services/SessionTurnService.d.ts +66 -0
  102. package/bin/session/services/SessionTurnService.d.ts.map +1 -0
  103. package/bin/session/services/SessionTurnService.js +137 -0
  104. package/bin/session/services/SessionTurnService.js.map +1 -0
  105. package/bin/session/services/SessionViewService.d.ts +105 -0
  106. package/bin/session/services/SessionViewService.d.ts.map +1 -0
  107. package/bin/session/services/SessionViewService.js +184 -0
  108. package/bin/session/services/SessionViewService.js.map +1 -0
  109. package/bin/types/agent/AgentOptions.d.ts +18 -0
  110. package/bin/types/agent/AgentOptions.d.ts.map +1 -1
  111. package/bin/types/agent/AgentTypes.d.ts +3 -1
  112. package/bin/types/agent/AgentTypes.d.ts.map +1 -1
  113. package/bin/types/agent/SessionTypes.d.ts.map +1 -1
  114. package/bin/types/executor/SessionRunContext.d.ts +66 -0
  115. package/bin/types/executor/SessionRunContext.d.ts.map +1 -0
  116. package/bin/types/executor/SessionRunContext.js +10 -0
  117. package/bin/types/executor/SessionRunContext.js.map +1 -0
  118. package/bin/types/plugin/ImagePlugin.d.ts +94 -0
  119. package/bin/types/plugin/ImagePlugin.d.ts.map +1 -0
  120. package/bin/types/plugin/ImagePlugin.js +10 -0
  121. package/bin/types/plugin/ImagePlugin.js.map +1 -0
  122. package/bin/types/session/SessionComposerOptions.d.ts +90 -0
  123. package/bin/types/session/SessionComposerOptions.d.ts.map +1 -0
  124. package/bin/types/session/SessionComposerOptions.js +10 -0
  125. package/bin/types/session/SessionComposerOptions.js.map +1 -0
  126. package/bin/types/session/SessionLocalState.d.ts +35 -0
  127. package/bin/types/session/SessionLocalState.d.ts.map +1 -0
  128. package/bin/types/session/SessionLocalState.js +10 -0
  129. package/bin/types/session/SessionLocalState.js.map +1 -0
  130. package/bin/types/session/SessionOptions.d.ts +85 -0
  131. package/bin/types/session/SessionOptions.d.ts.map +1 -0
  132. package/bin/types/session/SessionOptions.js +10 -0
  133. package/bin/types/session/SessionOptions.js.map +1 -0
  134. package/package.json +1 -1
  135. package/src/agent/local/Agent.ts +74 -433
  136. package/src/agent/local/AgentRuntimeFactory.ts +4 -25
  137. package/src/agent/local/services/AgentAssemblyService.ts +268 -0
  138. package/src/agent/local/services/AgentLifecycleService.ts +187 -0
  139. package/src/agent/local/services/AgentSessionManager.ts +291 -0
  140. package/src/executor/Executor.ts +103 -441
  141. package/src/executor/README.md +4 -4
  142. package/src/executor/SessionRunScope.ts +47 -71
  143. package/src/executor/composer/context/LocalSessionContextComposer.ts +24 -20
  144. package/src/executor/composer/context/SessionContextComposer.ts +13 -3
  145. package/src/executor/composer/system/SessionSystemComposer.ts +2 -1
  146. package/src/executor/composer/system/default/DefaultSessionSystemComposer.ts +3 -4
  147. package/src/executor/core-engine/CoreEngineRunner.ts +433 -0
  148. package/src/executor/core-engine/CoreEngineUiStreamCollector.ts +7 -5
  149. package/src/executor/services/ExecutorInflightService.ts +101 -0
  150. package/src/executor/services/ExecutorRecoveryPolicy.ts +213 -0
  151. package/src/executor/tools/plugin/PluginToolBridge.ts +161 -0
  152. package/src/executor/tools/plugin/PluginToolDefinition.ts +32 -0
  153. package/src/executor/tools/plugin/PluginToolSchemas.ts +20 -0
  154. package/src/executor/tools/plugin/types/PluginTool.ts +41 -0
  155. package/src/executor/tools/shell/ShellToolBridge.ts +3 -3
  156. package/src/executor/types/SessionRun.ts +20 -0
  157. package/src/index.ts +33 -0
  158. package/src/plugin/core/BasePlugin.ts +2 -2
  159. package/src/plugin/core/ImagePlugin.ts +128 -0
  160. package/src/session/Session.ts +178 -485
  161. package/src/session/SessionSystemBuilder.ts +3 -3
  162. package/src/session/services/SessionStateService.ts +341 -0
  163. package/src/session/services/SessionTurnService.ts +202 -0
  164. package/src/session/services/SessionViewService.ts +301 -0
  165. package/src/types/agent/AgentOptions.ts +25 -0
  166. package/src/types/agent/AgentTypes.ts +10 -0
  167. package/src/types/agent/SessionTypes.ts +1 -0
  168. package/src/types/executor/SessionRunContext.ts +76 -0
  169. package/src/types/plugin/ImagePlugin.ts +103 -0
  170. package/src/types/session/SessionComposerOptions.ts +107 -0
  171. package/src/types/session/SessionLocalState.ts +40 -0
  172. package/src/types/session/SessionOptions.ts +99 -0
  173. package/tsconfig.tsbuildinfo +1 -1
@@ -12,55 +12,24 @@ import { SessionHistoryWriter } from "@executor/composer/history/SessionHistoryW
12
12
  import type { SessionHistoryComposer } from "@executor/composer/history/SessionHistoryComposer.js";
13
13
  import type { SessionHistoryStore } from "@/executor/store/history/SessionHistoryStore.js";
14
14
  import { withSessionRunScope } from "@executor/SessionRunScope.js";
15
- import type { SessionRunScope } from "@executor/SessionRunScope.js";
16
15
  import { buildSessionStepParts } from "@executor/messages/SessionStepEventMapper.js";
17
16
  import { JsonlSessionCompactionComposer } from "@executor/composer/compaction/jsonl/JsonlSessionCompactionComposer.js";
18
17
  import { LocalSessionContextComposer } from "@executor/composer/context/LocalSessionContextComposer.js";
18
+ import { CoreEngineRunner } from "@executor/core-engine/CoreEngineRunner.js";
19
19
  import type { SessionCompactionComposer } from "@executor/composer/compaction/SessionCompactionComposer.js";
20
20
  import type { SessionContextComposer } from "@executor/composer/context/SessionContextComposer.js";
21
21
  import type { SessionSystemComposer } from "@executor/composer/system/SessionSystemComposer.js";
22
- import {
23
- MAX_INCOMPLETE_RESPONSE_RECOVERIES,
24
- MAX_TEXT_ONLY_CONTINUATIONS,
25
- MAX_TOOL_LOOP_STEPS,
26
- buildIncompleteResponseRecoveryNudge,
27
- buildTextOnlyContinuationNudge,
28
- detectIncompleteResponse,
29
- detectTextOnlyContinuationReason,
30
- mergeAssistantUiMessages,
31
- summarizeStepForDebug,
32
- summarizeUiMessageForDebug,
33
- toInlinePreview,
34
- } from "@executor/core-engine/CoreEngineSignals.js";
35
- import {
36
- evaluateCoreEngineLoopDecision,
37
- shouldContinueForTailMergedUserMessages,
38
- } from "@executor/core-engine/CoreEngineLoopDecision.js";
39
- import {
40
- resolveEffectiveCoreEngineError,
41
- summarizeStreamError,
42
- } from "@executor/core-engine/CoreEngineError.js";
43
- import { collectFinalAssistantMessageFromUiStream } from "@executor/core-engine/CoreEngineUiStreamCollector.js";
44
- import { CoreEngineMessageState } from "@executor/core-engine/CoreEngineMessageState.js";
45
- import {
46
- buildOpenAIResponsesProviderOptions,
47
- } from "@executor/messages/SessionMessageCodec.js";
48
- import { logAssistantMessageNow } from "@executor/messages/SessionMessageLog.js";
22
+ import { ExecutorInflightService } from "@executor/services/ExecutorInflightService.js";
23
+ import { ExecutorRecoveryPolicy } from "@executor/services/ExecutorRecoveryPolicy.js";
49
24
  import type { Logger } from "@/utils/logger/Logger.js";
50
25
  import type { JsonObject } from "@/types/common/Json.js";
51
26
  import type { SessionMessageV1 } from "@/executor/types/SessionMessages.js";
52
27
  import type { SessionExecutor } from "@/executor/types/SessionExecutor.js";
28
+ import type { SessionRunContext } from "@/types/executor/SessionRunContext.js";
53
29
  import type {
54
30
  SessionExecuteInput,
55
- SessionRunInput,
56
31
  SessionRunResult,
57
32
  } from "@/executor/types/SessionRun.js";
58
- import { generateId } from "@/utils/Id.js";
59
-
60
- /**
61
- * 可压缩错误的最大重试次数。
62
- */
63
- const MAX_COMPACTION_RETRY_ATTEMPTS = 3;
64
33
 
65
34
  type ExecutorOptions = {
66
35
  /**
@@ -134,9 +103,11 @@ export class Executor implements SessionExecutor {
134
103
  private readonly systemComposer: SessionSystemComposer;
135
104
  protected readonly contextComposer: SessionContextComposer;
136
105
  private readonly historyWriter: SessionHistoryWriter;
106
+ private readonly inflight_service: ExecutorInflightService;
107
+ private readonly recovery_policy: ExecutorRecoveryPolicy;
108
+ private readonly core_engine_runner: CoreEngineRunner;
137
109
 
138
110
  private executing = false;
139
- private retryCount = 0;
140
111
 
141
112
  constructor(options: ExecutorOptions) {
142
113
  const sessionId = String(options.sessionId || "").trim();
@@ -163,6 +134,24 @@ export class Executor implements SessionExecutor {
163
134
  getHistoryStore: () => this.getHistoryStore(),
164
135
  runAfterSessionUpdated: options.runAfterSessionUpdated,
165
136
  });
137
+ this.inflight_service = new ExecutorInflightService({
138
+ session_id: this.sessionId,
139
+ history_store: this.historyStore,
140
+ run_after_session_updated_async: async () =>
141
+ await this.afterSessionUpdatedAsync(),
142
+ });
143
+ this.recovery_policy = new ExecutorRecoveryPolicy({
144
+ compaction_composer: this.compactionComposer,
145
+ context_composer: this.contextComposer,
146
+ logger: this.logger,
147
+ });
148
+ this.core_engine_runner = new CoreEngineRunner({
149
+ history_store: this.historyStore,
150
+ context_composer: this.contextComposer,
151
+ logger: this.logger,
152
+ should_compact_on_error: (error) =>
153
+ this.compactionComposer.shouldCompactOnError(error),
154
+ });
166
155
  }
167
156
 
168
157
  /**
@@ -227,56 +216,6 @@ export class Executor implements SessionExecutor {
227
216
  await this.historyWriter.appendAssistantMessage(params);
228
217
  }
229
218
 
230
- /**
231
- * 把 step/tool 过程增量写入当前运行中的 assistant 快照。
232
- */
233
- private async appendAssistantStepPartsToInflight(
234
- parts: SessionMessageV1["parts"],
235
- ): Promise<void> {
236
- const normalized_parts = Array.isArray(parts)
237
- ? parts.filter((part) => part && typeof part === "object")
238
- : [];
239
- if (normalized_parts.length === 0) return;
240
-
241
- const current_inflight = await this.historyStore.readInflight();
242
- const next_message: SessionMessageV1 = current_inflight
243
- ? {
244
- ...current_inflight,
245
- metadata: {
246
- ...(current_inflight.metadata || {
247
- v: 1 as const,
248
- ts: Date.now(),
249
- sessionId: this.sessionId,
250
- }),
251
- ts: Date.now(),
252
- sessionId: this.sessionId,
253
- source: "egress",
254
- kind: "normal",
255
- },
256
- parts: [
257
- ...(Array.isArray(current_inflight.parts)
258
- ? current_inflight.parts
259
- : []),
260
- ...normalized_parts,
261
- ],
262
- }
263
- : {
264
- id: `a:${this.sessionId}:${generateId()}`,
265
- role: "assistant",
266
- metadata: {
267
- v: 1,
268
- ts: Date.now(),
269
- sessionId: this.sessionId,
270
- source: "egress",
271
- kind: "normal",
272
- },
273
- parts: normalized_parts,
274
- };
275
-
276
- await this.historyStore.writeInflight(next_message);
277
- await this.afterSessionUpdatedAsync();
278
- }
279
-
280
219
  /**
281
220
  * 运行当前 session 的一次请求。
282
221
  *
@@ -286,9 +225,7 @@ export class Executor implements SessionExecutor {
286
225
  */
287
226
  async run(params: {
288
227
  query: string;
289
- onStepCallback?: SessionRunScope["onStepCallback"];
290
- onAssistantStepCallback?: SessionRunScope["onAssistantStepCallback"];
291
- onUiMessageChunkCallback?: SessionRunScope["onUiMessageChunkCallback"];
228
+ runContext?: SessionRunContext;
292
229
  }): Promise<SessionRunResult> {
293
230
  if (this.executing) {
294
231
  // 关键点(中文):同一个 Session 实例只允许一个活跃 run,
@@ -296,19 +233,9 @@ export class Executor implements SessionExecutor {
296
233
  throw new Error("Executor.run does not support concurrent execution");
297
234
  }
298
235
  const query = String(params.query || "").trim();
299
- const sessionRunScope: Omit<SessionRunScope, "sessionId"> = {
300
- ...(typeof params.onStepCallback === "function"
301
- ? { onStepCallback: params.onStepCallback }
302
- : {}),
303
- ...(typeof params.onAssistantStepCallback === "function"
304
- ? { onAssistantStepCallback: params.onAssistantStepCallback }
305
- : {}),
306
- ...(typeof params.onUiMessageChunkCallback === "function"
307
- ? { onUiMessageChunkCallback: params.onUiMessageChunkCallback }
308
- : {}),
309
- };
236
+ const run_context = this.createRunContext(params.runContext);
310
237
  const providedOnAssistantStepCallback =
311
- sessionRunScope.onAssistantStepCallback;
238
+ run_context.onAssistantStepCallback;
312
239
 
313
240
  const wrappedOnAssistantStepCallback = async (step: {
314
241
  text: string;
@@ -323,95 +250,79 @@ export class Executor implements SessionExecutor {
323
250
  visibility: step.visibility,
324
251
  });
325
252
  if (step_parts.length > 0) {
326
- await this.appendAssistantStepPartsToInflight(step_parts);
253
+ await this.inflight_service.append_assistant_step_parts(step_parts);
327
254
  }
328
255
 
329
256
  if (typeof providedOnAssistantStepCallback === "function") {
330
257
  await providedOnAssistantStepCallback(step);
331
258
  }
332
259
  };
260
+ run_context.onAssistantStepCallback = wrappedOnAssistantStepCallback;
333
261
 
334
262
  this.executing = true;
335
- this.resetRunState();
263
+ this.recovery_policy.reset_run_state();
336
264
  try {
337
265
  const result = await withSessionRunScope(
338
266
  {
339
- sessionId: this.sessionId,
340
- ...sessionRunScope,
341
- onAssistantStepCallback: wrappedOnAssistantStepCallback,
267
+ runContext: run_context,
342
268
  },
343
- () => this.runWithRetry({ query }),
269
+ async () =>
270
+ await this.recovery_policy.run_with_retry({
271
+ query,
272
+ model: this.resolveModelOrThrow(),
273
+ run_context,
274
+ prepare_execute_input: async ({
275
+ query: next_query,
276
+ model,
277
+ run_context: next_run_context,
278
+ retry_count,
279
+ }) =>
280
+ await this.prepareExecuteInput(
281
+ next_query,
282
+ model,
283
+ next_run_context,
284
+ retry_count,
285
+ ),
286
+ execute_prepared_run: async ({
287
+ execute_input,
288
+ model,
289
+ run_context: next_run_context,
290
+ }) =>
291
+ await this.executePreparedRun(
292
+ execute_input,
293
+ model,
294
+ next_run_context,
295
+ ),
296
+ }),
344
297
  );
345
298
  return result;
346
299
  } finally {
347
- this.resetRunState();
300
+ this.recovery_policy.reset_run_state();
348
301
  this.executing = false;
349
302
  }
350
303
  }
351
304
 
352
- /**
353
- * 执行一次 session run(带可压缩错误重试)。
354
- */
355
- private async runWithRetry(input: SessionRunInput): Promise<SessionRunResult> {
356
- const model = this.resolveModelOrThrow();
357
- try {
358
- const query = String(input.query || "").trim();
359
- const prepared = await this.prepareExecuteInput(query, model);
360
- return await this.executePreparedRun(prepared, model);
361
- } catch (error) {
362
- if (this.compactionComposer.shouldCompactOnError(error)) {
363
- await this.logger.log("info", "[agent] compacting", {
364
- retryCount: this.retryCount,
365
- error: String(error),
366
- });
367
-
368
- if (this.retryCount < MAX_COMPACTION_RETRY_ATTEMPTS) {
369
- this.retryCount += 1;
370
- return this.runWithRetry(input);
371
- }
372
-
373
- return {
374
- success: false,
375
- error: "Context length exceeded and retries failed. Please resend your question.",
376
- assistantMessage: this.contextComposer.buildFallbackAssistantMessage(
377
- "Context length exceeded and retries failed. Please resend your question.",
378
- ),
379
- };
380
- }
381
-
382
- const errorMsg = String(error);
383
- await this.logger.log("error", "Executor execution failed", {
384
- error: errorMsg,
385
- });
386
- return {
387
- success: false,
388
- error: errorMsg,
389
- assistantMessage: this.contextComposer.buildFallbackAssistantMessage(
390
- `Execution failed: ${errorMsg}`,
391
- ),
392
- };
393
- }
394
- }
395
-
396
305
  /**
397
306
  * 调用 Composer 组装当前轮执行输入。
398
307
  */
399
308
  private async prepareExecuteInput(
400
309
  query: string,
401
310
  model: LanguageModel,
311
+ run_context: SessionRunContext,
312
+ retry_count: number,
402
313
  ): Promise<SessionExecuteInput> {
403
314
  if (!String(this.historyComposer.sessionId || "").trim()) {
404
315
  throw new Error("Executor.run requires historyComposer.sessionId");
405
316
  }
406
317
 
407
- const runContext = await this.contextComposer.compose();
408
- const tools = runContext.tools;
409
- const system = await this.systemComposer.resolve();
318
+ const composed_context = await this.contextComposer.compose(run_context);
319
+ const tools = composed_context.tools;
320
+ const system = await this.systemComposer.resolve(run_context);
410
321
 
411
322
  try {
412
- if (this.retryCount > 0) {
323
+ if (retry_count > 0) {
413
324
  await this.logger.log("info", "[agent] compacting", {
414
- retryCount: this.retryCount,
325
+ retryCount: retry_count,
415
326
  });
416
327
  }
417
328
 
@@ -419,7 +330,7 @@ export class Executor implements SessionExecutor {
419
330
  historyStore: this.historyStore,
420
331
  model,
421
332
  system,
422
- retryCount: this.retryCount,
333
+ retryCount: retry_count,
423
334
  });
424
335
  } catch {
425
336
  // 压缩失败不阻断主流程,继续使用当前历史消息执行。
@@ -430,7 +341,7 @@ export class Executor implements SessionExecutor {
430
341
  tools,
431
342
  system,
432
343
  model,
433
- retryCount: this.retryCount,
344
+ retryCount: retry_count,
434
345
  });
435
346
 
436
347
  return {
@@ -447,286 +358,44 @@ export class Executor implements SessionExecutor {
447
358
  private async executePreparedRun(
448
359
  input: SessionExecuteInput,
449
360
  model: LanguageModel,
361
+ run_context: SessionRunContext,
450
362
  ): Promise<SessionRunResult> {
451
- return await this.runCoreEngine(input, model);
363
+ return await this.core_engine_runner.run({
364
+ execute_input: input,
365
+ model,
366
+ run_context,
367
+ });
452
368
  }
453
369
 
454
370
  /**
455
- * 运行模型/tool-loop 核心。
456
- *
457
- * 关键点(中文)
458
- * - CoreEngine 是 Executor 内部机制,不是第二个 Executor 实例。
459
- * - 这里只返回最终 assistant message;是否写入长期 history 由外层 Session/Service 决定。
460
- * - 运行中生成的内部续写 user message 只进入本轮内存态,不直接落盘。
371
+ * 归一化本轮显式运行上下文。
461
372
  */
462
- private async runCoreEngine(
463
- input: SessionExecuteInput,
464
- model: LanguageModel,
465
- ): Promise<SessionRunResult> {
466
- const startTime = Date.now();
467
- const sessionId = String(this.historyStore.sessionId || "").trim();
468
- const system = Array.isArray(input.system) ? input.system : [];
469
- const tools = input.tools;
470
- let lastObservedStreamError: unknown = undefined;
471
-
472
- try {
473
- const messageState = await CoreEngineMessageState.create({
474
- messages: input.messages,
475
- tools,
476
- });
477
-
478
- const appendMergedUserMessages = (messages: SessionMessageV1[]) =>
479
- messageState.appendMergedUserMessages(messages);
480
-
481
- const contextComposerOnStepFinish =
482
- this.contextComposer.createOnStepFinishHandler();
483
- let stepCount = 0;
484
- let totalToolCallCount = 0;
485
- let totalToolResultCount = 0;
486
- const onStepFinish = async (stepResult: unknown): Promise<void> => {
487
- stepCount += 1;
488
- const summary = summarizeStepForDebug(stepResult);
489
- totalToolCallCount +=
490
- typeof summary.toolCallCount === "number" ? summary.toolCallCount : 0;
491
- totalToolResultCount +=
492
- typeof summary.toolResultCount === "number"
493
- ? summary.toolResultCount
494
- : 0;
495
- await this.logger.log("info", "[agent] step.finish", {
496
- sessionId,
497
- stepIndex: stepCount,
498
- ...summary,
499
- });
500
- await contextComposerOnStepFinish(stepResult);
501
- };
502
-
503
- const prepareStep = this.contextComposer.createPrepareStepHandler({
504
- system,
505
- appendMergedUserMessages,
506
- });
507
-
508
- let finalAssistantUiMessage: SessionMessageV1 | null = null;
509
- let textOnlyContinuationCount = 0;
510
- let incompleteResponseRecoveryCount = 0;
511
-
512
- while (stepCount < MAX_TOOL_LOOP_STEPS) {
513
- const result = streamText({
514
- model,
515
- system,
516
- onStepFinish,
517
- prepareStep,
518
- messages: messageState.modelMessages,
519
- tools,
520
- providerOptions: buildOpenAIResponsesProviderOptions(),
521
- onError: async ({ error }) => {
522
- lastObservedStreamError = error;
523
- await this.logger.log("error", "[agent] stream.error", {
524
- sessionId,
525
- ...summarizeStreamError(error),
526
- });
527
- },
528
- });
529
-
530
- const stepAssistantUiMessage = await collectFinalAssistantMessageFromUiStream({
531
- result,
532
- sessionId,
533
- logger: this.logger,
534
- buildFallbackAssistantMessage: (text) =>
535
- this.contextComposer.buildFallbackAssistantMessage(text),
536
- });
537
-
538
- const executedSteps = await result.steps;
539
- const lastStep = executedSteps[executedSteps.length - 1];
540
- if (!lastStep) break;
541
-
542
- const incompleteResponse = detectIncompleteResponse({
543
- stepResult: lastStep,
544
- assistantMessage: stepAssistantUiMessage,
545
- });
546
- const textOnlyContinuationReason =
547
- detectTextOnlyContinuationReason(lastStep);
548
- const loopDecision = evaluateCoreEngineLoopDecision({
549
- hasIncompleteResponse: incompleteResponse !== null,
550
- incompleteRecoveryCount: incompleteResponseRecoveryCount,
551
- maxIncompleteRecoveries: MAX_INCOMPLETE_RESPONSE_RECOVERIES,
552
- textOnlyContinuationReason,
553
- textOnlyContinuationCount,
554
- maxTextOnlyContinuations: MAX_TEXT_ONLY_CONTINUATIONS,
555
- hasTools: Object.keys(tools).length > 0,
556
- toolCallCount: lastStep.toolCalls.length,
557
- });
558
-
559
- await this.logger.log("info", "[agent] loop.decision", {
560
- sessionId,
561
- stepIndex: stepCount,
562
- continueForToolCalls: loopDecision.continueForToolCalls,
563
- continueForTextOnly: loopDecision.continueForTextOnly,
564
- continueForIncompleteRecovery:
565
- loopDecision.continueForIncompleteRecovery,
566
- decisionKind: loopDecision.kind,
567
- textOnlyContinuationReason,
568
- textOnlyContinuationCount,
569
- incompleteResponseReason: incompleteResponse?.reason ?? null,
570
- incompleteResponseRecoveryCount,
571
- toolCallCount: lastStep.toolCalls.length,
572
- toolResultCount: lastStep.toolResults.length,
573
- finishReason: lastStep.finishReason,
574
- textPreview: toInlinePreview(lastStep.text),
575
- });
576
-
577
- if (loopDecision.continueForIncompleteRecovery && incompleteResponse) {
578
- incompleteResponseRecoveryCount += 1;
579
- await this.logger.log("warn", "[agent] incomplete_response.recover", {
580
- sessionId,
581
- stepIndex: stepCount,
582
- recoveryCount: incompleteResponseRecoveryCount,
583
- reason: incompleteResponse.reason,
584
- ...incompleteResponse.details,
585
- });
586
- const recoveryMessage = this.historyStore.userText({
587
- text: buildIncompleteResponseRecoveryNudge(
588
- incompleteResponseRecoveryCount,
589
- ),
590
- metadata: {
591
- sessionId,
592
- extra: {
593
- internal: "agent_incomplete_response_recover",
594
- reason: incompleteResponse.reason,
595
- stepIndex: stepCount,
596
- },
597
- },
598
- });
599
- await messageState.appendUserTextMessage(recoveryMessage);
600
- continue;
601
- }
602
-
603
- if (incompleteResponse) {
604
- await this.logger.log("error", "[agent] incomplete_response", {
605
- sessionId,
606
- stepIndex: stepCount,
607
- reason: incompleteResponse.reason,
608
- recoveryCount: incompleteResponseRecoveryCount,
609
- ...incompleteResponse.details,
610
- });
611
- throw new Error(
612
- `Agent received incomplete response (${incompleteResponse.reason})`,
613
- );
614
- }
615
-
616
- const responseMessages = Array.isArray(lastStep.response?.messages)
617
- ? lastStep.response.messages
618
- : [];
619
- messageState.appendModelMessages(responseMessages);
620
-
621
- finalAssistantUiMessage = mergeAssistantUiMessages(
622
- finalAssistantUiMessage,
623
- stepAssistantUiMessage,
624
- );
625
-
626
- // 关键点(中文):把本 step 的 assistant UI 消息并入运行时上下文,保证后续全量重算不丢历史。
627
- messageState.appendRuntimeSessionMessage(stepAssistantUiMessage);
628
-
629
- if (loopDecision.continueForToolCalls) {
630
- textOnlyContinuationCount = 0;
631
- incompleteResponseRecoveryCount = 0;
632
- continue;
633
- }
634
-
635
- if (loopDecision.continueForTextOnly) {
636
- textOnlyContinuationCount += 1;
637
- incompleteResponseRecoveryCount = 0;
638
- const continuationMessage = this.historyStore.userText({
639
- text: buildTextOnlyContinuationNudge(textOnlyContinuationCount),
640
- metadata: {
641
- sessionId,
642
- extra: {
643
- internal: "agent_loop_auto_continue",
644
- reason: textOnlyContinuationReason,
645
- stepIndex: stepCount,
646
- },
647
- },
648
- });
649
- await messageState.appendUserTextMessage(continuationMessage);
650
- continue;
651
- }
652
-
653
- // 关键点(中文):stop 前做 tail merge,覆盖最后一个 step 后才入队的新 user 消息。
654
- const tailPrepared = await prepareStep({ messages: [] });
655
- const tailMergedMessageCount = Array.isArray(tailPrepared.messages)
656
- ? tailPrepared.messages.length
657
- : 0;
658
- if (
659
- shouldContinueForTailMergedUserMessages({
660
- mergedUserMessageCount: tailMergedMessageCount,
661
- })
662
- ) {
663
- textOnlyContinuationCount = 0;
664
- incompleteResponseRecoveryCount = 0;
665
- await this.logger.log("info", "[agent] loop.tail_merge_continue", {
666
- sessionId,
667
- stepIndex: stepCount,
668
- mergedUserMessageCount: tailMergedMessageCount,
669
- });
670
- continue;
671
- }
672
-
673
- break;
674
- }
675
-
676
- if (stepCount >= MAX_TOOL_LOOP_STEPS) {
677
- await this.logger.log("warn", "[agent] loop.max_steps_reached", {
678
- sessionId,
679
- stepCount,
680
- totalToolCallCount,
681
- totalToolResultCount,
682
- });
683
- }
684
-
685
- const finalMessage =
686
- finalAssistantUiMessage ||
687
- this.contextComposer.buildFallbackAssistantMessage("Execution completed");
688
-
689
- await this.logger.log("info", "[agent] final.message", {
690
- sessionId,
691
- ...summarizeUiMessageForDebug(finalMessage),
692
- });
693
- await logAssistantMessageNow(this.logger, finalMessage);
694
-
695
- const duration = Date.now() - startTime;
696
- await this.logger.log("info", "[agent] finish", {
697
- sessionId,
698
- duration,
699
- stepCount,
700
- totalToolCallCount,
701
- totalToolResultCount,
702
- });
703
-
704
- return {
705
- success: true,
706
- assistantMessage: finalMessage,
707
- };
708
- } catch (error) {
709
- if (this.compactionComposer.shouldCompactOnError(error)) {
710
- throw error;
711
- }
712
-
713
- const errorMsg = resolveEffectiveCoreEngineError({
714
- error,
715
- streamError: lastObservedStreamError,
716
- });
717
-
718
- await this.logger.log("error", "CoreEngine execution failed", {
719
- error: errorMsg,
720
- });
721
-
722
- return {
723
- success: false,
724
- error: errorMsg,
725
- assistantMessage: this.contextComposer.buildFallbackAssistantMessage(
726
- `Execution failed: ${errorMsg}`,
727
- ),
728
- };
729
- }
373
+ private createRunContext(
374
+ input?: SessionRunContext,
375
+ ): SessionRunContext {
376
+ return {
377
+ sessionId: String(input?.sessionId || this.sessionId).trim(),
378
+ ...(typeof input?.onStepCallback === "function"
379
+ ? { onStepCallback: input.onStepCallback }
380
+ : {}),
381
+ ...(typeof input?.onAssistantStepCallback === "function"
382
+ ? { onAssistantStepCallback: input.onAssistantStepCallback }
383
+ : {}),
384
+ ...(typeof input?.onUiMessageChunkCallback === "function"
385
+ ? { onUiMessageChunkCallback: input.onUiMessageChunkCallback }
386
+ : {}),
387
+ injectedUserMessages: Array.isArray(input?.injectedUserMessages)
388
+ ? [...input.injectedUserMessages]
389
+ : [],
390
+ deferredPersistedUserMessages: Array.isArray(
391
+ input?.deferredPersistedUserMessages,
392
+ )
393
+ ? [...input.deferredPersistedUserMessages]
394
+ : [],
395
+ pendingAssistantFileParts: Array.isArray(input?.pendingAssistantFileParts)
396
+ ? [...input.pendingAssistantFileParts]
397
+ : [],
398
+ };
730
399
  }
731
400
 
732
401
  /**
@@ -741,11 +410,4 @@ export class Executor implements SessionExecutor {
741
410
  }
742
411
  return model;
743
412
  }
744
-
745
- /**
746
- * 重置当前 run 级状态。
747
- */
748
- private resetRunState(): void {
749
- this.retryCount = 0;
750
- }
751
413
  }