@downcity/agent 1.1.43 → 1.1.62

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 (256) hide show
  1. package/README.md +11 -12
  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 +5 -5
  11. package/bin/config/AgentInitializer.d.ts.map +1 -1
  12. package/bin/config/AgentInitializer.js +15 -13
  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 +4 -112
  23. package/bin/config/DowncitySchema.js.map +1 -1
  24. package/bin/config/ExecutionBinding.d.ts +1 -16
  25. package/bin/config/ExecutionBinding.d.ts.map +1 -1
  26. package/bin/config/ExecutionBinding.js +2 -22
  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/compaction/jsonl/JsonlSessionCompactionExecutor.d.ts.map +1 -1
  37. package/bin/executor/composer/compaction/jsonl/JsonlSessionCompactionExecutor.js +0 -4
  38. package/bin/executor/composer/compaction/jsonl/JsonlSessionCompactionExecutor.js.map +1 -1
  39. package/bin/executor/composer/system/default/InitPrompts.d.ts +1 -1
  40. package/bin/executor/composer/system/default/InitPrompts.js +1 -1
  41. package/bin/executor/composer/system/default/SystemDomain.js +1 -1
  42. package/bin/executor/composer/system/default/assets/core.prompt.d.ts +1 -1
  43. package/bin/executor/composer/system/default/assets/core.prompt.js +1 -1
  44. package/bin/executor/composer/system/default/assets/init/PROFILE.md.d.ts +1 -1
  45. package/bin/executor/composer/system/default/assets/init/PROFILE.md.d.ts.map +1 -1
  46. package/bin/executor/composer/system/default/assets/init/PROFILE.md.js +1 -1
  47. package/bin/executor/composer/system/default/assets/init/PROFILE.md.js.map +1 -1
  48. package/bin/executor/composer/system/default/assets/plugin.prompt.d.ts +1 -1
  49. package/bin/executor/composer/system/default/assets/plugin.prompt.js +1 -1
  50. package/bin/executor/composer/system/default/assets/task.prompt.d.ts +1 -1
  51. package/bin/executor/composer/system/default/assets/task.prompt.js +1 -1
  52. package/bin/executor/messages/ChatMessageMarkupTypes.d.ts +1 -1
  53. package/bin/executor/messages/ChatMessageMarkupTypes.js +1 -1
  54. package/bin/executor/store/history/jsonl/JsonlSessionHistoryStore.d.ts +1 -2
  55. package/bin/executor/store/history/jsonl/JsonlSessionHistoryStore.d.ts.map +1 -1
  56. package/bin/executor/store/history/jsonl/JsonlSessionHistoryStore.js +17 -56
  57. package/bin/executor/store/history/jsonl/JsonlSessionHistoryStore.js.map +1 -1
  58. package/bin/executor/tools/shell/ShellToolBridge.js +1 -1
  59. package/bin/executor/tools/shell/ShellToolBridge.js.map +1 -1
  60. package/bin/executor/tools/shell/ShellToolDefinition.d.ts +2 -2
  61. package/bin/executor/tools/shell/ShellToolFormatting.d.ts +1 -1
  62. package/bin/executor/tools/shell/ShellToolFormatting.js +9 -9
  63. package/bin/executor/tools/shell/ShellToolFormatting.js.map +1 -1
  64. package/bin/executor/tools/shell/ShellToolSchemas.d.ts +7 -75
  65. package/bin/executor/tools/shell/ShellToolSchemas.d.ts.map +1 -1
  66. package/bin/executor/types/SessionHistoryMeta.d.ts +5 -24
  67. package/bin/executor/types/SessionHistoryMeta.d.ts.map +1 -1
  68. package/bin/executor/types/SessionHistoryMeta.js +1 -1
  69. package/bin/executor/types/SessionRun.d.ts +1 -1
  70. package/bin/index.d.ts +5 -6
  71. package/bin/index.d.ts.map +1 -1
  72. package/bin/index.js +6 -5
  73. package/bin/index.js.map +1 -1
  74. package/bin/model/CityModelAdapter.d.ts +23 -0
  75. package/bin/model/CityModelAdapter.d.ts.map +1 -0
  76. package/bin/model/CityModelAdapter.js +338 -0
  77. package/bin/model/CityModelAdapter.js.map +1 -0
  78. package/bin/plugin/core/PluginCommandRequest.d.ts +1 -1
  79. package/bin/plugin/core/PluginCommandRequest.js +1 -1
  80. package/bin/plugin/core/PluginLocalExecution.d.ts.map +1 -1
  81. package/bin/plugin/core/PluginLocalExecution.js +4 -8
  82. package/bin/plugin/core/PluginLocalExecution.js.map +1 -1
  83. package/bin/plugin/types/PluginApi.d.ts +1 -1
  84. package/bin/rpc/Client.d.ts +104 -0
  85. package/bin/rpc/Client.d.ts.map +1 -0
  86. package/bin/rpc/Client.js +300 -0
  87. package/bin/rpc/Client.js.map +1 -0
  88. package/bin/rpc/Server.d.ts +41 -0
  89. package/bin/rpc/Server.d.ts.map +1 -0
  90. package/bin/rpc/Server.js +164 -0
  91. package/bin/rpc/Server.js.map +1 -0
  92. package/bin/runtime/host/daemon/Api.d.ts +1 -1
  93. package/bin/runtime/host/daemon/Client.d.ts +1 -1
  94. package/bin/runtime/host/daemon/Client.d.ts.map +1 -1
  95. package/bin/runtime/host/daemon/Client.js +30 -20
  96. package/bin/runtime/host/daemon/Client.js.map +1 -1
  97. package/bin/runtime/host/daemon/Paths.d.ts +1 -1
  98. package/bin/runtime/host/daemon/Paths.js +1 -1
  99. package/bin/runtime/host/daemon/ProjectSetup.js +2 -2
  100. package/bin/runtime/server/http/control/OverviewRoutes.js +1 -1
  101. package/bin/runtime/server/http/control/OverviewRoutes.js.map +1 -1
  102. package/bin/session/Session.d.ts +1 -0
  103. package/bin/session/Session.d.ts.map +1 -1
  104. package/bin/session/Session.js +32 -6
  105. package/bin/session/Session.js.map +1 -1
  106. package/bin/session/SessionTitle.d.ts +49 -0
  107. package/bin/session/SessionTitle.d.ts.map +1 -0
  108. package/bin/session/SessionTitle.js +130 -0
  109. package/bin/session/SessionTitle.js.map +1 -0
  110. package/bin/session/browse/Browse.d.ts +2 -1
  111. package/bin/session/browse/Browse.d.ts.map +1 -1
  112. package/bin/session/browse/Browse.js +18 -15
  113. package/bin/session/browse/Browse.js.map +1 -1
  114. package/bin/session/index.d.ts +3 -2
  115. package/bin/session/index.d.ts.map +1 -1
  116. package/bin/session/index.js +3 -2
  117. package/bin/session/index.js.map +1 -1
  118. package/bin/session/storage/Metadata.d.ts +4 -0
  119. package/bin/session/storage/Metadata.d.ts.map +1 -1
  120. package/bin/session/storage/Metadata.js +12 -25
  121. package/bin/session/storage/Metadata.js.map +1 -1
  122. package/bin/session/storage/Paths.d.ts +0 -4
  123. package/bin/session/storage/Paths.d.ts.map +1 -1
  124. package/bin/session/storage/Paths.js +0 -6
  125. package/bin/session/storage/Paths.js.map +1 -1
  126. package/bin/session/storage/Persistence.d.ts.map +1 -1
  127. package/bin/session/storage/Persistence.js +1 -4
  128. package/bin/session/storage/Persistence.js.map +1 -1
  129. package/bin/types/agent/AgentTypes.d.ts +32 -57
  130. package/bin/types/agent/AgentTypes.d.ts.map +1 -1
  131. package/bin/types/config/AgentProject.d.ts +7 -6
  132. package/bin/types/config/AgentProject.d.ts.map +1 -1
  133. package/bin/types/config/DowncityConfig.d.ts +11 -69
  134. package/bin/types/config/DowncityConfig.d.ts.map +1 -1
  135. package/bin/types/config/ExecutionBinding.d.ts +4 -4
  136. package/bin/types/config/ExecutionBinding.js +1 -1
  137. package/bin/types/config/LlmConfig.d.ts +1 -1
  138. package/bin/types/config/Start.d.ts +3 -0
  139. package/bin/types/config/Start.d.ts.map +1 -1
  140. package/bin/types/config/Start.js +2 -0
  141. package/bin/types/config/Start.js.map +1 -1
  142. package/bin/types/runtime/auth/AuthPermission.js +2 -2
  143. package/bin/types/runtime/auth/AuthPermission.js.map +1 -1
  144. package/bin/types/runtime/host/Store.d.ts +10 -227
  145. package/bin/types/runtime/host/Store.d.ts.map +1 -1
  146. package/bin/types/runtime/host/Store.js +7 -0
  147. package/bin/types/runtime/host/Store.js.map +1 -1
  148. package/bin/types/runtime/http/InlineInstant.d.ts +1 -1
  149. package/bin/types/runtime/platform/Platform.d.ts +7 -7
  150. package/bin/types/runtime/platform/Platform.d.ts.map +1 -1
  151. package/bin/types/runtime/platform/PlatformGateway.d.ts +2 -2
  152. package/bin/types/runtime/platform/PlatformGateway.d.ts.map +1 -1
  153. package/bin/utils/Time.d.ts +0 -1
  154. package/bin/utils/Time.d.ts.map +1 -1
  155. package/bin/utils/Time.js +0 -7
  156. package/bin/utils/Time.js.map +1 -1
  157. package/bin/utils/storage/index.d.ts +0 -1
  158. package/bin/utils/storage/index.d.ts.map +1 -1
  159. package/bin/utils/storage/index.js +0 -6
  160. package/bin/utils/storage/index.js.map +1 -1
  161. package/package.json +22 -23
  162. package/src/agent/Agent.ts +57 -73
  163. package/src/agent/RemoteAgent.ts +515 -345
  164. package/src/config/AgentInitializer.ts +15 -13
  165. package/src/config/Config.ts +28 -85
  166. package/src/config/Defaults.ts +1 -9
  167. package/src/config/DowncitySchema.ts +4 -114
  168. package/src/config/ExecutionBinding.ts +2 -24
  169. package/src/config/Paths.ts +10 -43
  170. package/src/config/PlatformPaths.ts +5 -1
  171. package/src/executor/composer/compaction/jsonl/JsonlSessionCompactionExecutor.ts +0 -4
  172. package/src/executor/composer/system/default/InitPrompts.ts +1 -1
  173. package/src/executor/composer/system/default/SystemDomain.ts +1 -1
  174. package/src/executor/composer/system/default/assets/core.prompt.ts +1 -1
  175. package/src/executor/composer/system/default/assets/core.prompt.ts.txt +1 -1
  176. package/src/executor/composer/system/default/assets/init/PROFILE.md.ts +1 -1
  177. package/src/executor/composer/system/default/assets/init/PROFILE.md.ts.txt +1 -2
  178. package/src/executor/composer/system/default/assets/plugin.prompt.ts +1 -1
  179. package/src/executor/composer/system/default/assets/plugin.prompt.ts.txt +18 -18
  180. package/src/executor/composer/system/default/assets/task.prompt.ts +1 -1
  181. package/src/executor/composer/system/default/assets/task.prompt.ts.txt +1 -1
  182. package/src/executor/messages/ChatMessageMarkupTypes.ts +1 -1
  183. package/src/executor/store/history/jsonl/JsonlSessionHistoryStore.ts +17 -57
  184. package/src/executor/tools/shell/ShellToolBridge.ts +1 -1
  185. package/src/executor/tools/shell/ShellToolFormatting.ts +9 -9
  186. package/src/executor/types/SessionHistoryMeta.ts +5 -25
  187. package/src/executor/types/SessionRun.ts +1 -1
  188. package/src/index.ts +12 -19
  189. package/src/model/CityModelAdapter.ts +384 -0
  190. package/src/plugin/core/PluginCommandRequest.ts +1 -1
  191. package/src/plugin/core/PluginLocalExecution.ts +4 -8
  192. package/src/plugin/types/PluginApi.ts +1 -1
  193. package/src/rpc/Client.ts +467 -0
  194. package/src/rpc/Server.ts +302 -0
  195. package/src/runtime/host/daemon/Api.ts +1 -1
  196. package/src/runtime/host/daemon/Client.ts +44 -22
  197. package/src/runtime/host/daemon/Paths.ts +1 -1
  198. package/src/runtime/host/daemon/ProjectSetup.ts +2 -2
  199. package/src/runtime/server/http/control/OverviewRoutes.ts +1 -1
  200. package/src/session/Session.ts +40 -6
  201. package/src/session/SessionTitle.ts +192 -0
  202. package/src/session/browse/Browse.ts +22 -13
  203. package/src/session/index.ts +5 -1
  204. package/src/session/storage/Metadata.ts +14 -29
  205. package/src/session/storage/Paths.ts +0 -10
  206. package/src/session/storage/Persistence.ts +1 -4
  207. package/src/types/agent/AgentTypes.ts +33 -62
  208. package/src/types/config/AgentProject.ts +7 -6
  209. package/src/types/config/DowncityConfig.ts +11 -70
  210. package/src/types/config/ExecutionBinding.ts +4 -4
  211. package/src/types/config/LlmConfig.ts +1 -1
  212. package/src/types/config/Start.ts +3 -0
  213. package/src/types/runtime/auth/AuthPermission.ts +2 -2
  214. package/src/types/runtime/host/Store.ts +10 -235
  215. package/src/types/runtime/http/InlineInstant.ts +1 -1
  216. package/src/types/runtime/platform/Platform.ts +7 -7
  217. package/src/types/runtime/platform/PlatformGateway.ts +2 -2
  218. package/src/utils/Time.ts +0 -6
  219. package/src/utils/storage/index.ts +0 -7
  220. package/tsconfig.json +1 -0
  221. package/tsconfig.tsbuildinfo +1 -1
  222. package/bin/config/ConfigEnvResolver.d.ts +0 -22
  223. package/bin/config/ConfigEnvResolver.d.ts.map +0 -1
  224. package/bin/config/ConfigEnvResolver.js +0 -41
  225. package/bin/config/ConfigEnvResolver.js.map +0 -1
  226. package/bin/runtime/server/rpc/Server.d.ts +0 -18
  227. package/bin/runtime/server/rpc/Server.d.ts.map +0 -1
  228. package/bin/runtime/server/rpc/Server.js +0 -315
  229. package/bin/runtime/server/rpc/Server.js.map +0 -1
  230. package/bin/runtime/transport/rpc/Client.d.ts +0 -13
  231. package/bin/runtime/transport/rpc/Client.d.ts.map +0 -1
  232. package/bin/runtime/transport/rpc/Client.js +0 -98
  233. package/bin/runtime/transport/rpc/Client.js.map +0 -1
  234. package/bin/runtime/transport/rpc/Paths.d.ts +0 -14
  235. package/bin/runtime/transport/rpc/Paths.d.ts.map +0 -1
  236. package/bin/runtime/transport/rpc/Paths.js +0 -42
  237. package/bin/runtime/transport/rpc/Paths.js.map +0 -1
  238. package/bin/runtime/transport/rpc/Transport.d.ts +0 -21
  239. package/bin/runtime/transport/rpc/Transport.d.ts.map +0 -1
  240. package/bin/runtime/transport/rpc/Transport.js +0 -30
  241. package/bin/runtime/transport/rpc/Transport.js.map +0 -1
  242. package/bin/types/common/ResolvedConfigValue.d.ts +0 -12
  243. package/bin/types/common/ResolvedConfigValue.d.ts.map +0 -1
  244. package/bin/types/common/ResolvedConfigValue.js +0 -2
  245. package/bin/types/common/ResolvedConfigValue.js.map +0 -1
  246. package/bin/types/runtime/rpc/LocalRpc.d.ts +0 -69
  247. package/bin/types/runtime/rpc/LocalRpc.d.ts.map +0 -1
  248. package/bin/types/runtime/rpc/LocalRpc.js +0 -9
  249. package/bin/types/runtime/rpc/LocalRpc.js.map +0 -1
  250. package/src/config/ConfigEnvResolver.ts +0 -52
  251. package/src/runtime/server/rpc/Server.ts +0 -408
  252. package/src/runtime/transport/rpc/Client.ts +0 -113
  253. package/src/runtime/transport/rpc/Paths.ts +0 -50
  254. package/src/runtime/transport/rpc/Transport.ts +0 -43
  255. package/src/types/common/ResolvedConfigValue.ts +0 -16
  256. package/src/types/runtime/rpc/LocalRpc.ts +0 -72
@@ -1,10 +1,10 @@
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
 
10
10
  import type {
@@ -17,8 +17,8 @@ import type {
17
17
  AgentSessionSetInput,
18
18
  AgentSessionSummaryPage,
19
19
  AgentSessionSystemSnapshot,
20
- RemoteAgentSession,
21
20
  RemoteAgentOptions,
21
+ RemoteAgentSession,
22
22
  } from "@/types/agent/AgentTypes.js";
23
23
  import type {
24
24
  AgentSessionEvent,
@@ -31,6 +31,7 @@ import type {
31
31
  AgentSessionTurnResult,
32
32
  } from "@/types/sdk/AgentSessionTurn.js";
33
33
  import { SessionEventHub } from "@/session/runtime/SessionEventHub.js";
34
+ import { RpcClient, parse_rpc_url } from "@/rpc/Client.js";
34
35
 
35
36
  type Deferred<T> = {
36
37
  /**
@@ -55,22 +56,65 @@ type RemoteTurnLifecycle = {
55
56
  /**
56
57
  * 当前 turn 完成 Promise 控制器。
57
58
  */
58
- deferredFinished: Deferred<AgentSessionTurnResult>;
59
+ deferred_finished: Deferred<AgentSessionTurnResult>;
60
+ };
61
+
62
+ type TransportSubscription = {
63
+ /**
64
+ * 关闭当前订阅。
65
+ */
66
+ close(): Promise<void>;
59
67
  };
60
68
 
61
- type EventConnectionReady = {
69
+ type RemoteSessionTransport = {
70
+ /**
71
+ * 读取 session 信息。
72
+ */
73
+ get_info(session_id: string): Promise<AgentSessionInfo>;
74
+ /**
75
+ * 发送 prompt。
76
+ */
77
+ prompt(
78
+ session_id: string,
79
+ input: AgentSessionPromptInput,
80
+ ): Promise<{ id: string }>;
62
81
  /**
63
- * 当前 ready promise 是否已经完成。
82
+ * 订阅 session 事件。
64
83
  */
65
- settled: boolean;
84
+ subscribe(params: {
85
+ session_id: string;
86
+ on_ready: () => void;
87
+ on_event: (event: AgentSessionEvent) => void;
88
+ }): Promise<TransportSubscription>;
66
89
  /**
67
- * 标记事件连接已经在服务端完成订阅。
90
+ * 读取 history。
68
91
  */
69
- resolve: () => void;
92
+ history(
93
+ session_id: string,
94
+ input?: AgentSessionHistoryInput,
95
+ ): Promise<AgentSessionHistoryPage>;
70
96
  /**
71
- * 标记事件连接在 ready 前失败。
97
+ * 读取 system snapshot。
72
98
  */
73
- reject: (error: unknown) => void;
99
+ system(session_id: string): Promise<AgentSessionSystemSnapshot>;
100
+ /**
101
+ * 分叉 session。
102
+ */
103
+ fork(
104
+ session_id: string,
105
+ input?: AgentSessionForkInput | string,
106
+ ): Promise<AgentSessionInfo>;
107
+ };
108
+
109
+ type RemoteAgentTransport = RemoteSessionTransport & {
110
+ /**
111
+ * 新建 session。
112
+ */
113
+ create_session(input?: AgentCreateSessionInput): Promise<AgentSessionInfo>;
114
+ /**
115
+ * 列出 sessions。
116
+ */
117
+ list_sessions(input?: AgentListSessionsInput): Promise<AgentSessionSummaryPage>;
74
118
  };
75
119
 
76
120
  type SdkEventsReadyFrame = {
@@ -85,18 +129,19 @@ type SdkEventsReadyFrame = {
85
129
  */
86
130
  class RemoteSession implements RemoteAgentSession {
87
131
  readonly id: string;
88
- private readonly baseUrl: string;
89
- private readonly eventHub = new SessionEventHub();
90
- private readonly turnsById = new Map<string, RemoteTurnLifecycle>();
91
- private readonly completedTurnIds: string[] = [];
92
- private eventPumpConnectPromise: Promise<void> | null = null;
93
- private eventPumpAbortController: AbortController | null = null;
94
- private eventPumpRunning = false;
95
- private eventSubscriberCount = 0;
96
132
 
97
- constructor(baseUrl: string, sessionId: string) {
98
- this.baseUrl = baseUrl;
99
- this.id = sessionId;
133
+ private readonly transport: RemoteSessionTransport;
134
+ private readonly event_hub = new SessionEventHub();
135
+ private readonly turns_by_id = new Map<string, RemoteTurnLifecycle>();
136
+ private readonly completed_turn_ids: string[] = [];
137
+ private event_pump_connect_promise: Promise<void> | null = null;
138
+ private event_pump_running = false;
139
+ private event_subscriber_count = 0;
140
+ private event_subscription: TransportSubscription | null = null;
141
+
142
+ constructor(transport: RemoteSessionTransport, session_id: string) {
143
+ this.transport = transport;
144
+ this.id = session_id;
100
145
  }
101
146
 
102
147
  /**
@@ -112,18 +157,7 @@ class RemoteSession implements RemoteAgentSession {
112
157
  * 读取当前远程 session 详情。
113
158
  */
114
159
  async getInfo(): Promise<AgentSessionInfo> {
115
- const response = await fetch(
116
- `${this.baseUrl}/api/sdk/sessions/${encodeURIComponent(this.id)}`,
117
- );
118
- const payload = (await response.json()) as {
119
- success?: boolean;
120
- error?: string;
121
- session?: AgentSessionInfo;
122
- };
123
- if (!response.ok || !payload.success || !payload.session?.sessionId) {
124
- throw new Error(String(payload.error || "Remote session info failed"));
125
- }
126
- return payload.session;
160
+ return await this.transport.get_info(this.id);
127
161
  }
128
162
 
129
163
  /**
@@ -135,51 +169,28 @@ class RemoteSession implements RemoteAgentSession {
135
169
  throw new Error("remote session.prompt requires a non-empty query");
136
170
  }
137
171
 
138
- await this.ensureEventPump();
139
-
140
- const response = await fetch(
141
- `${this.baseUrl}/api/sdk/sessions/${encodeURIComponent(this.id)}/prompt`,
142
- {
143
- method: "POST",
144
- headers: {
145
- "Content-Type": "application/json",
146
- },
147
- body: JSON.stringify({
148
- query,
149
- }),
150
- },
151
- );
152
- const payload = (await response.json()) as {
153
- success?: boolean;
154
- error?: string;
155
- turn?: {
156
- id?: string;
157
- };
158
- };
159
- if (!response.ok || !payload.success || !payload.turn?.id) {
160
- throw new Error(String(payload.error || "Remote session prompt failed"));
161
- }
162
-
163
- const lifecycle = this.ensureTurnLifecycle(payload.turn.id);
164
- return createTurnHandle(lifecycle);
172
+ await this.ensure_event_pump();
173
+ const turn = await this.transport.prompt(this.id, input);
174
+ const lifecycle = this.ensure_turn_lifecycle(turn.id);
175
+ return create_turn_handle(lifecycle);
165
176
  }
166
177
 
167
178
  /**
168
179
  * 订阅当前远程 session 的 future 事件。
169
180
  */
170
181
  subscribe(subscriber: AgentSessionSubscriber): AgentSessionUnsubscribe {
171
- this.eventSubscriberCount += 1;
172
- void this.ensureEventPump().catch((error) => {
173
- this.eventHub.publish({
182
+ this.event_subscriber_count += 1;
183
+ void this.ensure_event_pump().catch((error) => {
184
+ this.event_hub.publish({
174
185
  type: "error",
175
186
  message: error instanceof Error ? error.message : String(error),
176
187
  });
177
188
  });
178
- const unsubscribe = this.eventHub.subscribe(subscriber);
189
+ const unsubscribe = this.event_hub.subscribe(subscriber);
179
190
  return () => {
180
191
  unsubscribe();
181
- this.eventSubscriberCount = Math.max(0, this.eventSubscriberCount - 1);
182
- this.maybeStopEventPump();
192
+ this.event_subscriber_count = Math.max(0, this.event_subscriber_count - 1);
193
+ void this.maybe_stop_event_pump();
183
194
  };
184
195
  }
185
196
 
@@ -187,208 +198,66 @@ class RemoteSession implements RemoteAgentSession {
187
198
  * 读取远程消息历史。
188
199
  */
189
200
  async history(input?: AgentSessionHistoryInput): Promise<AgentSessionHistoryPage> {
190
- const query = new URLSearchParams();
191
- if (input?.limit !== undefined) query.set("limit", String(input.limit));
192
- if (input?.cursor) query.set("cursor", input.cursor);
193
- if (input?.order) query.set("order", input.order);
194
- if (input?.view) query.set("view", input.view);
195
- const response = await fetch(
196
- `${this.baseUrl}/api/sdk/sessions/${encodeURIComponent(this.id)}/history${
197
- query.size > 0 ? `?${query.toString()}` : ""
198
- }`,
199
- );
200
- const payload = (await response.json()) as {
201
- success?: boolean;
202
- error?: string;
203
- history?: AgentSessionHistoryPage;
204
- };
205
- if (
206
- !response.ok ||
207
- !payload.success ||
208
- !payload.history ||
209
- !Array.isArray(payload.history.items)
210
- ) {
211
- throw new Error(String(payload.error || "Remote session history failed"));
212
- }
213
- return payload.history;
201
+ return await this.transport.history(this.id, input);
214
202
  }
215
203
 
216
204
  /**
217
205
  * 读取远程 session 当前生效的 system prompt 快照。
218
206
  */
219
207
  async system(): Promise<AgentSessionSystemSnapshot> {
220
- const response = await fetch(
221
- `${this.baseUrl}/api/sdk/sessions/${encodeURIComponent(this.id)}/system`,
222
- );
223
- const payload = (await response.json()) as {
224
- success?: boolean;
225
- error?: string;
226
- system?: AgentSessionSystemSnapshot;
227
- };
228
- if (
229
- !response.ok ||
230
- !payload.success ||
231
- !payload.system ||
232
- !Array.isArray(payload.system.blocks)
233
- ) {
234
- throw new Error(String(payload.error || "Remote session system failed"));
235
- }
236
- return payload.system;
208
+ return await this.transport.system(this.id);
237
209
  }
238
210
 
239
211
  /**
240
212
  * 分叉远程 session。
241
213
  */
242
214
  async fork(input?: AgentSessionForkInput | string): Promise<RemoteAgentSession> {
243
- const messageId =
244
- typeof input === "string"
245
- ? String(input || "").trim() || undefined
246
- : String(input?.messageId || "").trim() || undefined;
247
- const response = await fetch(
248
- `${this.baseUrl}/api/sdk/sessions/${encodeURIComponent(this.id)}/fork`,
249
- {
250
- method: "POST",
251
- headers: {
252
- "Content-Type": "application/json",
253
- },
254
- body: JSON.stringify({
255
- ...(messageId ? { messageId } : {}),
256
- }),
257
- },
258
- );
259
- const payload = (await response.json()) as {
260
- success?: boolean;
261
- error?: string;
262
- session?: AgentSessionInfo;
263
- };
264
- if (!response.ok || !payload.success || !payload.session?.sessionId) {
265
- throw new Error(String(payload.error || "Remote session fork failed"));
266
- }
267
- return new RemoteSession(this.baseUrl, payload.session.sessionId);
215
+ const info = await this.transport.fork(this.id, input);
216
+ return new RemoteSession(this.transport, info.sessionId);
268
217
  }
269
218
 
270
- private async ensureEventPump(): Promise<void> {
271
- if (this.eventPumpConnectPromise) {
272
- await this.eventPumpConnectPromise;
219
+ private async ensure_event_pump(): Promise<void> {
220
+ if (this.event_pump_connect_promise) {
221
+ await this.event_pump_connect_promise;
273
222
  return;
274
223
  }
275
- if (this.eventPumpRunning) return;
276
-
277
- this.eventPumpConnectPromise = (async () => {
278
- const abortController = new AbortController();
279
- this.eventPumpAbortController = abortController;
280
- const response = await fetch(
281
- `${this.baseUrl}/api/sdk/sessions/${encodeURIComponent(this.id)}/events`,
282
- {
283
- signal: abortController.signal,
224
+ if (this.event_pump_running) return;
225
+
226
+ this.event_pump_connect_promise = (async () => {
227
+ let resolved_ready = false;
228
+ this.event_subscription = await this.transport.subscribe({
229
+ session_id: this.id,
230
+ on_ready: () => {
231
+ resolved_ready = true;
284
232
  },
285
- );
286
- if (!response.ok || !response.body) {
287
- const text = await response.text().catch(() => "");
288
- throw new Error(
289
- text || `Remote session events failed (${response.status})`,
290
- );
233
+ on_event: (event) => {
234
+ this.handle_event(event);
235
+ },
236
+ });
237
+ this.event_pump_running = true;
238
+ if (!resolved_ready) {
239
+ throw new Error("Remote session events connection closed before ready");
291
240
  }
292
- this.eventPumpRunning = true;
293
- const ready = createEventConnectionReady();
294
- void this.consumeEventConnection(response.body, abortController, ready)
295
- .finally(() => {
296
- const wasAborted = abortController.signal.aborted;
297
- this.eventPumpRunning = false;
298
- if (this.eventPumpAbortController === abortController) {
299
- this.eventPumpAbortController = null;
300
- }
301
- if (!wasAborted) {
302
- this.failPendingTurns("Remote session events connection closed");
303
- this.eventHub.publish({
304
- type: "error",
305
- message: "Remote session events connection closed",
306
- });
307
- }
308
- });
309
- await ready.promise;
310
241
  })();
311
242
 
312
243
  try {
313
- await this.eventPumpConnectPromise;
314
- } catch (error) {
315
- this.eventPumpAbortController = null;
316
- throw error;
317
- } finally {
318
- this.eventPumpConnectPromise = null;
319
- }
320
- }
321
-
322
- private async consumeEventConnection(
323
- body: ReadableStream<Uint8Array>,
324
- abortController: AbortController,
325
- ready: EventConnectionReady,
326
- ): Promise<void> {
327
- const decoder = new TextDecoder();
328
- const reader = body.getReader();
329
- let buffered = "";
330
-
331
- try {
332
- while (true) {
333
- const { done, value } = await reader.read();
334
- if (done) break;
335
- buffered += decoder.decode(value, { stream: true });
336
- let newlineIndex = buffered.indexOf("\n");
337
- while (newlineIndex >= 0) {
338
- const line = buffered.slice(0, newlineIndex).trim();
339
- buffered = buffered.slice(newlineIndex + 1);
340
- if (line) {
341
- this.handleEventLine(JSON.parse(line) as unknown, ready);
342
- }
343
- newlineIndex = buffered.indexOf("\n");
344
- }
345
- }
346
- const tail = buffered.trim();
347
- if (tail) {
348
- this.handleEventLine(JSON.parse(tail) as unknown, ready);
349
- }
350
- rejectEventConnectionReady(
351
- ready,
352
- "Remote session events connection closed before ready",
353
- );
354
- } catch (error) {
355
- if (!abortController.signal.aborted) {
356
- const message = error instanceof Error ? error.message : String(error);
357
- rejectEventConnectionReady(ready, message);
358
- this.failPendingTurns(message);
359
- this.eventHub.publish({
360
- type: "error",
361
- message,
362
- });
363
- }
244
+ await this.event_pump_connect_promise;
364
245
  } finally {
365
- try {
366
- reader.releaseLock();
367
- } catch {
368
- // ignore
369
- }
246
+ this.event_pump_connect_promise = null;
370
247
  }
371
248
  }
372
249
 
373
- private handleEventLine(
374
- value: unknown,
375
- ready: EventConnectionReady,
376
- ): void {
377
- if (isSdkEventsReadyFrame(value)) {
378
- resolveEventConnectionReady(ready);
379
- return;
250
+ private handle_event(event: AgentSessionEvent): void {
251
+ if (event.type === "error") {
252
+ this.fail_pending_turns(event.message);
380
253
  }
381
- this.handleEvent(value as AgentSessionEvent);
382
- }
383
-
384
- private handleEvent(event: AgentSessionEvent): void {
385
- const turnId = extractTurnId(event);
386
- if (turnId) {
387
- this.ensureTurnLifecycle(turnId);
254
+ const turn_id = extract_turn_id(event);
255
+ if (turn_id) {
256
+ this.ensure_turn_lifecycle(turn_id);
388
257
  }
389
258
 
390
259
  if (event.type === "turn-finish") {
391
- const lifecycle = this.ensureTurnLifecycle(event.turnId);
260
+ const lifecycle = this.ensure_turn_lifecycle(event.turnId);
392
261
  const result: AgentSessionTurnResult = {
393
262
  turnId: event.turnId,
394
263
  text: event.text,
@@ -396,28 +265,28 @@ class RemoteSession implements RemoteAgentSession {
396
265
  ...(event.error ? { error: event.error } : {}),
397
266
  };
398
267
  lifecycle.result = result;
399
- lifecycle.deferredFinished.resolve(result);
400
- this.rememberCompletedTurn(event.turnId);
401
- this.maybeStopEventPump();
268
+ lifecycle.deferred_finished.resolve(result);
269
+ this.remember_completed_turn(event.turnId);
270
+ void this.maybe_stop_event_pump();
402
271
  }
403
272
 
404
- this.eventHub.publish(event);
273
+ this.event_hub.publish(event);
405
274
  }
406
275
 
407
- private ensureTurnLifecycle(turnId: string): RemoteTurnLifecycle {
408
- const cached = this.turnsById.get(turnId);
276
+ private ensure_turn_lifecycle(turn_id: string): RemoteTurnLifecycle {
277
+ const cached = this.turns_by_id.get(turn_id);
409
278
  if (cached) return cached;
410
279
  const created: RemoteTurnLifecycle = {
411
- turnId,
280
+ turnId: turn_id,
412
281
  result: null,
413
- deferredFinished: createDeferred<AgentSessionTurnResult>(),
282
+ deferred_finished: create_deferred<AgentSessionTurnResult>(),
414
283
  };
415
- this.turnsById.set(turnId, created);
284
+ this.turns_by_id.set(turn_id, created);
416
285
  return created;
417
286
  }
418
287
 
419
- private failPendingTurns(message: string): void {
420
- for (const lifecycle of this.turnsById.values()) {
288
+ private fail_pending_turns(message: string): void {
289
+ for (const lifecycle of this.turns_by_id.values()) {
421
290
  if (lifecycle.result) continue;
422
291
  const result: AgentSessionTurnResult = {
423
292
  turnId: lifecycle.turnId,
@@ -426,28 +295,33 @@ class RemoteSession implements RemoteAgentSession {
426
295
  error: message,
427
296
  };
428
297
  lifecycle.result = result;
429
- lifecycle.deferredFinished.resolve(result);
430
- this.rememberCompletedTurn(lifecycle.turnId);
298
+ lifecycle.deferred_finished.resolve(result);
299
+ this.remember_completed_turn(lifecycle.turnId);
431
300
  }
432
- this.maybeStopEventPump();
433
301
  }
434
302
 
435
- private rememberCompletedTurn(turnId: string): void {
436
- this.completedTurnIds.push(turnId);
437
- while (this.completedTurnIds.length > 200) {
438
- const oldestTurnId = this.completedTurnIds.shift();
439
- if (oldestTurnId) {
440
- this.turnsById.delete(oldestTurnId);
303
+ private remember_completed_turn(turn_id: string): void {
304
+ this.completed_turn_ids.push(turn_id);
305
+ while (this.completed_turn_ids.length > 200) {
306
+ const oldest_turn_id = this.completed_turn_ids.shift();
307
+ if (oldest_turn_id) {
308
+ this.turns_by_id.delete(oldest_turn_id);
441
309
  }
442
310
  }
443
311
  }
444
312
 
445
- private maybeStopEventPump(): void {
446
- if (this.eventSubscriberCount > 0) return;
447
- if ([...this.turnsById.values()].some((item) => item.result === null)) return;
448
- if (!this.eventPumpAbortController) return;
449
- this.eventPumpAbortController.abort();
450
- this.eventPumpAbortController = null;
313
+ private async maybe_stop_event_pump(): Promise<void> {
314
+ if (this.event_subscriber_count > 0) return;
315
+ if ([...this.turns_by_id.values()].some((item) => item.result === null)) return;
316
+ const current = this.event_subscription;
317
+ this.event_subscription = null;
318
+ this.event_pump_running = false;
319
+ if (!current) return;
320
+ await current.close().catch((error) => {
321
+ this.fail_pending_turns(
322
+ error instanceof Error ? error.message : String(error),
323
+ );
324
+ });
451
325
  }
452
326
  }
453
327
 
@@ -455,14 +329,14 @@ class RemoteSession implements RemoteAgentSession {
455
329
  * RemoteAgent:远程 Agent 客户端。
456
330
  */
457
331
  export class RemoteAgent {
458
- private readonly baseUrl: string;
332
+ private readonly transport: RemoteAgentTransport;
459
333
 
460
334
  constructor(options: RemoteAgentOptions) {
461
- const baseUrl = String(options.baseUrl || "").trim().replace(/\/+$/, "");
462
- if (!baseUrl) {
463
- throw new Error("RemoteAgent requires a non-empty baseUrl");
335
+ const url = String(options.url || "").trim();
336
+ if (!url) {
337
+ throw new Error("RemoteAgent requires a non-empty url");
464
338
  }
465
- this.baseUrl = baseUrl;
339
+ this.transport = create_remote_agent_transport(url);
466
340
  }
467
341
 
468
342
  /**
@@ -471,7 +345,45 @@ export class RemoteAgent {
471
345
  async createSession(
472
346
  input?: AgentCreateSessionInput,
473
347
  ): Promise<RemoteAgentSession> {
474
- const response = await fetch(`${this.baseUrl}/api/sdk/sessions`, {
348
+ const info = await this.transport.create_session(input);
349
+ return new RemoteSession(this.transport, info.sessionId);
350
+ }
351
+
352
+ /**
353
+ * 获取一个已存在的远程 session。
354
+ */
355
+ async getSession(sessionId: string): Promise<RemoteAgentSession> {
356
+ const resolved_session_id = String(sessionId || "").trim();
357
+ if (!resolved_session_id) {
358
+ throw new Error("getSession requires a non-empty sessionId");
359
+ }
360
+ const info = await this.transport.get_info(resolved_session_id);
361
+ return new RemoteSession(this.transport, info.sessionId);
362
+ }
363
+
364
+ /**
365
+ * 列出远程 agent 的 session 摘要页。
366
+ */
367
+ async listSessions(
368
+ input?: AgentListSessionsInput,
369
+ ): Promise<AgentSessionSummaryPage> {
370
+ return await this.transport.list_sessions(input);
371
+ }
372
+ }
373
+
374
+ class HttpRemoteAgentTransport implements RemoteAgentTransport {
375
+ private readonly base_url: string;
376
+
377
+ constructor(url: string) {
378
+ this.base_url = url.replace(/\/+$/, "");
379
+ }
380
+
381
+ async create_session(input?: AgentCreateSessionInput): Promise<AgentSessionInfo> {
382
+ const payload = await read_http_json<{
383
+ success?: boolean;
384
+ error?: string;
385
+ session?: AgentSessionInfo;
386
+ }>(`${this.base_url}/api/sdk/sessions`, {
475
387
  method: "POST",
476
388
  headers: {
477
389
  "Content-Type": "application/json",
@@ -480,111 +392,369 @@ export class RemoteAgent {
480
392
  ...(input?.sessionId ? { sessionId: input.sessionId } : {}),
481
393
  }),
482
394
  });
483
- const payload = (await response.json()) as {
395
+ if (!payload.success || !payload.session?.sessionId) {
396
+ throw new Error(String(payload.error || "Remote session create failed"));
397
+ }
398
+ return payload.session;
399
+ }
400
+
401
+ async get_info(session_id: string): Promise<AgentSessionInfo> {
402
+ const payload = await read_http_json<{
484
403
  success?: boolean;
485
404
  error?: string;
486
405
  session?: AgentSessionInfo;
487
- };
488
- if (!response.ok || !payload.success || !payload.session?.sessionId) {
489
- throw new Error(String(payload.error || "Remote session create failed"));
406
+ }>(`${this.base_url}/api/sdk/sessions/${encodeURIComponent(session_id)}`);
407
+ if (!payload.success || !payload.session?.sessionId) {
408
+ throw new Error(String(payload.error || "Remote session info failed"));
490
409
  }
491
- return new RemoteSession(this.baseUrl, payload.session.sessionId);
410
+ return payload.session;
492
411
  }
493
412
 
494
- /**
495
- * 获取一个已存在的远程 session。
496
- */
497
- async getSession(sessionId: string): Promise<RemoteAgentSession> {
498
- const resolvedSessionId = String(sessionId || "").trim();
499
- if (!resolvedSessionId) {
500
- throw new Error("getSession requires a non-empty sessionId");
413
+ async prompt(
414
+ session_id: string,
415
+ input: AgentSessionPromptInput,
416
+ ): Promise<{ id: string }> {
417
+ const payload = await read_http_json<{
418
+ success?: boolean;
419
+ error?: string;
420
+ turn?: {
421
+ id?: string;
422
+ };
423
+ }>(`${this.base_url}/api/sdk/sessions/${encodeURIComponent(session_id)}/prompt`, {
424
+ method: "POST",
425
+ headers: {
426
+ "Content-Type": "application/json",
427
+ },
428
+ body: JSON.stringify({
429
+ query: input.query,
430
+ }),
431
+ });
432
+ const id = String(payload.turn?.id || "").trim();
433
+ if (!payload.success || !id) {
434
+ throw new Error(String(payload.error || "Remote session prompt failed"));
501
435
  }
436
+ return { id };
437
+ }
438
+
439
+ async subscribe(params: {
440
+ session_id: string;
441
+ on_ready: () => void;
442
+ on_event: (event: AgentSessionEvent) => void;
443
+ }): Promise<TransportSubscription> {
444
+ const abort_controller = new AbortController();
445
+ let resolve_ready!: () => void;
446
+ let reject_ready!: (error: unknown) => void;
447
+ const ready_promise = new Promise<void>((resolve, reject) => {
448
+ resolve_ready = resolve;
449
+ reject_ready = reject;
450
+ });
502
451
  const response = await fetch(
503
- `${this.baseUrl}/api/sdk/sessions/${encodeURIComponent(resolvedSessionId)}`,
452
+ `${this.base_url}/api/sdk/sessions/${encodeURIComponent(params.session_id)}/events`,
453
+ {
454
+ signal: abort_controller.signal,
455
+ },
504
456
  );
505
- const payload = (await response.json()) as {
457
+ if (!response.ok || !response.body) {
458
+ const text = await response.text().catch(() => "");
459
+ throw new Error(text || `Remote session events failed (${response.status})`);
460
+ }
461
+ void consume_http_event_stream({
462
+ body: response.body,
463
+ abort_controller,
464
+ on_ready: () => {
465
+ params.on_ready();
466
+ resolve_ready();
467
+ },
468
+ on_ready_error: (error) => {
469
+ reject_ready(error);
470
+ },
471
+ on_event: params.on_event,
472
+ });
473
+ await ready_promise;
474
+ return {
475
+ close: async () => {
476
+ abort_controller.abort();
477
+ },
478
+ };
479
+ }
480
+
481
+ async history(
482
+ session_id: string,
483
+ input?: AgentSessionHistoryInput,
484
+ ): Promise<AgentSessionHistoryPage> {
485
+ const query = new URLSearchParams();
486
+ if (input?.limit !== undefined) query.set("limit", String(input.limit));
487
+ if (input?.cursor) query.set("cursor", input.cursor);
488
+ if (input?.order) query.set("order", input.order);
489
+ if (input?.view) query.set("view", input.view);
490
+ const payload = await read_http_json<{
491
+ success?: boolean;
492
+ error?: string;
493
+ history?: AgentSessionHistoryPage;
494
+ }>(
495
+ `${this.base_url}/api/sdk/sessions/${encodeURIComponent(session_id)}/history${
496
+ query.size > 0 ? `?${query.toString()}` : ""
497
+ }`,
498
+ );
499
+ if (!payload.success || !payload.history || !Array.isArray(payload.history.items)) {
500
+ throw new Error(String(payload.error || "Remote session history failed"));
501
+ }
502
+ return payload.history;
503
+ }
504
+
505
+ async system(session_id: string): Promise<AgentSessionSystemSnapshot> {
506
+ const payload = await read_http_json<{
507
+ success?: boolean;
508
+ error?: string;
509
+ system?: AgentSessionSystemSnapshot;
510
+ }>(`${this.base_url}/api/sdk/sessions/${encodeURIComponent(session_id)}/system`);
511
+ if (!payload.success || !payload.system || !Array.isArray(payload.system.blocks)) {
512
+ throw new Error(String(payload.error || "Remote session system failed"));
513
+ }
514
+ return payload.system;
515
+ }
516
+
517
+ async fork(
518
+ session_id: string,
519
+ input?: AgentSessionForkInput | string,
520
+ ): Promise<AgentSessionInfo> {
521
+ const message_id =
522
+ typeof input === "string"
523
+ ? String(input || "").trim() || undefined
524
+ : String(input?.messageId || "").trim() || undefined;
525
+ const payload = await read_http_json<{
506
526
  success?: boolean;
507
527
  error?: string;
508
528
  session?: AgentSessionInfo;
509
- };
510
- if (!response.ok || !payload.success || !payload.session?.sessionId) {
511
- throw new Error(String(payload.error || "Remote session get failed"));
529
+ }>(`${this.base_url}/api/sdk/sessions/${encodeURIComponent(session_id)}/fork`, {
530
+ method: "POST",
531
+ headers: {
532
+ "Content-Type": "application/json",
533
+ },
534
+ body: JSON.stringify({
535
+ ...(message_id ? { messageId: message_id } : {}),
536
+ }),
537
+ });
538
+ if (!payload.success || !payload.session?.sessionId) {
539
+ throw new Error(String(payload.error || "Remote session fork failed"));
512
540
  }
513
- return new RemoteSession(this.baseUrl, payload.session.sessionId);
541
+ return payload.session;
514
542
  }
515
543
 
516
- /**
517
- * 列出远程 agent 的 session 摘要页。
518
- */
519
- async listSessions(
520
- input?: AgentListSessionsInput,
521
- ): Promise<AgentSessionSummaryPage> {
544
+ async list_sessions(input?: AgentListSessionsInput): Promise<AgentSessionSummaryPage> {
522
545
  const query = new URLSearchParams();
523
546
  if (input?.limit !== undefined) query.set("limit", String(input.limit));
524
547
  if (input?.cursor) query.set("cursor", input.cursor);
525
548
  if (input?.query) query.set("query", input.query);
526
- const response = await fetch(
527
- `${this.baseUrl}/api/sdk/sessions${query.size > 0 ? `?${query.toString()}` : ""}`,
528
- );
529
- const payload = (await response.json()) as {
549
+ const payload = await read_http_json<{
530
550
  success?: boolean;
531
551
  error?: string;
532
552
  page?: AgentSessionSummaryPage;
533
- };
534
- if (!response.ok || !payload.success || !payload.page) {
553
+ }>(
554
+ `${this.base_url}/api/sdk/sessions${query.size > 0 ? `?${query.toString()}` : ""}`,
555
+ );
556
+ if (!payload.success || !payload.page) {
535
557
  throw new Error(String(payload.error || "Remote sessions list failed"));
536
558
  }
537
559
  return payload.page;
538
560
  }
539
561
  }
540
562
 
541
- function createDeferred<T>(): Deferred<T> {
542
- let resolve!: (value: T) => void;
543
- const promise = new Promise<T>((innerResolve) => {
544
- resolve = innerResolve;
545
- });
546
- return {
547
- promise,
548
- resolve,
549
- };
563
+ class RpcRemoteAgentTransport implements RemoteAgentTransport {
564
+ private readonly client: RpcClient;
565
+
566
+ constructor(url: string) {
567
+ this.client = new RpcClient(parse_rpc_url(url));
568
+ }
569
+
570
+ async create_session(input?: AgentCreateSessionInput): Promise<AgentSessionInfo> {
571
+ return await this.client.create_session(input);
572
+ }
573
+
574
+ async get_info(session_id: string): Promise<AgentSessionInfo> {
575
+ return await this.client.get_session(session_id);
576
+ }
577
+
578
+ async prompt(
579
+ session_id: string,
580
+ input: AgentSessionPromptInput,
581
+ ): Promise<{ id: string }> {
582
+ return await this.client.prompt_session({
583
+ session_id,
584
+ input,
585
+ });
586
+ }
587
+
588
+ async subscribe(params: {
589
+ session_id: string;
590
+ on_ready: () => void;
591
+ on_event: (event: AgentSessionEvent) => void;
592
+ }): Promise<TransportSubscription> {
593
+ const subscription = await this.client.subscribe_session({
594
+ session_id: params.session_id,
595
+ on_ready: params.on_ready,
596
+ on_event: params.on_event,
597
+ });
598
+ return {
599
+ close: async () => {
600
+ await subscription.unsubscribe();
601
+ },
602
+ };
603
+ }
604
+
605
+ async history(
606
+ session_id: string,
607
+ input?: AgentSessionHistoryInput,
608
+ ): Promise<AgentSessionHistoryPage> {
609
+ return await this.client.get_session_history({
610
+ session_id,
611
+ input,
612
+ });
613
+ }
614
+
615
+ async system(session_id: string): Promise<AgentSessionSystemSnapshot> {
616
+ return await this.client.get_session_system(session_id);
617
+ }
618
+
619
+ async fork(
620
+ session_id: string,
621
+ input?: AgentSessionForkInput | string,
622
+ ): Promise<AgentSessionInfo> {
623
+ const message_id =
624
+ typeof input === "string"
625
+ ? String(input || "").trim() || undefined
626
+ : String(input?.messageId || "").trim() || undefined;
627
+ return await this.client.fork_session({
628
+ session_id,
629
+ ...(message_id ? { message_id } : {}),
630
+ });
631
+ }
632
+
633
+ async list_sessions(input?: AgentListSessionsInput): Promise<AgentSessionSummaryPage> {
634
+ return await this.client.list_sessions(input);
635
+ }
550
636
  }
551
637
 
552
- function createEventConnectionReady(): EventConnectionReady & {
553
- /**
554
- * 等待事件连接 ready 的 Promise。
555
- */
556
- promise: Promise<void>;
557
- } {
558
- let resolve!: () => void;
559
- let reject!: (error: unknown) => void;
560
- const promise = new Promise<void>((innerResolve, innerReject) => {
561
- resolve = innerResolve;
562
- reject = innerReject;
638
+ function create_remote_agent_transport(url: string): RemoteAgentTransport {
639
+ if (/^https?:\/\//i.test(url)) {
640
+ return new HttpRemoteAgentTransport(url);
641
+ }
642
+ if (/^rpc:\/\//i.test(url)) {
643
+ return new RpcRemoteAgentTransport(url);
644
+ }
645
+ throw new Error(
646
+ `Unsupported RemoteAgent url protocol: ${url}. Expected http://, https://, or rpc://`,
647
+ );
648
+ }
649
+
650
+ async function read_http_json<T>(input: string, init?: RequestInit): Promise<T> {
651
+ const response = await fetch(input, init);
652
+ const payload = (await response.json().catch(() => ({}))) as T;
653
+ if (!response.ok) {
654
+ const message = extract_error_message(payload);
655
+ throw new Error(message || `HTTP ${response.status}`);
656
+ }
657
+ return payload;
658
+ }
659
+
660
+ async function consume_http_event_stream(params: {
661
+ body: ReadableStream<Uint8Array>;
662
+ abort_controller: AbortController;
663
+ on_ready: () => void;
664
+ on_ready_error: (error: unknown) => void;
665
+ on_event: (event: AgentSessionEvent) => void;
666
+ }): Promise<void> {
667
+ const decoder = new TextDecoder();
668
+ const reader = params.body.getReader();
669
+ let buffered = "";
670
+ let ready_resolved = false;
671
+
672
+ try {
673
+ while (true) {
674
+ const { done, value } = await reader.read();
675
+ if (done) break;
676
+ buffered += decoder.decode(value, { stream: true });
677
+ let newline_index = buffered.indexOf("\n");
678
+ while (newline_index >= 0) {
679
+ const line = buffered.slice(0, newline_index).trim();
680
+ buffered = buffered.slice(newline_index + 1);
681
+ if (line) {
682
+ const value = JSON.parse(line) as unknown;
683
+ if (is_sdk_events_ready_frame(value)) {
684
+ ready_resolved = true;
685
+ params.on_ready();
686
+ } else {
687
+ params.on_event(value as AgentSessionEvent);
688
+ }
689
+ }
690
+ newline_index = buffered.indexOf("\n");
691
+ }
692
+ }
693
+
694
+ const tail = buffered.trim();
695
+ if (tail) {
696
+ const value = JSON.parse(tail) as unknown;
697
+ if (is_sdk_events_ready_frame(value)) {
698
+ ready_resolved = true;
699
+ params.on_ready();
700
+ } else {
701
+ params.on_event(value as AgentSessionEvent);
702
+ }
703
+ }
704
+
705
+ if (!params.abort_controller.signal.aborted) {
706
+ if (!ready_resolved) {
707
+ const error = new Error("Remote session events connection closed before ready");
708
+ params.on_ready_error(error);
709
+ throw error;
710
+ }
711
+ params.on_event({
712
+ type: "error",
713
+ message: "Remote session events connection closed",
714
+ });
715
+ }
716
+ } catch (error) {
717
+ if (!params.abort_controller.signal.aborted) {
718
+ if (!ready_resolved) {
719
+ params.on_ready_error(error);
720
+ }
721
+ params.on_event({
722
+ type: "error",
723
+ message: error instanceof Error ? error.message : String(error),
724
+ });
725
+ }
726
+ } finally {
727
+ try {
728
+ reader.releaseLock();
729
+ } catch {
730
+ // ignore
731
+ }
732
+ }
733
+ }
734
+
735
+ function extract_error_message(payload: unknown): string {
736
+ if (!payload || typeof payload !== "object") return "";
737
+ if ("error" in payload && typeof payload.error === "string") {
738
+ return payload.error;
739
+ }
740
+ if ("message" in payload && typeof payload.message === "string") {
741
+ return payload.message;
742
+ }
743
+ return "";
744
+ }
745
+
746
+ function create_deferred<T>(): Deferred<T> {
747
+ let resolve!: (value: T) => void;
748
+ const promise = new Promise<T>((inner_resolve) => {
749
+ resolve = inner_resolve;
563
750
  });
564
751
  return {
565
752
  promise,
566
- settled: false,
567
753
  resolve,
568
- reject,
569
754
  };
570
755
  }
571
756
 
572
- function resolveEventConnectionReady(ready: EventConnectionReady): void {
573
- if (ready.settled) return;
574
- ready.settled = true;
575
- ready.resolve();
576
- }
577
-
578
- function rejectEventConnectionReady(
579
- ready: EventConnectionReady,
580
- error: unknown,
581
- ): void {
582
- if (ready.settled) return;
583
- ready.settled = true;
584
- ready.reject(error);
585
- }
586
-
587
- function isSdkEventsReadyFrame(value: unknown): value is SdkEventsReadyFrame {
757
+ function is_sdk_events_ready_frame(value: unknown): value is SdkEventsReadyFrame {
588
758
  return (
589
759
  typeof value === "object" &&
590
760
  value !== null &&
@@ -593,7 +763,7 @@ function isSdkEventsReadyFrame(value: unknown): value is SdkEventsReadyFrame {
593
763
  );
594
764
  }
595
765
 
596
- function createTurnHandle(
766
+ function create_turn_handle(
597
767
  lifecycle: RemoteTurnLifecycle,
598
768
  ): AgentSessionTurnHandle {
599
769
  return {
@@ -601,11 +771,11 @@ function createTurnHandle(
601
771
  get result() {
602
772
  return lifecycle.result;
603
773
  },
604
- finished: lifecycle.deferredFinished.promise,
774
+ finished: lifecycle.deferred_finished.promise,
605
775
  };
606
776
  }
607
777
 
608
- function extractTurnId(event: AgentSessionEvent): string | null {
778
+ function extract_turn_id(event: AgentSessionEvent): string | null {
609
779
  switch (event.type) {
610
780
  case "turn-start":
611
781
  case "text-delta":