@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.
- package/bin/agent/local/Agent.d.ts +1 -34
- package/bin/agent/local/Agent.d.ts.map +1 -1
- package/bin/agent/local/Agent.js +1 -44
- package/bin/agent/local/Agent.js.map +1 -1
- package/bin/agent/local/services/AgentAssemblyService.d.ts.map +1 -1
- package/bin/agent/local/services/AgentAssemblyService.js +35 -2
- package/bin/agent/local/services/AgentAssemblyService.js.map +1 -1
- package/bin/agent/local/services/AgentSessions.d.ts +6 -0
- package/bin/agent/local/services/AgentSessions.d.ts.map +1 -1
- package/bin/agent/local/services/AgentSessions.js +43 -4
- package/bin/agent/local/services/AgentSessions.js.map +1 -1
- package/bin/agent/remote/RemoteAgent.d.ts +1 -35
- package/bin/agent/remote/RemoteAgent.d.ts.map +1 -1
- package/bin/agent/remote/RemoteAgent.js +1 -37
- package/bin/agent/remote/RemoteAgent.js.map +1 -1
- package/bin/agent/remote/RemoteSession.d.ts +26 -41
- package/bin/agent/remote/RemoteSession.d.ts.map +1 -1
- package/bin/agent/remote/RemoteSession.js +75 -107
- package/bin/agent/remote/RemoteSession.js.map +1 -1
- package/bin/agent/remote/RemoteTransport.d.ts +15 -27
- package/bin/agent/remote/RemoteTransport.d.ts.map +1 -1
- package/bin/agent/remote/transports/HttpRemoteAgentTransport.d.ts +10 -21
- package/bin/agent/remote/transports/HttpRemoteAgentTransport.d.ts.map +1 -1
- package/bin/agent/remote/transports/HttpRemoteAgentTransport.js +24 -46
- package/bin/agent/remote/transports/HttpRemoteAgentTransport.js.map +1 -1
- package/bin/agent/remote/transports/RpcRemoteAgentTransport.d.ts +10 -20
- package/bin/agent/remote/transports/RpcRemoteAgentTransport.d.ts.map +1 -1
- package/bin/agent/remote/transports/RpcRemoteAgentTransport.js +10 -16
- package/bin/agent/remote/transports/RpcRemoteAgentTransport.js.map +1 -1
- package/bin/executor/store/history/jsonl/JsonlSessionHistoryStore.js +2 -2
- package/bin/executor/store/history/jsonl/JsonlSessionHistoryStore.js.map +1 -1
- package/bin/executor/types/SessionHistoryPaths.d.ts +1 -1
- package/bin/executor/types/SessionRecords.d.ts +2 -0
- package/bin/executor/types/SessionRecords.d.ts.map +1 -1
- package/bin/executor/types/SessionRecords.js.map +1 -1
- package/bin/index.d.ts +6 -3
- package/bin/index.d.ts.map +1 -1
- package/bin/index.js +1 -0
- package/bin/index.js.map +1 -1
- package/bin/rpc/Client.d.ts +12 -34
- package/bin/rpc/Client.d.ts.map +1 -1
- package/bin/rpc/Client.js +31 -70
- package/bin/rpc/Client.js.map +1 -1
- package/bin/session/Session.d.ts +22 -4
- package/bin/session/Session.d.ts.map +1 -1
- package/bin/session/Session.js +95 -21
- package/bin/session/Session.js.map +1 -1
- package/bin/session/browse/Browse.d.ts.map +1 -1
- package/bin/session/browse/Browse.js +49 -18
- package/bin/session/browse/Browse.js.map +1 -1
- package/bin/session/recorder/JsonlSessionMessageStore.d.ts +51 -0
- package/bin/session/recorder/JsonlSessionMessageStore.d.ts.map +1 -0
- package/bin/session/recorder/JsonlSessionMessageStore.js +231 -0
- package/bin/session/recorder/JsonlSessionMessageStore.js.map +1 -0
- package/bin/session/recorder/SessionMessageCodec.d.ts +20 -0
- package/bin/session/recorder/SessionMessageCodec.d.ts.map +1 -0
- package/bin/session/recorder/SessionMessageCodec.js +257 -0
- package/bin/session/recorder/SessionMessageCodec.js.map +1 -0
- package/bin/session/recorder/SessionRecorder.d.ts +186 -0
- package/bin/session/recorder/SessionRecorder.d.ts.map +1 -0
- package/bin/session/recorder/SessionRecorder.js +655 -0
- package/bin/session/recorder/SessionRecorder.js.map +1 -0
- package/bin/session/recorder/SessionRecorderCompaction.d.ts +18 -0
- package/bin/session/recorder/SessionRecorderCompaction.d.ts.map +1 -0
- package/bin/session/recorder/SessionRecorderCompaction.js +142 -0
- package/bin/session/recorder/SessionRecorderCompaction.js.map +1 -0
- package/bin/session/recorder/SessionRecorderHistoryStore.d.ts +66 -0
- package/bin/session/recorder/SessionRecorderHistoryStore.d.ts.map +1 -0
- package/bin/session/recorder/SessionRecorderHistoryStore.js +195 -0
- package/bin/session/recorder/SessionRecorderHistoryStore.js.map +1 -0
- package/bin/session/runtime/SessionEventHub.d.ts +12 -5
- package/bin/session/runtime/SessionEventHub.d.ts.map +1 -1
- package/bin/session/runtime/SessionEventHub.js +11 -6
- package/bin/session/runtime/SessionEventHub.js.map +1 -1
- package/bin/session/runtime/SessionPromptRuntime.d.ts +9 -15
- package/bin/session/runtime/SessionPromptRuntime.d.ts.map +1 -1
- package/bin/session/runtime/SessionPromptRuntime.js +48 -30
- package/bin/session/runtime/SessionPromptRuntime.js.map +1 -1
- package/bin/session/services/SessionStateService.d.ts +9 -5
- package/bin/session/services/SessionStateService.d.ts.map +1 -1
- package/bin/session/services/SessionStateService.js +138 -74
- package/bin/session/services/SessionStateService.js.map +1 -1
- package/bin/session/services/SessionTurnService.d.ts +6 -7
- package/bin/session/services/SessionTurnService.d.ts.map +1 -1
- package/bin/session/services/SessionTurnService.js +84 -60
- package/bin/session/services/SessionTurnService.js.map +1 -1
- package/bin/session/services/SessionViewService.d.ts +8 -3
- package/bin/session/services/SessionViewService.d.ts.map +1 -1
- package/bin/session/services/SessionViewService.js +10 -5
- package/bin/session/services/SessionViewService.js.map +1 -1
- package/bin/session/storage/Paths.d.ts +1 -1
- package/bin/session/storage/Paths.d.ts.map +1 -1
- package/bin/session/storage/Paths.js +2 -2
- package/bin/session/storage/Paths.js.map +1 -1
- package/bin/session/storage/Persistence.d.ts +6 -0
- package/bin/session/storage/Persistence.d.ts.map +1 -1
- package/bin/session/storage/Persistence.js +7 -0
- package/bin/session/storage/Persistence.js.map +1 -1
- package/bin/session/storage/RuntimeSessionPort.d.ts +3 -3
- package/bin/session/storage/RuntimeSessionPort.d.ts.map +1 -1
- package/bin/session/storage/RuntimeSessionPort.js +2 -2
- package/bin/session/storage/RuntimeSessionPort.js.map +1 -1
- package/bin/types/agent/SessionActor.d.ts +15 -5
- package/bin/types/agent/SessionActor.d.ts.map +1 -1
- package/bin/types/rpc/RpcProtocol.d.ts +48 -61
- package/bin/types/rpc/RpcProtocol.d.ts.map +1 -1
- package/bin/types/runtime/agent/AgentContext.d.ts +5 -4
- package/bin/types/runtime/agent/AgentContext.d.ts.map +1 -1
- package/bin/types/runtime/agent/AgentContext.js.map +1 -1
- package/bin/types/session/SessionApproval.d.ts +97 -0
- package/bin/types/session/SessionApproval.d.ts.map +1 -0
- package/bin/types/session/SessionApproval.js +7 -0
- package/bin/types/session/SessionApproval.js.map +1 -0
- package/bin/types/session/SessionMessage.d.ts +223 -0
- package/bin/types/session/SessionMessage.d.ts.map +1 -0
- package/bin/types/session/SessionMessage.js +8 -0
- package/bin/types/session/SessionMessage.js.map +1 -0
- package/bin/types/session/SessionMutation.d.ts +116 -0
- package/bin/types/session/SessionMutation.d.ts.map +1 -0
- package/bin/types/session/SessionMutation.js +19 -0
- package/bin/types/session/SessionMutation.js.map +1 -0
- package/bin/types/session/SessionOptions.d.ts +9 -0
- package/bin/types/session/SessionOptions.d.ts.map +1 -1
- package/package.json +3 -3
- package/scripts/agent-env.test.mjs +4 -4
- package/scripts/remote-session-reconnect.test.mjs +7 -3
- package/scripts/session-action-message.test.mjs +98 -27
- package/scripts/session-history-inflight.test.mjs +2 -2
- package/scripts/session-list-title.test.mjs +6 -19
- package/scripts/session-prompt-runtime.test.mjs +7 -19
- package/scripts/session-recorder.test.mjs +199 -0
- package/scripts/session-title-event.test.mjs +10 -18
- package/src/agent/local/Agent.ts +1 -55
- package/src/agent/local/services/AgentAssemblyService.ts +39 -3
- package/src/agent/local/services/AgentSessions.ts +48 -3
- package/src/agent/remote/RemoteAgent.ts +1 -54
- package/src/agent/remote/RemoteSession.ts +106 -136
- package/src/agent/remote/RemoteTransport.ts +30 -30
- package/src/agent/remote/transports/HttpRemoteAgentTransport.ts +52 -77
- package/src/agent/remote/transports/RpcRemoteAgentTransport.ts +30 -34
- package/src/executor/store/history/jsonl/JsonlSessionHistoryStore.ts +2 -2
- package/src/executor/types/SessionHistoryPaths.ts +1 -1
- package/src/executor/types/SessionRecords.ts +2 -0
- package/src/index.ts +36 -10
- package/src/rpc/Client.ts +58 -95
- package/src/session/Session.ts +128 -37
- package/src/session/browse/Browse.ts +51 -22
- package/src/session/recorder/JsonlSessionMessageStore.ts +246 -0
- package/src/session/recorder/SessionMessageCodec.ts +284 -0
- package/src/session/recorder/SessionRecorder.ts +871 -0
- package/src/session/recorder/SessionRecorderCompaction.ts +188 -0
- package/src/session/recorder/SessionRecorderHistoryStore.ts +276 -0
- package/src/session/runtime/SessionEventHub.ts +25 -12
- package/src/session/runtime/SessionPromptRuntime.ts +60 -43
- package/src/session/services/SessionStateService.ts +159 -86
- package/src/session/services/SessionTurnService.ts +91 -73
- package/src/session/services/SessionViewService.ts +25 -13
- package/src/session/storage/Paths.ts +2 -2
- package/src/session/storage/Persistence.ts +13 -0
- package/src/session/storage/RuntimeSessionPort.ts +8 -9
- package/src/types/agent/SessionActor.ts +31 -8
- package/src/types/rpc/RpcProtocol.ts +44 -67
- package/src/types/runtime/agent/AgentContext.ts +9 -7
- package/src/types/session/SessionApproval.ts +108 -0
- package/src/types/session/SessionMessage.ts +253 -0
- package/src/types/session/SessionMutation.ts +141 -0
- package/src/types/session/SessionOptions.ts +25 -0
- package/tsconfig.tsbuildinfo +1 -1
- package/bin/session/SessionEventMapper.d.ts +0 -32
- package/bin/session/SessionEventMapper.d.ts.map +0 -1
- package/bin/session/SessionEventMapper.js +0 -131
- package/bin/session/SessionEventMapper.js.map +0 -1
- package/bin/types/sdk/AgentSessionEvent.d.ts +0 -302
- package/bin/types/sdk/AgentSessionEvent.d.ts.map +0 -1
- package/bin/types/sdk/AgentSessionEvent.js +0 -9
- package/bin/types/sdk/AgentSessionEvent.js.map +0 -1
- package/bin/types/sdk/InternalUiChunkEvent.d.ts +0 -100
- package/bin/types/sdk/InternalUiChunkEvent.d.ts.map +0 -1
- package/bin/types/sdk/InternalUiChunkEvent.js +0 -9
- package/bin/types/sdk/InternalUiChunkEvent.js.map +0 -1
- package/scripts/session-history-archive.test.mjs +0 -188
- package/src/session/SessionEventMapper.ts +0 -148
- package/src/types/sdk/AgentSessionEvent.ts +0 -370
- 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 {
|
|
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
|
-
|
|
87
|
-
await this.
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
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(
|
|
111
|
-
this.event_hub.publish(
|
|
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
|
|
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:
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
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
|
|
177
|
-
|
|
178
|
-
|
|
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
|
-
|
|
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 {
|
|
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:
|
|
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:
|
|
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:
|
|
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
|
|
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
|
-
?
|
|
289
|
-
: this.resolve_fork_messages(
|
|
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:
|
|
347
|
+
messages: SessionMessage[],
|
|
337
348
|
message_id: string,
|
|
338
|
-
):
|
|
349
|
+
): SessionMessage[] {
|
|
339
350
|
const target_index = messages.findIndex(
|
|
340
|
-
(message) =>
|
|
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:
|
|
363
|
+
history_store: SessionHistoryStore;
|
|
364
|
+
recorder: SessionRecorder;
|
|
353
365
|
state_service: SessionStateService;
|
|
354
366
|
},
|
|
355
|
-
messages:
|
|
367
|
+
messages: SessionMessage[],
|
|
356
368
|
): Promise<void> {
|
|
357
|
-
await forked_bundle.
|
|
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
|
|
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
|
-
"
|
|
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
|
-
|
|
15
|
-
|
|
16
|
-
|
|
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:
|
|
45
|
-
) =>
|
|
43
|
+
subscriber: SessionMutationSubscriber,
|
|
44
|
+
) => SessionMutationUnsubscribe;
|
|
46
45
|
/**
|
|
47
46
|
* 发布一条 session runtime 事件。
|
|
48
47
|
*/
|
|
49
|
-
publishEvent:
|
|
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
|
-
|
|
28
|
-
|
|
29
|
-
} from "@/types/
|
|
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(
|
|
83
|
+
subscribe(
|
|
84
|
+
subscriber: SessionMutationSubscriber,
|
|
85
|
+
): SessionMutationUnsubscribe;
|
|
75
86
|
|
|
76
|
-
/** 读取当前 session
|
|
77
|
-
|
|
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 {
|
|
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
|
|
130
|
-
method: "sdk.sessions.
|
|
131
|
-
/**
|
|
132
|
+
/** 读取 session messages。 */
|
|
133
|
+
method: "sdk.sessions.messages";
|
|
134
|
+
/** messages 查询参数。 */
|
|
132
135
|
params: {
|
|
133
136
|
/** 目标 session id。 */
|
|
134
137
|
sessionId: string;
|
|
135
|
-
/**
|
|
136
|
-
input?:
|
|
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:
|
|
376
|
+
event: SessionMutation;
|
|
400
377
|
}
|
|
401
378
|
|
|
402
379
|
/**
|