@downcity/agent 1.1.228 → 1.1.236

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 (221) hide show
  1. package/bin/agent/local/Agent.d.ts +6 -1
  2. package/bin/agent/local/Agent.d.ts.map +1 -1
  3. package/bin/agent/local/Agent.js +30 -3
  4. package/bin/agent/local/Agent.js.map +1 -1
  5. package/bin/agent/local/services/AgentSessions.d.ts +23 -0
  6. package/bin/agent/local/services/AgentSessions.d.ts.map +1 -1
  7. package/bin/agent/local/services/AgentSessions.js +44 -0
  8. package/bin/agent/local/services/AgentSessions.js.map +1 -1
  9. package/bin/agent/remote/RemoteSession.d.ts +2 -0
  10. package/bin/agent/remote/RemoteSession.d.ts.map +1 -1
  11. package/bin/agent/remote/RemoteSession.js +4 -0
  12. package/bin/agent/remote/RemoteSession.js.map +1 -1
  13. package/bin/agent/remote/RemoteTransport.d.ts +2 -0
  14. package/bin/agent/remote/RemoteTransport.d.ts.map +1 -1
  15. package/bin/agent/remote/transports/HttpRemoteAgentTransport.d.ts +1 -0
  16. package/bin/agent/remote/transports/HttpRemoteAgentTransport.d.ts.map +1 -1
  17. package/bin/agent/remote/transports/HttpRemoteAgentTransport.js +11 -7
  18. package/bin/agent/remote/transports/HttpRemoteAgentTransport.js.map +1 -1
  19. package/bin/agent/remote/transports/RpcRemoteAgentTransport.d.ts +1 -0
  20. package/bin/agent/remote/transports/RpcRemoteAgentTransport.d.ts.map +1 -1
  21. package/bin/agent/remote/transports/RpcRemoteAgentTransport.js +3 -0
  22. package/bin/agent/remote/transports/RpcRemoteAgentTransport.js.map +1 -1
  23. package/bin/executor/Executor.d.ts +43 -0
  24. package/bin/executor/Executor.d.ts.map +1 -1
  25. package/bin/executor/Executor.js +127 -7
  26. package/bin/executor/Executor.js.map +1 -1
  27. package/bin/executor/composer/compaction/SessionCompactionComposer.d.ts +10 -0
  28. package/bin/executor/composer/compaction/SessionCompactionComposer.d.ts.map +1 -1
  29. package/bin/executor/composer/compaction/jsonl/JsonlSessionCompactionComposer.d.ts +0 -10
  30. package/bin/executor/composer/compaction/jsonl/JsonlSessionCompactionComposer.d.ts.map +1 -1
  31. package/bin/executor/composer/compaction/jsonl/JsonlSessionCompactionComposer.js +4 -34
  32. package/bin/executor/composer/compaction/jsonl/JsonlSessionCompactionComposer.js.map +1 -1
  33. package/bin/executor/composer/compaction/jsonl/JsonlSessionCompactionExecutor.d.ts +1 -3
  34. package/bin/executor/composer/compaction/jsonl/JsonlSessionCompactionExecutor.d.ts.map +1 -1
  35. package/bin/executor/composer/compaction/jsonl/JsonlSessionCompactionExecutor.js +7 -59
  36. package/bin/executor/composer/compaction/jsonl/JsonlSessionCompactionExecutor.js.map +1 -1
  37. package/bin/executor/core-engine/CoreEngineContextCompaction.d.ts +38 -0
  38. package/bin/executor/core-engine/CoreEngineContextCompaction.d.ts.map +1 -0
  39. package/bin/executor/core-engine/CoreEngineContextCompaction.js +392 -0
  40. package/bin/executor/core-engine/CoreEngineContextCompaction.js.map +1 -0
  41. package/bin/executor/core-engine/CoreEngineMessageState.d.ts +15 -0
  42. package/bin/executor/core-engine/CoreEngineMessageState.d.ts.map +1 -1
  43. package/bin/executor/core-engine/CoreEngineMessageState.js +20 -0
  44. package/bin/executor/core-engine/CoreEngineMessageState.js.map +1 -1
  45. package/bin/executor/core-engine/CoreEngineRunner.d.ts +15 -0
  46. package/bin/executor/core-engine/CoreEngineRunner.d.ts.map +1 -1
  47. package/bin/executor/core-engine/CoreEngineRunner.js +150 -35
  48. package/bin/executor/core-engine/CoreEngineRunner.js.map +1 -1
  49. package/bin/executor/store/history/SessionHistoryStore.d.ts +7 -23
  50. package/bin/executor/store/history/SessionHistoryStore.d.ts.map +1 -1
  51. package/bin/executor/store/history/jsonl/JsonlSessionHistoryStore.d.ts.map +1 -1
  52. package/bin/executor/store/history/jsonl/JsonlSessionHistoryStore.js +2 -4
  53. package/bin/executor/store/history/jsonl/JsonlSessionHistoryStore.js.map +1 -1
  54. package/bin/executor/tools/plugin/PluginToolBridge.d.ts.map +1 -1
  55. package/bin/executor/tools/plugin/PluginToolBridge.js +4 -2
  56. package/bin/executor/tools/plugin/PluginToolBridge.js.map +1 -1
  57. package/bin/executor/types/SessionRun.d.ts +8 -0
  58. package/bin/executor/types/SessionRun.d.ts.map +1 -1
  59. package/bin/index.d.ts +3 -2
  60. package/bin/index.d.ts.map +1 -1
  61. package/bin/index.js +2 -2
  62. package/bin/index.js.map +1 -1
  63. package/bin/model/CityModelAdapter.d.ts +6 -0
  64. package/bin/model/CityModelAdapter.d.ts.map +1 -1
  65. package/bin/model/CityModelAdapter.js +13 -0
  66. package/bin/model/CityModelAdapter.js.map +1 -1
  67. package/bin/plugin/core/PluginLocalExecution.js +3 -3
  68. package/bin/plugin/core/PluginLocalExecution.js.map +1 -1
  69. package/bin/plugin/core/PluginRegistry.d.ts +41 -2
  70. package/bin/plugin/core/PluginRegistry.d.ts.map +1 -1
  71. package/bin/plugin/core/PluginRegistry.js +145 -6
  72. package/bin/plugin/core/PluginRegistry.js.map +1 -1
  73. package/bin/rpc/Client.d.ts +4 -0
  74. package/bin/rpc/Client.d.ts.map +1 -1
  75. package/bin/rpc/Client.js +11 -0
  76. package/bin/rpc/Client.js.map +1 -1
  77. package/bin/session/Session.d.ts +15 -1
  78. package/bin/session/Session.d.ts.map +1 -1
  79. package/bin/session/Session.js +83 -11
  80. package/bin/session/Session.js.map +1 -1
  81. package/bin/session/SessionSystemBuilder.d.ts.map +1 -1
  82. package/bin/session/SessionSystemBuilder.js +3 -1
  83. package/bin/session/SessionSystemBuilder.js.map +1 -1
  84. package/bin/session/browse/Browse.d.ts +0 -8
  85. package/bin/session/browse/Browse.d.ts.map +1 -1
  86. package/bin/session/browse/Browse.js +72 -57
  87. package/bin/session/browse/Browse.js.map +1 -1
  88. package/bin/session/recorder/JsonlSessionMessageStore.d.ts +69 -16
  89. package/bin/session/recorder/JsonlSessionMessageStore.d.ts.map +1 -1
  90. package/bin/session/recorder/JsonlSessionMessageStore.js +283 -60
  91. package/bin/session/recorder/JsonlSessionMessageStore.js.map +1 -1
  92. package/bin/session/recorder/SessionRecorder.d.ts +20 -2
  93. package/bin/session/recorder/SessionRecorder.d.ts.map +1 -1
  94. package/bin/session/recorder/SessionRecorder.js +91 -46
  95. package/bin/session/recorder/SessionRecorder.js.map +1 -1
  96. package/bin/session/recorder/SessionRecorderCompaction.d.ts +3 -3
  97. package/bin/session/recorder/SessionRecorderCompaction.d.ts.map +1 -1
  98. package/bin/session/recorder/SessionRecorderCompaction.js +118 -88
  99. package/bin/session/recorder/SessionRecorderCompaction.js.map +1 -1
  100. package/bin/session/recorder/SessionRecorderHistoryStore.d.ts +2 -2
  101. package/bin/session/recorder/SessionRecorderHistoryStore.d.ts.map +1 -1
  102. package/bin/session/recorder/SessionRecorderHistoryStore.js +31 -36
  103. package/bin/session/recorder/SessionRecorderHistoryStore.js.map +1 -1
  104. package/bin/session/runtime/SessionPromptRuntime.d.ts +21 -1
  105. package/bin/session/runtime/SessionPromptRuntime.d.ts.map +1 -1
  106. package/bin/session/runtime/SessionPromptRuntime.js +65 -8
  107. package/bin/session/runtime/SessionPromptRuntime.js.map +1 -1
  108. package/bin/session/services/SessionStateService.d.ts +19 -1
  109. package/bin/session/services/SessionStateService.d.ts.map +1 -1
  110. package/bin/session/services/SessionStateService.js +70 -40
  111. package/bin/session/services/SessionStateService.js.map +1 -1
  112. package/bin/session/services/SessionTurnService.d.ts +15 -0
  113. package/bin/session/services/SessionTurnService.d.ts.map +1 -1
  114. package/bin/session/services/SessionTurnService.js +104 -7
  115. package/bin/session/services/SessionTurnService.js.map +1 -1
  116. package/bin/session/services/SessionViewService.d.ts +1 -5
  117. package/bin/session/services/SessionViewService.d.ts.map +1 -1
  118. package/bin/session/services/SessionViewService.js +4 -34
  119. package/bin/session/services/SessionViewService.js.map +1 -1
  120. package/bin/session/storage/Paths.d.ts +3 -15
  121. package/bin/session/storage/Paths.d.ts.map +1 -1
  122. package/bin/session/storage/Paths.js +5 -21
  123. package/bin/session/storage/Paths.js.map +1 -1
  124. package/bin/types/agent/SessionActor.d.ts +8 -0
  125. package/bin/types/agent/SessionActor.d.ts.map +1 -1
  126. package/bin/types/agent/SessionTypes.d.ts +2 -24
  127. package/bin/types/agent/SessionTypes.d.ts.map +1 -1
  128. package/bin/types/executor/SessionRunContext.d.ts +29 -0
  129. package/bin/types/executor/SessionRunContext.d.ts.map +1 -1
  130. package/bin/types/plugin/PluginRuntime.d.ts +59 -2
  131. package/bin/types/plugin/PluginRuntime.d.ts.map +1 -1
  132. package/bin/types/plugin/PluginState.d.ts +10 -0
  133. package/bin/types/plugin/PluginState.d.ts.map +1 -1
  134. package/bin/types/rpc/RpcProtocol.d.ts +10 -0
  135. package/bin/types/rpc/RpcProtocol.d.ts.map +1 -1
  136. package/bin/types/runtime/agent/AgentContext.d.ts +17 -5
  137. package/bin/types/runtime/agent/AgentContext.d.ts.map +1 -1
  138. package/bin/types/runtime/agent/AgentContext.js +29 -6
  139. package/bin/types/runtime/agent/AgentContext.js.map +1 -1
  140. package/bin/types/session/SessionLocalState.d.ts +8 -0
  141. package/bin/types/session/SessionLocalState.d.ts.map +1 -1
  142. package/bin/types/session/SessionMessage.d.ts +16 -12
  143. package/bin/types/session/SessionMessage.d.ts.map +1 -1
  144. package/bin/types/session/SessionMutation.d.ts +1 -1
  145. package/bin/types/session/SessionMutation.js +1 -1
  146. package/bin/types/session/SessionOptions.d.ts +16 -0
  147. package/bin/types/session/SessionOptions.d.ts.map +1 -1
  148. package/bin/types/session/SessionQueueCommand.d.ts +68 -0
  149. package/bin/types/session/SessionQueueCommand.d.ts.map +1 -0
  150. package/bin/types/session/SessionQueueCommand.js +10 -0
  151. package/bin/types/session/SessionQueueCommand.js.map +1 -0
  152. package/bin/types/session/SessionSegment.d.ts +57 -0
  153. package/bin/types/session/SessionSegment.d.ts.map +1 -0
  154. package/bin/types/session/SessionSegment.js +8 -0
  155. package/bin/types/session/SessionSegment.js.map +1 -0
  156. package/bin/utils/logger/Logger.d.ts +7 -9
  157. package/bin/utils/logger/Logger.d.ts.map +1 -1
  158. package/bin/utils/logger/Logger.js +11 -9
  159. package/bin/utils/logger/Logger.js.map +1 -1
  160. package/package.json +3 -3
  161. package/scripts/city-model-tool-loop.test.mjs +32 -0
  162. package/scripts/core-engine-context-compaction.test.mjs +411 -0
  163. package/scripts/logger-isolation.test.mjs +55 -0
  164. package/scripts/multi-agent-plugin-isolation.test.mjs +289 -0
  165. package/scripts/plugin-tool-bridge.test.mjs +52 -0
  166. package/scripts/remote-session-reconnect.test.mjs +18 -0
  167. package/scripts/session-action-message.test.mjs +59 -13
  168. package/scripts/session-compaction-context-window.test.mjs +96 -0
  169. package/scripts/session-config-turn-boundary.test.mjs +278 -0
  170. package/scripts/session-list-title.test.mjs +21 -3
  171. package/scripts/session-prompt-runtime.test.mjs +93 -0
  172. package/scripts/session-recorder.test.mjs +335 -15
  173. package/src/agent/local/Agent.ts +34 -3
  174. package/src/agent/local/services/AgentSessions.ts +61 -0
  175. package/src/agent/remote/RemoteSession.ts +5 -0
  176. package/src/agent/remote/RemoteTransport.ts +2 -0
  177. package/src/agent/remote/transports/HttpRemoteAgentTransport.ts +15 -5
  178. package/src/agent/remote/transports/RpcRemoteAgentTransport.ts +4 -0
  179. package/src/executor/Executor.ts +161 -7
  180. package/src/executor/composer/compaction/SessionCompactionComposer.ts +12 -0
  181. package/src/executor/composer/compaction/jsonl/JsonlSessionCompactionComposer.ts +4 -55
  182. package/src/executor/composer/compaction/jsonl/JsonlSessionCompactionExecutor.ts +8 -66
  183. package/src/executor/core-engine/CoreEngineContextCompaction.ts +477 -0
  184. package/src/executor/core-engine/CoreEngineMessageState.ts +29 -0
  185. package/src/executor/core-engine/CoreEngineRunner.ts +211 -43
  186. package/src/executor/store/history/SessionHistoryStore.ts +7 -26
  187. package/src/executor/store/history/jsonl/JsonlSessionHistoryStore.ts +2 -4
  188. package/src/executor/tools/plugin/PluginToolBridge.ts +4 -2
  189. package/src/executor/types/SessionRun.ts +9 -0
  190. package/src/index.ts +9 -1
  191. package/src/model/CityModelAdapter.ts +15 -0
  192. package/src/plugin/core/PluginLocalExecution.ts +3 -3
  193. package/src/plugin/core/PluginRegistry.ts +187 -6
  194. package/src/rpc/Client.ts +12 -0
  195. package/src/session/Session.ts +97 -11
  196. package/src/session/SessionSystemBuilder.ts +4 -1
  197. package/src/session/browse/Browse.ts +73 -69
  198. package/src/session/recorder/JsonlSessionMessageStore.ts +378 -60
  199. package/src/session/recorder/SessionRecorder.ts +116 -49
  200. package/src/session/recorder/SessionRecorderCompaction.ts +131 -108
  201. package/src/session/recorder/SessionRecorderHistoryStore.ts +29 -49
  202. package/src/session/runtime/SessionPromptRuntime.ts +86 -10
  203. package/src/session/services/SessionStateService.ts +89 -45
  204. package/src/session/services/SessionTurnService.ts +112 -7
  205. package/src/session/services/SessionViewService.ts +4 -50
  206. package/src/session/storage/Paths.ts +5 -38
  207. package/src/types/agent/SessionActor.ts +9 -0
  208. package/src/types/agent/SessionTypes.ts +2 -24
  209. package/src/types/executor/SessionRunContext.ts +34 -0
  210. package/src/types/plugin/PluginRuntime.ts +63 -2
  211. package/src/types/plugin/PluginState.ts +10 -0
  212. package/src/types/rpc/RpcProtocol.ts +11 -0
  213. package/src/types/runtime/agent/AgentContext.ts +32 -7
  214. package/src/types/session/SessionLocalState.ts +9 -0
  215. package/src/types/session/SessionMessage.ts +16 -12
  216. package/src/types/session/SessionMutation.ts +1 -1
  217. package/src/types/session/SessionOptions.ts +21 -0
  218. package/src/types/session/SessionQueueCommand.ts +78 -0
  219. package/src/types/session/SessionSegment.ts +62 -0
  220. package/src/utils/logger/Logger.ts +10 -10
  221. package/tsconfig.tsbuildinfo +1 -1
@@ -7,7 +7,13 @@
7
7
  * - 保持失败返回结构稳定,避免对外 Session 行为变化。
8
8
  */
9
9
 
10
- import { streamText, type FileUIPart, type LanguageModel } from "ai";
10
+ import {
11
+ streamText,
12
+ type FileUIPart,
13
+ type LanguageModel,
14
+ type StepResult,
15
+ type Tool,
16
+ } from "ai";
11
17
  import { buildOpenAIResponsesProviderOptions } from "@executor/messages/SessionMessageCodec.js";
12
18
  import { logAssistantMessageNow } from "@executor/messages/SessionMessageLog.js";
13
19
  import {
@@ -33,6 +39,11 @@ import {
33
39
  } from "@executor/core-engine/CoreEngineError.js";
34
40
  import { collectFinalAssistantMessageFromUiStream } from "@executor/core-engine/CoreEngineUiStreamCollector.js";
35
41
  import { CoreEngineMessageState } from "@executor/core-engine/CoreEngineMessageState.js";
42
+ import {
43
+ deep_compact_model_messages,
44
+ resolve_model_usage_ratio,
45
+ should_compact_after_usage,
46
+ } from "@executor/core-engine/CoreEngineContextCompaction.js";
36
47
  import type { Logger } from "@/utils/logger/Logger.js";
37
48
  import type { SessionHistoryStore } from "@/executor/store/history/SessionHistoryStore.js";
38
49
  import type { SessionContextComposer } from "@executor/composer/context/SessionContextComposer.js";
@@ -48,6 +59,9 @@ import type {
48
59
 
49
60
  const TURN_STOPPED_MESSAGE = "Turn stopped";
50
61
 
62
+ /** Provider context-length error 在当前 step 内最多压缩重试三次。 */
63
+ const MAX_CONTEXT_ERROR_COMPACTION_RETRIES = 3;
64
+
51
65
  /**
52
66
  * 生成 file part 去重 key。
53
67
  */
@@ -125,6 +139,20 @@ interface CoreEngineRunInput {
125
139
  * 当前显式运行上下文。
126
140
  */
127
141
  run_context: SessionRunContext;
142
+
143
+ /**
144
+ * 在统一输入队列提交后解析当前 Session step 的 effective 配置。
145
+ */
146
+ resolve_step_inputs: () => Promise<{
147
+ /** 当前 Session step 使用的模型。 */
148
+ model: LanguageModel;
149
+ /** 当前 Session step 使用的 system messages。 */
150
+ system: SessionExecuteInput["system"];
151
+ /** 当前 Session step 使用的工具集合。 */
152
+ tools: SessionExecuteInput["tools"];
153
+ /** 当前 Session step 模型支持的总上下文窗口长度。 */
154
+ context_window?: number;
155
+ }>;
128
156
  }
129
157
 
130
158
  /**
@@ -136,6 +164,9 @@ export class CoreEngineRunner {
136
164
  private readonly logger: Logger;
137
165
  private readonly should_compact_on_error: CoreEngineRunnerOptions["should_compact_on_error"];
138
166
 
167
+ /** 最近一次已经通过真实 usage 验收的持久化 Summary 标识。 */
168
+ private validated_compaction_summary_id = "";
169
+
139
170
  constructor(options: CoreEngineRunnerOptions) {
140
171
  this.history_store = options.history_store;
141
172
  this.context_composer = options.context_composer;
@@ -149,12 +180,13 @@ export class CoreEngineRunner {
149
180
  async run(input: CoreEngineRunInput): Promise<SessionRunResult> {
150
181
  const start_time = Date.now();
151
182
  const session_id = String(this.history_store.sessionId || "").trim();
152
- const system = Array.isArray(input.execute_input.system)
183
+ let system = Array.isArray(input.execute_input.system)
153
184
  ? input.execute_input.system
154
185
  : [];
155
- const tools = input.execute_input.tools;
186
+ let tools = input.execute_input.tools;
156
187
  let last_observed_stream_error: unknown = undefined;
157
188
  let final_assistant_ui_message: SessionMessageRecordV1 | null = null;
189
+ let compact_required = false;
158
190
 
159
191
  try {
160
192
  const message_state = await CoreEngineMessageState.create({
@@ -162,6 +194,9 @@ export class CoreEngineRunner {
162
194
  tools,
163
195
  projectRoot: input.run_context.projectRoot,
164
196
  });
197
+ let persisted_compaction_summary_id = resolve_compaction_summary_id(
198
+ input.execute_input.messages,
199
+ );
165
200
 
166
201
  const append_merged_user_messages = (messages: SessionRecordV1[]) =>
167
202
  message_state.appendMergedUserMessages(messages);
@@ -188,7 +223,7 @@ export class CoreEngineRunner {
188
223
  await context_composer_on_step_finish(step_result);
189
224
  };
190
225
 
191
- const prepare_step = this.context_composer.createPrepareStepHandler({
226
+ const prepare_step_inputs = this.context_composer.createPrepareStepHandler({
192
227
  system,
193
228
  appendMergedUserMessages: append_merged_user_messages,
194
229
  runContext: input.run_context,
@@ -196,53 +231,169 @@ export class CoreEngineRunner {
196
231
 
197
232
  let text_only_continuation_count = 0;
198
233
  let incomplete_response_recovery_count = 0;
234
+ let context_error_compaction_retries = 0;
235
+ let compact_pending = false;
236
+ let compact_validation_pending = Boolean(
237
+ persisted_compaction_summary_id &&
238
+ persisted_compaction_summary_id !==
239
+ this.validated_compaction_summary_id,
240
+ );
241
+ let compact_depth = 0;
199
242
 
200
243
  while (step_count < MAX_TOOL_LOOP_STEPS) {
201
- const result = streamText({
202
- model: input.model,
203
- system,
204
- onStepFinish: on_step_finish,
205
- prepareStep: prepare_step,
206
- messages: message_state.modelMessages,
207
- tools,
208
- abortSignal: input.run_context.abortSignal,
209
- providerOptions: buildOpenAIResponsesProviderOptions(),
210
- onError: async ({ error }) => {
211
- last_observed_stream_error = error;
212
- await this.logger.log("error", "[agent] stream.error", {
213
- sessionId: session_id,
214
- ...summarizeStreamError(error),
215
- });
216
- },
217
- });
218
-
219
- const step_assistant_ui_message =
220
- await collectFinalAssistantMessageFromUiStream({
221
- result,
244
+ // 关键点(中文):steer command 在同一个 Session step 检查点执行。
245
+ // 当前流与 tool callback 保持原执行视图,下一 step 再统一读取 effective 配置。
246
+ await prepare_step_inputs({ messages: [] });
247
+ const step_inputs = await input.resolve_step_inputs();
248
+ system = Array.isArray(step_inputs.system) ? step_inputs.system : [];
249
+ tools = step_inputs.tools;
250
+ if (input.run_context.consume_history_reload?.()) {
251
+ const canonical_records = await this.history_store.list_records();
252
+ await message_state.replace_session_messages(canonical_records, tools);
253
+ persisted_compaction_summary_id = resolve_compaction_summary_id(
254
+ canonical_records,
255
+ );
256
+ compact_validation_pending = Boolean(
257
+ persisted_compaction_summary_id &&
258
+ persisted_compaction_summary_id !==
259
+ this.validated_compaction_summary_id,
260
+ );
261
+ await this.logger.log("info", "[agent] context.history_reloaded", {
222
262
  sessionId: session_id,
223
- logger: this.logger,
224
- buildFallbackAssistantMessage: (text) =>
225
- this.context_composer.buildFallbackAssistantMessage(
226
- text,
227
- input.run_context,
228
- ),
229
- onUiMessageChunkCallback: input.run_context.onUiMessageChunkCallback,
263
+ recordCount: canonical_records.length,
264
+ compactionSummaryId: persisted_compaction_summary_id || undefined,
265
+ });
266
+ }
267
+ if (compact_pending) {
268
+ const previous_message_count = message_state.modelMessages.length;
269
+ message_state.replace_model_messages(
270
+ deep_compact_model_messages(
271
+ message_state.modelMessages,
272
+ compact_depth,
273
+ ),
274
+ );
275
+ compact_depth += 1;
276
+ compact_pending = false;
277
+ compact_validation_pending = true;
278
+ compact_required = true;
279
+ await this.logger.log("info", "[agent] context.compacted", {
280
+ sessionId: session_id,
281
+ reason: "usage_threshold",
282
+ compactDepth: compact_depth,
283
+ previousMessageCount: previous_message_count,
284
+ nextMessageCount: message_state.modelMessages.length,
285
+ });
286
+ }
287
+
288
+ last_observed_stream_error = undefined;
289
+ let step_assistant_ui_message: SessionMessageRecordV1;
290
+ let executed_steps: StepResult<Record<string, Tool>>[];
291
+ try {
292
+ const result = streamText({
293
+ model: step_inputs.model,
294
+ system,
295
+ onStepFinish: on_step_finish,
296
+ messages: message_state.modelMessages,
297
+ tools,
230
298
  abortSignal: input.run_context.abortSignal,
299
+ providerOptions: buildOpenAIResponsesProviderOptions(),
300
+ onError: async ({ error }) => {
301
+ last_observed_stream_error = error;
302
+ await this.logger.log("error", "[agent] stream.error", {
303
+ sessionId: session_id,
304
+ ...summarizeStreamError(error),
305
+ });
306
+ },
231
307
  });
232
308
 
233
- final_assistant_ui_message = mergeAssistantUiMessages(
234
- final_assistant_ui_message,
235
- step_assistant_ui_message,
236
- );
309
+ step_assistant_ui_message =
310
+ await collectFinalAssistantMessageFromUiStream({
311
+ result,
312
+ sessionId: session_id,
313
+ logger: this.logger,
314
+ buildFallbackAssistantMessage: (text) =>
315
+ this.context_composer.buildFallbackAssistantMessage(
316
+ text,
317
+ input.run_context,
318
+ ),
319
+ onUiMessageChunkCallback: input.run_context.onUiMessageChunkCallback,
320
+ abortSignal: input.run_context.abortSignal,
321
+ });
237
322
 
238
- // 关键点(中文):先保存本 step 已收敛的 assistant 消息,再等待 steps。
239
- // stop/abort 时 `result.steps` 可能抛错,但当前已经生成的文本仍应沉淀。
240
- message_state.appendRuntimeSessionMessage(step_assistant_ui_message);
323
+ final_assistant_ui_message = mergeAssistantUiMessages(
324
+ final_assistant_ui_message,
325
+ step_assistant_ui_message,
326
+ );
327
+
328
+ // 关键点(中文):先保存本 step 已收敛的 assistant 消息,再等待 steps。
329
+ // stop/abort 时 `result.steps` 可能抛错,但当前已经生成的文本仍应沉淀。
330
+ message_state.appendRuntimeSessionMessage(step_assistant_ui_message);
331
+ executed_steps = await result.steps;
332
+ } catch (error) {
333
+ const compact_error = this.should_compact_on_error(error)
334
+ ? error
335
+ : last_observed_stream_error;
336
+ if (
337
+ this.should_compact_on_error(compact_error) &&
338
+ context_error_compaction_retries <
339
+ MAX_CONTEXT_ERROR_COMPACTION_RETRIES
340
+ ) {
341
+ context_error_compaction_retries += 1;
342
+ const previous_message_count = message_state.modelMessages.length;
343
+ message_state.replace_model_messages(
344
+ deep_compact_model_messages(
345
+ message_state.modelMessages,
346
+ compact_depth,
347
+ ),
348
+ );
349
+ compact_depth += 1;
350
+ compact_pending = false;
351
+ compact_validation_pending = true;
352
+ compact_required = true;
353
+ await this.logger.log("warn", "[agent] context.compacted", {
354
+ sessionId: session_id,
355
+ reason: "provider_context_error",
356
+ retryCount: context_error_compaction_retries,
357
+ compactDepth: compact_depth,
358
+ previousMessageCount: previous_message_count,
359
+ nextMessageCount: message_state.modelMessages.length,
360
+ ...summarizeStreamError(compact_error),
361
+ });
362
+ continue;
363
+ }
364
+ throw error;
365
+ }
241
366
 
242
- const executed_steps = await result.steps;
367
+ context_error_compaction_retries = 0;
243
368
  const last_step = executed_steps[executed_steps.length - 1];
244
369
  if (!last_step) break;
245
370
 
371
+ const usage_ratio = resolve_model_usage_ratio(
372
+ last_step.usage,
373
+ step_inputs.context_window,
374
+ );
375
+ if (usage_ratio !== null) {
376
+ const validating_compaction = compact_validation_pending;
377
+ compact_pending = should_compact_after_usage(
378
+ usage_ratio,
379
+ validating_compaction,
380
+ );
381
+ compact_validation_pending = false;
382
+ if (validating_compaction && persisted_compaction_summary_id) {
383
+ this.validated_compaction_summary_id =
384
+ persisted_compaction_summary_id;
385
+ }
386
+ if (compact_pending) compact_required = true;
387
+ await this.logger.log("info", "[agent] context.usage", {
388
+ sessionId: session_id,
389
+ stepIndex: step_count,
390
+ usageRatio: usage_ratio,
391
+ contextWindow: step_inputs.context_window,
392
+ validatingCompaction: validating_compaction,
393
+ compactPending: compact_pending,
394
+ });
395
+ }
396
+
246
397
  const incomplete_response = detectIncompleteResponse({
247
398
  stepResult: last_step,
248
399
  assistantMessage: step_assistant_ui_message,
@@ -350,9 +501,8 @@ export class CoreEngineRunner {
350
501
  }
351
502
 
352
503
  // 关键点(中文):stop 前做 tail merge,覆盖最后一个 step 后才入队的新 user 消息。
353
- const tail_prepared = await prepare_step({ messages: [] });
354
- const tail_merged_message_count = Array.isArray(tail_prepared.messages)
355
- ? tail_prepared.messages.length
504
+ const tail_merged_message_count = input.run_context.hasPendingStepInput?.()
505
+ ? 1
356
506
  : 0;
357
507
  if (
358
508
  shouldContinueForTailMergedUserMessages({
@@ -408,6 +558,7 @@ export class CoreEngineRunner {
408
558
  return {
409
559
  success: true,
410
560
  assistantMessage: final_message,
561
+ ...(compact_required ? { compact_required: true } : {}),
411
562
  deferredPersistedUserMessages: [
412
563
  ...input.run_context.deferredPersistedUserMessages,
413
564
  ],
@@ -428,6 +579,7 @@ export class CoreEngineRunner {
428
579
  success: false,
429
580
  error: error_text,
430
581
  ...(stopped_message ? { assistantMessage: stopped_message } : {}),
582
+ ...(compact_required ? { compact_required: true } : {}),
431
583
  deferredPersistedUserMessages: [
432
584
  ...input.run_context.deferredPersistedUserMessages,
433
585
  ],
@@ -454,6 +606,7 @@ export class CoreEngineRunner {
454
606
  `Execution failed: ${error_text}`,
455
607
  input.run_context,
456
608
  ),
609
+ ...(compact_required ? { compact_required: true } : {}),
457
610
  deferredPersistedUserMessages: [
458
611
  ...input.run_context.deferredPersistedUserMessages,
459
612
  ],
@@ -461,3 +614,18 @@ export class CoreEngineRunner {
461
614
  }
462
615
  }
463
616
  }
617
+
618
+ /** 读取当前模型上下文中最新的持久化 compact Summary 标识。 */
619
+ function resolve_compaction_summary_id(messages: SessionRecordV1[]): string {
620
+ for (let index = messages.length - 1; index >= 0; index -= 1) {
621
+ const message = messages[index];
622
+ if (!("role" in message) || message.role !== "assistant") continue;
623
+ if (
624
+ message.metadata?.source === "compact" &&
625
+ message.metadata.kind === "summary"
626
+ ) {
627
+ return String(message.id || "").trim();
628
+ }
629
+ }
630
+ return "";
631
+ }
@@ -30,24 +30,11 @@ export type SessionHistoryCompactInput = {
30
30
  system: SessionSystemMessage[];
31
31
 
32
32
  /**
33
- * 保留最近消息条数。
34
- */
35
- keepLastMessages: number;
36
-
37
- /**
38
- * 输入 token 近似上限。
39
- */
40
- maxInputTokensApprox: number;
41
-
42
- /**
43
- * compact 时是否归档旧消息。
44
- */
45
- archiveOnCompact: boolean;
46
-
47
- /**
48
- * 前段压缩比例(0-1)。
33
+ * 是否明确要求本次执行持久化 deep compact。
34
+ *
35
+ * 真实 usage 触发与内存消息折叠由 CoreEngine 决定;Store 只负责归档已经完成的历史。
49
36
  */
50
- compactRatio: number;
37
+ force?: boolean;
51
38
 
52
39
  /**
53
40
  * 可选 action 发布回调。
@@ -88,19 +75,13 @@ export interface SessionHistoryStore {
88
75
  */
89
76
  finalize_inflight(message?: SessionRecordV1 | null): Promise<void>;
90
77
 
91
- /**
92
- * 读取完整 session records。
93
- */
78
+ /** 读取当前模型上下文所需的 session records。 */
94
79
  list_records(): Promise<SessionRecordV1[]>;
95
80
 
96
- /**
97
- * 读取 session record 区间 [start, end)。
98
- */
81
+ /** 读取当前模型上下文 record 区间 [start, end)。 */
99
82
  slice_records(start: number, end: number): Promise<SessionRecordV1[]>;
100
83
 
101
- /**
102
- * 读取 session record 总条数。
103
- */
84
+ /** 读取当前模型上下文 record 总条数。 */
104
85
  record_count(): Promise<number>;
105
86
 
106
87
  /**
@@ -665,10 +665,8 @@ export class JsonlSessionHistoryStore implements SessionHistoryStore {
665
665
  {
666
666
  model: input.model,
667
667
  system: this.normalizeSystem(input.system),
668
- keepLastMessages: input.keepLastMessages,
669
- maxInputTokensApprox: input.maxInputTokensApprox,
670
- archiveOnCompact: input.archiveOnCompact,
671
- compactRatio: input.compactRatio,
668
+ archiveOnCompact: false,
669
+ force: input.force,
672
670
  ...(input.onAction ? { onAction: input.onAction } : {}),
673
671
  },
674
672
  );
@@ -138,7 +138,8 @@ export async function invokePluginCallTool(
138
138
  }
139
139
 
140
140
  try {
141
- const result = await params.plugins.runAction({
141
+ const plugins = getSessionRunContext()?.agentPlugins || params.plugins;
142
+ const result = await plugins.runAction({
142
143
  plugin,
143
144
  action,
144
145
  payload,
@@ -191,7 +192,8 @@ export async function invokePluginReadTool(
191
192
  ): Promise<PluginReadToolResult> {
192
193
  const input: PluginReadInput = params.input;
193
194
  try {
194
- const data = params.plugins.read({
195
+ const plugins = getSessionRunContext()?.agentPlugins || params.plugins;
196
+ const data = plugins.read({
195
197
  plugin: typeof input.plugin === "string" ? input.plugin : undefined,
196
198
  action: typeof input.action === "string" ? input.action : undefined,
197
199
  });
@@ -112,6 +112,15 @@ export interface SessionRunResult {
112
112
  * - 为保证消息顺序稳定,统一在 assistant 结果落盘后再由外层 Session 持久化。
113
113
  */
114
114
  deferredPersistedUserMessages?: SessionUserMessageV1[];
115
+
116
+ /**
117
+ * 本轮结束后是否需要把已完成的 canonical 历史持久化压缩。
118
+ *
119
+ * 关键点(中文)
120
+ * - 真实 usage 达到 95% 或本轮已经执行过内存 compact 时为 true。
121
+ * - 上层必须等 Assistant writer 收口后再执行,避免压缩流式草稿。
122
+ */
123
+ compact_required?: boolean;
115
124
  }
116
125
 
117
126
  /**
package/src/index.ts CHANGED
@@ -15,6 +15,7 @@ export type { SessionOptions } from "./types/session/SessionOptions.js";
15
15
  export {
16
16
  inferAgentModelLabel,
17
17
  normalizeAgentModel,
18
+ read_agent_model_context_window,
18
19
  } from "./model/CityModelAdapter.js";
19
20
  export type { AgentModel } from "./model/CityModelAdapter.js";
20
21
  export type {
@@ -48,6 +49,13 @@ export type {
48
49
  SessionUserMessage,
49
50
  SessionUserMessagePart,
50
51
  } from "./types/session/SessionMessage.js";
52
+ export type {
53
+ SessionContextSnapshot,
54
+ SessionMessageStorageStats,
55
+ SessionSegmentRange,
56
+ SessionSegmentSnapshot,
57
+ SessionSegmentSummary,
58
+ } from "./types/session/SessionSegment.js";
51
59
  export {
52
60
  is_session_mutation,
53
61
  } from "./types/session/SessionMutation.js";
@@ -239,7 +247,7 @@ export {
239
247
  export { assertProjectExecutionTarget } from "./config/ExecutionBinding.js";
240
248
 
241
249
  // 日志
242
- export { getLogger, logger, type Logger } from "./utils/logger/Logger.js";
250
+ export { getLogger, type Logger } from "./utils/logger/Logger.js";
243
251
  export { generateId } from "./utils/Id.js";
244
252
  export {
245
253
  formatDateTimeInTimezone,
@@ -44,6 +44,21 @@ export function normalizeAgentModel(model: AgentModel): LanguageModel {
44
44
  return model;
45
45
  }
46
46
 
47
+ /**
48
+ * 从 Agent 模型输入读取已声明的上下文窗口。
49
+ *
50
+ * 只有 CityModel 携带可信的模型目录元数据;普通 AI SDK LanguageModel 未声明时返回空。
51
+ */
52
+ export function read_agent_model_context_window(
53
+ model: AgentModel | undefined,
54
+ ): number | undefined {
55
+ if (!model || !isCityModel(model)) return undefined;
56
+ const context_window = model.context_window;
57
+ return Number.isSafeInteger(context_window) && Number(context_window) > 0
58
+ ? Number(context_window)
59
+ : undefined;
60
+ }
61
+
47
62
  /**
48
63
  * 从 Agent 模型输入推导展示标签。
49
64
  */
@@ -8,7 +8,7 @@
8
8
  */
9
9
 
10
10
  import path from "node:path";
11
- import { logger as defaultLogger } from "@/utils/logger/Logger.js";
11
+ import { getLogger } from "@/utils/logger/Logger.js";
12
12
  import { resolve_agent_env } from "@/config/AgentEnv.js";
13
13
  import { findPluginByName } from "@/plugin/core/PluginCatalog.js";
14
14
  import {
@@ -45,12 +45,12 @@ export function createLocalPluginCommandContext(
45
45
  : input.config;
46
46
  const agent_id = String(config.id || "").trim() || path.basename(rootPath) || "agent";
47
47
 
48
- defaultLogger.bindProjectRoot(rootPath);
48
+ const logger = getLogger(rootPath);
49
49
 
50
50
  return {
51
51
  cwd: rootPath,
52
52
  rootPath,
53
- logger: defaultLogger,
53
+ logger,
54
54
  config,
55
55
  env,
56
56
  paths: createAgentPathRuntime(