@downcity/agent 1.1.64 → 1.1.69
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/agent/Agent.d.ts.map +1 -1
- package/bin/agent/Agent.js +1 -0
- package/bin/agent/Agent.js.map +1 -1
- package/bin/agent/RemoteAgent.d.ts +8 -0
- package/bin/agent/RemoteAgent.d.ts.map +1 -1
- package/bin/agent/RemoteAgent.js +45 -14
- package/bin/agent/RemoteAgent.js.map +1 -1
- package/bin/model/CityModelAdapter.d.ts +4 -3
- package/bin/model/CityModelAdapter.d.ts.map +1 -1
- package/bin/model/CityModelAdapter.js +24 -410
- package/bin/model/CityModelAdapter.js.map +1 -1
- package/bin/rpc/Client.d.ts +49 -0
- package/bin/rpc/Client.d.ts.map +1 -1
- package/bin/rpc/Client.js +77 -0
- package/bin/rpc/Client.js.map +1 -1
- package/bin/rpc/Server.d.ts +3 -0
- package/bin/rpc/Server.d.ts.map +1 -1
- package/bin/rpc/Server.js +82 -0
- package/bin/rpc/Server.js.map +1 -1
- package/bin/session/SessionTitle.d.ts +2 -6
- package/bin/session/SessionTitle.d.ts.map +1 -1
- package/bin/session/SessionTitle.js +5 -27
- package/bin/session/SessionTitle.js.map +1 -1
- package/bin/session/browse/Browse.d.ts +1 -5
- package/bin/session/browse/Browse.d.ts.map +1 -1
- package/bin/session/browse/Browse.js +2 -8
- package/bin/session/browse/Browse.js.map +1 -1
- package/bin/session/index.d.ts +2 -2
- package/bin/session/index.d.ts.map +1 -1
- package/bin/session/index.js +2 -2
- package/bin/session/index.js.map +1 -1
- package/bin/types/agent/AgentTypes.d.ts +8 -0
- package/bin/types/agent/AgentTypes.d.ts.map +1 -1
- package/package.json +3 -2
- package/scripts/city-model-tool-loop.test.mjs +154 -44
- package/scripts/session-title-event.test.mjs +74 -7
- package/src/agent/Agent.ts +1 -0
- package/src/agent/RemoteAgent.ts +55 -12
- package/src/model/CityModelAdapter.ts +26 -501
- package/src/rpc/Client.ts +162 -0
- package/src/rpc/Server.ts +139 -0
- package/src/session/SessionTitle.ts +5 -34
- package/src/session/browse/Browse.ts +1 -9
- package/src/session/index.ts +0 -2
- package/src/types/agent/AgentTypes.ts +9 -0
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @file 验证 CityModel
|
|
2
|
+
* @file 验证 CityModel 会优先走 OpenAI-compatible LanguageModel 并完成 tool loop。
|
|
3
3
|
*
|
|
4
4
|
* 关键点(中文)
|
|
5
5
|
* - 这里直接走编译后的 Agent / City 产物,避免测试只覆盖源码级辅助函数。
|
|
6
|
-
* - 重点锁住
|
|
6
|
+
* - 重点锁住 CityModel -> LanguageModel -> tool-call -> 本地执行 -> tool-result 回传链路。
|
|
7
|
+
* - 新路径不应再调用 `/v1/ai/stream`,避免 UIMessage stream 反向适配丢失 finish 语义。
|
|
7
8
|
*/
|
|
8
9
|
|
|
9
10
|
import test from "node:test";
|
|
@@ -17,15 +18,15 @@ import { City } from "../../city/bin/index.js";
|
|
|
17
18
|
import { tool } from "ai";
|
|
18
19
|
import { z } from "zod";
|
|
19
20
|
|
|
20
|
-
function
|
|
21
|
+
function write_openai_sse(res, chunks) {
|
|
21
22
|
res.writeHead(200, {
|
|
22
23
|
"content-type": "text/event-stream; charset=utf-8",
|
|
23
24
|
"cache-control": "no-cache",
|
|
24
25
|
connection: "keep-alive",
|
|
25
|
-
"x-vercel-ai-ui-message-stream": "v1",
|
|
26
26
|
});
|
|
27
27
|
for (const chunk of chunks) {
|
|
28
|
-
|
|
28
|
+
const payload = typeof chunk === "string" ? chunk : JSON.stringify(chunk);
|
|
29
|
+
res.write(`data: ${payload}\n\n`);
|
|
29
30
|
}
|
|
30
31
|
res.end();
|
|
31
32
|
}
|
|
@@ -42,8 +43,10 @@ async function read_json_body(req) {
|
|
|
42
43
|
return JSON.parse(String(raw || "{}"));
|
|
43
44
|
}
|
|
44
45
|
|
|
45
|
-
test("CityModel sends tool result back
|
|
46
|
+
test("CityModel uses direct LanguageModel path and sends tool result back", async () => {
|
|
46
47
|
const requests = [];
|
|
48
|
+
const agent_requests = [];
|
|
49
|
+
let stream_requests = 0;
|
|
47
50
|
let tool_executed = false;
|
|
48
51
|
|
|
49
52
|
const server = http.createServer(async (req, res) => {
|
|
@@ -68,30 +71,149 @@ test("CityModel sends tool result back on the next round", async () => {
|
|
|
68
71
|
}
|
|
69
72
|
|
|
70
73
|
if (req.method === "POST" && url.pathname === "/v1/ai/stream") {
|
|
74
|
+
stream_requests += 1;
|
|
75
|
+
res.writeHead(500, { "content-type": "application/json" });
|
|
76
|
+
res.end(JSON.stringify({ error: "legacy stream endpoint should not be called" }));
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (req.method === "POST" && url.pathname === "/v1/ai/chat/completions") {
|
|
71
81
|
const body = await read_json_body(req);
|
|
72
82
|
requests.push(body);
|
|
73
83
|
|
|
74
|
-
if (
|
|
75
|
-
|
|
76
|
-
{
|
|
77
|
-
|
|
84
|
+
if (!Array.isArray(body.tools)) {
|
|
85
|
+
write_openai_sse(res, [
|
|
86
|
+
{
|
|
87
|
+
id: "chatcmpl_title",
|
|
88
|
+
object: "chat.completion.chunk",
|
|
89
|
+
created: 1,
|
|
90
|
+
model: "mock-model",
|
|
91
|
+
choices: [
|
|
92
|
+
{
|
|
93
|
+
index: 0,
|
|
94
|
+
delta: { role: "assistant", content: "Tool loop" },
|
|
95
|
+
finish_reason: null,
|
|
96
|
+
},
|
|
97
|
+
],
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
id: "chatcmpl_title",
|
|
101
|
+
object: "chat.completion.chunk",
|
|
102
|
+
created: 1,
|
|
103
|
+
model: "mock-model",
|
|
104
|
+
choices: [
|
|
105
|
+
{
|
|
106
|
+
index: 0,
|
|
107
|
+
delta: {},
|
|
108
|
+
finish_reason: "stop",
|
|
109
|
+
},
|
|
110
|
+
],
|
|
111
|
+
},
|
|
112
|
+
"[DONE]",
|
|
113
|
+
]);
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
agent_requests.push(body);
|
|
118
|
+
if (agent_requests.length === 1) {
|
|
119
|
+
write_openai_sse(res, [
|
|
120
|
+
{
|
|
121
|
+
id: "chatcmpl_1",
|
|
122
|
+
object: "chat.completion.chunk",
|
|
123
|
+
created: 1,
|
|
124
|
+
model: "mock-model",
|
|
125
|
+
choices: [
|
|
126
|
+
{
|
|
127
|
+
index: 0,
|
|
128
|
+
delta: { role: "assistant" },
|
|
129
|
+
finish_reason: null,
|
|
130
|
+
},
|
|
131
|
+
],
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
id: "chatcmpl_1",
|
|
135
|
+
object: "chat.completion.chunk",
|
|
136
|
+
created: 1,
|
|
137
|
+
model: "mock-model",
|
|
138
|
+
choices: [
|
|
139
|
+
{
|
|
140
|
+
index: 0,
|
|
141
|
+
delta: {
|
|
142
|
+
tool_calls: [
|
|
143
|
+
{
|
|
144
|
+
index: 0,
|
|
145
|
+
id: "call_1",
|
|
146
|
+
type: "function",
|
|
147
|
+
function: {
|
|
148
|
+
name: "ping",
|
|
149
|
+
arguments: "{\"value\":\"hello\"}",
|
|
150
|
+
},
|
|
151
|
+
},
|
|
152
|
+
],
|
|
153
|
+
},
|
|
154
|
+
finish_reason: null,
|
|
155
|
+
},
|
|
156
|
+
],
|
|
157
|
+
},
|
|
78
158
|
{
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
159
|
+
id: "chatcmpl_1",
|
|
160
|
+
object: "chat.completion.chunk",
|
|
161
|
+
created: 1,
|
|
162
|
+
model: "mock-model",
|
|
163
|
+
choices: [
|
|
164
|
+
{
|
|
165
|
+
index: 0,
|
|
166
|
+
delta: {},
|
|
167
|
+
finish_reason: "tool_calls",
|
|
168
|
+
},
|
|
169
|
+
],
|
|
83
170
|
},
|
|
84
|
-
|
|
171
|
+
"[DONE]",
|
|
85
172
|
]);
|
|
86
173
|
return;
|
|
87
174
|
}
|
|
88
175
|
|
|
89
|
-
|
|
90
|
-
{
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
176
|
+
write_openai_sse(res, [
|
|
177
|
+
{
|
|
178
|
+
id: "chatcmpl_2",
|
|
179
|
+
object: "chat.completion.chunk",
|
|
180
|
+
created: 1,
|
|
181
|
+
model: "mock-model",
|
|
182
|
+
choices: [
|
|
183
|
+
{
|
|
184
|
+
index: 0,
|
|
185
|
+
delta: { role: "assistant" },
|
|
186
|
+
finish_reason: null,
|
|
187
|
+
},
|
|
188
|
+
],
|
|
189
|
+
},
|
|
190
|
+
{
|
|
191
|
+
id: "chatcmpl_2",
|
|
192
|
+
object: "chat.completion.chunk",
|
|
193
|
+
created: 1,
|
|
194
|
+
model: "mock-model",
|
|
195
|
+
choices: [
|
|
196
|
+
{
|
|
197
|
+
index: 0,
|
|
198
|
+
delta: { content: "done" },
|
|
199
|
+
finish_reason: null,
|
|
200
|
+
},
|
|
201
|
+
],
|
|
202
|
+
},
|
|
203
|
+
{
|
|
204
|
+
id: "chatcmpl_2",
|
|
205
|
+
object: "chat.completion.chunk",
|
|
206
|
+
created: 1,
|
|
207
|
+
model: "mock-model",
|
|
208
|
+
choices: [
|
|
209
|
+
{
|
|
210
|
+
index: 0,
|
|
211
|
+
delta: {},
|
|
212
|
+
finish_reason: "stop",
|
|
213
|
+
},
|
|
214
|
+
],
|
|
215
|
+
},
|
|
216
|
+
"[DONE]",
|
|
95
217
|
]);
|
|
96
218
|
return;
|
|
97
219
|
}
|
|
@@ -142,31 +264,19 @@ test("CityModel sends tool result back on the next round", async () => {
|
|
|
142
264
|
|
|
143
265
|
assert.equal(result.success, true);
|
|
144
266
|
assert.equal(tool_executed, true);
|
|
145
|
-
assert.equal(
|
|
267
|
+
assert.equal(stream_requests, 0);
|
|
268
|
+
assert.equal(agent_requests.length, 2);
|
|
269
|
+
assert.equal(requests.every((request) => request?.town_id === "town_demo"), true);
|
|
270
|
+
assert.equal(agent_requests[0]?.model, "mock-model");
|
|
146
271
|
|
|
147
|
-
const second_request_messages = Array.isArray(
|
|
148
|
-
?
|
|
272
|
+
const second_request_messages = Array.isArray(agent_requests[1]?.messages)
|
|
273
|
+
? agent_requests[1].messages
|
|
149
274
|
: [];
|
|
150
|
-
const
|
|
151
|
-
|
|
152
|
-
);
|
|
153
|
-
assert.
|
|
154
|
-
|
|
155
|
-
? second_assistant_message.parts
|
|
156
|
-
: [];
|
|
157
|
-
|
|
158
|
-
const tool_call_part = second_assistant_parts.find(
|
|
159
|
-
(part) => part && part.type === "dynamic-tool" && part.state === "output-available",
|
|
160
|
-
);
|
|
161
|
-
assert.deepEqual(tool_call_part, {
|
|
162
|
-
type: "dynamic-tool",
|
|
163
|
-
toolName: "ping",
|
|
164
|
-
toolCallId: "call_1",
|
|
165
|
-
state: "output-available",
|
|
166
|
-
input: { value: "hello" },
|
|
167
|
-
output: { echoed: "hello" },
|
|
168
|
-
providerExecuted: false,
|
|
169
|
-
});
|
|
275
|
+
const serialized_second_messages = JSON.stringify(second_request_messages);
|
|
276
|
+
assert.match(serialized_second_messages, /"role":"tool"/);
|
|
277
|
+
assert.match(serialized_second_messages, /call_1/);
|
|
278
|
+
assert.match(serialized_second_messages, /echoed/);
|
|
279
|
+
assert.match(serialized_second_messages, /hello/);
|
|
170
280
|
} finally {
|
|
171
281
|
await new Promise((resolve, reject) => {
|
|
172
282
|
server.close((error) => {
|
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
*
|
|
4
4
|
* 关键点(中文)
|
|
5
5
|
* - 这里走编译后的公开 SDK,锁住调用方实际可见行为。
|
|
6
|
-
* -
|
|
6
|
+
* - title 默认允许为空;没有可用模型时不会再回退成首条 user message。
|
|
7
|
+
* - 当后续补上模型且 title 仍为空时,应允许再次尝试生成。
|
|
7
8
|
*/
|
|
8
9
|
|
|
9
10
|
import test from "node:test";
|
|
@@ -12,9 +13,39 @@ import os from "node:os";
|
|
|
12
13
|
import path from "node:path";
|
|
13
14
|
import fs from "node:fs/promises";
|
|
14
15
|
|
|
16
|
+
import { MockLanguageModelV3 } from "ai/test";
|
|
15
17
|
import { Agent } from "../bin/index.js";
|
|
16
18
|
|
|
17
|
-
|
|
19
|
+
function create_mock_title_model(title_text) {
|
|
20
|
+
return new MockLanguageModelV3({
|
|
21
|
+
modelId: "mock-session-title-model",
|
|
22
|
+
doGenerate: async () => ({
|
|
23
|
+
content: [
|
|
24
|
+
{
|
|
25
|
+
type: "text",
|
|
26
|
+
text: title_text,
|
|
27
|
+
},
|
|
28
|
+
],
|
|
29
|
+
finishReason: "stop",
|
|
30
|
+
usage: {
|
|
31
|
+
inputTokens: {
|
|
32
|
+
total: 0,
|
|
33
|
+
noCache: 0,
|
|
34
|
+
cacheRead: 0,
|
|
35
|
+
cacheWrite: 0,
|
|
36
|
+
},
|
|
37
|
+
outputTokens: {
|
|
38
|
+
total: 0,
|
|
39
|
+
text: 0,
|
|
40
|
+
reasoning: 0,
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
warnings: [],
|
|
44
|
+
}),
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
test("Session keeps title empty when no model is available", async () => {
|
|
18
49
|
const agent_path = await fs.mkdtemp(
|
|
19
50
|
path.join(os.tmpdir(), "downcity-agent-session-title-"),
|
|
20
51
|
);
|
|
@@ -33,18 +64,54 @@ test("Session publishes title event and exposes title in history", async () => {
|
|
|
33
64
|
text: "Use shell tools to inspect the current workspace",
|
|
34
65
|
});
|
|
35
66
|
|
|
67
|
+
const title_event = events.find((event) => event.type === "session-title");
|
|
68
|
+
assert.equal(title_event, undefined);
|
|
69
|
+
|
|
70
|
+
const history = await session.history();
|
|
71
|
+
assert.equal(history.session.title, undefined);
|
|
72
|
+
} finally {
|
|
73
|
+
unsubscribe();
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
test("Session retries title generation after model becomes available", async () => {
|
|
78
|
+
const agent_path = await fs.mkdtemp(
|
|
79
|
+
path.join(os.tmpdir(), "downcity-agent-session-title-retry-"),
|
|
80
|
+
);
|
|
81
|
+
const agent = new Agent({
|
|
82
|
+
id: "title_retry_agent",
|
|
83
|
+
path: agent_path,
|
|
84
|
+
});
|
|
85
|
+
const session = await agent.createSession();
|
|
86
|
+
const events = [];
|
|
87
|
+
const unsubscribe = session.subscribe((event) => {
|
|
88
|
+
events.push(event);
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
try {
|
|
92
|
+
await session.appendUserMessage({
|
|
93
|
+
text: "Investigate flaky session title generation in the SDK",
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
const history_before_model = await session.history();
|
|
97
|
+
assert.equal(history_before_model.session.title, undefined);
|
|
98
|
+
|
|
99
|
+
await session.set({
|
|
100
|
+
model: create_mock_title_model("排查 session 标题"),
|
|
101
|
+
});
|
|
102
|
+
await session.appendUserMessage({
|
|
103
|
+
text: "Need another prompt to trigger the retry path",
|
|
104
|
+
});
|
|
105
|
+
|
|
36
106
|
const title_event = events.find((event) => event.type === "session-title");
|
|
37
107
|
assert.deepEqual(title_event, {
|
|
38
108
|
type: "session-title",
|
|
39
109
|
sessionId: session.id,
|
|
40
|
-
title: "
|
|
110
|
+
title: "排查 session 标题",
|
|
41
111
|
});
|
|
42
112
|
|
|
43
113
|
const history = await session.history();
|
|
44
|
-
assert.equal(
|
|
45
|
-
history.session.title,
|
|
46
|
-
"Use shell tools to inspect the current workspace",
|
|
47
|
-
);
|
|
114
|
+
assert.equal(history.session.title, "排查 session 标题");
|
|
48
115
|
} finally {
|
|
49
116
|
unsubscribe();
|
|
50
117
|
}
|
package/src/agent/Agent.ts
CHANGED
package/src/agent/RemoteAgent.ts
CHANGED
|
@@ -115,6 +115,10 @@ type RemoteAgentTransport = RemoteSessionTransport & {
|
|
|
115
115
|
* 列出 sessions。
|
|
116
116
|
*/
|
|
117
117
|
list_sessions(input?: AgentListSessionsInput): Promise<AgentSessionSummaryPage>;
|
|
118
|
+
/**
|
|
119
|
+
* 关闭 transport 持有的长期连接。
|
|
120
|
+
*/
|
|
121
|
+
close?(): Promise<void>;
|
|
118
122
|
};
|
|
119
123
|
|
|
120
124
|
type SdkEventsReadyFrame = {
|
|
@@ -336,7 +340,7 @@ export class RemoteAgent {
|
|
|
336
340
|
if (!url) {
|
|
337
341
|
throw new Error("RemoteAgent requires a non-empty url");
|
|
338
342
|
}
|
|
339
|
-
this.transport = create_remote_agent_transport(url);
|
|
343
|
+
this.transport = create_remote_agent_transport(url, options.token);
|
|
340
344
|
}
|
|
341
345
|
|
|
342
346
|
/**
|
|
@@ -369,13 +373,34 @@ export class RemoteAgent {
|
|
|
369
373
|
): Promise<AgentSessionSummaryPage> {
|
|
370
374
|
return await this.transport.list_sessions(input);
|
|
371
375
|
}
|
|
376
|
+
|
|
377
|
+
/**
|
|
378
|
+
* 关闭远程 transport。
|
|
379
|
+
*
|
|
380
|
+
* 关键点(中文)
|
|
381
|
+
* - `rpc://` 会关闭底层长连接。
|
|
382
|
+
* - `http://` / `https://` 没有常驻连接,调用时是安全 no-op。
|
|
383
|
+
*/
|
|
384
|
+
async close(): Promise<void> {
|
|
385
|
+
await this.transport.close?.();
|
|
386
|
+
}
|
|
372
387
|
}
|
|
373
388
|
|
|
374
389
|
class HttpRemoteAgentTransport implements RemoteAgentTransport {
|
|
375
390
|
private readonly base_url: string;
|
|
391
|
+
private readonly token: string;
|
|
376
392
|
|
|
377
|
-
constructor(url: string) {
|
|
393
|
+
constructor(url: string, token?: string) {
|
|
378
394
|
this.base_url = url.replace(/\/+$/, "");
|
|
395
|
+
this.token = String(token || "").trim();
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
private headers(input?: Record<string, string>): Headers {
|
|
399
|
+
const headers = new Headers(input);
|
|
400
|
+
if (this.token) {
|
|
401
|
+
headers.set("Authorization", `Bearer ${this.token}`);
|
|
402
|
+
}
|
|
403
|
+
return headers;
|
|
379
404
|
}
|
|
380
405
|
|
|
381
406
|
async create_session(input?: AgentCreateSessionInput): Promise<AgentSessionInfo> {
|
|
@@ -385,9 +410,9 @@ class HttpRemoteAgentTransport implements RemoteAgentTransport {
|
|
|
385
410
|
session?: AgentSessionInfo;
|
|
386
411
|
}>(`${this.base_url}/api/sdk/sessions`, {
|
|
387
412
|
method: "POST",
|
|
388
|
-
headers: {
|
|
413
|
+
headers: this.headers({
|
|
389
414
|
"Content-Type": "application/json",
|
|
390
|
-
},
|
|
415
|
+
}),
|
|
391
416
|
body: JSON.stringify({
|
|
392
417
|
...(input?.sessionId ? { sessionId: input.sessionId } : {}),
|
|
393
418
|
}),
|
|
@@ -403,7 +428,9 @@ class HttpRemoteAgentTransport implements RemoteAgentTransport {
|
|
|
403
428
|
success?: boolean;
|
|
404
429
|
error?: string;
|
|
405
430
|
session?: AgentSessionInfo;
|
|
406
|
-
}>(`${this.base_url}/api/sdk/sessions/${encodeURIComponent(session_id)}
|
|
431
|
+
}>(`${this.base_url}/api/sdk/sessions/${encodeURIComponent(session_id)}`, {
|
|
432
|
+
headers: this.headers(),
|
|
433
|
+
});
|
|
407
434
|
if (!payload.success || !payload.session?.sessionId) {
|
|
408
435
|
throw new Error(String(payload.error || "Remote session info failed"));
|
|
409
436
|
}
|
|
@@ -422,9 +449,9 @@ class HttpRemoteAgentTransport implements RemoteAgentTransport {
|
|
|
422
449
|
};
|
|
423
450
|
}>(`${this.base_url}/api/sdk/sessions/${encodeURIComponent(session_id)}/prompt`, {
|
|
424
451
|
method: "POST",
|
|
425
|
-
headers: {
|
|
452
|
+
headers: this.headers({
|
|
426
453
|
"Content-Type": "application/json",
|
|
427
|
-
},
|
|
454
|
+
}),
|
|
428
455
|
body: JSON.stringify({
|
|
429
456
|
query: input.query,
|
|
430
457
|
}),
|
|
@@ -451,6 +478,7 @@ class HttpRemoteAgentTransport implements RemoteAgentTransport {
|
|
|
451
478
|
const response = await fetch(
|
|
452
479
|
`${this.base_url}/api/sdk/sessions/${encodeURIComponent(params.session_id)}/events`,
|
|
453
480
|
{
|
|
481
|
+
headers: this.headers(),
|
|
454
482
|
signal: abort_controller.signal,
|
|
455
483
|
},
|
|
456
484
|
);
|
|
@@ -495,6 +523,9 @@ class HttpRemoteAgentTransport implements RemoteAgentTransport {
|
|
|
495
523
|
`${this.base_url}/api/sdk/sessions/${encodeURIComponent(session_id)}/history${
|
|
496
524
|
query.size > 0 ? `?${query.toString()}` : ""
|
|
497
525
|
}`,
|
|
526
|
+
{
|
|
527
|
+
headers: this.headers(),
|
|
528
|
+
},
|
|
498
529
|
);
|
|
499
530
|
if (!payload.success || !payload.history || !Array.isArray(payload.history.items)) {
|
|
500
531
|
throw new Error(String(payload.error || "Remote session history failed"));
|
|
@@ -507,7 +538,9 @@ class HttpRemoteAgentTransport implements RemoteAgentTransport {
|
|
|
507
538
|
success?: boolean;
|
|
508
539
|
error?: string;
|
|
509
540
|
system?: AgentSessionSystemSnapshot;
|
|
510
|
-
}>(`${this.base_url}/api/sdk/sessions/${encodeURIComponent(session_id)}/system
|
|
541
|
+
}>(`${this.base_url}/api/sdk/sessions/${encodeURIComponent(session_id)}/system`, {
|
|
542
|
+
headers: this.headers(),
|
|
543
|
+
});
|
|
511
544
|
if (!payload.success || !payload.system || !Array.isArray(payload.system.blocks)) {
|
|
512
545
|
throw new Error(String(payload.error || "Remote session system failed"));
|
|
513
546
|
}
|
|
@@ -528,9 +561,9 @@ class HttpRemoteAgentTransport implements RemoteAgentTransport {
|
|
|
528
561
|
session?: AgentSessionInfo;
|
|
529
562
|
}>(`${this.base_url}/api/sdk/sessions/${encodeURIComponent(session_id)}/fork`, {
|
|
530
563
|
method: "POST",
|
|
531
|
-
headers: {
|
|
564
|
+
headers: this.headers({
|
|
532
565
|
"Content-Type": "application/json",
|
|
533
|
-
},
|
|
566
|
+
}),
|
|
534
567
|
body: JSON.stringify({
|
|
535
568
|
...(message_id ? { messageId: message_id } : {}),
|
|
536
569
|
}),
|
|
@@ -552,6 +585,9 @@ class HttpRemoteAgentTransport implements RemoteAgentTransport {
|
|
|
552
585
|
page?: AgentSessionSummaryPage;
|
|
553
586
|
}>(
|
|
554
587
|
`${this.base_url}/api/sdk/sessions${query.size > 0 ? `?${query.toString()}` : ""}`,
|
|
588
|
+
{
|
|
589
|
+
headers: this.headers(),
|
|
590
|
+
},
|
|
555
591
|
);
|
|
556
592
|
if (!payload.success || !payload.page) {
|
|
557
593
|
throw new Error(String(payload.error || "Remote sessions list failed"));
|
|
@@ -633,11 +669,18 @@ class RpcRemoteAgentTransport implements RemoteAgentTransport {
|
|
|
633
669
|
async list_sessions(input?: AgentListSessionsInput): Promise<AgentSessionSummaryPage> {
|
|
634
670
|
return await this.client.list_sessions(input);
|
|
635
671
|
}
|
|
672
|
+
|
|
673
|
+
async close(): Promise<void> {
|
|
674
|
+
await this.client.close();
|
|
675
|
+
}
|
|
636
676
|
}
|
|
637
677
|
|
|
638
|
-
function create_remote_agent_transport(
|
|
678
|
+
function create_remote_agent_transport(
|
|
679
|
+
url: string,
|
|
680
|
+
token?: string,
|
|
681
|
+
): RemoteAgentTransport {
|
|
639
682
|
if (/^https?:\/\//i.test(url)) {
|
|
640
|
-
return new HttpRemoteAgentTransport(url);
|
|
683
|
+
return new HttpRemoteAgentTransport(url, token);
|
|
641
684
|
}
|
|
642
685
|
if (/^rpc:\/\//i.test(url)) {
|
|
643
686
|
return new RpcRemoteAgentTransport(url);
|