@downcity/agent 1.1.43 → 1.1.51

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 (171) hide show
  1. package/README.md +6 -8
  2. package/bin/agent/Agent.d.ts +31 -16
  3. package/bin/agent/Agent.d.ts.map +1 -1
  4. package/bin/agent/Agent.js +47 -63
  5. package/bin/agent/Agent.js.map +1 -1
  6. package/bin/agent/RemoteAgent.d.ts +6 -6
  7. package/bin/agent/RemoteAgent.d.ts.map +1 -1
  8. package/bin/agent/RemoteAgent.js +377 -267
  9. package/bin/agent/RemoteAgent.js.map +1 -1
  10. package/bin/config/AgentInitializer.d.ts +4 -4
  11. package/bin/config/AgentInitializer.d.ts.map +1 -1
  12. package/bin/config/AgentInitializer.js +14 -12
  13. package/bin/config/AgentInitializer.js.map +1 -1
  14. package/bin/config/Config.d.ts +9 -30
  15. package/bin/config/Config.d.ts.map +1 -1
  16. package/bin/config/Config.js +23 -70
  17. package/bin/config/Config.js.map +1 -1
  18. package/bin/config/Defaults.d.ts.map +1 -1
  19. package/bin/config/Defaults.js +1 -9
  20. package/bin/config/Defaults.js.map +1 -1
  21. package/bin/config/DowncitySchema.d.ts.map +1 -1
  22. package/bin/config/DowncitySchema.js +3 -111
  23. package/bin/config/DowncitySchema.js.map +1 -1
  24. package/bin/config/ExecutionBinding.d.ts +0 -15
  25. package/bin/config/ExecutionBinding.d.ts.map +1 -1
  26. package/bin/config/ExecutionBinding.js +1 -21
  27. package/bin/config/ExecutionBinding.js.map +1 -1
  28. package/bin/config/Paths.d.ts +4 -43
  29. package/bin/config/Paths.d.ts.map +1 -1
  30. package/bin/config/Paths.js +10 -30
  31. package/bin/config/Paths.js.map +1 -1
  32. package/bin/config/PlatformPaths.d.ts +0 -4
  33. package/bin/config/PlatformPaths.d.ts.map +1 -1
  34. package/bin/config/PlatformPaths.js +5 -1
  35. package/bin/config/PlatformPaths.js.map +1 -1
  36. package/bin/executor/composer/system/default/assets/init/PROFILE.md.d.ts +1 -1
  37. package/bin/executor/composer/system/default/assets/init/PROFILE.md.d.ts.map +1 -1
  38. package/bin/executor/composer/system/default/assets/init/PROFILE.md.js +1 -1
  39. package/bin/executor/composer/system/default/assets/init/PROFILE.md.js.map +1 -1
  40. package/bin/executor/tools/shell/ShellToolFormatting.js +2 -2
  41. package/bin/executor/tools/shell/ShellToolFormatting.js.map +1 -1
  42. package/bin/executor/types/SessionRun.d.ts +1 -1
  43. package/bin/index.d.ts +4 -6
  44. package/bin/index.d.ts.map +1 -1
  45. package/bin/index.js +5 -5
  46. package/bin/index.js.map +1 -1
  47. package/bin/plugin/core/PluginCommandRequest.d.ts +1 -1
  48. package/bin/plugin/core/PluginCommandRequest.js +1 -1
  49. package/bin/plugin/core/PluginLocalExecution.d.ts.map +1 -1
  50. package/bin/plugin/core/PluginLocalExecution.js +4 -8
  51. package/bin/plugin/core/PluginLocalExecution.js.map +1 -1
  52. package/bin/plugin/types/PluginApi.d.ts +1 -1
  53. package/bin/rpc/Client.d.ts +104 -0
  54. package/bin/rpc/Client.d.ts.map +1 -0
  55. package/bin/rpc/Client.js +300 -0
  56. package/bin/rpc/Client.js.map +1 -0
  57. package/bin/rpc/Server.d.ts +41 -0
  58. package/bin/rpc/Server.d.ts.map +1 -0
  59. package/bin/rpc/Server.js +164 -0
  60. package/bin/rpc/Server.js.map +1 -0
  61. package/bin/runtime/host/daemon/Api.d.ts +1 -1
  62. package/bin/runtime/host/daemon/Client.d.ts +1 -1
  63. package/bin/runtime/host/daemon/Client.d.ts.map +1 -1
  64. package/bin/runtime/host/daemon/Client.js +30 -20
  65. package/bin/runtime/host/daemon/Client.js.map +1 -1
  66. package/bin/runtime/server/http/control/OverviewRoutes.js +1 -1
  67. package/bin/runtime/server/http/control/OverviewRoutes.js.map +1 -1
  68. package/bin/session/index.d.ts +1 -1
  69. package/bin/session/index.d.ts.map +1 -1
  70. package/bin/session/index.js +1 -1
  71. package/bin/session/index.js.map +1 -1
  72. package/bin/session/storage/Paths.d.ts +0 -4
  73. package/bin/session/storage/Paths.d.ts.map +1 -1
  74. package/bin/session/storage/Paths.js +0 -6
  75. package/bin/session/storage/Paths.js.map +1 -1
  76. package/bin/types/agent/AgentTypes.d.ts +23 -52
  77. package/bin/types/agent/AgentTypes.d.ts.map +1 -1
  78. package/bin/types/config/AgentProject.d.ts +6 -5
  79. package/bin/types/config/AgentProject.d.ts.map +1 -1
  80. package/bin/types/config/DowncityConfig.d.ts +8 -66
  81. package/bin/types/config/DowncityConfig.d.ts.map +1 -1
  82. package/bin/types/config/LlmConfig.d.ts +1 -1
  83. package/bin/types/config/Start.d.ts +3 -0
  84. package/bin/types/config/Start.d.ts.map +1 -1
  85. package/bin/types/config/Start.js +2 -0
  86. package/bin/types/config/Start.js.map +1 -1
  87. package/bin/types/runtime/host/Store.d.ts +7 -50
  88. package/bin/types/runtime/host/Store.d.ts.map +1 -1
  89. package/bin/types/runtime/platform/Platform.d.ts +6 -6
  90. package/bin/types/runtime/platform/Platform.d.ts.map +1 -1
  91. package/bin/types/runtime/platform/PlatformGateway.d.ts +2 -2
  92. package/bin/types/runtime/platform/PlatformGateway.d.ts.map +1 -1
  93. package/bin/utils/Time.d.ts +0 -1
  94. package/bin/utils/Time.d.ts.map +1 -1
  95. package/bin/utils/Time.js +0 -7
  96. package/bin/utils/Time.js.map +1 -1
  97. package/bin/utils/storage/index.d.ts +0 -1
  98. package/bin/utils/storage/index.d.ts.map +1 -1
  99. package/bin/utils/storage/index.js +0 -6
  100. package/bin/utils/storage/index.js.map +1 -1
  101. package/package.json +4 -6
  102. package/src/agent/Agent.ts +54 -71
  103. package/src/agent/RemoteAgent.ts +515 -345
  104. package/src/config/AgentInitializer.ts +14 -12
  105. package/src/config/Config.ts +28 -85
  106. package/src/config/Defaults.ts +1 -9
  107. package/src/config/DowncitySchema.ts +3 -113
  108. package/src/config/ExecutionBinding.ts +1 -23
  109. package/src/config/Paths.ts +10 -43
  110. package/src/config/PlatformPaths.ts +5 -1
  111. package/src/executor/composer/system/default/assets/init/PROFILE.md.ts +1 -1
  112. package/src/executor/composer/system/default/assets/init/PROFILE.md.ts.txt +1 -2
  113. package/src/executor/tools/shell/ShellToolFormatting.ts +2 -2
  114. package/src/executor/types/SessionRun.ts +1 -1
  115. package/src/index.ts +7 -14
  116. package/src/plugin/core/PluginCommandRequest.ts +1 -1
  117. package/src/plugin/core/PluginLocalExecution.ts +4 -8
  118. package/src/plugin/types/PluginApi.ts +1 -1
  119. package/src/rpc/Client.ts +467 -0
  120. package/src/rpc/Server.ts +302 -0
  121. package/src/runtime/host/daemon/Api.ts +1 -1
  122. package/src/runtime/host/daemon/Client.ts +44 -22
  123. package/src/runtime/server/http/control/OverviewRoutes.ts +1 -1
  124. package/src/session/index.ts +0 -1
  125. package/src/session/storage/Paths.ts +0 -10
  126. package/src/types/agent/AgentTypes.ts +23 -57
  127. package/src/types/config/AgentProject.ts +6 -5
  128. package/src/types/config/DowncityConfig.ts +8 -67
  129. package/src/types/config/LlmConfig.ts +1 -1
  130. package/src/types/config/Start.ts +3 -0
  131. package/src/types/runtime/host/Store.ts +7 -53
  132. package/src/types/runtime/platform/Platform.ts +6 -6
  133. package/src/types/runtime/platform/PlatformGateway.ts +2 -2
  134. package/src/utils/Time.ts +0 -6
  135. package/src/utils/storage/index.ts +0 -7
  136. package/tsconfig.tsbuildinfo +1 -1
  137. package/bin/config/ConfigEnvResolver.d.ts +0 -22
  138. package/bin/config/ConfigEnvResolver.d.ts.map +0 -1
  139. package/bin/config/ConfigEnvResolver.js +0 -41
  140. package/bin/config/ConfigEnvResolver.js.map +0 -1
  141. package/bin/runtime/server/rpc/Server.d.ts +0 -18
  142. package/bin/runtime/server/rpc/Server.d.ts.map +0 -1
  143. package/bin/runtime/server/rpc/Server.js +0 -315
  144. package/bin/runtime/server/rpc/Server.js.map +0 -1
  145. package/bin/runtime/transport/rpc/Client.d.ts +0 -13
  146. package/bin/runtime/transport/rpc/Client.d.ts.map +0 -1
  147. package/bin/runtime/transport/rpc/Client.js +0 -98
  148. package/bin/runtime/transport/rpc/Client.js.map +0 -1
  149. package/bin/runtime/transport/rpc/Paths.d.ts +0 -14
  150. package/bin/runtime/transport/rpc/Paths.d.ts.map +0 -1
  151. package/bin/runtime/transport/rpc/Paths.js +0 -42
  152. package/bin/runtime/transport/rpc/Paths.js.map +0 -1
  153. package/bin/runtime/transport/rpc/Transport.d.ts +0 -21
  154. package/bin/runtime/transport/rpc/Transport.d.ts.map +0 -1
  155. package/bin/runtime/transport/rpc/Transport.js +0 -30
  156. package/bin/runtime/transport/rpc/Transport.js.map +0 -1
  157. package/bin/types/common/ResolvedConfigValue.d.ts +0 -12
  158. package/bin/types/common/ResolvedConfigValue.d.ts.map +0 -1
  159. package/bin/types/common/ResolvedConfigValue.js +0 -2
  160. package/bin/types/common/ResolvedConfigValue.js.map +0 -1
  161. package/bin/types/runtime/rpc/LocalRpc.d.ts +0 -69
  162. package/bin/types/runtime/rpc/LocalRpc.d.ts.map +0 -1
  163. package/bin/types/runtime/rpc/LocalRpc.js +0 -9
  164. package/bin/types/runtime/rpc/LocalRpc.js.map +0 -1
  165. package/src/config/ConfigEnvResolver.ts +0 -52
  166. package/src/runtime/server/rpc/Server.ts +0 -408
  167. package/src/runtime/transport/rpc/Client.ts +0 -113
  168. package/src/runtime/transport/rpc/Paths.ts +0 -50
  169. package/src/runtime/transport/rpc/Transport.ts +0 -43
  170. package/src/types/common/ResolvedConfigValue.ts +0 -16
  171. package/src/types/runtime/rpc/LocalRpc.ts +0 -72
@@ -1,28 +1,29 @@
1
1
  /**
2
- * RemoteAgent:远程 SDK 客户端。
2
+ * RemoteAgent:统一远程 SDK 客户端。
3
3
  *
4
4
  * 关键点(中文)
5
- * - 面向已通过 `agent.start({ http: { ... } })` 暴露出来的远程/本地 HTTP 服务。
6
- * - 与本地 `Session` 保持同一套 Session actor 使用面:`prompt()` + `subscribe()`。
7
- * - `RemoteAgent` 只负责 transport 与本地 turn handle 合成,不重复实现第二套会话编排器。
5
+ * - 对外只暴露一个 `url` 入口,不向用户暴露 transport 细节。
6
+ * - 当前内部支持 `http/https` `rpc` 两种访问方式。
7
+ * - `RemoteAgent` 只负责远程访问与 turn handle 合成,不重复实现第二套会话编排器。
8
8
  */
9
9
  import { SessionEventHub } from "../session/runtime/SessionEventHub.js";
10
+ import { RpcClient, parse_rpc_url } from "../rpc/Client.js";
10
11
  /**
11
12
  * 远程 Session 客户端。
12
13
  */
13
14
  class RemoteSession {
14
15
  id;
15
- baseUrl;
16
- eventHub = new SessionEventHub();
17
- turnsById = new Map();
18
- completedTurnIds = [];
19
- eventPumpConnectPromise = null;
20
- eventPumpAbortController = null;
21
- eventPumpRunning = false;
22
- eventSubscriberCount = 0;
23
- constructor(baseUrl, sessionId) {
24
- this.baseUrl = baseUrl;
25
- this.id = sessionId;
16
+ transport;
17
+ event_hub = new SessionEventHub();
18
+ turns_by_id = new Map();
19
+ completed_turn_ids = [];
20
+ event_pump_connect_promise = null;
21
+ event_pump_running = false;
22
+ event_subscriber_count = 0;
23
+ event_subscription = null;
24
+ constructor(transport, session_id) {
25
+ this.transport = transport;
26
+ this.id = session_id;
26
27
  }
27
28
  /**
28
29
  * 远程 session 当前不支持直接注入本地模型实例。
@@ -34,12 +35,7 @@ class RemoteSession {
34
35
  * 读取当前远程 session 详情。
35
36
  */
36
37
  async getInfo() {
37
- const response = await fetch(`${this.baseUrl}/api/sdk/sessions/${encodeURIComponent(this.id)}`);
38
- const payload = (await response.json());
39
- if (!response.ok || !payload.success || !payload.session?.sessionId) {
40
- throw new Error(String(payload.error || "Remote session info failed"));
41
- }
42
- return payload.session;
38
+ return await this.transport.get_info(this.id);
43
39
  }
44
40
  /**
45
41
  * 向当前远程 session 追加一条新的 prompt。
@@ -49,207 +45,88 @@ class RemoteSession {
49
45
  if (!query) {
50
46
  throw new Error("remote session.prompt requires a non-empty query");
51
47
  }
52
- await this.ensureEventPump();
53
- const response = await fetch(`${this.baseUrl}/api/sdk/sessions/${encodeURIComponent(this.id)}/prompt`, {
54
- method: "POST",
55
- headers: {
56
- "Content-Type": "application/json",
57
- },
58
- body: JSON.stringify({
59
- query,
60
- }),
61
- });
62
- const payload = (await response.json());
63
- if (!response.ok || !payload.success || !payload.turn?.id) {
64
- throw new Error(String(payload.error || "Remote session prompt failed"));
65
- }
66
- const lifecycle = this.ensureTurnLifecycle(payload.turn.id);
67
- return createTurnHandle(lifecycle);
48
+ await this.ensure_event_pump();
49
+ const turn = await this.transport.prompt(this.id, input);
50
+ const lifecycle = this.ensure_turn_lifecycle(turn.id);
51
+ return create_turn_handle(lifecycle);
68
52
  }
69
53
  /**
70
54
  * 订阅当前远程 session 的 future 事件。
71
55
  */
72
56
  subscribe(subscriber) {
73
- this.eventSubscriberCount += 1;
74
- void this.ensureEventPump().catch((error) => {
75
- this.eventHub.publish({
57
+ this.event_subscriber_count += 1;
58
+ void this.ensure_event_pump().catch((error) => {
59
+ this.event_hub.publish({
76
60
  type: "error",
77
61
  message: error instanceof Error ? error.message : String(error),
78
62
  });
79
63
  });
80
- const unsubscribe = this.eventHub.subscribe(subscriber);
64
+ const unsubscribe = this.event_hub.subscribe(subscriber);
81
65
  return () => {
82
66
  unsubscribe();
83
- this.eventSubscriberCount = Math.max(0, this.eventSubscriberCount - 1);
84
- this.maybeStopEventPump();
67
+ this.event_subscriber_count = Math.max(0, this.event_subscriber_count - 1);
68
+ void this.maybe_stop_event_pump();
85
69
  };
86
70
  }
87
71
  /**
88
72
  * 读取远程消息历史。
89
73
  */
90
74
  async history(input) {
91
- const query = new URLSearchParams();
92
- if (input?.limit !== undefined)
93
- query.set("limit", String(input.limit));
94
- if (input?.cursor)
95
- query.set("cursor", input.cursor);
96
- if (input?.order)
97
- query.set("order", input.order);
98
- if (input?.view)
99
- query.set("view", input.view);
100
- const response = await fetch(`${this.baseUrl}/api/sdk/sessions/${encodeURIComponent(this.id)}/history${query.size > 0 ? `?${query.toString()}` : ""}`);
101
- const payload = (await response.json());
102
- if (!response.ok ||
103
- !payload.success ||
104
- !payload.history ||
105
- !Array.isArray(payload.history.items)) {
106
- throw new Error(String(payload.error || "Remote session history failed"));
107
- }
108
- return payload.history;
75
+ return await this.transport.history(this.id, input);
109
76
  }
110
77
  /**
111
78
  * 读取远程 session 当前生效的 system prompt 快照。
112
79
  */
113
80
  async system() {
114
- const response = await fetch(`${this.baseUrl}/api/sdk/sessions/${encodeURIComponent(this.id)}/system`);
115
- const payload = (await response.json());
116
- if (!response.ok ||
117
- !payload.success ||
118
- !payload.system ||
119
- !Array.isArray(payload.system.blocks)) {
120
- throw new Error(String(payload.error || "Remote session system failed"));
121
- }
122
- return payload.system;
81
+ return await this.transport.system(this.id);
123
82
  }
124
83
  /**
125
84
  * 分叉远程 session。
126
85
  */
127
86
  async fork(input) {
128
- const messageId = typeof input === "string"
129
- ? String(input || "").trim() || undefined
130
- : String(input?.messageId || "").trim() || undefined;
131
- const response = await fetch(`${this.baseUrl}/api/sdk/sessions/${encodeURIComponent(this.id)}/fork`, {
132
- method: "POST",
133
- headers: {
134
- "Content-Type": "application/json",
135
- },
136
- body: JSON.stringify({
137
- ...(messageId ? { messageId } : {}),
138
- }),
139
- });
140
- const payload = (await response.json());
141
- if (!response.ok || !payload.success || !payload.session?.sessionId) {
142
- throw new Error(String(payload.error || "Remote session fork failed"));
143
- }
144
- return new RemoteSession(this.baseUrl, payload.session.sessionId);
87
+ const info = await this.transport.fork(this.id, input);
88
+ return new RemoteSession(this.transport, info.sessionId);
145
89
  }
146
- async ensureEventPump() {
147
- if (this.eventPumpConnectPromise) {
148
- await this.eventPumpConnectPromise;
90
+ async ensure_event_pump() {
91
+ if (this.event_pump_connect_promise) {
92
+ await this.event_pump_connect_promise;
149
93
  return;
150
94
  }
151
- if (this.eventPumpRunning)
95
+ if (this.event_pump_running)
152
96
  return;
153
- this.eventPumpConnectPromise = (async () => {
154
- const abortController = new AbortController();
155
- this.eventPumpAbortController = abortController;
156
- const response = await fetch(`${this.baseUrl}/api/sdk/sessions/${encodeURIComponent(this.id)}/events`, {
157
- signal: abortController.signal,
97
+ this.event_pump_connect_promise = (async () => {
98
+ let resolved_ready = false;
99
+ this.event_subscription = await this.transport.subscribe({
100
+ session_id: this.id,
101
+ on_ready: () => {
102
+ resolved_ready = true;
103
+ },
104
+ on_event: (event) => {
105
+ this.handle_event(event);
106
+ },
158
107
  });
159
- if (!response.ok || !response.body) {
160
- const text = await response.text().catch(() => "");
161
- throw new Error(text || `Remote session events failed (${response.status})`);
108
+ this.event_pump_running = true;
109
+ if (!resolved_ready) {
110
+ throw new Error("Remote session events connection closed before ready");
162
111
  }
163
- this.eventPumpRunning = true;
164
- const ready = createEventConnectionReady();
165
- void this.consumeEventConnection(response.body, abortController, ready)
166
- .finally(() => {
167
- const wasAborted = abortController.signal.aborted;
168
- this.eventPumpRunning = false;
169
- if (this.eventPumpAbortController === abortController) {
170
- this.eventPumpAbortController = null;
171
- }
172
- if (!wasAborted) {
173
- this.failPendingTurns("Remote session events connection closed");
174
- this.eventHub.publish({
175
- type: "error",
176
- message: "Remote session events connection closed",
177
- });
178
- }
179
- });
180
- await ready.promise;
181
112
  })();
182
113
  try {
183
- await this.eventPumpConnectPromise;
184
- }
185
- catch (error) {
186
- this.eventPumpAbortController = null;
187
- throw error;
188
- }
189
- finally {
190
- this.eventPumpConnectPromise = null;
191
- }
192
- }
193
- async consumeEventConnection(body, abortController, ready) {
194
- const decoder = new TextDecoder();
195
- const reader = body.getReader();
196
- let buffered = "";
197
- try {
198
- while (true) {
199
- const { done, value } = await reader.read();
200
- if (done)
201
- break;
202
- buffered += decoder.decode(value, { stream: true });
203
- let newlineIndex = buffered.indexOf("\n");
204
- while (newlineIndex >= 0) {
205
- const line = buffered.slice(0, newlineIndex).trim();
206
- buffered = buffered.slice(newlineIndex + 1);
207
- if (line) {
208
- this.handleEventLine(JSON.parse(line), ready);
209
- }
210
- newlineIndex = buffered.indexOf("\n");
211
- }
212
- }
213
- const tail = buffered.trim();
214
- if (tail) {
215
- this.handleEventLine(JSON.parse(tail), ready);
216
- }
217
- rejectEventConnectionReady(ready, "Remote session events connection closed before ready");
218
- }
219
- catch (error) {
220
- if (!abortController.signal.aborted) {
221
- const message = error instanceof Error ? error.message : String(error);
222
- rejectEventConnectionReady(ready, message);
223
- this.failPendingTurns(message);
224
- this.eventHub.publish({
225
- type: "error",
226
- message,
227
- });
228
- }
114
+ await this.event_pump_connect_promise;
229
115
  }
230
116
  finally {
231
- try {
232
- reader.releaseLock();
233
- }
234
- catch {
235
- // ignore
236
- }
117
+ this.event_pump_connect_promise = null;
237
118
  }
238
119
  }
239
- handleEventLine(value, ready) {
240
- if (isSdkEventsReadyFrame(value)) {
241
- resolveEventConnectionReady(ready);
242
- return;
120
+ handle_event(event) {
121
+ if (event.type === "error") {
122
+ this.fail_pending_turns(event.message);
243
123
  }
244
- this.handleEvent(value);
245
- }
246
- handleEvent(event) {
247
- const turnId = extractTurnId(event);
248
- if (turnId) {
249
- this.ensureTurnLifecycle(turnId);
124
+ const turn_id = extract_turn_id(event);
125
+ if (turn_id) {
126
+ this.ensure_turn_lifecycle(turn_id);
250
127
  }
251
128
  if (event.type === "turn-finish") {
252
- const lifecycle = this.ensureTurnLifecycle(event.turnId);
129
+ const lifecycle = this.ensure_turn_lifecycle(event.turnId);
253
130
  const result = {
254
131
  turnId: event.turnId,
255
132
  text: event.text,
@@ -257,26 +134,26 @@ class RemoteSession {
257
134
  ...(event.error ? { error: event.error } : {}),
258
135
  };
259
136
  lifecycle.result = result;
260
- lifecycle.deferredFinished.resolve(result);
261
- this.rememberCompletedTurn(event.turnId);
262
- this.maybeStopEventPump();
137
+ lifecycle.deferred_finished.resolve(result);
138
+ this.remember_completed_turn(event.turnId);
139
+ void this.maybe_stop_event_pump();
263
140
  }
264
- this.eventHub.publish(event);
141
+ this.event_hub.publish(event);
265
142
  }
266
- ensureTurnLifecycle(turnId) {
267
- const cached = this.turnsById.get(turnId);
143
+ ensure_turn_lifecycle(turn_id) {
144
+ const cached = this.turns_by_id.get(turn_id);
268
145
  if (cached)
269
146
  return cached;
270
147
  const created = {
271
- turnId,
148
+ turnId: turn_id,
272
149
  result: null,
273
- deferredFinished: createDeferred(),
150
+ deferred_finished: create_deferred(),
274
151
  };
275
- this.turnsById.set(turnId, created);
152
+ this.turns_by_id.set(turn_id, created);
276
153
  return created;
277
154
  }
278
- failPendingTurns(message) {
279
- for (const lifecycle of this.turnsById.values()) {
155
+ fail_pending_turns(message) {
156
+ for (const lifecycle of this.turns_by_id.values()) {
280
157
  if (lifecycle.result)
281
158
  continue;
282
159
  const result = {
@@ -286,48 +163,78 @@ class RemoteSession {
286
163
  error: message,
287
164
  };
288
165
  lifecycle.result = result;
289
- lifecycle.deferredFinished.resolve(result);
290
- this.rememberCompletedTurn(lifecycle.turnId);
166
+ lifecycle.deferred_finished.resolve(result);
167
+ this.remember_completed_turn(lifecycle.turnId);
291
168
  }
292
- this.maybeStopEventPump();
293
- }
294
- rememberCompletedTurn(turnId) {
295
- this.completedTurnIds.push(turnId);
296
- while (this.completedTurnIds.length > 200) {
297
- const oldestTurnId = this.completedTurnIds.shift();
298
- if (oldestTurnId) {
299
- this.turnsById.delete(oldestTurnId);
169
+ }
170
+ remember_completed_turn(turn_id) {
171
+ this.completed_turn_ids.push(turn_id);
172
+ while (this.completed_turn_ids.length > 200) {
173
+ const oldest_turn_id = this.completed_turn_ids.shift();
174
+ if (oldest_turn_id) {
175
+ this.turns_by_id.delete(oldest_turn_id);
300
176
  }
301
177
  }
302
178
  }
303
- maybeStopEventPump() {
304
- if (this.eventSubscriberCount > 0)
179
+ async maybe_stop_event_pump() {
180
+ if (this.event_subscriber_count > 0)
305
181
  return;
306
- if ([...this.turnsById.values()].some((item) => item.result === null))
182
+ if ([...this.turns_by_id.values()].some((item) => item.result === null))
307
183
  return;
308
- if (!this.eventPumpAbortController)
184
+ const current = this.event_subscription;
185
+ this.event_subscription = null;
186
+ this.event_pump_running = false;
187
+ if (!current)
309
188
  return;
310
- this.eventPumpAbortController.abort();
311
- this.eventPumpAbortController = null;
189
+ await current.close().catch((error) => {
190
+ this.fail_pending_turns(error instanceof Error ? error.message : String(error));
191
+ });
312
192
  }
313
193
  }
314
194
  /**
315
195
  * RemoteAgent:远程 Agent 客户端。
316
196
  */
317
197
  export class RemoteAgent {
318
- baseUrl;
198
+ transport;
319
199
  constructor(options) {
320
- const baseUrl = String(options.baseUrl || "").trim().replace(/\/+$/, "");
321
- if (!baseUrl) {
322
- throw new Error("RemoteAgent requires a non-empty baseUrl");
200
+ const url = String(options.url || "").trim();
201
+ if (!url) {
202
+ throw new Error("RemoteAgent requires a non-empty url");
323
203
  }
324
- this.baseUrl = baseUrl;
204
+ this.transport = create_remote_agent_transport(url);
325
205
  }
326
206
  /**
327
207
  * 新建一个远程 session。
328
208
  */
329
209
  async createSession(input) {
330
- const response = await fetch(`${this.baseUrl}/api/sdk/sessions`, {
210
+ const info = await this.transport.create_session(input);
211
+ return new RemoteSession(this.transport, info.sessionId);
212
+ }
213
+ /**
214
+ * 获取一个已存在的远程 session。
215
+ */
216
+ async getSession(sessionId) {
217
+ const resolved_session_id = String(sessionId || "").trim();
218
+ if (!resolved_session_id) {
219
+ throw new Error("getSession requires a non-empty sessionId");
220
+ }
221
+ const info = await this.transport.get_info(resolved_session_id);
222
+ return new RemoteSession(this.transport, info.sessionId);
223
+ }
224
+ /**
225
+ * 列出远程 agent 的 session 摘要页。
226
+ */
227
+ async listSessions(input) {
228
+ return await this.transport.list_sessions(input);
229
+ }
230
+ }
231
+ class HttpRemoteAgentTransport {
232
+ base_url;
233
+ constructor(url) {
234
+ this.base_url = url.replace(/\/+$/, "");
235
+ }
236
+ async create_session(input) {
237
+ const payload = await read_http_json(`${this.base_url}/api/sdk/sessions`, {
331
238
  method: "POST",
332
239
  headers: {
333
240
  "Content-Type": "application/json",
@@ -336,31 +243,110 @@ export class RemoteAgent {
336
243
  ...(input?.sessionId ? { sessionId: input.sessionId } : {}),
337
244
  }),
338
245
  });
339
- const payload = (await response.json());
340
- if (!response.ok || !payload.success || !payload.session?.sessionId) {
246
+ if (!payload.success || !payload.session?.sessionId) {
341
247
  throw new Error(String(payload.error || "Remote session create failed"));
342
248
  }
343
- return new RemoteSession(this.baseUrl, payload.session.sessionId);
249
+ return payload.session;
344
250
  }
345
- /**
346
- * 获取一个已存在的远程 session。
347
- */
348
- async getSession(sessionId) {
349
- const resolvedSessionId = String(sessionId || "").trim();
350
- if (!resolvedSessionId) {
351
- throw new Error("getSession requires a non-empty sessionId");
251
+ async get_info(session_id) {
252
+ const payload = await read_http_json(`${this.base_url}/api/sdk/sessions/${encodeURIComponent(session_id)}`);
253
+ if (!payload.success || !payload.session?.sessionId) {
254
+ throw new Error(String(payload.error || "Remote session info failed"));
352
255
  }
353
- const response = await fetch(`${this.baseUrl}/api/sdk/sessions/${encodeURIComponent(resolvedSessionId)}`);
354
- const payload = (await response.json());
355
- if (!response.ok || !payload.success || !payload.session?.sessionId) {
356
- throw new Error(String(payload.error || "Remote session get failed"));
256
+ return payload.session;
257
+ }
258
+ async prompt(session_id, input) {
259
+ const payload = await read_http_json(`${this.base_url}/api/sdk/sessions/${encodeURIComponent(session_id)}/prompt`, {
260
+ method: "POST",
261
+ headers: {
262
+ "Content-Type": "application/json",
263
+ },
264
+ body: JSON.stringify({
265
+ query: input.query,
266
+ }),
267
+ });
268
+ const id = String(payload.turn?.id || "").trim();
269
+ if (!payload.success || !id) {
270
+ throw new Error(String(payload.error || "Remote session prompt failed"));
357
271
  }
358
- return new RemoteSession(this.baseUrl, payload.session.sessionId);
272
+ return { id };
359
273
  }
360
- /**
361
- * 列出远程 agent session 摘要页。
362
- */
363
- async listSessions(input) {
274
+ async subscribe(params) {
275
+ const abort_controller = new AbortController();
276
+ let resolve_ready;
277
+ let reject_ready;
278
+ const ready_promise = new Promise((resolve, reject) => {
279
+ resolve_ready = resolve;
280
+ reject_ready = reject;
281
+ });
282
+ const response = await fetch(`${this.base_url}/api/sdk/sessions/${encodeURIComponent(params.session_id)}/events`, {
283
+ signal: abort_controller.signal,
284
+ });
285
+ if (!response.ok || !response.body) {
286
+ const text = await response.text().catch(() => "");
287
+ throw new Error(text || `Remote session events failed (${response.status})`);
288
+ }
289
+ void consume_http_event_stream({
290
+ body: response.body,
291
+ abort_controller,
292
+ on_ready: () => {
293
+ params.on_ready();
294
+ resolve_ready();
295
+ },
296
+ on_ready_error: (error) => {
297
+ reject_ready(error);
298
+ },
299
+ on_event: params.on_event,
300
+ });
301
+ await ready_promise;
302
+ return {
303
+ close: async () => {
304
+ abort_controller.abort();
305
+ },
306
+ };
307
+ }
308
+ async history(session_id, input) {
309
+ const query = new URLSearchParams();
310
+ if (input?.limit !== undefined)
311
+ query.set("limit", String(input.limit));
312
+ if (input?.cursor)
313
+ query.set("cursor", input.cursor);
314
+ if (input?.order)
315
+ query.set("order", input.order);
316
+ if (input?.view)
317
+ query.set("view", input.view);
318
+ const payload = await read_http_json(`${this.base_url}/api/sdk/sessions/${encodeURIComponent(session_id)}/history${query.size > 0 ? `?${query.toString()}` : ""}`);
319
+ if (!payload.success || !payload.history || !Array.isArray(payload.history.items)) {
320
+ throw new Error(String(payload.error || "Remote session history failed"));
321
+ }
322
+ return payload.history;
323
+ }
324
+ async system(session_id) {
325
+ const payload = await read_http_json(`${this.base_url}/api/sdk/sessions/${encodeURIComponent(session_id)}/system`);
326
+ if (!payload.success || !payload.system || !Array.isArray(payload.system.blocks)) {
327
+ throw new Error(String(payload.error || "Remote session system failed"));
328
+ }
329
+ return payload.system;
330
+ }
331
+ async fork(session_id, input) {
332
+ const message_id = typeof input === "string"
333
+ ? String(input || "").trim() || undefined
334
+ : String(input?.messageId || "").trim() || undefined;
335
+ const payload = await read_http_json(`${this.base_url}/api/sdk/sessions/${encodeURIComponent(session_id)}/fork`, {
336
+ method: "POST",
337
+ headers: {
338
+ "Content-Type": "application/json",
339
+ },
340
+ body: JSON.stringify({
341
+ ...(message_id ? { messageId: message_id } : {}),
342
+ }),
343
+ });
344
+ if (!payload.success || !payload.session?.sessionId) {
345
+ throw new Error(String(payload.error || "Remote session fork failed"));
346
+ }
347
+ return payload.session;
348
+ }
349
+ async list_sessions(input) {
364
350
  const query = new URLSearchParams();
365
351
  if (input?.limit !== undefined)
366
352
  query.set("limit", String(input.limit));
@@ -368,66 +354,190 @@ export class RemoteAgent {
368
354
  query.set("cursor", input.cursor);
369
355
  if (input?.query)
370
356
  query.set("query", input.query);
371
- const response = await fetch(`${this.baseUrl}/api/sdk/sessions${query.size > 0 ? `?${query.toString()}` : ""}`);
372
- const payload = (await response.json());
373
- if (!response.ok || !payload.success || !payload.page) {
357
+ const payload = await read_http_json(`${this.base_url}/api/sdk/sessions${query.size > 0 ? `?${query.toString()}` : ""}`);
358
+ if (!payload.success || !payload.page) {
374
359
  throw new Error(String(payload.error || "Remote sessions list failed"));
375
360
  }
376
361
  return payload.page;
377
362
  }
378
363
  }
379
- function createDeferred() {
380
- let resolve;
381
- const promise = new Promise((innerResolve) => {
382
- resolve = innerResolve;
383
- });
384
- return {
385
- promise,
386
- resolve,
387
- };
364
+ class RpcRemoteAgentTransport {
365
+ client;
366
+ constructor(url) {
367
+ this.client = new RpcClient(parse_rpc_url(url));
368
+ }
369
+ async create_session(input) {
370
+ return await this.client.create_session(input);
371
+ }
372
+ async get_info(session_id) {
373
+ return await this.client.get_session(session_id);
374
+ }
375
+ async prompt(session_id, input) {
376
+ return await this.client.prompt_session({
377
+ session_id,
378
+ input,
379
+ });
380
+ }
381
+ async subscribe(params) {
382
+ const subscription = await this.client.subscribe_session({
383
+ session_id: params.session_id,
384
+ on_ready: params.on_ready,
385
+ on_event: params.on_event,
386
+ });
387
+ return {
388
+ close: async () => {
389
+ await subscription.unsubscribe();
390
+ },
391
+ };
392
+ }
393
+ async history(session_id, input) {
394
+ return await this.client.get_session_history({
395
+ session_id,
396
+ input,
397
+ });
398
+ }
399
+ async system(session_id) {
400
+ return await this.client.get_session_system(session_id);
401
+ }
402
+ async fork(session_id, input) {
403
+ const message_id = typeof input === "string"
404
+ ? String(input || "").trim() || undefined
405
+ : String(input?.messageId || "").trim() || undefined;
406
+ return await this.client.fork_session({
407
+ session_id,
408
+ ...(message_id ? { message_id } : {}),
409
+ });
410
+ }
411
+ async list_sessions(input) {
412
+ return await this.client.list_sessions(input);
413
+ }
414
+ }
415
+ function create_remote_agent_transport(url) {
416
+ if (/^https?:\/\//i.test(url)) {
417
+ return new HttpRemoteAgentTransport(url);
418
+ }
419
+ if (/^rpc:\/\//i.test(url)) {
420
+ return new RpcRemoteAgentTransport(url);
421
+ }
422
+ throw new Error(`Unsupported RemoteAgent url protocol: ${url}. Expected http://, https://, or rpc://`);
423
+ }
424
+ async function read_http_json(input, init) {
425
+ const response = await fetch(input, init);
426
+ const payload = (await response.json().catch(() => ({})));
427
+ if (!response.ok) {
428
+ const message = extract_error_message(payload);
429
+ throw new Error(message || `HTTP ${response.status}`);
430
+ }
431
+ return payload;
388
432
  }
389
- function createEventConnectionReady() {
433
+ async function consume_http_event_stream(params) {
434
+ const decoder = new TextDecoder();
435
+ const reader = params.body.getReader();
436
+ let buffered = "";
437
+ let ready_resolved = false;
438
+ try {
439
+ while (true) {
440
+ const { done, value } = await reader.read();
441
+ if (done)
442
+ break;
443
+ buffered += decoder.decode(value, { stream: true });
444
+ let newline_index = buffered.indexOf("\n");
445
+ while (newline_index >= 0) {
446
+ const line = buffered.slice(0, newline_index).trim();
447
+ buffered = buffered.slice(newline_index + 1);
448
+ if (line) {
449
+ const value = JSON.parse(line);
450
+ if (is_sdk_events_ready_frame(value)) {
451
+ ready_resolved = true;
452
+ params.on_ready();
453
+ }
454
+ else {
455
+ params.on_event(value);
456
+ }
457
+ }
458
+ newline_index = buffered.indexOf("\n");
459
+ }
460
+ }
461
+ const tail = buffered.trim();
462
+ if (tail) {
463
+ const value = JSON.parse(tail);
464
+ if (is_sdk_events_ready_frame(value)) {
465
+ ready_resolved = true;
466
+ params.on_ready();
467
+ }
468
+ else {
469
+ params.on_event(value);
470
+ }
471
+ }
472
+ if (!params.abort_controller.signal.aborted) {
473
+ if (!ready_resolved) {
474
+ const error = new Error("Remote session events connection closed before ready");
475
+ params.on_ready_error(error);
476
+ throw error;
477
+ }
478
+ params.on_event({
479
+ type: "error",
480
+ message: "Remote session events connection closed",
481
+ });
482
+ }
483
+ }
484
+ catch (error) {
485
+ if (!params.abort_controller.signal.aborted) {
486
+ if (!ready_resolved) {
487
+ params.on_ready_error(error);
488
+ }
489
+ params.on_event({
490
+ type: "error",
491
+ message: error instanceof Error ? error.message : String(error),
492
+ });
493
+ }
494
+ }
495
+ finally {
496
+ try {
497
+ reader.releaseLock();
498
+ }
499
+ catch {
500
+ // ignore
501
+ }
502
+ }
503
+ }
504
+ function extract_error_message(payload) {
505
+ if (!payload || typeof payload !== "object")
506
+ return "";
507
+ if ("error" in payload && typeof payload.error === "string") {
508
+ return payload.error;
509
+ }
510
+ if ("message" in payload && typeof payload.message === "string") {
511
+ return payload.message;
512
+ }
513
+ return "";
514
+ }
515
+ function create_deferred() {
390
516
  let resolve;
391
- let reject;
392
- const promise = new Promise((innerResolve, innerReject) => {
393
- resolve = innerResolve;
394
- reject = innerReject;
517
+ const promise = new Promise((inner_resolve) => {
518
+ resolve = inner_resolve;
395
519
  });
396
520
  return {
397
521
  promise,
398
- settled: false,
399
522
  resolve,
400
- reject,
401
523
  };
402
524
  }
403
- function resolveEventConnectionReady(ready) {
404
- if (ready.settled)
405
- return;
406
- ready.settled = true;
407
- ready.resolve();
408
- }
409
- function rejectEventConnectionReady(ready, error) {
410
- if (ready.settled)
411
- return;
412
- ready.settled = true;
413
- ready.reject(error);
414
- }
415
- function isSdkEventsReadyFrame(value) {
525
+ function is_sdk_events_ready_frame(value) {
416
526
  return (typeof value === "object" &&
417
527
  value !== null &&
418
528
  "type" in value &&
419
529
  value.type === "sdk-events-ready");
420
530
  }
421
- function createTurnHandle(lifecycle) {
531
+ function create_turn_handle(lifecycle) {
422
532
  return {
423
533
  id: lifecycle.turnId,
424
534
  get result() {
425
535
  return lifecycle.result;
426
536
  },
427
- finished: lifecycle.deferredFinished.promise,
537
+ finished: lifecycle.deferred_finished.promise,
428
538
  };
429
539
  }
430
- function extractTurnId(event) {
540
+ function extract_turn_id(event) {
431
541
  switch (event.type) {
432
542
  case "turn-start":
433
543
  case "text-delta":