@downcity/agent 1.1.79 → 1.1.82

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 +25 -84
  90. package/bin/session/Session.d.ts.map +1 -1
  91. package/bin/session/Session.js +162 -367
  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 +106 -0
  106. package/bin/session/services/SessionViewService.d.ts.map +1 -0
  107. package/bin/session/services/SessionViewService.js +198 -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 +290 -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 +216 -500
  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 +323 -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
@@ -3,124 +3,55 @@
3
3
  *
4
4
  * 关键点(中文)
5
5
  * - 面向 `new Agent(...)` 的本地会话使用场景。
6
- * - 统一收口消息落盘、session 级模型配置、prompt/subscribe/fork 等高层 API
6
+ * - 对外保留稳定 Session facade,把状态、turn、view 逻辑下沉到独立 service
7
7
  * - 内部继续复用 `Executor` / `JsonlSessionHistoryStore` / Composer 体系。
8
8
  */
9
9
 
10
- import { nanoid } from "nanoid";
11
- import type { Tool } from "ai";
12
10
  import { Executor } from "@executor/Executor.js";
11
+ import type { Tool } from "ai";
13
12
  import { JsonlSessionHistoryComposer } from "@executor/composer/history/jsonl/JsonlSessionHistoryComposer.js";
14
13
  import { JsonlSessionHistoryStore } from "@/executor/store/history/jsonl/JsonlSessionHistoryStore.js";
15
- import { extractTextFromUiMessage } from "@/executor/messages/UIMessageTransformer.js";
16
14
  import type {
17
15
  AgentSession,
18
- AgentSessionHistoryInput,
19
- AgentSessionHistoryPage,
20
16
  AgentSessionConfigSnapshot,
21
17
  AgentSessionForkInput,
18
+ AgentSessionHistoryInput,
19
+ AgentSessionHistoryPage,
22
20
  AgentSessionInfo,
23
21
  AgentSessionSetInput,
24
22
  AgentSessionSystemBlock,
25
23
  AgentSessionSystemSnapshot,
26
24
  } from "@/types/agent/AgentTypes.js";
27
- import type { SessionMessageV1 } from "@/executor/types/SessionMessages.js";
28
- import {
29
- buildSessionSystemBlocks,
30
- SessionSystemBuilder,
31
- } from "@/session/SessionSystemBuilder.js";
32
- import {
33
- buildSessionHistoryPage,
34
- buildSessionInfo,
35
- patchSessionModelLabel,
36
- readSessionMetadata,
37
- resolveSystemTimezone,
38
- writeSessionMetadata,
39
- } from "@/session/index.js";
40
25
  import {
41
26
  getSdkAgentSessionArchiveDirPath,
42
27
  getSdkAgentSessionDirPath,
43
28
  getSdkAgentSessionInflightPath,
29
+ resolveSystemTimezone,
30
+ createRuntimeSessionPort,
44
31
  } from "@/session/index.js";
32
+ import { SessionSystemBuilder } from "@/session/SessionSystemBuilder.js";
45
33
  import type { SessionPort } from "@/types/runtime/agent/AgentContext.js";
46
- import {
47
- mapAgentEventToSessionEvent,
48
- mapUiMessageChunkToAgentEvent,
49
- } from "@/session/SessionEventMapper.js";
50
- import {
51
- persistSdkAssistantResult,
52
- touchSessionMetadata,
53
- } from "@/session/index.js";
54
- import { createRuntimeSessionPort } from "@/session/index.js";
55
- import { drainDeferredPersistedUserMessages } from "@executor/SessionRunScope.js";
56
34
  import type {
57
35
  AgentSessionSubscriber,
58
36
  AgentSessionUnsubscribe,
59
37
  } from "@/types/sdk/AgentSessionEvent.js";
60
38
  import type { AgentSessionPromptInput } from "@/types/sdk/AgentSessionPrompt.js";
61
39
  import type { AgentSessionTurnHandle } from "@/types/sdk/AgentSessionTurn.js";
62
- import type { SessionUserMessageV1 } from "@/executor/types/SessionMessages.js";
63
40
  import { SessionEventHub } from "@/session/runtime/SessionEventHub.js";
64
- import { SessionPromptRuntime } from "@/session/runtime/SessionPromptRuntime.js";
65
- import {
66
- inferAgentModelLabel,
67
- normalizeAgentModel,
68
- } from "@/model/CityModelAdapter.js";
69
- import { ensureSessionTitle } from "@/session/SessionTitle.js";
70
-
71
- type SessionOptions = {
72
- /**
73
- * 当前 agent 稳定标识。
74
- */
75
- agentId: string;
76
-
77
- /**
78
- * 当前项目根目录。
79
- */
80
- projectRoot: string;
81
-
82
- /**
83
- * 当前 sessionId。
84
- */
85
- sessionId: string;
86
-
87
- /**
88
- * 当前 agent 默认工具集合。
89
- */
90
- tools: Record<string, Tool>;
91
-
92
- /**
93
- * 统一日志器。
94
- */
95
- logger: {
96
- info(message: string, details?: Record<string, unknown>): void;
97
- warn(message: string, details?: Record<string, unknown>): void;
98
- };
99
-
100
- /**
101
- * 读取当前 SDK 调用方传入的 instruction system blocks。
102
- */
103
- getInstructionSystemBlocks: () => AgentSessionSystemBlock[];
104
-
105
- /**
106
- * 读取当前 agent 显式注入的受托管 plugin system blocks。
107
- */
108
- getManagedPluginSystemBlocks: () => Promise<AgentSessionSystemBlock[]>;
109
-
110
- /**
111
- * 读取当前 agent 显式注册 plugin 的 system blocks。
112
- */
113
- getPluginSystemBlocks: () => Promise<AgentSessionSystemBlock[]>;
114
-
115
- /**
116
- * 在执行前确保当前 session 已完成宿主侧默认配置。
117
- *
118
- * 关键点(中文)
119
- * - 这里通常由 `Agent` 注入,用于补齐默认 model 等一次性装配。
120
- * - 所有执行入口都应通过这里兜底,避免只在 SDK `agent.createSession()` / `agent.getSession()` 链路上做配置。
121
- */
122
- ensureConfigured?: (session: Session) => Promise<void>;
123
- };
41
+ import { SessionStateService } from "@/session/services/SessionStateService.js";
42
+ import { SessionTurnService } from "@/session/services/SessionTurnService.js";
43
+ import { SessionViewService } from "@/session/services/SessionViewService.js";
44
+ import type { SessionLocalState } from "@/types/session/SessionLocalState.js";
45
+ import type {
46
+ SessionComposerFactoryContext,
47
+ SessionComposerInput,
48
+ SessionComposerOptions,
49
+ } from "@/types/session/SessionComposerOptions.js";
50
+ import type { SessionCompactionComposer } from "@/executor/composer/compaction/SessionCompactionComposer.js";
51
+ import type { SessionContextComposer } from "@/executor/composer/context/SessionContextComposer.js";
52
+ import type { SessionHistoryComposer } from "@/executor/composer/history/SessionHistoryComposer.js";
53
+ import type { SessionSystemComposer } from "@/executor/composer/system/SessionSystemComposer.js";
54
+ import type { SessionOptions } from "@/types/session/SessionOptions.js";
124
55
 
125
56
  /**
126
57
  * SDK 本地 Session。
@@ -136,16 +67,15 @@ export class Session implements AgentSession {
136
67
  private readonly getManagedPluginSystemBlocks: SessionOptions["getManagedPluginSystemBlocks"];
137
68
  private readonly getPluginSystemBlocks: SessionOptions["getPluginSystemBlocks"];
138
69
  private readonly ensureConfiguredHook?: SessionOptions["ensureConfigured"];
70
+ private readonly composers?: SessionComposerOptions;
139
71
  private readonly historyStore: JsonlSessionHistoryStore;
140
- private readonly historyComposer: JsonlSessionHistoryComposer;
72
+ private readonly historyComposer: SessionHistoryComposer;
141
73
  private readonly executor: Executor;
142
74
  private readonly eventHub = new SessionEventHub();
143
- private readonly promptRuntime: SessionPromptRuntime;
144
- private sessionConfig: AgentSessionConfigSnapshot = {};
145
- private createdAt = Date.now();
146
- private timezone = resolveSystemTimezone();
147
- private initializePromise: Promise<this> | null = null;
148
- private ensureConfiguredPromise: Promise<void> | null = null;
75
+ private readonly localState: SessionLocalState;
76
+ private readonly stateService: SessionStateService;
77
+ private readonly turnService: SessionTurnService;
78
+ private readonly viewService: SessionViewService<this>;
149
79
  private runtimePort: SessionPort | null = null;
150
80
 
151
81
  constructor(options: SessionOptions) {
@@ -158,6 +88,7 @@ export class Session implements AgentSession {
158
88
  this.getManagedPluginSystemBlocks = options.getManagedPluginSystemBlocks;
159
89
  this.getPluginSystemBlocks = options.getPluginSystemBlocks;
160
90
  this.ensureConfiguredHook = options.ensureConfigured;
91
+ this.composers = options.composers;
161
92
  if (!this.id) {
162
93
  throw new Error("Session requires a non-empty sessionId");
163
94
  }
@@ -168,282 +99,181 @@ export class Session implements AgentSession {
168
99
  throw new Error("Session requires a non-empty projectRoot");
169
100
  }
170
101
 
171
- const sessionDirPath = getSdkAgentSessionDirPath(
172
- this.projectRoot,
173
- this.agentId,
174
- this.id,
102
+ this.historyStore = this.create_history_store();
103
+ this.localState = this.create_local_state();
104
+ const composer_context = this.create_composer_context();
105
+ this.historyComposer = this.resolve_composer(
106
+ this.composers?.historyComposer,
107
+ composer_context,
108
+ () =>
109
+ new JsonlSessionHistoryComposer({
110
+ store: this.historyStore,
111
+ }),
175
112
  );
176
- const messagesDirPath = `${sessionDirPath}/messages`;
177
- this.historyStore = new JsonlSessionHistoryStore({
178
- rootPath: this.projectRoot,
179
- agentId: this.agentId,
180
- sessionId: this.id,
181
- paths: {
182
- sessionDirPath,
183
- messagesDirPath,
184
- messagesFilePath: `${messagesDirPath}/messages.jsonl`,
185
- metaFilePath: `${messagesDirPath}/meta.json`,
186
- archiveDirPath: getSdkAgentSessionArchiveDirPath(
187
- this.projectRoot,
188
- this.agentId,
189
- this.id,
190
- ),
191
- inflightFilePath: getSdkAgentSessionInflightPath(
192
- this.projectRoot,
193
- this.agentId,
194
- this.id,
195
- ),
196
- },
197
- });
198
- this.historyComposer = new JsonlSessionHistoryComposer({
199
- store: this.historyStore,
200
- });
201
-
113
+ const system_composer = this.resolve_composer(
114
+ this.composers?.systemComposer,
115
+ composer_context,
116
+ () =>
117
+ new SessionSystemBuilder({
118
+ agentId: this.agentId,
119
+ projectRoot: this.projectRoot,
120
+ getSessionCreatedAt: () => this.localState.createdAt,
121
+ getSessionTimezone: () => this.localState.timezone,
122
+ getInstructionSystemBlocks: this.getInstructionSystemBlocks,
123
+ getManagedPluginSystemBlocks: this.getManagedPluginSystemBlocks,
124
+ getPluginSystemBlocks: this.getPluginSystemBlocks,
125
+ }),
126
+ );
127
+ const context_composer = this.resolve_optional_composer<
128
+ SessionContextComposer
129
+ >(this.composers?.contextComposer, composer_context);
130
+ const compaction_composer = this.resolve_optional_composer<
131
+ SessionCompactionComposer
132
+ >(this.composers?.compactionComposer, composer_context);
202
133
  this.executor = new Executor({
203
134
  sessionId: this.id,
204
135
  historyStore: this.historyStore,
205
136
  historyComposer: this.historyComposer,
206
- getModel: () => this.sessionConfig.model,
137
+ getModel: () => this.localState.sessionConfig.model,
207
138
  logger: this.logger as never,
208
- systemComposer: new SessionSystemBuilder({
209
- agentId: this.agentId,
210
- projectRoot: this.projectRoot,
211
- getSessionCreatedAt: () => this.createdAt,
212
- getSessionTimezone: () => this.timezone,
213
- getInstructionSystemBlocks: this.getInstructionSystemBlocks,
214
- getManagedPluginSystemBlocks: this.getManagedPluginSystemBlocks,
215
- getPluginSystemBlocks: this.getPluginSystemBlocks,
216
- }),
139
+ systemComposer: system_composer,
217
140
  getTools: () => this.tools,
141
+ ...(context_composer ? { contextComposer: context_composer } : {}),
142
+ ...(compaction_composer
143
+ ? { compactionComposer: compaction_composer }
144
+ : {}),
218
145
  });
219
- this.promptRuntime = new SessionPromptRuntime({
220
- sessionId: this.id,
221
- publish: (event) => {
146
+ this.stateService = new SessionStateService({
147
+ agent_id: this.agentId,
148
+ project_root: this.projectRoot,
149
+ session_id: this.id,
150
+ history_store: this.historyStore,
151
+ executor: this.executor,
152
+ state: this.localState,
153
+ ensure_configured_hook: this.ensureConfiguredHook
154
+ ? async () => {
155
+ await this.ensureConfiguredHook?.(this);
156
+ }
157
+ : undefined,
158
+ publish_event: (event) => {
222
159
  this.eventHub.publish(event);
223
160
  },
224
- createAndPersistUserMessage: async (input) => {
225
- return await this.createAndPersistUserPromptMessage(input);
226
- },
227
- executeTurn: async ({ turnId, promptInput, onStepMerge }) => {
228
- return await this.executePromptTurn({
229
- turnId,
230
- promptInput,
231
- onStepMerge,
232
- });
161
+ });
162
+ this.turnService = new SessionTurnService({
163
+ session_id: this.id,
164
+ executor: this.executor,
165
+ state_service: this.stateService,
166
+ event_hub: this.eventHub,
167
+ });
168
+ this.viewService = new SessionViewService<this>({
169
+ agent_id: this.agentId,
170
+ project_root: this.projectRoot,
171
+ session_id: this.id,
172
+ history_store: this.historyStore,
173
+ state_service: this.stateService,
174
+ is_executing: () => this.isExecuting(),
175
+ get_instruction_system_blocks: this.getInstructionSystemBlocks,
176
+ get_managed_plugin_system_blocks: this.getManagedPluginSystemBlocks,
177
+ get_plugin_system_blocks: this.getPluginSystemBlocks,
178
+ ...(this.composers?.systemComposer
179
+ ? { custom_system_composer: system_composer }
180
+ : {}),
181
+ create_fork_session: async (session_id) => {
182
+ const session = this.create_fork_session(session_id);
183
+ await session.initialize();
184
+ return {
185
+ session,
186
+ history_store: session.historyStore,
187
+ state_service: session.stateService,
188
+ };
233
189
  },
234
190
  });
235
191
  }
236
192
 
237
193
  /**
238
- * 初始化当前 session 的 meta 信息与内存配置。
194
+ * 初始化当前 session
239
195
  */
240
196
  async initialize(): Promise<this> {
241
- if (this.initializePromise) {
242
- return await this.initializePromise;
243
- }
244
- this.initializePromise = (async () => {
245
- const metadata = await readSessionMetadata({
246
- projectRoot: this.projectRoot,
247
- agentId: this.agentId,
248
- sessionId: this.id,
249
- });
250
- const createdAt =
251
- typeof metadata.createdAt === "number" ? metadata.createdAt : Date.now();
252
- const timezone =
253
- typeof metadata.timezone === "string" && metadata.timezone.trim()
254
- ? metadata.timezone.trim()
255
- : resolveSystemTimezone();
256
- await writeSessionMetadata({
257
- projectRoot: this.projectRoot,
258
- agentId: this.agentId,
259
- sessionId: this.id,
260
- meta: {
261
- ...metadata,
262
- agentId: this.agentId,
263
- createdAt,
264
- timezone,
265
- },
266
- });
267
- this.createdAt = createdAt;
268
- this.timezone = timezone;
269
- this.sessionConfig = {
270
- ...(metadata.modelLabel
271
- ? { modelLabel: metadata.modelLabel }
272
- : {}),
273
- };
274
- return this;
275
- })();
276
- return await this.initializePromise;
197
+ await this.stateService.initialize();
198
+ return this;
277
199
  }
278
200
 
279
201
  /**
280
202
  * 读取当前 session 配置快照。
281
203
  */
282
204
  get config(): AgentSessionConfigSnapshot {
283
- return {
284
- ...this.sessionConfig,
285
- };
205
+ return this.stateService.get_config();
286
206
  }
287
207
 
288
208
  /**
289
209
  * 写入当前 session 默认配置。
290
210
  */
291
211
  async set(input: AgentSessionSetInput): Promise<void> {
292
- if (input.model) {
293
- this.sessionConfig.model = normalizeAgentModel(input.model);
294
- this.sessionConfig.modelLabel = inferAgentModelLabel(input.model);
295
- this.executor.clearExecutor();
296
- }
297
- await patchSessionModelLabel({
298
- projectRoot: this.projectRoot,
299
- agentId: this.agentId,
300
- sessionId: this.id,
301
- model: this.sessionConfig.model,
302
- });
212
+ await this.stateService.set(input);
303
213
  }
304
214
 
305
215
  /**
306
216
  * 追加一条新的 Session prompt。
307
- *
308
- * 关键点(中文)
309
- * - 这是 Session actor 模型下唯一的输入入口。
310
- * - 首条输入、运行中补充输入、排到下一轮的输入,调用方式完全一致。
311
217
  */
312
218
  async prompt(input: AgentSessionPromptInput): Promise<AgentSessionTurnHandle> {
313
- const query = String(input.query || "").trim();
314
- if (!query) {
315
- throw new Error("session.prompt requires a non-empty query");
316
- }
317
- await this.ensureRunnable();
318
- return await this.promptRuntime.prompt({
319
- query,
320
- });
219
+ return await this.turnService.prompt(input);
321
220
  }
322
221
 
323
222
  /**
324
223
  * 订阅当前 Session 的未来事件。
325
- *
326
- * 关键点(中文)
327
- * - 只广播订阅之后产生的事件。
328
- * - 不做历史回放;历史仍通过 `history()` 读取。
329
224
  */
330
225
  subscribe(subscriber: AgentSessionSubscriber): AgentSessionUnsubscribe {
331
- return this.eventHub.subscribe(subscriber);
226
+ return this.turnService.subscribe(subscriber);
332
227
  }
333
228
 
334
229
  /**
335
230
  * 追加一条 user 文本消息。
336
231
  */
337
232
  async appendUserMessage(input: {
338
- /**
339
- * 需要写入的用户文本。
340
- */
341
233
  text: string;
342
234
  }): Promise<void> {
343
- await this.executor.appendUserMessage({
235
+ await this.stateService.append_user_message({
344
236
  text: String(input.text || "").trim(),
345
237
  });
346
- await this.ensureTitleFromHistory({ generate: true });
347
- await this.touchMetadata();
348
238
  }
349
239
 
350
240
  /**
351
241
  * 追加一条 assistant 文本消息。
352
242
  */
353
243
  async appendAssistantMessage(input: {
354
- /**
355
- * 需要写入的 assistant 文本。
356
- */
357
244
  text: string;
358
245
  }): Promise<void> {
359
- await this.executor.appendAssistantMessage({
246
+ await this.stateService.append_assistant_message({
360
247
  fallbackText: String(input.text || "").trim(),
361
248
  });
362
- await this.touchMetadata();
363
249
  }
364
250
 
365
251
  /**
366
252
  * 读取当前 session 详情。
367
253
  */
368
254
  async getInfo(): Promise<AgentSessionInfo> {
369
- const [metadata, messages] = await Promise.all([
370
- readSessionMetadata({
371
- projectRoot: this.projectRoot,
372
- agentId: this.agentId,
373
- sessionId: this.id,
374
- }),
375
- this.historyStore.list(),
376
- ]);
377
- const metadataWithTitle = metadata.title
378
- ? metadata
379
- : await ensureSessionTitle({
380
- projectRoot: this.projectRoot,
381
- agentId: this.agentId,
382
- sessionId: this.id,
383
- messages,
384
- });
385
- return buildSessionInfo({
386
- projectRoot: this.projectRoot,
387
- agentId: this.agentId,
388
- sessionId: this.id,
389
- metadata: metadataWithTitle,
390
- messages,
391
- executing: this.isExecuting(),
392
- });
255
+ return await this.viewService.get_info();
393
256
  }
394
257
 
395
258
  /**
396
259
  * 读取当前 session 历史分页。
397
260
  */
398
261
  async history(input?: AgentSessionHistoryInput): Promise<AgentSessionHistoryPage> {
399
- const [session, messages] = await Promise.all([
400
- this.getInfo(),
401
- this.historyStore.list(),
402
- ]);
403
- return buildSessionHistoryPage({
404
- session,
405
- messages,
406
- input,
407
- });
262
+ return await this.viewService.history(input);
408
263
  }
409
264
 
410
265
  /**
411
- * 读取当前 session 生效的 system prompt 文本集合。
412
- *
413
- * 关键点(中文)
414
- * - 返回内容与实际 run 时使用的 SDK system composer 同源。
415
- * - 包含 instruction/core、受托管 plugin system、显式注册 plugin system 与 session 上下文。
416
- * - 返回结构化快照,不把 system prompt 写入会话历史。
266
+ * 读取当前 session 生效的 system 快照。
417
267
  */
418
268
  async system(): Promise<AgentSessionSystemSnapshot> {
419
- const blocks = await buildSessionSystemBlocks({
420
- agentId: this.agentId,
421
- projectRoot: this.projectRoot,
422
- sessionId: this.id,
423
- createdAt: this.createdAt,
424
- timezone: this.timezone,
425
- getInstructionSystemBlocks: this.getInstructionSystemBlocks,
426
- getManagedPluginSystemBlocks: this.getManagedPluginSystemBlocks,
427
- getPluginSystemBlocks: this.getPluginSystemBlocks,
428
- });
429
- return {
430
- sessionId: this.id,
431
- session: {
432
- agentId: this.agentId,
433
- sessionId: this.id,
434
- projectRoot: this.projectRoot,
435
- createdAt: new Date(this.createdAt).toISOString(),
436
- timezone: this.timezone,
437
- },
438
- blocks,
439
- };
269
+ return await this.viewService.system();
440
270
  }
441
271
 
442
272
  /**
443
273
  * 返回当前 session 是否正在执行。
444
274
  */
445
275
  isExecuting(): boolean {
446
- return this.promptRuntime.isActive() || this.executor.isExecuting();
276
+ return this.turnService.is_prompt_runtime_active() || this.executor.isExecuting();
447
277
  }
448
278
 
449
279
  /**
@@ -456,49 +286,8 @@ export class Session implements AgentSession {
456
286
  /**
457
287
  * 从当前 session 创建一个分叉会话。
458
288
  */
459
- async fork(input?: AgentSessionForkInput | string): Promise<Session> {
460
- const messageId =
461
- typeof input === "string"
462
- ? String(input || "").trim() || undefined
463
- : String(input?.messageId || "").trim() || undefined;
464
- const messages = await this.historyStore.list();
465
- const forkMessages =
466
- !messageId
467
- ? messages
468
- : (() => {
469
- const targetIndex = messages.findIndex(
470
- (message) => String(message.id || "").trim() === messageId,
471
- );
472
- if (targetIndex < 0) {
473
- throw new Error(
474
- `Cannot fork session "${this.id}": messageId "${messageId}" not found.`,
475
- );
476
- }
477
- return messages.slice(0, targetIndex + 1);
478
- })();
479
-
480
- const forked = new Session({
481
- agentId: this.agentId,
482
- projectRoot: this.projectRoot,
483
- sessionId: `fork-${Date.now()}-${nanoid(8)}`,
484
- tools: this.tools,
485
- logger: this.logger,
486
- getInstructionSystemBlocks: this.getInstructionSystemBlocks,
487
- getManagedPluginSystemBlocks: this.getManagedPluginSystemBlocks,
488
- getPluginSystemBlocks: this.getPluginSystemBlocks,
489
- });
490
- await forked.initialize();
491
- if (this.sessionConfig.model) {
492
- await forked.set({
493
- model: this.sessionConfig.model,
494
- });
495
- }
496
- for (const message of forkMessages) {
497
- await forked.historyStore.append(message);
498
- }
499
- await forked.ensureTitleFromHistory({ generate: true });
500
- await forked.touchMetadata();
501
- return forked;
289
+ async fork(input?: AgentSessionForkInput | string): Promise<this> {
290
+ return await this.viewService.fork(input);
502
291
  }
503
292
 
504
293
  /**
@@ -509,25 +298,19 @@ export class Session implements AgentSession {
509
298
  this.runtimePort = createRuntimeSessionPort({
510
299
  sessionId: this.id,
511
300
  getExecutor: () => this.executor.getExecutor(),
512
- prompt: async (input) => {
513
- return await this.prompt(input);
514
- },
515
- subscribe: (subscriber) => {
516
- return this.subscribe(subscriber);
517
- },
301
+ prompt: async (input) => await this.prompt(input),
302
+ subscribe: (subscriber) => this.subscribe(subscriber),
518
303
  clearExecutor: () => {
519
304
  this.executor.clearExecutor();
520
305
  },
521
306
  afterSessionUpdatedAsync: async () => {
522
307
  await this.executor.afterSessionUpdatedAsync();
523
308
  },
524
- appendUserMessage: async (messageParams) => {
525
- await this.executor.appendUserMessage(messageParams);
526
- await this.ensureTitleFromHistory({ generate: true });
527
- await this.touchMetadata();
309
+ appendUserMessage: async (message_params) => {
310
+ await this.stateService.append_user_message(message_params);
528
311
  },
529
- appendAssistantMessage: async (messageParams) => {
530
- await this.executor.appendAssistantMessage(messageParams);
312
+ appendAssistantMessage: async (message_params) => {
313
+ await this.stateService.append_assistant_message(message_params);
531
314
  },
532
315
  isExecuting: () => this.isExecuting(),
533
316
  historyStore: this.historyStore,
@@ -535,7 +318,7 @@ export class Session implements AgentSession {
535
318
  await this.ensureReadyForExecution();
536
319
  },
537
320
  touchMetadata: async () => {
538
- await this.touchMetadata();
321
+ await this.stateService.touch_metadata();
539
322
  },
540
323
  });
541
324
  return this.runtimePort;
@@ -545,180 +328,113 @@ export class Session implements AgentSession {
545
328
  * 在执行前确保 session 已完成初始化与宿主装配。
546
329
  */
547
330
  async ensureReadyForExecution(): Promise<void> {
548
- await this.initialize();
549
- if (this.ensureConfiguredPromise) {
550
- await this.ensureConfiguredPromise;
551
- return;
552
- }
553
- this.ensureConfiguredPromise = (async () => {
554
- if (!this.ensureConfiguredHook) return;
555
- await this.ensureConfiguredHook(this);
556
- })();
557
- try {
558
- await this.ensureConfiguredPromise;
559
- } catch (error) {
560
- this.ensureConfiguredPromise = null;
561
- throw error;
562
- }
331
+ await this.stateService.ensure_ready_for_execution();
563
332
  }
564
333
 
565
- private async ensureRunnable(): Promise<void> {
566
- await this.ensureReadyForExecution();
567
- if (!this.sessionConfig.model) {
568
- throw new Error(
569
- `Session "${this.id}" requires a configured model. Pass model to new Agent({ model }) or call session.set({ model }) first.`,
570
- );
571
- }
572
- }
573
-
574
- private async touchMetadata(): Promise<void> {
575
- await touchSessionMetadata({
576
- projectRoot: this.projectRoot,
334
+ private create_fork_session(session_id: string): this {
335
+ return this.create_child_session({
577
336
  agentId: this.agentId,
578
- sessionId: this.id,
579
- sessionConfig: this.sessionConfig,
337
+ projectRoot: this.projectRoot,
338
+ sessionId: session_id,
339
+ tools: this.tools,
340
+ logger: this.logger,
341
+ getInstructionSystemBlocks: this.getInstructionSystemBlocks,
342
+ getManagedPluginSystemBlocks: this.getManagedPluginSystemBlocks,
343
+ getPluginSystemBlocks: this.getPluginSystemBlocks,
344
+ ensureConfigured: this.ensureConfiguredHook,
345
+ composers: this.composers,
580
346
  });
581
347
  }
582
348
 
583
- private async ensureTitleFromHistory(input?: {
584
- /**
585
- * 是否允许调用模型生成标题。
586
- */
587
- generate?: boolean;
588
- }): Promise<void> {
589
- const messages = await this.historyStore.list();
590
- const beforeMetadata = await readSessionMetadata({
591
- projectRoot: this.projectRoot,
592
- agentId: this.agentId,
593
- sessionId: this.id,
594
- });
595
- const beforeTitle = String(beforeMetadata.title || "").trim();
596
- const nextMetadata = await ensureSessionTitle({
597
- projectRoot: this.projectRoot,
598
- agentId: this.agentId,
599
- sessionId: this.id,
600
- messages,
601
- ...(input?.generate ? { model: this.sessionConfig.model } : {}),
602
- generate: input?.generate === true,
603
- });
604
- const nextTitle = String(nextMetadata.title || "").trim();
605
- if (!nextTitle || nextTitle === beforeTitle) return;
606
- this.eventHub.publish({
607
- type: "session-title",
608
- sessionId: this.id,
609
- title: nextTitle,
610
- });
349
+ /**
350
+ * 创建当前 Session 的同类子会话。
351
+ *
352
+ * 关键点(中文)
353
+ * - 默认沿用当前实例的 class,避免自定义 Session 在 fork 后退回默认实现。
354
+ * - 子类仍可覆盖该方法,接管更特殊的子会话创建逻辑。
355
+ */
356
+ protected create_child_session(options: SessionOptions): this {
357
+ const SessionClass = this.constructor as new (
358
+ options: SessionOptions,
359
+ ) => Session;
360
+ return new SessionClass(options) as this;
611
361
  }
612
362
 
613
- private async persistAssistantResult(
614
- assistantMessage: SessionMessageV1,
615
- ): Promise<void> {
616
- await persistSdkAssistantResult({
617
- projectRoot: this.projectRoot,
363
+ private create_history_store(): JsonlSessionHistoryStore {
364
+ const session_dir_path = getSdkAgentSessionDirPath(
365
+ this.projectRoot,
366
+ this.agentId,
367
+ this.id,
368
+ );
369
+ const messages_dir_path = `${session_dir_path}/messages`;
370
+ return new JsonlSessionHistoryStore({
371
+ rootPath: this.projectRoot,
618
372
  agentId: this.agentId,
619
373
  sessionId: this.id,
620
- sessionConfig: this.sessionConfig,
621
- executor: this.executor,
622
- assistantMessage,
374
+ paths: {
375
+ sessionDirPath: session_dir_path,
376
+ messagesDirPath: messages_dir_path,
377
+ messagesFilePath: `${messages_dir_path}/messages.jsonl`,
378
+ metaFilePath: `${messages_dir_path}/meta.json`,
379
+ archiveDirPath: getSdkAgentSessionArchiveDirPath(
380
+ this.projectRoot,
381
+ this.agentId,
382
+ this.id,
383
+ ),
384
+ inflightFilePath: getSdkAgentSessionInflightPath(
385
+ this.projectRoot,
386
+ this.agentId,
387
+ this.id,
388
+ ),
389
+ },
623
390
  });
624
391
  }
625
392
 
626
- private async createAndPersistUserPromptMessage(
627
- input: AgentSessionPromptInput,
628
- ): Promise<SessionUserMessageV1> {
629
- const message = this.historyStore.userText({
630
- text: String(input.query || "").trim(),
631
- metadata: {
632
- sessionId: this.id,
633
- },
634
- }) as SessionUserMessageV1;
635
- await this.executor.appendUserMessage({
636
- message,
637
- });
638
- await this.ensureTitleFromHistory({ generate: true });
639
- await this.touchMetadata();
640
- return message;
393
+ private create_local_state(): SessionLocalState {
394
+ return {
395
+ sessionConfig: {},
396
+ createdAt: Date.now(),
397
+ timezone: resolveSystemTimezone(),
398
+ initializePromise: null,
399
+ ensureConfiguredPromise: null,
400
+ };
641
401
  }
642
402
 
643
- private async executePromptTurn(input: {
644
- turnId: string;
645
- promptInput: AgentSessionPromptInput;
646
- onStepMerge: () => Promise<SessionUserMessageV1[]>;
647
- }): Promise<{
648
- text: string;
649
- success: boolean;
650
- assistantMessage: SessionMessageV1;
651
- error?: string;
652
- }> {
653
- const toolNameByCallId = new Map<string, string>();
654
- const result = await this.executor.run({
655
- query: input.promptInput.query,
656
- onStepCallback: input.onStepMerge,
657
- onAssistantStepCallback: async (step) => {
658
- this.eventHub.publish({
659
- type: "assistant-step",
660
- turnId: input.turnId,
661
- text: step.text,
662
- stepIndex: step.stepIndex,
663
- ...(step.visibility ? { visibility: step.visibility } : {}),
664
- });
665
- },
666
- onUiMessageChunkCallback: async (chunk) => {
667
- if (chunk.type === "tool-input-start") {
668
- toolNameByCallId.set(chunk.toolCallId, chunk.toolName);
669
- return;
670
- }
671
- const event = mapUiMessageChunkToAgentEvent(chunk);
672
- if (!event) return;
673
- const resolvedEvent =
674
- (
675
- event.type === "tool-result" ||
676
- event.type === "tool-error"
677
- ) &&
678
- event.toolName === "unknown"
679
- ? {
680
- ...event,
681
- toolName:
682
- toolNameByCallId.get(event.toolCallId) || event.toolName,
683
- }
684
- : event;
685
- if (
686
- resolvedEvent.type === "tool-call" ||
687
- resolvedEvent.type === "tool-error"
688
- ) {
689
- toolNameByCallId.set(
690
- resolvedEvent.toolCallId,
691
- resolvedEvent.toolName,
692
- );
693
- }
694
- const sessionEvent = mapAgentEventToSessionEvent({
695
- event: resolvedEvent,
696
- turnId: input.turnId,
697
- });
698
- if (sessionEvent) {
699
- this.eventHub.publish(sessionEvent);
700
- }
701
- },
702
- });
703
- await this.persistAssistantResult(result.assistantMessage);
704
- await this.persistDeferredUserMessages();
403
+ private create_composer_context(): SessionComposerFactoryContext {
705
404
  return {
706
- text: extractTextFromUiMessage(result.assistantMessage),
707
- success: result.success,
708
- assistantMessage: result.assistantMessage,
709
- ...(result.error ? { error: result.error } : {}),
405
+ agentId: this.agentId,
406
+ projectRoot: this.projectRoot,
407
+ sessionId: this.id,
408
+ historyStore: this.historyStore,
409
+ getTools: () => this.tools,
410
+ getInstructionSystemBlocks: this.getInstructionSystemBlocks,
411
+ getManagedPluginSystemBlocks: this.getManagedPluginSystemBlocks,
412
+ getPluginSystemBlocks: this.getPluginSystemBlocks,
413
+ getSessionCreatedAt: () => this.localState.createdAt,
414
+ getSessionTimezone: () => this.localState.timezone,
710
415
  };
711
416
  }
712
417
 
713
- private async persistDeferredUserMessages(): Promise<void> {
714
- const deferredMessages = drainDeferredPersistedUserMessages(this.id);
715
- for (const message of deferredMessages) {
716
- await this.executor.appendUserMessage({
717
- message,
718
- });
719
- }
720
- if (deferredMessages.length > 0) {
721
- await this.touchMetadata();
418
+ private resolve_composer<TComposer>(
419
+ input: SessionComposerInput<TComposer> | undefined,
420
+ context: SessionComposerFactoryContext,
421
+ create_default: () => TComposer,
422
+ ): TComposer {
423
+ const composer = this.resolve_optional_composer(input, context);
424
+ return composer || create_default();
425
+ }
426
+
427
+ private resolve_optional_composer<TComposer>(
428
+ input: SessionComposerInput<TComposer> | undefined,
429
+ context: SessionComposerFactoryContext,
430
+ ): TComposer | undefined {
431
+ if (!input) return undefined;
432
+ if (typeof input === "function") {
433
+ const create_composer = input as (
434
+ context: SessionComposerFactoryContext,
435
+ ) => TComposer;
436
+ return create_composer(context);
722
437
  }
438
+ return input;
723
439
  }
724
440
  }