@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.
- package/README.md +6 -8
- package/bin/agent/Agent.d.ts +31 -16
- package/bin/agent/Agent.d.ts.map +1 -1
- package/bin/agent/Agent.js +47 -63
- package/bin/agent/Agent.js.map +1 -1
- package/bin/agent/RemoteAgent.d.ts +6 -6
- package/bin/agent/RemoteAgent.d.ts.map +1 -1
- package/bin/agent/RemoteAgent.js +377 -267
- package/bin/agent/RemoteAgent.js.map +1 -1
- package/bin/config/AgentInitializer.d.ts +4 -4
- package/bin/config/AgentInitializer.d.ts.map +1 -1
- package/bin/config/AgentInitializer.js +14 -12
- package/bin/config/AgentInitializer.js.map +1 -1
- package/bin/config/Config.d.ts +9 -30
- package/bin/config/Config.d.ts.map +1 -1
- package/bin/config/Config.js +23 -70
- package/bin/config/Config.js.map +1 -1
- package/bin/config/Defaults.d.ts.map +1 -1
- package/bin/config/Defaults.js +1 -9
- package/bin/config/Defaults.js.map +1 -1
- package/bin/config/DowncitySchema.d.ts.map +1 -1
- package/bin/config/DowncitySchema.js +3 -111
- package/bin/config/DowncitySchema.js.map +1 -1
- package/bin/config/ExecutionBinding.d.ts +0 -15
- package/bin/config/ExecutionBinding.d.ts.map +1 -1
- package/bin/config/ExecutionBinding.js +1 -21
- package/bin/config/ExecutionBinding.js.map +1 -1
- package/bin/config/Paths.d.ts +4 -43
- package/bin/config/Paths.d.ts.map +1 -1
- package/bin/config/Paths.js +10 -30
- package/bin/config/Paths.js.map +1 -1
- package/bin/config/PlatformPaths.d.ts +0 -4
- package/bin/config/PlatformPaths.d.ts.map +1 -1
- package/bin/config/PlatformPaths.js +5 -1
- package/bin/config/PlatformPaths.js.map +1 -1
- package/bin/executor/composer/system/default/assets/init/PROFILE.md.d.ts +1 -1
- package/bin/executor/composer/system/default/assets/init/PROFILE.md.d.ts.map +1 -1
- package/bin/executor/composer/system/default/assets/init/PROFILE.md.js +1 -1
- package/bin/executor/composer/system/default/assets/init/PROFILE.md.js.map +1 -1
- package/bin/executor/tools/shell/ShellToolFormatting.js +2 -2
- package/bin/executor/tools/shell/ShellToolFormatting.js.map +1 -1
- package/bin/executor/types/SessionRun.d.ts +1 -1
- package/bin/index.d.ts +4 -6
- package/bin/index.d.ts.map +1 -1
- package/bin/index.js +5 -5
- package/bin/index.js.map +1 -1
- package/bin/plugin/core/PluginCommandRequest.d.ts +1 -1
- package/bin/plugin/core/PluginCommandRequest.js +1 -1
- package/bin/plugin/core/PluginLocalExecution.d.ts.map +1 -1
- package/bin/plugin/core/PluginLocalExecution.js +4 -8
- package/bin/plugin/core/PluginLocalExecution.js.map +1 -1
- package/bin/plugin/types/PluginApi.d.ts +1 -1
- package/bin/rpc/Client.d.ts +104 -0
- package/bin/rpc/Client.d.ts.map +1 -0
- package/bin/rpc/Client.js +300 -0
- package/bin/rpc/Client.js.map +1 -0
- package/bin/rpc/Server.d.ts +41 -0
- package/bin/rpc/Server.d.ts.map +1 -0
- package/bin/rpc/Server.js +164 -0
- package/bin/rpc/Server.js.map +1 -0
- package/bin/runtime/host/daemon/Api.d.ts +1 -1
- package/bin/runtime/host/daemon/Client.d.ts +1 -1
- package/bin/runtime/host/daemon/Client.d.ts.map +1 -1
- package/bin/runtime/host/daemon/Client.js +30 -20
- package/bin/runtime/host/daemon/Client.js.map +1 -1
- package/bin/runtime/server/http/control/OverviewRoutes.js +1 -1
- package/bin/runtime/server/http/control/OverviewRoutes.js.map +1 -1
- package/bin/session/index.d.ts +1 -1
- package/bin/session/index.d.ts.map +1 -1
- package/bin/session/index.js +1 -1
- package/bin/session/index.js.map +1 -1
- package/bin/session/storage/Paths.d.ts +0 -4
- package/bin/session/storage/Paths.d.ts.map +1 -1
- package/bin/session/storage/Paths.js +0 -6
- package/bin/session/storage/Paths.js.map +1 -1
- package/bin/types/agent/AgentTypes.d.ts +23 -52
- package/bin/types/agent/AgentTypes.d.ts.map +1 -1
- package/bin/types/config/AgentProject.d.ts +6 -5
- package/bin/types/config/AgentProject.d.ts.map +1 -1
- package/bin/types/config/DowncityConfig.d.ts +8 -66
- package/bin/types/config/DowncityConfig.d.ts.map +1 -1
- package/bin/types/config/LlmConfig.d.ts +1 -1
- package/bin/types/config/Start.d.ts +3 -0
- package/bin/types/config/Start.d.ts.map +1 -1
- package/bin/types/config/Start.js +2 -0
- package/bin/types/config/Start.js.map +1 -1
- package/bin/types/runtime/host/Store.d.ts +7 -50
- package/bin/types/runtime/host/Store.d.ts.map +1 -1
- package/bin/types/runtime/platform/Platform.d.ts +6 -6
- package/bin/types/runtime/platform/Platform.d.ts.map +1 -1
- package/bin/types/runtime/platform/PlatformGateway.d.ts +2 -2
- package/bin/types/runtime/platform/PlatformGateway.d.ts.map +1 -1
- package/bin/utils/Time.d.ts +0 -1
- package/bin/utils/Time.d.ts.map +1 -1
- package/bin/utils/Time.js +0 -7
- package/bin/utils/Time.js.map +1 -1
- package/bin/utils/storage/index.d.ts +0 -1
- package/bin/utils/storage/index.d.ts.map +1 -1
- package/bin/utils/storage/index.js +0 -6
- package/bin/utils/storage/index.js.map +1 -1
- package/package.json +4 -6
- package/src/agent/Agent.ts +54 -71
- package/src/agent/RemoteAgent.ts +515 -345
- package/src/config/AgentInitializer.ts +14 -12
- package/src/config/Config.ts +28 -85
- package/src/config/Defaults.ts +1 -9
- package/src/config/DowncitySchema.ts +3 -113
- package/src/config/ExecutionBinding.ts +1 -23
- package/src/config/Paths.ts +10 -43
- package/src/config/PlatformPaths.ts +5 -1
- package/src/executor/composer/system/default/assets/init/PROFILE.md.ts +1 -1
- package/src/executor/composer/system/default/assets/init/PROFILE.md.ts.txt +1 -2
- package/src/executor/tools/shell/ShellToolFormatting.ts +2 -2
- package/src/executor/types/SessionRun.ts +1 -1
- package/src/index.ts +7 -14
- package/src/plugin/core/PluginCommandRequest.ts +1 -1
- package/src/plugin/core/PluginLocalExecution.ts +4 -8
- package/src/plugin/types/PluginApi.ts +1 -1
- package/src/rpc/Client.ts +467 -0
- package/src/rpc/Server.ts +302 -0
- package/src/runtime/host/daemon/Api.ts +1 -1
- package/src/runtime/host/daemon/Client.ts +44 -22
- package/src/runtime/server/http/control/OverviewRoutes.ts +1 -1
- package/src/session/index.ts +0 -1
- package/src/session/storage/Paths.ts +0 -10
- package/src/types/agent/AgentTypes.ts +23 -57
- package/src/types/config/AgentProject.ts +6 -5
- package/src/types/config/DowncityConfig.ts +8 -67
- package/src/types/config/LlmConfig.ts +1 -1
- package/src/types/config/Start.ts +3 -0
- package/src/types/runtime/host/Store.ts +7 -53
- package/src/types/runtime/platform/Platform.ts +6 -6
- package/src/types/runtime/platform/PlatformGateway.ts +2 -2
- package/src/utils/Time.ts +0 -6
- package/src/utils/storage/index.ts +0 -7
- package/tsconfig.tsbuildinfo +1 -1
- package/bin/config/ConfigEnvResolver.d.ts +0 -22
- package/bin/config/ConfigEnvResolver.d.ts.map +0 -1
- package/bin/config/ConfigEnvResolver.js +0 -41
- package/bin/config/ConfigEnvResolver.js.map +0 -1
- package/bin/runtime/server/rpc/Server.d.ts +0 -18
- package/bin/runtime/server/rpc/Server.d.ts.map +0 -1
- package/bin/runtime/server/rpc/Server.js +0 -315
- package/bin/runtime/server/rpc/Server.js.map +0 -1
- package/bin/runtime/transport/rpc/Client.d.ts +0 -13
- package/bin/runtime/transport/rpc/Client.d.ts.map +0 -1
- package/bin/runtime/transport/rpc/Client.js +0 -98
- package/bin/runtime/transport/rpc/Client.js.map +0 -1
- package/bin/runtime/transport/rpc/Paths.d.ts +0 -14
- package/bin/runtime/transport/rpc/Paths.d.ts.map +0 -1
- package/bin/runtime/transport/rpc/Paths.js +0 -42
- package/bin/runtime/transport/rpc/Paths.js.map +0 -1
- package/bin/runtime/transport/rpc/Transport.d.ts +0 -21
- package/bin/runtime/transport/rpc/Transport.d.ts.map +0 -1
- package/bin/runtime/transport/rpc/Transport.js +0 -30
- package/bin/runtime/transport/rpc/Transport.js.map +0 -1
- package/bin/types/common/ResolvedConfigValue.d.ts +0 -12
- package/bin/types/common/ResolvedConfigValue.d.ts.map +0 -1
- package/bin/types/common/ResolvedConfigValue.js +0 -2
- package/bin/types/common/ResolvedConfigValue.js.map +0 -1
- package/bin/types/runtime/rpc/LocalRpc.d.ts +0 -69
- package/bin/types/runtime/rpc/LocalRpc.d.ts.map +0 -1
- package/bin/types/runtime/rpc/LocalRpc.js +0 -9
- package/bin/types/runtime/rpc/LocalRpc.js.map +0 -1
- package/src/config/ConfigEnvResolver.ts +0 -52
- package/src/runtime/server/rpc/Server.ts +0 -408
- package/src/runtime/transport/rpc/Client.ts +0 -113
- package/src/runtime/transport/rpc/Paths.ts +0 -50
- package/src/runtime/transport/rpc/Transport.ts +0 -43
- package/src/types/common/ResolvedConfigValue.ts +0 -16
- package/src/types/runtime/rpc/LocalRpc.ts +0 -72
package/src/agent/RemoteAgent.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* RemoteAgent
|
|
2
|
+
* RemoteAgent:统一远程 SDK 客户端。
|
|
3
3
|
*
|
|
4
4
|
* 关键点(中文)
|
|
5
|
-
* -
|
|
6
|
-
* -
|
|
7
|
-
* - `RemoteAgent`
|
|
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
|
-
|
|
59
|
+
deferred_finished: Deferred<AgentSessionTurnResult>;
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
type TransportSubscription = {
|
|
63
|
+
/**
|
|
64
|
+
* 关闭当前订阅。
|
|
65
|
+
*/
|
|
66
|
+
close(): Promise<void>;
|
|
59
67
|
};
|
|
60
68
|
|
|
61
|
-
type
|
|
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
|
-
*
|
|
82
|
+
* 订阅 session 事件。
|
|
64
83
|
*/
|
|
65
|
-
|
|
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
|
-
|
|
92
|
+
history(
|
|
93
|
+
session_id: string,
|
|
94
|
+
input?: AgentSessionHistoryInput,
|
|
95
|
+
): Promise<AgentSessionHistoryPage>;
|
|
70
96
|
/**
|
|
71
|
-
*
|
|
97
|
+
* 读取 system snapshot。
|
|
72
98
|
*/
|
|
73
|
-
|
|
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
|
-
|
|
98
|
-
|
|
99
|
-
|
|
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
|
-
|
|
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.
|
|
139
|
-
|
|
140
|
-
const
|
|
141
|
-
|
|
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.
|
|
172
|
-
void this.
|
|
173
|
-
this.
|
|
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.
|
|
189
|
+
const unsubscribe = this.event_hub.subscribe(subscriber);
|
|
179
190
|
return () => {
|
|
180
191
|
unsubscribe();
|
|
181
|
-
this.
|
|
182
|
-
this.
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
244
|
-
|
|
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
|
|
271
|
-
if (this.
|
|
272
|
-
await this.
|
|
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.
|
|
276
|
-
|
|
277
|
-
this.
|
|
278
|
-
|
|
279
|
-
this.
|
|
280
|
-
|
|
281
|
-
|
|
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
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
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.
|
|
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
|
-
|
|
366
|
-
reader.releaseLock();
|
|
367
|
-
} catch {
|
|
368
|
-
// ignore
|
|
369
|
-
}
|
|
246
|
+
this.event_pump_connect_promise = null;
|
|
370
247
|
}
|
|
371
248
|
}
|
|
372
249
|
|
|
373
|
-
private
|
|
374
|
-
|
|
375
|
-
|
|
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
|
-
|
|
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.
|
|
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.
|
|
400
|
-
this.
|
|
401
|
-
this.
|
|
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.
|
|
273
|
+
this.event_hub.publish(event);
|
|
405
274
|
}
|
|
406
275
|
|
|
407
|
-
private
|
|
408
|
-
const cached = this.
|
|
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
|
-
|
|
282
|
+
deferred_finished: create_deferred<AgentSessionTurnResult>(),
|
|
414
283
|
};
|
|
415
|
-
this.
|
|
284
|
+
this.turns_by_id.set(turn_id, created);
|
|
416
285
|
return created;
|
|
417
286
|
}
|
|
418
287
|
|
|
419
|
-
private
|
|
420
|
-
for (const lifecycle of this.
|
|
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.
|
|
430
|
-
this.
|
|
298
|
+
lifecycle.deferred_finished.resolve(result);
|
|
299
|
+
this.remember_completed_turn(lifecycle.turnId);
|
|
431
300
|
}
|
|
432
|
-
this.maybeStopEventPump();
|
|
433
301
|
}
|
|
434
302
|
|
|
435
|
-
private
|
|
436
|
-
this.
|
|
437
|
-
while (this.
|
|
438
|
-
const
|
|
439
|
-
if (
|
|
440
|
-
this.
|
|
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
|
|
446
|
-
if (this.
|
|
447
|
-
if ([...this.
|
|
448
|
-
|
|
449
|
-
this.
|
|
450
|
-
this.
|
|
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
|
|
332
|
+
private readonly transport: RemoteAgentTransport;
|
|
459
333
|
|
|
460
334
|
constructor(options: RemoteAgentOptions) {
|
|
461
|
-
const
|
|
462
|
-
if (!
|
|
463
|
-
throw new Error("RemoteAgent requires a non-empty
|
|
335
|
+
const url = String(options.url || "").trim();
|
|
336
|
+
if (!url) {
|
|
337
|
+
throw new Error("RemoteAgent requires a non-empty url");
|
|
464
338
|
}
|
|
465
|
-
this.
|
|
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
|
|
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
|
-
|
|
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 (!
|
|
489
|
-
throw new Error(String(payload.error || "Remote session
|
|
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
|
|
410
|
+
return payload.session;
|
|
492
411
|
}
|
|
493
412
|
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
const
|
|
499
|
-
|
|
500
|
-
|
|
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.
|
|
452
|
+
`${this.base_url}/api/sdk/sessions/${encodeURIComponent(params.session_id)}/events`,
|
|
453
|
+
{
|
|
454
|
+
signal: abort_controller.signal,
|
|
455
|
+
},
|
|
504
456
|
);
|
|
505
|
-
|
|
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
|
-
|
|
511
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
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
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
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
|
|
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
|
|
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.
|
|
774
|
+
finished: lifecycle.deferred_finished.promise,
|
|
605
775
|
};
|
|
606
776
|
}
|
|
607
777
|
|
|
608
|
-
function
|
|
778
|
+
function extract_turn_id(event: AgentSessionEvent): string | null {
|
|
609
779
|
switch (event.type) {
|
|
610
780
|
case "turn-start":
|
|
611
781
|
case "text-delta":
|