@downcity/agent 1.1.216 → 1.1.222
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 +2 -0
- package/bin/agent/local/Agent.d.ts.map +1 -1
- package/bin/agent/local/Agent.js +6 -0
- package/bin/agent/local/Agent.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 +8 -1
- package/bin/agent/local/services/AgentSessions.js.map +1 -1
- package/bin/agent/remote/RemoteSession.d.ts +11 -7
- package/bin/agent/remote/RemoteSession.d.ts.map +1 -1
- package/bin/agent/remote/RemoteSession.js +36 -11
- package/bin/agent/remote/RemoteSession.js.map +1 -1
- package/bin/agent/remote/RemoteTransport.d.ts +9 -3
- package/bin/agent/remote/RemoteTransport.d.ts.map +1 -1
- package/bin/agent/remote/transports/HttpRemoteAgentTransport.d.ts +6 -2
- package/bin/agent/remote/transports/HttpRemoteAgentTransport.d.ts.map +1 -1
- package/bin/agent/remote/transports/HttpRemoteAgentTransport.js +38 -11
- package/bin/agent/remote/transports/HttpRemoteAgentTransport.js.map +1 -1
- package/bin/agent/remote/transports/RpcRemoteAgentTransport.d.ts +6 -2
- package/bin/agent/remote/transports/RpcRemoteAgentTransport.d.ts.map +1 -1
- package/bin/agent/remote/transports/RpcRemoteAgentTransport.js +8 -2
- package/bin/agent/remote/transports/RpcRemoteAgentTransport.js.map +1 -1
- package/bin/executor/types/SessionHistoryMeta.d.ts +2 -0
- package/bin/executor/types/SessionHistoryMeta.d.ts.map +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 +5 -2
- 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 +18 -4
- package/bin/rpc/Client.d.ts.map +1 -1
- package/bin/rpc/Client.js +25 -3
- package/bin/rpc/Client.js.map +1 -1
- package/bin/session/Session.d.ts +13 -3
- package/bin/session/Session.d.ts.map +1 -1
- package/bin/session/Session.js +48 -18
- package/bin/session/Session.js.map +1 -1
- package/bin/session/browse/Browse.d.ts.map +1 -1
- package/bin/session/browse/Browse.js +3 -0
- package/bin/session/browse/Browse.js.map +1 -1
- package/bin/session/recorder/JsonlSessionMessageStore.d.ts +45 -0
- package/bin/session/recorder/JsonlSessionMessageStore.d.ts.map +1 -0
- package/bin/session/recorder/JsonlSessionMessageStore.js +127 -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 +252 -0
- package/bin/session/recorder/SessionMessageCodec.js.map +1 -0
- package/bin/session/recorder/SessionMessageReducer.d.ts +12 -0
- package/bin/session/recorder/SessionMessageReducer.d.ts.map +1 -0
- package/bin/session/recorder/SessionMessageReducer.js +109 -0
- package/bin/session/recorder/SessionMessageReducer.js.map +1 -0
- package/bin/session/recorder/SessionRecorder.d.ts +177 -0
- package/bin/session/recorder/SessionRecorder.d.ts.map +1 -0
- package/bin/session/recorder/SessionRecorder.js +610 -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 +141 -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/SessionMessageEventHub.d.ts +15 -0
- package/bin/session/runtime/SessionMessageEventHub.d.ts.map +1 -0
- package/bin/session/runtime/SessionMessageEventHub.js +28 -0
- package/bin/session/runtime/SessionMessageEventHub.js.map +1 -0
- package/bin/session/runtime/SessionPromptRuntime.d.ts +7 -13
- package/bin/session/runtime/SessionPromptRuntime.d.ts.map +1 -1
- package/bin/session/runtime/SessionPromptRuntime.js +13 -13
- package/bin/session/runtime/SessionPromptRuntime.js.map +1 -1
- package/bin/session/services/SessionStateService.d.ts +15 -3
- package/bin/session/services/SessionStateService.d.ts.map +1 -1
- package/bin/session/services/SessionStateService.js +165 -76
- package/bin/session/services/SessionStateService.js.map +1 -1
- package/bin/session/services/SessionTurnService.d.ts +4 -0
- package/bin/session/services/SessionTurnService.d.ts.map +1 -1
- package/bin/session/services/SessionTurnService.js +82 -52
- 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 +13 -5
- package/bin/session/services/SessionViewService.js.map +1 -1
- package/bin/session/storage/Metadata.d.ts +2 -0
- package/bin/session/storage/Metadata.d.ts.map +1 -1
- package/bin/session/storage/Metadata.js +10 -0
- package/bin/session/storage/Metadata.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 +12 -0
- package/bin/session/storage/Persistence.js.map +1 -1
- package/bin/session/storage/RuntimeSessionPort.d.ts +3 -2
- package/bin/session/storage/RuntimeSessionPort.d.ts.map +1 -1
- package/bin/session/storage/RuntimeSessionPort.js.map +1 -1
- package/bin/types/agent/AgentOptions.d.ts +10 -0
- package/bin/types/agent/AgentOptions.d.ts.map +1 -1
- package/bin/types/agent/SessionActor.d.ts +12 -4
- package/bin/types/agent/SessionActor.d.ts.map +1 -1
- package/bin/types/agent/SessionTypes.d.ts +13 -1
- package/bin/types/agent/SessionTypes.d.ts.map +1 -1
- package/bin/types/rpc/RpcProtocol.d.ts +32 -6
- package/bin/types/rpc/RpcProtocol.d.ts.map +1 -1
- package/bin/types/runtime/agent/AgentContext.d.ts +4 -3
- package/bin/types/runtime/agent/AgentContext.d.ts.map +1 -1
- package/bin/types/runtime/agent/AgentContext.js.map +1 -1
- package/bin/types/sdk/AgentSessionEvent.d.ts +2 -1
- package/bin/types/sdk/AgentSessionEvent.d.ts.map +1 -1
- package/bin/types/session/SessionMessage.d.ts +217 -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/SessionMessageMutation.d.ts +92 -0
- package/bin/types/session/SessionMessageMutation.d.ts.map +1 -0
- package/bin/types/session/SessionMessageMutation.js +21 -0
- package/bin/types/session/SessionMessageMutation.js.map +1 -0
- package/bin/types/session/SessionOptions.d.ts +3 -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 +33 -0
- package/scripts/session-action-message.test.mjs +148 -27
- package/scripts/session-list-title.test.mjs +6 -19
- package/scripts/session-prompt-runtime.test.mjs +4 -16
- package/scripts/session-recorder.test.mjs +245 -0
- package/scripts/session-title-event.test.mjs +10 -18
- package/src/agent/local/Agent.ts +6 -0
- package/src/agent/local/services/AgentSessions.ts +14 -1
- package/src/agent/remote/RemoteSession.ts +52 -16
- package/src/agent/remote/RemoteTransport.ts +20 -6
- package/src/agent/remote/transports/HttpRemoteAgentTransport.ts +67 -13
- package/src/agent/remote/transports/RpcRemoteAgentTransport.ts +27 -6
- package/src/executor/types/SessionHistoryMeta.ts +2 -0
- package/src/executor/types/SessionRecords.ts +2 -0
- package/src/index.ts +23 -5
- package/src/rpc/Client.ts +47 -8
- package/src/session/Session.ts +66 -31
- package/src/session/browse/Browse.ts +3 -0
- package/src/session/recorder/JsonlSessionMessageStore.ts +162 -0
- package/src/session/recorder/SessionMessageCodec.ts +279 -0
- package/src/session/recorder/SessionMessageReducer.ts +155 -0
- package/src/session/recorder/SessionRecorder.ts +812 -0
- package/src/session/recorder/SessionRecorderCompaction.ts +187 -0
- package/src/session/recorder/SessionRecorderHistoryStore.ts +276 -0
- package/src/session/runtime/SessionMessageEventHub.ts +35 -0
- package/src/session/runtime/SessionPromptRuntime.ts +23 -24
- package/src/session/services/SessionStateService.ts +189 -84
- package/src/session/services/SessionTurnService.ts +88 -59
- package/src/session/services/SessionViewService.ts +28 -13
- package/src/session/storage/Metadata.ts +13 -0
- package/src/session/storage/Persistence.ts +18 -0
- package/src/session/storage/RuntimeSessionPort.ts +6 -4
- package/src/types/agent/AgentOptions.ts +12 -0
- package/src/types/agent/SessionActor.ts +28 -5
- package/src/types/agent/SessionTypes.ts +13 -1
- package/src/types/rpc/RpcProtocol.ts +34 -6
- package/src/types/runtime/agent/AgentContext.ts +8 -2
- package/src/types/sdk/AgentSessionEvent.ts +2 -0
- package/src/types/session/SessionMessage.ts +247 -0
- package/src/types/session/SessionMessageMutation.ts +121 -0
- package/src/types/session/SessionOptions.ts +4 -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/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/InternalUiChunkEvent.ts +0 -108
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file 验证 SessionRecorder 单一 Mutation 日志与实时/历史一致性。
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import test from "node:test";
|
|
6
|
+
import assert from "node:assert/strict";
|
|
7
|
+
import fs from "node:fs/promises";
|
|
8
|
+
import os from "node:os";
|
|
9
|
+
import path from "node:path";
|
|
10
|
+
|
|
11
|
+
import { JsonlSessionMessageStore } from "../bin/session/recorder/JsonlSessionMessageStore.js";
|
|
12
|
+
import { SessionRecorder } from "../bin/session/recorder/SessionRecorder.js";
|
|
13
|
+
import { SessionRecorderHistoryStore } from "../bin/session/recorder/SessionRecorderHistoryStore.js";
|
|
14
|
+
import { MockLanguageModelV3 } from "ai/test";
|
|
15
|
+
|
|
16
|
+
async function create_recorder(session_id = "session-recorder-test") {
|
|
17
|
+
const root_path = await fs.mkdtemp(
|
|
18
|
+
path.join(os.tmpdir(), "downcity-session-recorder-"),
|
|
19
|
+
);
|
|
20
|
+
const file_path = path.join(root_path, "messages.jsonl");
|
|
21
|
+
const events = [];
|
|
22
|
+
const recorder = new SessionRecorder({
|
|
23
|
+
session_id,
|
|
24
|
+
store: new JsonlSessionMessageStore({ session_id, file_path }),
|
|
25
|
+
publish: (mutation) => events.push(mutation),
|
|
26
|
+
});
|
|
27
|
+
await recorder.initialize();
|
|
28
|
+
return { recorder, events, file_path };
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
test("每个 assistant delta 都先持久化为独立 Mutation 再发布", async () => {
|
|
32
|
+
const { recorder, events, file_path } = await create_recorder();
|
|
33
|
+
await recorder.append_user_message({
|
|
34
|
+
turn_id: "turn-1",
|
|
35
|
+
input_type: "prompt",
|
|
36
|
+
parts: [{
|
|
37
|
+
part_id: "user-text-1",
|
|
38
|
+
type: "text",
|
|
39
|
+
text: "你好",
|
|
40
|
+
state: "done",
|
|
41
|
+
}],
|
|
42
|
+
});
|
|
43
|
+
const writer = await recorder.open_assistant_message({
|
|
44
|
+
turn_id: "turn-1",
|
|
45
|
+
segment_index: 1,
|
|
46
|
+
});
|
|
47
|
+
await writer.apply_chunk({ type: "text-start", id: "text-1" });
|
|
48
|
+
await writer.apply_chunk({ type: "text-delta", id: "text-1", delta: "你" });
|
|
49
|
+
await writer.apply_chunk({ type: "text-delta", id: "text-1", delta: "好" });
|
|
50
|
+
await writer.apply_chunk({ type: "text-end", id: "text-1" });
|
|
51
|
+
await writer.complete();
|
|
52
|
+
|
|
53
|
+
const changes = await recorder.list_message_changes({
|
|
54
|
+
after_commit_sequence: 0,
|
|
55
|
+
});
|
|
56
|
+
const persisted_lines = (await fs.readFile(file_path, "utf8"))
|
|
57
|
+
.trim()
|
|
58
|
+
.split("\n");
|
|
59
|
+
assert.equal(persisted_lines.length, changes.items.length);
|
|
60
|
+
assert.deepEqual(
|
|
61
|
+
changes.items.map((item) => item.commit_sequence),
|
|
62
|
+
events.map((item) => item.commit_sequence),
|
|
63
|
+
);
|
|
64
|
+
assert.deepEqual(
|
|
65
|
+
changes.items.map((item) => `${item.variant}:${item.type}`),
|
|
66
|
+
[
|
|
67
|
+
"message:user",
|
|
68
|
+
"message:assistant",
|
|
69
|
+
"part:text",
|
|
70
|
+
"delta:text",
|
|
71
|
+
"delta:text",
|
|
72
|
+
"part:text",
|
|
73
|
+
"message:assistant",
|
|
74
|
+
],
|
|
75
|
+
);
|
|
76
|
+
assert.deepEqual(
|
|
77
|
+
changes.items
|
|
78
|
+
.filter((item) => item.variant === "delta")
|
|
79
|
+
.map((item) => item.delta),
|
|
80
|
+
["你", "好"],
|
|
81
|
+
);
|
|
82
|
+
|
|
83
|
+
const page = await recorder.list_messages();
|
|
84
|
+
assert.deepEqual(page.items.map((item) => item.type), ["user", "assistant"]);
|
|
85
|
+
const assistant = page.items[1];
|
|
86
|
+
assert.equal(assistant.status, "completed");
|
|
87
|
+
assert.equal(assistant.parts[0].text, "你好");
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
test("tool 与 action 在原 Message 和 part 上更新", async () => {
|
|
91
|
+
const { recorder } = await create_recorder("tool-action-test");
|
|
92
|
+
const writer = await recorder.open_assistant_message({
|
|
93
|
+
turn_id: "turn-1",
|
|
94
|
+
segment_index: 1,
|
|
95
|
+
});
|
|
96
|
+
await writer.apply_chunk({
|
|
97
|
+
type: "tool-input-start",
|
|
98
|
+
toolCallId: "call-1",
|
|
99
|
+
toolName: "search",
|
|
100
|
+
});
|
|
101
|
+
await writer.apply_chunk({
|
|
102
|
+
type: "tool-input-available",
|
|
103
|
+
toolCallId: "call-1",
|
|
104
|
+
toolName: "search",
|
|
105
|
+
input: { query: "downcity" },
|
|
106
|
+
});
|
|
107
|
+
await writer.apply_chunk({
|
|
108
|
+
type: "tool-output-available",
|
|
109
|
+
toolCallId: "call-1",
|
|
110
|
+
output: { count: 1 },
|
|
111
|
+
});
|
|
112
|
+
await writer.complete();
|
|
113
|
+
|
|
114
|
+
const action = await recorder.open_action_message({
|
|
115
|
+
message_id: "action-1",
|
|
116
|
+
action_type: "compact",
|
|
117
|
+
title: "Compacting",
|
|
118
|
+
});
|
|
119
|
+
const running = recorder.get_message("action-1");
|
|
120
|
+
await action.complete({ title: "Compacted" });
|
|
121
|
+
const completed = recorder.get_message("action-1");
|
|
122
|
+
|
|
123
|
+
const assistant = (await recorder.list_messages()).items[0];
|
|
124
|
+
assert.equal(assistant.parts.length, 1);
|
|
125
|
+
assert.equal(assistant.parts[0].state, "completed");
|
|
126
|
+
assert.deepEqual(assistant.parts[0].output, { count: 1 });
|
|
127
|
+
assert.equal(running.sequence, completed.sequence);
|
|
128
|
+
assert.equal(completed.revision, running.revision + 1);
|
|
129
|
+
assert.equal(completed.status, "completed");
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
test("snapshot、changes 分页和重启恢复保持单调顺序", async () => {
|
|
133
|
+
const { recorder, file_path } = await create_recorder("recovery-test");
|
|
134
|
+
await recorder.append_user_message({
|
|
135
|
+
turn_id: "turn-1",
|
|
136
|
+
input_type: "prompt",
|
|
137
|
+
parts: [{ part_id: "u1", type: "text", text: "one", state: "done" }],
|
|
138
|
+
});
|
|
139
|
+
await recorder.open_assistant_message({
|
|
140
|
+
turn_id: "turn-1",
|
|
141
|
+
segment_index: 1,
|
|
142
|
+
});
|
|
143
|
+
await recorder.open_action_message({
|
|
144
|
+
message_id: "running-action",
|
|
145
|
+
action_type: "fork",
|
|
146
|
+
title: "Forking",
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
const recovered_events = [];
|
|
150
|
+
const recovered = new SessionRecorder({
|
|
151
|
+
session_id: "recovery-test",
|
|
152
|
+
store: new JsonlSessionMessageStore({
|
|
153
|
+
session_id: "recovery-test",
|
|
154
|
+
file_path,
|
|
155
|
+
}),
|
|
156
|
+
publish: (mutation) => recovered_events.push(mutation),
|
|
157
|
+
});
|
|
158
|
+
await recovered.initialize();
|
|
159
|
+
|
|
160
|
+
const first_page = await recovered.list_messages({ limit: 2 });
|
|
161
|
+
const second_page = await recovered.list_messages({
|
|
162
|
+
limit: 2,
|
|
163
|
+
cursor: first_page.next_cursor,
|
|
164
|
+
});
|
|
165
|
+
assert.equal(first_page.items.length, 2);
|
|
166
|
+
assert.equal(second_page.items.length, 1);
|
|
167
|
+
assert.ok(first_page.items[1].sequence < second_page.items[0].sequence);
|
|
168
|
+
|
|
169
|
+
const messages = [...first_page.items, ...second_page.items];
|
|
170
|
+
assert.equal(messages[1].status, "stopped");
|
|
171
|
+
assert.equal(messages[2].status, "failed");
|
|
172
|
+
assert.deepEqual(
|
|
173
|
+
recovered_events.map((item) => `${item.variant}:${item.type}`),
|
|
174
|
+
["message:assistant", "message:action"],
|
|
175
|
+
);
|
|
176
|
+
|
|
177
|
+
const first_changes = await recovered.list_message_changes({
|
|
178
|
+
after_commit_sequence: 0,
|
|
179
|
+
limit: 2,
|
|
180
|
+
});
|
|
181
|
+
const next_changes = await recovered.list_message_changes({
|
|
182
|
+
after_commit_sequence: first_changes.next_commit_sequence,
|
|
183
|
+
limit: 100,
|
|
184
|
+
});
|
|
185
|
+
assert.equal(first_changes.has_more, true);
|
|
186
|
+
assert.ok(
|
|
187
|
+
first_changes.items.at(-1).commit_sequence <
|
|
188
|
+
next_changes.items[0].commit_sequence,
|
|
189
|
+
);
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
test("compact 追加 internal summary 且不重写 Mutation 日志", async () => {
|
|
193
|
+
const { recorder, file_path } = await create_recorder("compact-test");
|
|
194
|
+
for (let index = 1; index <= 6; index += 1) {
|
|
195
|
+
await recorder.append_user_message({
|
|
196
|
+
turn_id: `turn-${String(index)}`,
|
|
197
|
+
input_type: "prompt",
|
|
198
|
+
parts: [{
|
|
199
|
+
part_id: `user-${String(index)}`,
|
|
200
|
+
type: "text",
|
|
201
|
+
text: `message ${String(index)}`,
|
|
202
|
+
state: "done",
|
|
203
|
+
}],
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
const before = await fs.readFile(file_path, "utf8");
|
|
207
|
+
const history_store = new SessionRecorderHistoryStore({
|
|
208
|
+
session_id: "compact-test",
|
|
209
|
+
recorder,
|
|
210
|
+
});
|
|
211
|
+
const model = new MockLanguageModelV3({
|
|
212
|
+
modelId: "compact-model",
|
|
213
|
+
doGenerate: async () => ({
|
|
214
|
+
content: [{ type: "text", text: "Compact summary" }],
|
|
215
|
+
finishReason: { unified: "stop", raw: "stop" },
|
|
216
|
+
usage: {
|
|
217
|
+
inputTokens: { total: 0, noCache: 0, cacheRead: 0, cacheWrite: 0 },
|
|
218
|
+
outputTokens: { total: 0, text: 0, reasoning: 0 },
|
|
219
|
+
},
|
|
220
|
+
warnings: [],
|
|
221
|
+
}),
|
|
222
|
+
});
|
|
223
|
+
const result = await history_store.compact({
|
|
224
|
+
model,
|
|
225
|
+
system: [],
|
|
226
|
+
keepLastMessages: 2,
|
|
227
|
+
maxInputTokensApprox: 1,
|
|
228
|
+
archiveOnCompact: false,
|
|
229
|
+
compactRatio: 0.5,
|
|
230
|
+
});
|
|
231
|
+
assert.equal(result.compacted, true);
|
|
232
|
+
|
|
233
|
+
const after = await fs.readFile(file_path, "utf8");
|
|
234
|
+
assert.equal(after.startsWith(before), true);
|
|
235
|
+
const all = await recorder.list_messages({
|
|
236
|
+
limit: 100,
|
|
237
|
+
include_internal: true,
|
|
238
|
+
});
|
|
239
|
+
const summary = all.items.find(
|
|
240
|
+
(message) => message.type === "assistant" && message.kind === "summary",
|
|
241
|
+
);
|
|
242
|
+
assert.equal(summary.visibility, "internal");
|
|
243
|
+
assert.equal(summary.summary_through_message_id, all.items[2].message_id);
|
|
244
|
+
assert.equal((await history_store.list_records()).length, 4);
|
|
245
|
+
});
|
|
@@ -110,11 +110,13 @@ test("Session keeps title empty when no model is available", async () => {
|
|
|
110
110
|
text: "Use shell tools to inspect the current workspace",
|
|
111
111
|
});
|
|
112
112
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
113
|
+
assert.equal(
|
|
114
|
+
events.some(
|
|
115
|
+
(event) => event.variant === "message" && event.type === "user",
|
|
116
|
+
),
|
|
117
|
+
true,
|
|
118
|
+
);
|
|
119
|
+
assert.equal((await session.get_info()).title, undefined);
|
|
118
120
|
} finally {
|
|
119
121
|
unsubscribe();
|
|
120
122
|
await agent.dispose();
|
|
@@ -137,8 +139,7 @@ test("Session logs title generation failure without blocking the session", async
|
|
|
137
139
|
text: "Diagnose why session title generation is flaky",
|
|
138
140
|
});
|
|
139
141
|
|
|
140
|
-
|
|
141
|
-
assert.equal(records.session.title, undefined);
|
|
142
|
+
assert.equal((await session.get_info()).title, undefined);
|
|
142
143
|
|
|
143
144
|
await agent.getLogger().saveAllLogs();
|
|
144
145
|
const log_lines = await read_log_lines(agent_path);
|
|
@@ -185,8 +186,7 @@ test("Session retries title generation after model becomes available", async ()
|
|
|
185
186
|
text: "Investigate flaky session title generation in the SDK",
|
|
186
187
|
});
|
|
187
188
|
|
|
188
|
-
|
|
189
|
-
assert.equal(history_before_model.session.title, undefined);
|
|
189
|
+
assert.equal((await session.get_info()).title, undefined);
|
|
190
190
|
|
|
191
191
|
await session.set({
|
|
192
192
|
model: create_mock_title_model("排查 session 标题"),
|
|
@@ -195,15 +195,7 @@ test("Session retries title generation after model becomes available", async ()
|
|
|
195
195
|
text: "Need another prompt to trigger the retry path",
|
|
196
196
|
});
|
|
197
197
|
|
|
198
|
-
|
|
199
|
-
assert.deepEqual(title_event, {
|
|
200
|
-
type: "session-title",
|
|
201
|
-
sessionId: session.id,
|
|
202
|
-
title: "排查 session 标题",
|
|
203
|
-
});
|
|
204
|
-
|
|
205
|
-
const records = await session.records();
|
|
206
|
-
assert.equal(records.session.title, "排查 session 标题");
|
|
198
|
+
assert.equal((await session.get_info()).title, "排查 session 标题");
|
|
207
199
|
} finally {
|
|
208
200
|
unsubscribe();
|
|
209
201
|
await agent.dispose();
|
package/src/agent/local/Agent.ts
CHANGED
|
@@ -47,6 +47,8 @@ export class Agent {
|
|
|
47
47
|
private readonly config: DowncityConfig;
|
|
48
48
|
private readonly env: Record<string, string>;
|
|
49
49
|
private readonly defaultModel?: AgentModel;
|
|
50
|
+
private readonly default_model_id?: string;
|
|
51
|
+
private readonly resolve_model?: AgentOptions["resolve_model"];
|
|
50
52
|
private readonly SessionClass: AgentOptions["Session"];
|
|
51
53
|
private readonly backgroundService: AgentBackgroundService;
|
|
52
54
|
private readonly shell?: AgentOptions["shell"];
|
|
@@ -55,6 +57,8 @@ export class Agent {
|
|
|
55
57
|
|
|
56
58
|
constructor(options: AgentOptions) {
|
|
57
59
|
this.defaultModel = options.model;
|
|
60
|
+
this.default_model_id = String(options.model_id || "").trim() || undefined;
|
|
61
|
+
this.resolve_model = options.resolve_model;
|
|
58
62
|
this.SessionClass = options.Session;
|
|
59
63
|
let sessions_ref: LocalAgentSessions | null = null;
|
|
60
64
|
const assembly_service = new AgentAssemblyService({
|
|
@@ -261,6 +265,8 @@ export class Agent {
|
|
|
261
265
|
await this.backgroundService.ready();
|
|
262
266
|
},
|
|
263
267
|
default_model: this.defaultModel,
|
|
268
|
+
default_model_id: this.default_model_id,
|
|
269
|
+
resolve_model: this.resolve_model,
|
|
264
270
|
SessionClass: this.SessionClass,
|
|
265
271
|
});
|
|
266
272
|
}
|
|
@@ -92,6 +92,12 @@ type AgentSessionsOptions = {
|
|
|
92
92
|
*/
|
|
93
93
|
default_model?: AgentModel;
|
|
94
94
|
|
|
95
|
+
/** 当前默认模型的稳定 ID。 */
|
|
96
|
+
default_model_id?: string;
|
|
97
|
+
|
|
98
|
+
/** 按稳定 ID 解析 Session 模型。 */
|
|
99
|
+
resolve_model?: (model_id: string) => Promise<AgentModel>;
|
|
100
|
+
|
|
95
101
|
/**
|
|
96
102
|
* 当前 agent 使用的本地 Session 类。
|
|
97
103
|
*/
|
|
@@ -110,6 +116,8 @@ export class AgentSessions implements AgentSessionsApi<AgentSession> {
|
|
|
110
116
|
private readonly get_instruction: AgentSessionsOptions["get_instruction"];
|
|
111
117
|
private readonly ensure_agent_ready: AgentSessionsOptions["ensure_agent_ready"];
|
|
112
118
|
private readonly default_model?: AgentModel;
|
|
119
|
+
private readonly default_model_id?: string;
|
|
120
|
+
private readonly resolve_model?: AgentSessionsOptions["resolve_model"];
|
|
113
121
|
private readonly SessionClass: AgentSessionConstructor;
|
|
114
122
|
private readonly sessions_by_id = new Map<string, AgentManagedSession>();
|
|
115
123
|
private readonly configured_session_ids = new Set<string>();
|
|
@@ -123,6 +131,8 @@ export class AgentSessions implements AgentSessionsApi<AgentSession> {
|
|
|
123
131
|
this.get_instruction = options.get_instruction;
|
|
124
132
|
this.ensure_agent_ready = options.ensure_agent_ready;
|
|
125
133
|
this.default_model = options.default_model;
|
|
134
|
+
this.default_model_id = options.default_model_id;
|
|
135
|
+
this.resolve_model = options.resolve_model;
|
|
126
136
|
this.SessionClass = options.SessionClass || Session;
|
|
127
137
|
}
|
|
128
138
|
|
|
@@ -337,6 +347,7 @@ export class AgentSessions implements AgentSessionsApi<AgentSession> {
|
|
|
337
347
|
await this.ensure_agent_ready();
|
|
338
348
|
await this.apply_session_defaults(session);
|
|
339
349
|
},
|
|
350
|
+
...(this.resolve_model ? { resolve_model: this.resolve_model } : {}),
|
|
340
351
|
});
|
|
341
352
|
this.sessions_by_id.set(resolved_session_id, created);
|
|
342
353
|
return created;
|
|
@@ -346,9 +357,11 @@ export class AgentSessions implements AgentSessionsApi<AgentSession> {
|
|
|
346
357
|
session: AgentManagedSession,
|
|
347
358
|
): Promise<void> {
|
|
348
359
|
if (this.configured_session_ids.has(session.id)) return;
|
|
349
|
-
|
|
360
|
+
const persisted_model_id = String(session.config.modelId || "").trim();
|
|
361
|
+
if (!persisted_model_id && this.default_model) {
|
|
350
362
|
await session.set({
|
|
351
363
|
model: this.default_model,
|
|
364
|
+
...(this.default_model_id ? { modelId: this.default_model_id } : {}),
|
|
352
365
|
});
|
|
353
366
|
}
|
|
354
367
|
this.configured_session_ids.add(session.id);
|
|
@@ -9,8 +9,6 @@
|
|
|
9
9
|
import type {
|
|
10
10
|
AgentSessionConfigSnapshot,
|
|
11
11
|
AgentSessionForkInput,
|
|
12
|
-
AgentSessionRecordsInput,
|
|
13
|
-
AgentSessionRecordsPage,
|
|
14
12
|
AgentSessionInfo,
|
|
15
13
|
AgentSessionSetInput,
|
|
16
14
|
AgentSessionSystemSnapshot,
|
|
@@ -28,6 +26,17 @@ import type {
|
|
|
28
26
|
AgentSessionTurnHandle,
|
|
29
27
|
AgentSessionTurnResult,
|
|
30
28
|
} from "@/types/sdk/AgentSessionTurn.js";
|
|
29
|
+
import type {
|
|
30
|
+
ListSessionMessagesInput,
|
|
31
|
+
SessionMessagePage,
|
|
32
|
+
} from "@/types/session/SessionMessage.js";
|
|
33
|
+
import {
|
|
34
|
+
is_session_message_mutation,
|
|
35
|
+
type ListSessionMessageChangesInput,
|
|
36
|
+
type SessionMessageMutationPage,
|
|
37
|
+
type SessionMessageMutationSubscriber,
|
|
38
|
+
type SessionMessageMutationUnsubscribe,
|
|
39
|
+
} from "@/types/session/SessionMessageMutation.js";
|
|
31
40
|
import { SessionEventHub } from "@/session/runtime/SessionEventHub.js";
|
|
32
41
|
import type {
|
|
33
42
|
RemoteSessionTransport,
|
|
@@ -71,17 +80,25 @@ export class RemoteSession implements RemoteAgentSession {
|
|
|
71
80
|
this.transport = transport;
|
|
72
81
|
this.id = info.sessionId;
|
|
73
82
|
this.agentId = info.agentId;
|
|
74
|
-
// 远程 session
|
|
75
|
-
this.config = {
|
|
83
|
+
// 远程 session 不暴露服务端模型实例,只保留可序列化配置。
|
|
84
|
+
this.config = {
|
|
85
|
+
...(info.modelId ? { modelId: info.modelId } : {}),
|
|
86
|
+
...(info.modelLabel ? { modelLabel: info.modelLabel } : {}),
|
|
87
|
+
};
|
|
76
88
|
}
|
|
77
89
|
|
|
78
|
-
/**
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
)
|
|
90
|
+
/** 按稳定模型 ID 更新远程 Session 模型。 */
|
|
91
|
+
async set(input: AgentSessionSetInput): Promise<void> {
|
|
92
|
+
if (input.model) {
|
|
93
|
+
throw new Error("Remote session.set does not accept a local model instance.");
|
|
94
|
+
}
|
|
95
|
+
const model_id = String(input.modelId || "").trim();
|
|
96
|
+
if (!model_id) {
|
|
97
|
+
throw new Error("Remote session.set requires modelId.");
|
|
98
|
+
}
|
|
99
|
+
const info = await this.transport.set(this.id, { modelId: model_id });
|
|
100
|
+
this.config.modelId = info.modelId;
|
|
101
|
+
this.config.modelLabel = info.modelLabel;
|
|
85
102
|
}
|
|
86
103
|
|
|
87
104
|
/**
|
|
@@ -116,7 +133,9 @@ export class RemoteSession implements RemoteAgentSession {
|
|
|
116
133
|
/**
|
|
117
134
|
* 订阅当前远程 session 的 future 事件。
|
|
118
135
|
*/
|
|
119
|
-
subscribe(
|
|
136
|
+
subscribe(
|
|
137
|
+
subscriber: SessionMessageMutationSubscriber,
|
|
138
|
+
): SessionMessageMutationUnsubscribe {
|
|
120
139
|
this.event_subscriber_count += 1;
|
|
121
140
|
void this.ensure_event_pump().catch((error) => {
|
|
122
141
|
this.event_hub.publish({
|
|
@@ -124,7 +143,9 @@ export class RemoteSession implements RemoteAgentSession {
|
|
|
124
143
|
message: error instanceof Error ? error.message : String(error),
|
|
125
144
|
});
|
|
126
145
|
});
|
|
127
|
-
const unsubscribe = this.event_hub.subscribe(
|
|
146
|
+
const unsubscribe = this.event_hub.subscribe((event) => {
|
|
147
|
+
if (is_session_message_mutation(event)) subscriber(event);
|
|
148
|
+
});
|
|
128
149
|
return () => {
|
|
129
150
|
unsubscribe();
|
|
130
151
|
this.event_subscriber_count = Math.max(0, this.event_subscriber_count - 1);
|
|
@@ -132,11 +153,23 @@ export class RemoteSession implements RemoteAgentSession {
|
|
|
132
153
|
};
|
|
133
154
|
}
|
|
134
155
|
|
|
156
|
+
/** 订阅 transport 使用的 Message 与 lifecycle 事件。 */
|
|
157
|
+
subscribe_transport(subscriber: AgentSessionSubscriber): AgentSessionUnsubscribe {
|
|
158
|
+
return this.event_hub.subscribe(subscriber);
|
|
159
|
+
}
|
|
160
|
+
|
|
135
161
|
/**
|
|
136
162
|
* 读取远程消息历史。
|
|
137
163
|
*/
|
|
138
|
-
async
|
|
139
|
-
return await this.transport.
|
|
164
|
+
async messages(input?: ListSessionMessagesInput): Promise<SessionMessagePage> {
|
|
165
|
+
return await this.transport.messages(this.id, input);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/** 读取远程增量 Message Mutation。 */
|
|
169
|
+
async message_changes(
|
|
170
|
+
input: ListSessionMessageChangesInput,
|
|
171
|
+
): Promise<SessionMessageMutationPage> {
|
|
172
|
+
return await this.transport.message_changes(this.id, input);
|
|
140
173
|
}
|
|
141
174
|
|
|
142
175
|
/**
|
|
@@ -189,7 +222,7 @@ export class RemoteSession implements RemoteAgentSession {
|
|
|
189
222
|
}
|
|
190
223
|
|
|
191
224
|
private handle_event(event: AgentSessionEvent): void {
|
|
192
|
-
if (event.type === "error") {
|
|
225
|
+
if (!is_session_message_mutation(event) && event.type === "error") {
|
|
193
226
|
this.fail_pending_turns(event.message);
|
|
194
227
|
}
|
|
195
228
|
const turn_id = extract_turn_id(event);
|
|
@@ -306,6 +339,9 @@ function create_turn_handle(
|
|
|
306
339
|
}
|
|
307
340
|
|
|
308
341
|
function extract_turn_id(event: AgentSessionEvent): string | null {
|
|
342
|
+
if (is_session_message_mutation(event)) {
|
|
343
|
+
return event.turn_id || null;
|
|
344
|
+
}
|
|
309
345
|
switch (event.type) {
|
|
310
346
|
case "turn-start":
|
|
311
347
|
case "text-delta":
|
|
@@ -15,11 +15,10 @@ import type {
|
|
|
15
15
|
AgentArchiveSessionsResult,
|
|
16
16
|
AgentCleanArchiveResult,
|
|
17
17
|
AgentSessionForkInput,
|
|
18
|
-
AgentSessionRecordsInput,
|
|
19
|
-
AgentSessionRecordsPage,
|
|
20
18
|
AgentSessionInfo,
|
|
21
19
|
AgentSessionSummaryPage,
|
|
22
20
|
AgentSessionSystemSnapshot,
|
|
21
|
+
AgentSessionSetInput,
|
|
23
22
|
} from "@/types/agent/SessionTypes.js";
|
|
24
23
|
import type {
|
|
25
24
|
RemoteAgentPluginActionInput,
|
|
@@ -28,6 +27,14 @@ import type {
|
|
|
28
27
|
import type { AgentSessionEvent } from "@/types/sdk/AgentSessionEvent.js";
|
|
29
28
|
import type { AgentSessionPromptInput } from "@/types/sdk/AgentSessionPrompt.js";
|
|
30
29
|
import type { AgentSessionStopResult } from "@/types/sdk/AgentSessionStop.js";
|
|
30
|
+
import type {
|
|
31
|
+
ListSessionMessagesInput,
|
|
32
|
+
SessionMessagePage,
|
|
33
|
+
} from "@/types/session/SessionMessage.js";
|
|
34
|
+
import type {
|
|
35
|
+
ListSessionMessageChangesInput,
|
|
36
|
+
SessionMessageMutationPage,
|
|
37
|
+
} from "@/types/session/SessionMessageMutation.js";
|
|
31
38
|
import type {
|
|
32
39
|
ShellApprovalMode,
|
|
33
40
|
ShellApprovalDecisionResult,
|
|
@@ -51,6 +58,8 @@ export type TransportSubscription = {
|
|
|
51
58
|
export type RemoteSessionTransport = {
|
|
52
59
|
/** 读取 session 信息。 */
|
|
53
60
|
get_info(session_id: string): Promise<AgentSessionInfo>;
|
|
61
|
+
/** 按稳定模型 ID 更新远程 session 配置。 */
|
|
62
|
+
set(session_id: string, input: AgentSessionSetInput): Promise<AgentSessionInfo>;
|
|
54
63
|
/** 发送 prompt。 */
|
|
55
64
|
prompt(
|
|
56
65
|
session_id: string,
|
|
@@ -66,11 +75,16 @@ export type RemoteSessionTransport = {
|
|
|
66
75
|
/** 底层事件连接结束后的通知;主动关闭不触发。 */
|
|
67
76
|
on_close: (error?: unknown) => void;
|
|
68
77
|
}): Promise<TransportSubscription>;
|
|
69
|
-
/** 读取
|
|
70
|
-
|
|
78
|
+
/** 读取 Session Message。 */
|
|
79
|
+
messages(
|
|
80
|
+
session_id: string,
|
|
81
|
+
input?: ListSessionMessagesInput,
|
|
82
|
+
): Promise<SessionMessagePage>;
|
|
83
|
+
/** 读取增量 Message Mutation。 */
|
|
84
|
+
message_changes(
|
|
71
85
|
session_id: string,
|
|
72
|
-
input
|
|
73
|
-
): Promise<
|
|
86
|
+
input: ListSessionMessageChangesInput,
|
|
87
|
+
): Promise<SessionMessageMutationPage>;
|
|
74
88
|
/** 读取 system snapshot。 */
|
|
75
89
|
system(session_id: string): Promise<AgentSessionSystemSnapshot>;
|
|
76
90
|
/** 分叉 session。 */
|
|
@@ -15,12 +15,19 @@ import type {
|
|
|
15
15
|
AgentArchiveSessionsResult,
|
|
16
16
|
AgentCleanArchiveResult,
|
|
17
17
|
AgentSessionForkInput,
|
|
18
|
-
AgentSessionRecordsInput,
|
|
19
|
-
AgentSessionRecordsPage,
|
|
20
18
|
AgentSessionInfo,
|
|
21
19
|
AgentSessionSummaryPage,
|
|
22
20
|
AgentSessionSystemSnapshot,
|
|
21
|
+
AgentSessionSetInput,
|
|
23
22
|
} from "@/types/agent/SessionTypes.js";
|
|
23
|
+
import type {
|
|
24
|
+
ListSessionMessagesInput,
|
|
25
|
+
SessionMessagePage,
|
|
26
|
+
} from "@/types/session/SessionMessage.js";
|
|
27
|
+
import type {
|
|
28
|
+
ListSessionMessageChangesInput,
|
|
29
|
+
SessionMessageMutationPage,
|
|
30
|
+
} from "@/types/session/SessionMessageMutation.js";
|
|
24
31
|
import type {
|
|
25
32
|
RemoteAgentPluginActionInput,
|
|
26
33
|
RemoteAgentPluginActionResult,
|
|
@@ -100,6 +107,27 @@ export class HttpRemoteAgentTransport implements RemoteAgentTransport {
|
|
|
100
107
|
return payload.session;
|
|
101
108
|
}
|
|
102
109
|
|
|
110
|
+
async set(
|
|
111
|
+
session_id: string,
|
|
112
|
+
input: AgentSessionSetInput,
|
|
113
|
+
): Promise<AgentSessionInfo> {
|
|
114
|
+
const payload = await read_http_json<{
|
|
115
|
+
success?: boolean;
|
|
116
|
+
error?: string;
|
|
117
|
+
session?: AgentSessionInfo;
|
|
118
|
+
}>(`${this.base_url}/api/sdk/sessions/${encodeURIComponent(session_id)}/model`, {
|
|
119
|
+
method: "PUT",
|
|
120
|
+
headers: this.headers({
|
|
121
|
+
"Content-Type": "application/json",
|
|
122
|
+
}),
|
|
123
|
+
body: JSON.stringify({ modelId: input.modelId }),
|
|
124
|
+
});
|
|
125
|
+
if (!payload.success || !payload.session?.sessionId) {
|
|
126
|
+
throw new Error(String(payload.error || "Remote session model update failed"));
|
|
127
|
+
}
|
|
128
|
+
return payload.session;
|
|
129
|
+
}
|
|
130
|
+
|
|
103
131
|
async prompt(
|
|
104
132
|
session_id: string,
|
|
105
133
|
input: AgentSessionPromptInput,
|
|
@@ -191,32 +219,58 @@ export class HttpRemoteAgentTransport implements RemoteAgentTransport {
|
|
|
191
219
|
};
|
|
192
220
|
}
|
|
193
221
|
|
|
194
|
-
async
|
|
222
|
+
async messages(
|
|
195
223
|
session_id: string,
|
|
196
|
-
input?:
|
|
197
|
-
): Promise<
|
|
224
|
+
input?: ListSessionMessagesInput,
|
|
225
|
+
): Promise<SessionMessagePage> {
|
|
198
226
|
const query = new URLSearchParams();
|
|
199
227
|
if (input?.limit !== undefined) query.set("limit", String(input.limit));
|
|
200
228
|
if (input?.cursor) query.set("cursor", input.cursor);
|
|
201
|
-
if (input?.
|
|
202
|
-
|
|
203
|
-
|
|
229
|
+
if (input?.before_sequence !== undefined) {
|
|
230
|
+
query.set("before_sequence", String(input.before_sequence));
|
|
231
|
+
}
|
|
232
|
+
if (input?.include_internal) query.set("include_internal", "true");
|
|
233
|
+
if (input?.through_sequence !== undefined) {
|
|
234
|
+
query.set("through_sequence", String(input.through_sequence));
|
|
235
|
+
}
|
|
204
236
|
const payload = await read_http_json<{
|
|
205
237
|
success?: boolean;
|
|
206
238
|
error?: string;
|
|
207
|
-
|
|
239
|
+
messages?: SessionMessagePage;
|
|
208
240
|
}>(
|
|
209
|
-
`${this.base_url}/api/sdk/sessions/${encodeURIComponent(session_id)}/
|
|
241
|
+
`${this.base_url}/api/sdk/sessions/${encodeURIComponent(session_id)}/messages${
|
|
210
242
|
query.size > 0 ? `?${query.toString()}` : ""
|
|
211
243
|
}`,
|
|
212
244
|
{
|
|
213
245
|
headers: this.headers(),
|
|
214
246
|
},
|
|
215
247
|
);
|
|
216
|
-
if (!payload.success || !payload.
|
|
217
|
-
throw new Error(String(payload.error || "Remote session
|
|
248
|
+
if (!payload.success || !payload.messages || !Array.isArray(payload.messages.items)) {
|
|
249
|
+
throw new Error(String(payload.error || "Remote session messages failed"));
|
|
250
|
+
}
|
|
251
|
+
return payload.messages;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
async message_changes(
|
|
255
|
+
session_id: string,
|
|
256
|
+
input: ListSessionMessageChangesInput,
|
|
257
|
+
): Promise<SessionMessageMutationPage> {
|
|
258
|
+
const query = new URLSearchParams({
|
|
259
|
+
after_commit_sequence: String(input.after_commit_sequence),
|
|
260
|
+
});
|
|
261
|
+
if (input.limit !== undefined) query.set("limit", String(input.limit));
|
|
262
|
+
const payload = await read_http_json<{
|
|
263
|
+
success?: boolean;
|
|
264
|
+
error?: string;
|
|
265
|
+
changes?: SessionMessageMutationPage;
|
|
266
|
+
}>(
|
|
267
|
+
`${this.base_url}/api/sdk/sessions/${encodeURIComponent(session_id)}/message-changes?${query.toString()}`,
|
|
268
|
+
{ headers: this.headers() },
|
|
269
|
+
);
|
|
270
|
+
if (!payload.success || !payload.changes) {
|
|
271
|
+
throw new Error(String(payload.error || "Remote session message changes failed"));
|
|
218
272
|
}
|
|
219
|
-
return payload.
|
|
273
|
+
return payload.changes;
|
|
220
274
|
}
|
|
221
275
|
|
|
222
276
|
async system(session_id: string): Promise<AgentSessionSystemSnapshot> {
|