@downcity/agent 1.1.217 → 1.1.224

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 (184) hide show
  1. package/bin/agent/local/Agent.d.ts +1 -34
  2. package/bin/agent/local/Agent.d.ts.map +1 -1
  3. package/bin/agent/local/Agent.js +1 -44
  4. package/bin/agent/local/Agent.js.map +1 -1
  5. package/bin/agent/local/services/AgentAssemblyService.d.ts.map +1 -1
  6. package/bin/agent/local/services/AgentAssemblyService.js +35 -2
  7. package/bin/agent/local/services/AgentAssemblyService.js.map +1 -1
  8. package/bin/agent/local/services/AgentSessions.d.ts +6 -0
  9. package/bin/agent/local/services/AgentSessions.d.ts.map +1 -1
  10. package/bin/agent/local/services/AgentSessions.js +43 -4
  11. package/bin/agent/local/services/AgentSessions.js.map +1 -1
  12. package/bin/agent/remote/RemoteAgent.d.ts +1 -35
  13. package/bin/agent/remote/RemoteAgent.d.ts.map +1 -1
  14. package/bin/agent/remote/RemoteAgent.js +1 -37
  15. package/bin/agent/remote/RemoteAgent.js.map +1 -1
  16. package/bin/agent/remote/RemoteSession.d.ts +26 -41
  17. package/bin/agent/remote/RemoteSession.d.ts.map +1 -1
  18. package/bin/agent/remote/RemoteSession.js +75 -107
  19. package/bin/agent/remote/RemoteSession.js.map +1 -1
  20. package/bin/agent/remote/RemoteTransport.d.ts +15 -27
  21. package/bin/agent/remote/RemoteTransport.d.ts.map +1 -1
  22. package/bin/agent/remote/transports/HttpRemoteAgentTransport.d.ts +10 -21
  23. package/bin/agent/remote/transports/HttpRemoteAgentTransport.d.ts.map +1 -1
  24. package/bin/agent/remote/transports/HttpRemoteAgentTransport.js +24 -46
  25. package/bin/agent/remote/transports/HttpRemoteAgentTransport.js.map +1 -1
  26. package/bin/agent/remote/transports/RpcRemoteAgentTransport.d.ts +10 -20
  27. package/bin/agent/remote/transports/RpcRemoteAgentTransport.d.ts.map +1 -1
  28. package/bin/agent/remote/transports/RpcRemoteAgentTransport.js +10 -16
  29. package/bin/agent/remote/transports/RpcRemoteAgentTransport.js.map +1 -1
  30. package/bin/executor/store/history/jsonl/JsonlSessionHistoryStore.js +2 -2
  31. package/bin/executor/store/history/jsonl/JsonlSessionHistoryStore.js.map +1 -1
  32. package/bin/executor/types/SessionHistoryPaths.d.ts +1 -1
  33. package/bin/executor/types/SessionRecords.d.ts +2 -0
  34. package/bin/executor/types/SessionRecords.d.ts.map +1 -1
  35. package/bin/executor/types/SessionRecords.js.map +1 -1
  36. package/bin/index.d.ts +6 -3
  37. package/bin/index.d.ts.map +1 -1
  38. package/bin/index.js +1 -0
  39. package/bin/index.js.map +1 -1
  40. package/bin/rpc/Client.d.ts +12 -34
  41. package/bin/rpc/Client.d.ts.map +1 -1
  42. package/bin/rpc/Client.js +31 -70
  43. package/bin/rpc/Client.js.map +1 -1
  44. package/bin/session/Session.d.ts +22 -4
  45. package/bin/session/Session.d.ts.map +1 -1
  46. package/bin/session/Session.js +95 -21
  47. package/bin/session/Session.js.map +1 -1
  48. package/bin/session/browse/Browse.d.ts.map +1 -1
  49. package/bin/session/browse/Browse.js +49 -18
  50. package/bin/session/browse/Browse.js.map +1 -1
  51. package/bin/session/recorder/JsonlSessionMessageStore.d.ts +51 -0
  52. package/bin/session/recorder/JsonlSessionMessageStore.d.ts.map +1 -0
  53. package/bin/session/recorder/JsonlSessionMessageStore.js +231 -0
  54. package/bin/session/recorder/JsonlSessionMessageStore.js.map +1 -0
  55. package/bin/session/recorder/SessionMessageCodec.d.ts +20 -0
  56. package/bin/session/recorder/SessionMessageCodec.d.ts.map +1 -0
  57. package/bin/session/recorder/SessionMessageCodec.js +257 -0
  58. package/bin/session/recorder/SessionMessageCodec.js.map +1 -0
  59. package/bin/session/recorder/SessionRecorder.d.ts +186 -0
  60. package/bin/session/recorder/SessionRecorder.d.ts.map +1 -0
  61. package/bin/session/recorder/SessionRecorder.js +655 -0
  62. package/bin/session/recorder/SessionRecorder.js.map +1 -0
  63. package/bin/session/recorder/SessionRecorderCompaction.d.ts +18 -0
  64. package/bin/session/recorder/SessionRecorderCompaction.d.ts.map +1 -0
  65. package/bin/session/recorder/SessionRecorderCompaction.js +142 -0
  66. package/bin/session/recorder/SessionRecorderCompaction.js.map +1 -0
  67. package/bin/session/recorder/SessionRecorderHistoryStore.d.ts +66 -0
  68. package/bin/session/recorder/SessionRecorderHistoryStore.d.ts.map +1 -0
  69. package/bin/session/recorder/SessionRecorderHistoryStore.js +195 -0
  70. package/bin/session/recorder/SessionRecorderHistoryStore.js.map +1 -0
  71. package/bin/session/runtime/SessionEventHub.d.ts +12 -5
  72. package/bin/session/runtime/SessionEventHub.d.ts.map +1 -1
  73. package/bin/session/runtime/SessionEventHub.js +11 -6
  74. package/bin/session/runtime/SessionEventHub.js.map +1 -1
  75. package/bin/session/runtime/SessionPromptRuntime.d.ts +9 -15
  76. package/bin/session/runtime/SessionPromptRuntime.d.ts.map +1 -1
  77. package/bin/session/runtime/SessionPromptRuntime.js +48 -30
  78. package/bin/session/runtime/SessionPromptRuntime.js.map +1 -1
  79. package/bin/session/services/SessionStateService.d.ts +9 -5
  80. package/bin/session/services/SessionStateService.d.ts.map +1 -1
  81. package/bin/session/services/SessionStateService.js +138 -74
  82. package/bin/session/services/SessionStateService.js.map +1 -1
  83. package/bin/session/services/SessionTurnService.d.ts +6 -7
  84. package/bin/session/services/SessionTurnService.d.ts.map +1 -1
  85. package/bin/session/services/SessionTurnService.js +84 -60
  86. package/bin/session/services/SessionTurnService.js.map +1 -1
  87. package/bin/session/services/SessionViewService.d.ts +8 -3
  88. package/bin/session/services/SessionViewService.d.ts.map +1 -1
  89. package/bin/session/services/SessionViewService.js +10 -5
  90. package/bin/session/services/SessionViewService.js.map +1 -1
  91. package/bin/session/storage/Paths.d.ts +1 -1
  92. package/bin/session/storage/Paths.d.ts.map +1 -1
  93. package/bin/session/storage/Paths.js +2 -2
  94. package/bin/session/storage/Paths.js.map +1 -1
  95. package/bin/session/storage/Persistence.d.ts +6 -0
  96. package/bin/session/storage/Persistence.d.ts.map +1 -1
  97. package/bin/session/storage/Persistence.js +7 -0
  98. package/bin/session/storage/Persistence.js.map +1 -1
  99. package/bin/session/storage/RuntimeSessionPort.d.ts +3 -3
  100. package/bin/session/storage/RuntimeSessionPort.d.ts.map +1 -1
  101. package/bin/session/storage/RuntimeSessionPort.js +2 -2
  102. package/bin/session/storage/RuntimeSessionPort.js.map +1 -1
  103. package/bin/types/agent/SessionActor.d.ts +15 -5
  104. package/bin/types/agent/SessionActor.d.ts.map +1 -1
  105. package/bin/types/rpc/RpcProtocol.d.ts +48 -61
  106. package/bin/types/rpc/RpcProtocol.d.ts.map +1 -1
  107. package/bin/types/runtime/agent/AgentContext.d.ts +5 -4
  108. package/bin/types/runtime/agent/AgentContext.d.ts.map +1 -1
  109. package/bin/types/runtime/agent/AgentContext.js.map +1 -1
  110. package/bin/types/session/SessionApproval.d.ts +97 -0
  111. package/bin/types/session/SessionApproval.d.ts.map +1 -0
  112. package/bin/types/session/SessionApproval.js +7 -0
  113. package/bin/types/session/SessionApproval.js.map +1 -0
  114. package/bin/types/session/SessionMessage.d.ts +223 -0
  115. package/bin/types/session/SessionMessage.d.ts.map +1 -0
  116. package/bin/types/session/SessionMessage.js +8 -0
  117. package/bin/types/session/SessionMessage.js.map +1 -0
  118. package/bin/types/session/SessionMutation.d.ts +116 -0
  119. package/bin/types/session/SessionMutation.d.ts.map +1 -0
  120. package/bin/types/session/SessionMutation.js +19 -0
  121. package/bin/types/session/SessionMutation.js.map +1 -0
  122. package/bin/types/session/SessionOptions.d.ts +9 -0
  123. package/bin/types/session/SessionOptions.d.ts.map +1 -1
  124. package/package.json +3 -3
  125. package/scripts/agent-env.test.mjs +4 -4
  126. package/scripts/remote-session-reconnect.test.mjs +7 -3
  127. package/scripts/session-action-message.test.mjs +98 -27
  128. package/scripts/session-history-inflight.test.mjs +2 -2
  129. package/scripts/session-list-title.test.mjs +6 -19
  130. package/scripts/session-prompt-runtime.test.mjs +7 -19
  131. package/scripts/session-recorder.test.mjs +199 -0
  132. package/scripts/session-title-event.test.mjs +10 -18
  133. package/src/agent/local/Agent.ts +1 -55
  134. package/src/agent/local/services/AgentAssemblyService.ts +39 -3
  135. package/src/agent/local/services/AgentSessions.ts +48 -3
  136. package/src/agent/remote/RemoteAgent.ts +1 -54
  137. package/src/agent/remote/RemoteSession.ts +106 -136
  138. package/src/agent/remote/RemoteTransport.ts +30 -30
  139. package/src/agent/remote/transports/HttpRemoteAgentTransport.ts +52 -77
  140. package/src/agent/remote/transports/RpcRemoteAgentTransport.ts +30 -34
  141. package/src/executor/store/history/jsonl/JsonlSessionHistoryStore.ts +2 -2
  142. package/src/executor/types/SessionHistoryPaths.ts +1 -1
  143. package/src/executor/types/SessionRecords.ts +2 -0
  144. package/src/index.ts +36 -10
  145. package/src/rpc/Client.ts +58 -95
  146. package/src/session/Session.ts +128 -37
  147. package/src/session/browse/Browse.ts +51 -22
  148. package/src/session/recorder/JsonlSessionMessageStore.ts +246 -0
  149. package/src/session/recorder/SessionMessageCodec.ts +284 -0
  150. package/src/session/recorder/SessionRecorder.ts +871 -0
  151. package/src/session/recorder/SessionRecorderCompaction.ts +188 -0
  152. package/src/session/recorder/SessionRecorderHistoryStore.ts +276 -0
  153. package/src/session/runtime/SessionEventHub.ts +25 -12
  154. package/src/session/runtime/SessionPromptRuntime.ts +60 -43
  155. package/src/session/services/SessionStateService.ts +159 -86
  156. package/src/session/services/SessionTurnService.ts +91 -73
  157. package/src/session/services/SessionViewService.ts +25 -13
  158. package/src/session/storage/Paths.ts +2 -2
  159. package/src/session/storage/Persistence.ts +13 -0
  160. package/src/session/storage/RuntimeSessionPort.ts +8 -9
  161. package/src/types/agent/SessionActor.ts +31 -8
  162. package/src/types/rpc/RpcProtocol.ts +44 -67
  163. package/src/types/runtime/agent/AgentContext.ts +9 -7
  164. package/src/types/session/SessionApproval.ts +108 -0
  165. package/src/types/session/SessionMessage.ts +253 -0
  166. package/src/types/session/SessionMutation.ts +141 -0
  167. package/src/types/session/SessionOptions.ts +25 -0
  168. package/tsconfig.tsbuildinfo +1 -1
  169. package/bin/session/SessionEventMapper.d.ts +0 -32
  170. package/bin/session/SessionEventMapper.d.ts.map +0 -1
  171. package/bin/session/SessionEventMapper.js +0 -131
  172. package/bin/session/SessionEventMapper.js.map +0 -1
  173. package/bin/types/sdk/AgentSessionEvent.d.ts +0 -302
  174. package/bin/types/sdk/AgentSessionEvent.d.ts.map +0 -1
  175. package/bin/types/sdk/AgentSessionEvent.js +0 -9
  176. package/bin/types/sdk/AgentSessionEvent.js.map +0 -1
  177. package/bin/types/sdk/InternalUiChunkEvent.d.ts +0 -100
  178. package/bin/types/sdk/InternalUiChunkEvent.d.ts.map +0 -1
  179. package/bin/types/sdk/InternalUiChunkEvent.js +0 -9
  180. package/bin/types/sdk/InternalUiChunkEvent.js.map +0 -1
  181. package/scripts/session-history-archive.test.mjs +0 -188
  182. package/src/session/SessionEventMapper.ts +0 -148
  183. package/src/types/sdk/AgentSessionEvent.ts +0 -370
  184. package/src/types/sdk/InternalUiChunkEvent.ts +0 -108
@@ -9,11 +9,7 @@
9
9
 
10
10
  import { extractTextFromParts, extractTextFromUiMessage } from "@/executor/messages/UIMessageTransformer.js";
11
11
  import type { Executor } from "@executor/Executor.js";
12
- import type { AgentSessionEvent } from "@/types/sdk/AgentSessionEvent.js";
13
- import type {
14
- AgentSessionSubscriber,
15
- AgentSessionUnsubscribe,
16
- } from "@/types/sdk/AgentSessionEvent.js";
12
+ import type { SessionMutation } from "@/types/session/SessionMutation.js";
17
13
  import { isAgentSessionPromptInputEmpty } from "@/types/sdk/AgentSessionPrompt.js";
18
14
  import type { AgentSessionPromptInput } from "@/types/sdk/AgentSessionPrompt.js";
19
15
  import type { AgentSessionStopResult } from "@/types/sdk/AgentSessionStop.js";
@@ -23,11 +19,15 @@ import type {
23
19
  SessionMessageRecordV1,
24
20
  SessionUserMessageV1,
25
21
  } from "@/executor/types/SessionRecords.js";
26
- import { mapAgentEventToSessionEvent, mapUiMessageChunkToAgentEvent } from "@/session/SessionEventMapper.js";
27
22
  import { SessionEventHub } from "@/session/runtime/SessionEventHub.js";
28
23
  import { SessionPromptRuntime } from "@/session/runtime/SessionPromptRuntime.js";
29
24
  import type { SessionRunContext } from "@/types/executor/SessionRunContext.js";
30
25
  import { SessionStateService } from "@/session/services/SessionStateService.js";
26
+ import {
27
+ SessionAssistantMessageWriter,
28
+ SessionRecorder,
29
+ } from "@/session/recorder/SessionRecorder.js";
30
+ import { from_ui_assistant_parts } from "@/session/recorder/SessionMessageCodec.js";
31
31
 
32
32
  type SessionTurnServiceOptions = {
33
33
  /**
@@ -54,6 +54,9 @@ type SessionTurnServiceOptions = {
54
54
  * 当前 session 共享事件总线。
55
55
  */
56
56
  event_hub: SessionEventHub;
57
+
58
+ /** 当前 Session Message Recorder。 */
59
+ recorder: SessionRecorder;
57
60
  };
58
61
 
59
62
  /**
@@ -65,6 +68,7 @@ export class SessionTurnService {
65
68
  private readonly executor: Executor;
66
69
  private readonly state_service: SessionStateService;
67
70
  private readonly event_hub: SessionEventHub;
71
+ private readonly recorder: SessionRecorder;
68
72
  private readonly prompt_runtime: SessionPromptRuntime;
69
73
 
70
74
  constructor(options: SessionTurnServiceOptions) {
@@ -73,23 +77,26 @@ export class SessionTurnService {
73
77
  this.executor = options.executor;
74
78
  this.state_service = options.state_service;
75
79
  this.event_hub = options.event_hub;
80
+ this.recorder = options.recorder;
76
81
  this.prompt_runtime = new SessionPromptRuntime({
77
82
  sessionId: this.session_id,
78
83
  publish: (event) => {
79
84
  this.publish_event(event);
80
85
  },
81
- createAndPersistUserMessage: async (input) => {
86
+ createAndPersistUserMessage: async (input, turn_id, input_type) => {
82
87
  return await this.state_service.create_and_persist_user_prompt_message(
83
88
  input,
89
+ turn_id,
90
+ input_type,
84
91
  );
85
92
  },
86
- emit_steer_action: async ({ turn_id, message }) => {
87
- await this.state_service.emit_action_event({
88
- id: `steer-message:${message.id}`,
89
- title: "Session steer message sent",
90
- description: "Merged a new user message into the active turn.",
91
- state: "completed",
92
- turnId: turn_id,
93
+ appendErrorMessage: async ({ turn_id, message }) => {
94
+ await this.recorder.append_error_message({
95
+ scope: "turn",
96
+ turn_id,
97
+ code: "turn_execution_failed",
98
+ message,
99
+ recoverable: true,
93
100
  });
94
101
  },
95
102
  executeTurn: async ({ turnId, promptInput, onStepMerge, abortSignal }) => {
@@ -107,15 +114,8 @@ export class SessionTurnService {
107
114
  /**
108
115
  * 发布一条 session 事件。
109
116
  */
110
- publish_event(event: AgentSessionEvent): void {
111
- this.event_hub.publish(event);
112
- }
113
-
114
- /**
115
- * 订阅当前 session 的未来事件。
116
- */
117
- subscribe(subscriber: AgentSessionSubscriber): AgentSessionUnsubscribe {
118
- return this.event_hub.subscribe(subscriber);
117
+ publish_event(mutation: SessionMutation): void {
118
+ this.event_hub.publish(mutation);
119
119
  }
120
120
 
121
121
  /**
@@ -157,64 +157,38 @@ export class SessionTurnService {
157
157
  assistantMessage?: SessionMessageRecordV1 | null;
158
158
  error?: string;
159
159
  }> {
160
- const tool_name_by_call_id = new Map<string, string>();
160
+ const assistant_writer_ref: {
161
+ current: SessionAssistantMessageWriter | null;
162
+ } = { current: null };
163
+ let assistant_segment_index = 0;
164
+ const ensure_assistant_writer = async (): Promise<SessionAssistantMessageWriter> => {
165
+ if (assistant_writer_ref.current) return assistant_writer_ref.current;
166
+ assistant_segment_index += 1;
167
+ assistant_writer_ref.current = await this.recorder.open_assistant_message({
168
+ turn_id: input.turnId,
169
+ segment_index: assistant_segment_index,
170
+ });
171
+ return assistant_writer_ref.current;
172
+ };
161
173
  const run_context: SessionRunContext = {
162
174
  turnId: input.turnId,
163
175
  sessionId: this.session_id,
164
176
  projectRoot: this.project_root,
165
- onStepCallback: input.onStepMerge,
166
- onAssistantStepCallback: async (step) => {
167
- this.publish_event({
168
- type: "assistant-step",
169
- turnId: input.turnId,
170
- text: step.text,
171
- stepIndex: step.stepIndex,
172
- ...(step.visibility ? { visibility: step.visibility } : {}),
173
- });
177
+ onStepCallback: async () => {
178
+ const merged = await input.onStepMerge();
179
+ if (merged.length > 0 && assistant_writer_ref.current) {
180
+ await assistant_writer_ref.current.complete();
181
+ assistant_writer_ref.current = null;
182
+ }
183
+ return merged;
174
184
  },
175
185
  onUiMessageChunkCallback: async (chunk) => {
176
- if (chunk.type === "tool-input-start") {
177
- tool_name_by_call_id.set(chunk.toolCallId, chunk.toolName);
178
- return;
179
- }
180
- const event = mapUiMessageChunkToAgentEvent(chunk);
181
- if (!event) return;
182
- const resolved_event =
183
- (
184
- event.type === "tool-result" ||
185
- event.type === "tool-error"
186
- ) &&
187
- event.toolName === "unknown"
188
- ? {
189
- ...event,
190
- toolName:
191
- tool_name_by_call_id.get(event.toolCallId) || event.toolName,
192
- }
193
- : event;
194
- if (
195
- resolved_event.type === "tool-call" ||
196
- resolved_event.type === "tool-error"
197
- ) {
198
- tool_name_by_call_id.set(
199
- resolved_event.toolCallId,
200
- resolved_event.toolName,
201
- );
202
- }
203
- const session_event = mapAgentEventToSessionEvent({
204
- event: resolved_event,
205
- turnId: input.turnId,
206
- });
207
- if (session_event) {
208
- this.publish_event(session_event);
209
- }
186
+ if (!is_assistant_content_chunk(chunk.type)) return;
187
+ const writer = await ensure_assistant_writer();
188
+ await writer.apply_chunk(chunk);
210
189
  },
211
190
  onActionCallback: async (event) => {
212
- try {
213
- await this.state_service.persist_action_event(event);
214
- } catch {
215
- // action 持久化失败不应阻断当前 turn。
216
- }
217
- this.publish_event(event);
191
+ await this.state_service.persist_action_event(event);
218
192
  },
219
193
  injectedUserMessages: [],
220
194
  deferredPersistedUserMessages: [],
@@ -227,6 +201,37 @@ export class SessionTurnService {
227
201
  query: executor_query,
228
202
  runContext: run_context,
229
203
  });
204
+ const final_assistant_writer = assistant_writer_ref.current;
205
+ if (final_assistant_writer) {
206
+ if (input.abortSignal?.aborted) {
207
+ await final_assistant_writer.stop();
208
+ } else if (result.success) {
209
+ await final_assistant_writer.complete();
210
+ } else {
211
+ await final_assistant_writer.fail(result.error);
212
+ }
213
+ } else if (result.assistantMessage) {
214
+ const parts = from_ui_assistant_parts(result.assistantMessage.parts);
215
+ if (parts.length > 0) {
216
+ assistant_segment_index += 1;
217
+ const fallback_writer = await this.recorder.open_assistant_message({
218
+ turn_id: input.turnId,
219
+ segment_index: assistant_segment_index,
220
+ });
221
+ for (const part of parts) await fallback_writer.upsert_part(part);
222
+ if (result.success) await fallback_writer.complete();
223
+ else await fallback_writer.fail(result.error);
224
+ }
225
+ }
226
+ if (!result.success && !input.abortSignal?.aborted && result.error) {
227
+ await this.recorder.append_error_message({
228
+ scope: "turn",
229
+ turn_id: input.turnId,
230
+ code: "turn_execution_failed",
231
+ message: result.error,
232
+ recoverable: true,
233
+ });
234
+ }
230
235
  await this.state_service.persist_assistant_result(result.assistantMessage);
231
236
  await this.state_service.persist_deferred_user_messages(
232
237
  result.deferredPersistedUserMessages,
@@ -243,3 +248,16 @@ export class SessionTurnService {
243
248
  };
244
249
  }
245
250
  }
251
+
252
+ function is_assistant_content_chunk(type: string): boolean {
253
+ return (
254
+ type === "text-start" ||
255
+ type === "text-delta" ||
256
+ type === "text-end" ||
257
+ type === "reasoning-start" ||
258
+ type === "reasoning-delta" ||
259
+ type === "reasoning-end" ||
260
+ type.startsWith("tool-") ||
261
+ type === "file"
262
+ );
263
+ }
@@ -17,7 +17,7 @@ import { getSdkAgentSessionArchiveFilePath } from "@/session/storage/Paths.js";
17
17
  import { ensureSessionTitle } from "@/session/SessionTitle.js";
18
18
  import { readSessionMetadata } from "@/session/storage/Metadata.js";
19
19
  import { buildSessionSystemBlocks } from "@/session/SessionSystemBuilder.js";
20
- import type { JsonlSessionHistoryStore } from "@/executor/store/history/jsonl/JsonlSessionHistoryStore.js";
20
+ import type { SessionHistoryStore } from "@/executor/store/history/SessionHistoryStore.js";
21
21
  import type { SessionSystemComposer } from "@/executor/composer/system/SessionSystemComposer.js";
22
22
  import type {
23
23
  AgentSessionForkInput,
@@ -32,6 +32,8 @@ import type { SessionRecordV1 } from "@/executor/types/SessionRecords.js";
32
32
  import { SessionStateService } from "@/session/services/SessionStateService.js";
33
33
  import type { SessionRunContext } from "@/types/executor/SessionRunContext.js";
34
34
  import type { Logger } from "@/utils/logger/Logger.js";
35
+ import { SessionRecorder } from "@/session/recorder/SessionRecorder.js";
36
+ import type { SessionMessage } from "@/types/session/SessionMessage.js";
35
37
 
36
38
  type SessionViewServiceOptions<TSession extends Pick<AgentSession, "set">> = {
37
39
  /**
@@ -52,7 +54,10 @@ type SessionViewServiceOptions<TSession extends Pick<AgentSession, "set">> = {
52
54
  /**
53
55
  * 当前 session 历史事实源。
54
56
  */
55
- history_store: JsonlSessionHistoryStore;
57
+ history_store: SessionHistoryStore;
58
+
59
+ /** 当前 Session Message Recorder。 */
60
+ recorder: SessionRecorder;
56
61
 
57
62
  /**
58
63
  * 当前 session 状态服务。
@@ -98,7 +103,8 @@ type SessionViewServiceOptions<TSession extends Pick<AgentSession, "set">> = {
98
103
  */
99
104
  create_fork_session: (session_id: string) => Promise<{
100
105
  session: TSession;
101
- history_store: JsonlSessionHistoryStore;
106
+ history_store: SessionHistoryStore;
107
+ recorder: SessionRecorder;
102
108
  state_service: SessionStateService;
103
109
  }>;
104
110
  };
@@ -110,7 +116,8 @@ export class SessionViewService<TSession extends Pick<AgentSession, "set">> {
110
116
  private readonly agent_id: string;
111
117
  private readonly project_root: string;
112
118
  private readonly session_id: string;
113
- private readonly history_store: JsonlSessionHistoryStore;
119
+ private readonly history_store: SessionHistoryStore;
120
+ private readonly recorder: SessionRecorder;
114
121
  private readonly state_service: SessionStateService;
115
122
  private readonly logger: Logger;
116
123
  private readonly is_executing: SessionViewServiceOptions<TSession>["is_executing"];
@@ -125,6 +132,7 @@ export class SessionViewService<TSession extends Pick<AgentSession, "set">> {
125
132
  this.project_root = options.project_root;
126
133
  this.session_id = options.session_id;
127
134
  this.history_store = options.history_store;
135
+ this.recorder = options.recorder;
128
136
  this.state_service = options.state_service;
129
137
  this.logger = options.logger;
130
138
  this.is_executing = options.is_executing;
@@ -282,11 +290,14 @@ export class SessionViewService<TSession extends Pick<AgentSession, "set">> {
282
290
  typeof input === "string"
283
291
  ? String(input || "").trim() || undefined
284
292
  : String(input?.messageId || "").trim() || undefined;
285
- const messages = await this.history_store.list_records();
293
+ const message_page = await this.recorder.list_messages({
294
+ limit: 500,
295
+ include_internal: true,
296
+ });
286
297
  const fork_messages =
287
298
  !message_id
288
- ? messages
289
- : this.resolve_fork_messages(messages, message_id);
299
+ ? message_page.items
300
+ : this.resolve_fork_messages(message_page.items, message_id);
290
301
  const action_id = `history-forking:${this.session_id}:${Date.now()}:${nanoid(8)}`;
291
302
 
292
303
  await this.state_service.emit_action_event({
@@ -333,11 +344,11 @@ export class SessionViewService<TSession extends Pick<AgentSession, "set">> {
333
344
  }
334
345
 
335
346
  private resolve_fork_messages(
336
- messages: SessionRecordV1[],
347
+ messages: SessionMessage[],
337
348
  message_id: string,
338
- ): SessionRecordV1[] {
349
+ ): SessionMessage[] {
339
350
  const target_index = messages.findIndex(
340
- (message) => String(message.id || "").trim() === message_id,
351
+ (message) => message.message_id === message_id,
341
352
  );
342
353
  if (target_index < 0) {
343
354
  throw new Error(
@@ -349,12 +360,13 @@ export class SessionViewService<TSession extends Pick<AgentSession, "set">> {
349
360
 
350
361
  private async append_fork_messages(
351
362
  forked_bundle: {
352
- history_store: JsonlSessionHistoryStore;
363
+ history_store: SessionHistoryStore;
364
+ recorder: SessionRecorder;
353
365
  state_service: SessionStateService;
354
366
  },
355
- messages: SessionRecordV1[],
367
+ messages: SessionMessage[],
356
368
  ): Promise<void> {
357
- await forked_bundle.history_store.write_records(messages);
369
+ await forked_bundle.recorder.import_messages(messages);
358
370
  }
359
371
 
360
372
  private stringify_system_content(content: unknown): string {
@@ -212,13 +212,13 @@ export function getSdkAgentSessionArchiveFilePath(
212
212
  * - step / tool 过程会持续重写这个文件,避免中途中断后过程完全丢失。
213
213
  * - 完成后再把最终 assistant 合并进 `messages.jsonl`,并清理该文件。
214
214
  */
215
- export function getSdkAgentSessionInflightPath(
215
+ export function getSdkAgentSessionAssistantMessagePath(
216
216
  projectRoot: string,
217
217
  agentId: string,
218
218
  sessionId: string,
219
219
  ): string {
220
220
  return path.join(
221
221
  getSdkAgentSessionMessagesDirPath(projectRoot, agentId, sessionId),
222
- "inflight.json",
222
+ "assistant_message.json",
223
223
  );
224
224
  }
@@ -39,6 +39,12 @@ export interface TouchSessionMetadataParams {
39
39
  * 当前 session 内存配置快照。
40
40
  */
41
41
  sessionConfig: AgentSessionConfigSnapshot;
42
+ /** 当前 canonical Message 总数。 */
43
+ message_count?: number;
44
+ /** 当前 Message 日志文件字节数。 */
45
+ history_bytes?: number;
46
+ /** 最近一条用户可见 Message 的文本摘要。 */
47
+ preview_text?: string;
42
48
  }
43
49
 
44
50
  /**
@@ -98,6 +104,13 @@ export async function touchSessionMetadata(
98
104
  modelId: params.sessionConfig.modelId,
99
105
  }
100
106
  : {}),
107
+ ...(typeof params.message_count === "number"
108
+ ? { messageCount: params.message_count }
109
+ : {}),
110
+ ...(typeof params.history_bytes === "number"
111
+ ? { historyBytes: params.history_bytes }
112
+ : {}),
113
+ ...(params.preview_text ? { previewText: params.preview_text } : {}),
101
114
  };
102
115
  await writeSessionMetadata({
103
116
  projectRoot: params.projectRoot,
@@ -11,10 +11,9 @@ import type { SessionHistoryStore } from "@/executor/store/history/SessionHistor
11
11
  import type { AgentSessionPromptInput } from "@/types/sdk/AgentSessionPrompt.js";
12
12
  import type { AgentSessionStopResult } from "@/types/sdk/AgentSessionStop.js";
13
13
  import type {
14
- AgentSessionEvent,
15
- AgentSessionSubscriber,
16
- AgentSessionUnsubscribe,
17
- } from "@/types/sdk/AgentSessionEvent.js";
14
+ SessionMutationSubscriber,
15
+ SessionMutationUnsubscribe,
16
+ } from "@/types/session/SessionMutation.js";
18
17
  import type { AgentSessionTurnHandle } from "@/types/sdk/AgentSessionTurn.js";
19
18
 
20
19
  /**
@@ -41,12 +40,12 @@ export interface CreateRuntimeSessionPortParams {
41
40
  * 订阅当前 session 的 future 事件。
42
41
  */
43
42
  subscribe: (
44
- subscriber: AgentSessionSubscriber,
45
- ) => AgentSessionUnsubscribe;
43
+ subscriber: SessionMutationSubscriber,
44
+ ) => SessionMutationUnsubscribe;
46
45
  /**
47
46
  * 发布一条 session runtime 事件。
48
47
  */
49
- publishEvent: (event: AgentSessionEvent) => void;
48
+ publishEvent: SessionPort["publishEvent"];
50
49
  /**
51
50
  * 追加 user 消息到底层历史。
52
51
  */
@@ -90,8 +89,8 @@ export function createRuntimeSessionPort(
90
89
  subscribe: (subscriber) => {
91
90
  return params.subscribe(subscriber);
92
91
  },
93
- publishEvent: (event) => {
94
- params.publishEvent(event);
92
+ publishEvent: async (event) => {
93
+ await params.publishEvent(event);
95
94
  },
96
95
  append_user_message: async (messageParams) => {
97
96
  await params.append_user_message(messageParams);
@@ -16,17 +16,26 @@ import type {
16
16
  AgentListSessionsInput,
17
17
  AgentSessionConfigSnapshot,
18
18
  AgentSessionForkInput,
19
- AgentSessionRecordsInput,
20
- AgentSessionRecordsPage,
21
19
  AgentSessionInfo,
22
20
  AgentSessionSetInput,
23
21
  AgentSessionSummaryPage,
24
22
  AgentSessionSystemSnapshot,
25
23
  } from "@/types/agent/SessionTypes.js";
26
24
  import type {
27
- AgentSessionSubscriber,
28
- AgentSessionUnsubscribe,
29
- } from "@/types/sdk/AgentSessionEvent.js";
25
+ SessionMutationSubscriber,
26
+ SessionMutationUnsubscribe,
27
+ } from "@/types/session/SessionMutation.js";
28
+ import type {
29
+ ResolveSessionApprovalInput,
30
+ SessionApproval,
31
+ SessionApprovalModeSnapshot,
32
+ SessionApprovalResult,
33
+ SetSessionApprovalModeInput,
34
+ } from "@/types/session/SessionApproval.js";
35
+ import type {
36
+ ListSessionMessagesInput,
37
+ SessionMessagePage,
38
+ } from "@/types/session/SessionMessage.js";
30
39
  import type { AgentSessionPromptInput } from "@/types/sdk/AgentSessionPrompt.js";
31
40
  import type { AgentSessionStopResult } from "@/types/sdk/AgentSessionStop.js";
32
41
  import type { AgentSessionTurnHandle } from "@/types/sdk/AgentSessionTurn.js";
@@ -71,13 +80,27 @@ export interface AgentSessionActor {
71
80
  stop(): Promise<AgentSessionStopResult>;
72
81
 
73
82
  /** 订阅当前 session 的未来事件。 */
74
- subscribe(subscriber: AgentSessionSubscriber): AgentSessionUnsubscribe;
83
+ subscribe(
84
+ subscriber: SessionMutationSubscriber,
85
+ ): SessionMutationUnsubscribe;
75
86
 
76
- /** 读取当前 session records 分页。 */
77
- records(input?: AgentSessionRecordsInput): Promise<AgentSessionRecordsPage>;
87
+ /** 读取当前 session messages 分页。 */
88
+ messages(input?: ListSessionMessagesInput): Promise<SessionMessagePage>;
78
89
 
79
90
  /** 读取当前 session 生效的 system 快照。 */
80
91
  system(): Promise<AgentSessionSystemSnapshot>;
92
+
93
+ /** 列出当前 Session 的 pending 工具审批。 */
94
+ approvals(): Promise<SessionApproval[]>;
95
+
96
+ /** 读取当前 Session 的工具审批模式。 */
97
+ approval_mode(): Promise<SessionApprovalModeSnapshot>;
98
+
99
+ /** 更新当前 Session 的工具审批模式。 */
100
+ set_approval_mode(input: SetSessionApprovalModeInput): Promise<SessionApprovalModeSnapshot>;
101
+
102
+ /** 处理当前 Session 的 pending 工具审批。 */
103
+ resolve_approval(input: ResolveSessionApprovalInput): Promise<SessionApprovalResult>;
81
104
  }
82
105
 
83
106
  /**
@@ -15,16 +15,19 @@ import type {
15
15
  AgentArchiveSessionResult,
16
16
  AgentArchiveSessionsResult,
17
17
  AgentCleanArchiveResult,
18
- AgentSessionRecordsInput,
19
18
  AgentSessionSystemSnapshot,
20
19
  AgentSessionSetInput,
21
20
  } from "@/types/agent/SessionTypes.js";
22
- import type { AgentSessionEvent } from "@/types/sdk/AgentSessionEvent.js";
21
+ import type { SessionMutation } from "@/types/session/SessionMutation.js";
22
+ import type {
23
+ ResolveSessionApprovalInput,
24
+ SetSessionApprovalModeInput,
25
+ } from "@/types/session/SessionApproval.js";
23
26
  import type { AgentSessionPromptInput } from "@/types/sdk/AgentSessionPrompt.js";
24
27
  import type { AgentSessionStopResult } from "@/types/sdk/AgentSessionStop.js";
28
+ import type { ListSessionMessagesInput } from "@/types/session/SessionMessage.js";
25
29
  import type { JsonObject, JsonValue } from "@/types/common/Json.js";
26
30
  import type { PluginControlAction } from "@/types/plugin/PluginState.js";
27
- import type { ShellApprovalMode } from "@downcity/shell";
28
31
 
29
32
  /**
30
33
  * RPC 请求。
@@ -126,16 +129,48 @@ export type RpcRequest =
126
129
  | {
127
130
  /** 请求 id,用于匹配响应。 */
128
131
  id: string;
129
- /** 读取 session records。 */
130
- method: "sdk.sessions.records";
131
- /** records 查询参数。 */
132
+ /** 读取 session messages。 */
133
+ method: "sdk.sessions.messages";
134
+ /** messages 查询参数。 */
132
135
  params: {
133
136
  /** 目标 session id。 */
134
137
  sessionId: string;
135
- /** records 分页与视图参数。 */
136
- input?: AgentSessionRecordsInput;
138
+ /** messages 分页参数。 */
139
+ input?: ListSessionMessagesInput;
137
140
  };
138
141
  }
142
+ | {
143
+ /** 请求 id,用于匹配响应。 */
144
+ id: string;
145
+ /** 列出指定 Session 的 pending 工具审批。 */
146
+ method: "sdk.sessions.approvals";
147
+ /** 目标 Session 参数。 */
148
+ params: { sessionId: string };
149
+ }
150
+ | {
151
+ /** 请求 id,用于匹配响应。 */
152
+ id: string;
153
+ /** 读取指定 Session 的工具审批模式。 */
154
+ method: "sdk.sessions.approvalMode";
155
+ /** 目标 Session 参数。 */
156
+ params: { sessionId: string };
157
+ }
158
+ | {
159
+ /** 请求 id,用于匹配响应。 */
160
+ id: string;
161
+ /** 更新指定 Session 的工具审批模式。 */
162
+ method: "sdk.sessions.setApprovalMode";
163
+ /** Session 与审批模式参数。 */
164
+ params: { sessionId: string; input: SetSessionApprovalModeInput };
165
+ }
166
+ | {
167
+ /** 请求 id,用于匹配响应。 */
168
+ id: string;
169
+ /** 处理指定 Session 的 pending 工具审批。 */
170
+ method: "sdk.sessions.resolveApproval";
171
+ /** Session 与审批决策参数。 */
172
+ params: { sessionId: string; input: ResolveSessionApprovalInput };
173
+ }
139
174
  | {
140
175
  /** 请求 id,用于匹配响应。 */
141
176
  id: string;
@@ -288,64 +323,6 @@ export type RpcRequest =
288
323
  /** action payload。 */
289
324
  payload?: JsonValue;
290
325
  };
291
- }
292
- | {
293
- /** 请求 id,用于匹配响应。 */
294
- id: string;
295
- /** 列出 shell approvals。 */
296
- method: "internal.shell.approvals";
297
- }
298
- | {
299
- /** 请求 id,用于匹配响应。 */
300
- id: string;
301
- /** 列出 shell approval 模式。 */
302
- method: "internal.shell.approvalModes";
303
- }
304
- | {
305
- /** 请求 id,用于匹配响应。 */
306
- id: string;
307
- /** 读取 shell approval 模式。 */
308
- method: "internal.shell.approvalMode";
309
- /** session 参数。 */
310
- params: {
311
- /** session id。 */
312
- sessionId: string;
313
- };
314
- }
315
- | {
316
- /** 请求 id,用于匹配响应。 */
317
- id: string;
318
- /** 设置 shell approval 模式。 */
319
- method: "internal.shell.setApprovalMode";
320
- /** session 模式参数。 */
321
- params: {
322
- /** session id。 */
323
- sessionId: string;
324
- /** approval 模式。 */
325
- mode: ShellApprovalMode;
326
- };
327
- }
328
- | {
329
- /** 请求 id,用于匹配响应。 */
330
- id: string;
331
- /** 批准 shell approval。 */
332
- method: "internal.shell.approve";
333
- /** approval 参数。 */
334
- params: {
335
- /** approval id。 */
336
- approvalId: string;
337
- };
338
- }
339
- | {
340
- /** 请求 id,用于匹配响应。 */
341
- id: string;
342
- /** 拒绝 shell approval。 */
343
- method: "internal.shell.deny";
344
- /** approval 参数。 */
345
- params: {
346
- /** approval id。 */
347
- approvalId: string;
348
- };
349
326
  };
350
327
 
351
328
  /**
@@ -396,7 +373,7 @@ export interface RpcEventFrame {
396
373
  /** 当前订阅 id。 */
397
374
  subscriptionId: string;
398
375
  /** session 事件。 */
399
- event: AgentSessionEvent;
376
+ event: SessionMutation;
400
377
  }
401
378
 
402
379
  /**