@downcity/agent 1.1.63 → 1.1.66
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/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/session/Session.d.ts.map +1 -1
- package/bin/session/Session.js +15 -1
- package/bin/session/Session.js.map +1 -1
- package/bin/types/sdk/AgentSessionEvent.d.ts +22 -1
- package/bin/types/sdk/AgentSessionEvent.d.ts.map +1 -1
- package/package.json +3 -2
- package/scripts/city-model-tool-loop.test.mjs +154 -44
- package/scripts/session-prompt-runtime.test.mjs +1 -1
- package/scripts/session-title-event.test.mjs +51 -0
- package/src/model/CityModelAdapter.ts +26 -501
- package/src/session/Session.ts +14 -1
- package/src/types/sdk/AgentSessionEvent.ts +25 -0
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -133,6 +133,27 @@ export interface AgentSessionAssistantStepEvent {
|
|
|
133
133
|
*/
|
|
134
134
|
visibility?: SessionAssistantStepVisibility;
|
|
135
135
|
}
|
|
136
|
+
/**
|
|
137
|
+
* Session 标题更新事件。
|
|
138
|
+
*/
|
|
139
|
+
export interface AgentSessionTitleEvent {
|
|
140
|
+
/**
|
|
141
|
+
* 当前事件类型。
|
|
142
|
+
*/
|
|
143
|
+
type: "session-title";
|
|
144
|
+
/**
|
|
145
|
+
* 当前 session 唯一标识。
|
|
146
|
+
*/
|
|
147
|
+
sessionId: string;
|
|
148
|
+
/**
|
|
149
|
+
* 当前 session 最新标题。
|
|
150
|
+
*
|
|
151
|
+
* 说明(中文)
|
|
152
|
+
* - 标题已持久化到 session meta。
|
|
153
|
+
* - 新 session 通常在首条 user message 落盘后生成标题。
|
|
154
|
+
*/
|
|
155
|
+
title: string;
|
|
156
|
+
}
|
|
136
157
|
/**
|
|
137
158
|
* 单个 turn 完成事件。
|
|
138
159
|
*/
|
|
@@ -174,7 +195,7 @@ export interface AgentSessionErrorEvent {
|
|
|
174
195
|
/**
|
|
175
196
|
* Session 订阅可见事件联合类型。
|
|
176
197
|
*/
|
|
177
|
-
export type AgentSessionEvent = AgentSessionTurnStartEvent | AgentSessionTextDeltaEvent | AgentSessionReasoningDeltaEvent | AgentSessionToolCallEvent | AgentSessionToolResultEvent | AgentSessionAssistantStepEvent | AgentSessionTurnFinishEvent | AgentSessionErrorEvent;
|
|
198
|
+
export type AgentSessionEvent = AgentSessionTurnStartEvent | AgentSessionTextDeltaEvent | AgentSessionReasoningDeltaEvent | AgentSessionToolCallEvent | AgentSessionToolResultEvent | AgentSessionAssistantStepEvent | AgentSessionTitleEvent | AgentSessionTurnFinishEvent | AgentSessionErrorEvent;
|
|
178
199
|
/**
|
|
179
200
|
* Session 事件订阅回调。
|
|
180
201
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AgentSessionEvent.d.ts","sourceRoot":"","sources":["../../../src/types/sdk/AgentSessionEvent.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACxD,OAAO,KAAK,EAAE,8BAA8B,EAAE,MAAM,gCAAgC,CAAC;AAErF;;GAEG;AACH,MAAM,WAAW,0BAA0B;IACzC;;OAEG;IACH,IAAI,EAAE,YAAY,CAAC;IAEnB;;;;;;OAMG;IACH,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;GAEG;AACH,MAAM,WAAW,0BAA0B;IACzC;;OAEG;IACH,IAAI,EAAE,YAAY,CAAC;IAEnB;;OAEG;IACH,MAAM,EAAE,MAAM,CAAC;IAEf;;OAEG;IACH,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;GAEG;AACH,MAAM,WAAW,+BAA+B;IAC9C;;OAEG;IACH,IAAI,EAAE,iBAAiB,CAAC;IAExB;;OAEG;IACH,MAAM,EAAE,MAAM,CAAC;IAEf;;OAEG;IACH,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;GAEG;AACH,MAAM,WAAW,yBAAyB;IACxC;;OAEG;IACH,IAAI,EAAE,WAAW,CAAC;IAElB;;OAEG;IACH,MAAM,EAAE,MAAM,CAAC;IAEf;;OAEG;IACH,UAAU,EAAE,MAAM,CAAC;IAEnB;;OAEG;IACH,QAAQ,EAAE,MAAM,CAAC;IAEjB;;OAEG;IACH,IAAI,EAAE,SAAS,CAAC;CACjB;AAED;;GAEG;AACH,MAAM,WAAW,2BAA2B;IAC1C;;OAEG;IACH,IAAI,EAAE,aAAa,CAAC;IAEpB;;OAEG;IACH,MAAM,EAAE,MAAM,CAAC;IAEf;;OAEG;IACH,UAAU,EAAE,MAAM,CAAC;IAEnB;;OAEG;IACH,QAAQ,EAAE,MAAM,CAAC;IAEjB;;OAEG;IACH,MAAM,EAAE,SAAS,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,8BAA8B;IAC7C;;OAEG;IACH,IAAI,EAAE,gBAAgB,CAAC;IAEvB;;OAEG;IACH,MAAM,EAAE,MAAM,CAAC;IAEf;;OAEG;IACH,IAAI,EAAE,MAAM,CAAC;IAEb;;OAEG;IACH,SAAS,EAAE,MAAM,CAAC;IAElB;;OAEG;IACH,UAAU,CAAC,EAAE,8BAA8B,CAAC;CAC7C;AAED;;GAEG;AACH,MAAM,WAAW,2BAA2B;IAC1C;;OAEG;IACH,IAAI,EAAE,aAAa,CAAC;IAEpB;;OAEG;IACH,MAAM,EAAE,MAAM,CAAC;IAEf;;OAEG;IACH,IAAI,EAAE,MAAM,CAAC;IAEb;;OAEG;IACH,OAAO,EAAE,OAAO,CAAC;IAEjB;;OAEG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACrC;;OAEG;IACH,IAAI,EAAE,OAAO,CAAC;IAEd;;OAEG;IACH,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;GAEG;AACH,MAAM,MAAM,iBAAiB,GACzB,0BAA0B,GAC1B,0BAA0B,GAC1B,+BAA+B,GAC/B,yBAAyB,GACzB,2BAA2B,GAC3B,8BAA8B,GAC9B,2BAA2B,GAC3B,sBAAsB,CAAC;AAE3B;;GAEG;AACH,MAAM,MAAM,sBAAsB,GAAG,CAAC,KAAK,EAAE,iBAAiB,KAAK,IAAI,CAAC;AAExE;;GAEG;AACH,MAAM,MAAM,uBAAuB,GAAG,MAAM,IAAI,CAAC"}
|
|
1
|
+
{"version":3,"file":"AgentSessionEvent.d.ts","sourceRoot":"","sources":["../../../src/types/sdk/AgentSessionEvent.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACxD,OAAO,KAAK,EAAE,8BAA8B,EAAE,MAAM,gCAAgC,CAAC;AAErF;;GAEG;AACH,MAAM,WAAW,0BAA0B;IACzC;;OAEG;IACH,IAAI,EAAE,YAAY,CAAC;IAEnB;;;;;;OAMG;IACH,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;GAEG;AACH,MAAM,WAAW,0BAA0B;IACzC;;OAEG;IACH,IAAI,EAAE,YAAY,CAAC;IAEnB;;OAEG;IACH,MAAM,EAAE,MAAM,CAAC;IAEf;;OAEG;IACH,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;GAEG;AACH,MAAM,WAAW,+BAA+B;IAC9C;;OAEG;IACH,IAAI,EAAE,iBAAiB,CAAC;IAExB;;OAEG;IACH,MAAM,EAAE,MAAM,CAAC;IAEf;;OAEG;IACH,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;GAEG;AACH,MAAM,WAAW,yBAAyB;IACxC;;OAEG;IACH,IAAI,EAAE,WAAW,CAAC;IAElB;;OAEG;IACH,MAAM,EAAE,MAAM,CAAC;IAEf;;OAEG;IACH,UAAU,EAAE,MAAM,CAAC;IAEnB;;OAEG;IACH,QAAQ,EAAE,MAAM,CAAC;IAEjB;;OAEG;IACH,IAAI,EAAE,SAAS,CAAC;CACjB;AAED;;GAEG;AACH,MAAM,WAAW,2BAA2B;IAC1C;;OAEG;IACH,IAAI,EAAE,aAAa,CAAC;IAEpB;;OAEG;IACH,MAAM,EAAE,MAAM,CAAC;IAEf;;OAEG;IACH,UAAU,EAAE,MAAM,CAAC;IAEnB;;OAEG;IACH,QAAQ,EAAE,MAAM,CAAC;IAEjB;;OAEG;IACH,MAAM,EAAE,SAAS,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,8BAA8B;IAC7C;;OAEG;IACH,IAAI,EAAE,gBAAgB,CAAC;IAEvB;;OAEG;IACH,MAAM,EAAE,MAAM,CAAC;IAEf;;OAEG;IACH,IAAI,EAAE,MAAM,CAAC;IAEb;;OAEG;IACH,SAAS,EAAE,MAAM,CAAC;IAElB;;OAEG;IACH,UAAU,CAAC,EAAE,8BAA8B,CAAC;CAC7C;AAED;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACrC;;OAEG;IACH,IAAI,EAAE,eAAe,CAAC;IAEtB;;OAEG;IACH,SAAS,EAAE,MAAM,CAAC;IAElB;;;;;;OAMG;IACH,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;GAEG;AACH,MAAM,WAAW,2BAA2B;IAC1C;;OAEG;IACH,IAAI,EAAE,aAAa,CAAC;IAEpB;;OAEG;IACH,MAAM,EAAE,MAAM,CAAC;IAEf;;OAEG;IACH,IAAI,EAAE,MAAM,CAAC;IAEb;;OAEG;IACH,OAAO,EAAE,OAAO,CAAC;IAEjB;;OAEG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACrC;;OAEG;IACH,IAAI,EAAE,OAAO,CAAC;IAEd;;OAEG;IACH,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;GAEG;AACH,MAAM,MAAM,iBAAiB,GACzB,0BAA0B,GAC1B,0BAA0B,GAC1B,+BAA+B,GAC/B,yBAAyB,GACzB,2BAA2B,GAC3B,8BAA8B,GAC9B,sBAAsB,GACtB,2BAA2B,GAC3B,sBAAsB,CAAC;AAE3B;;GAEG;AACH,MAAM,MAAM,sBAAsB,GAAG,CAAC,KAAK,EAAE,iBAAiB,KAAK,IAAI,CAAC;AAExE;;GAEG;AACH,MAAM,MAAM,uBAAuB,GAAG,MAAM,IAAI,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@downcity/agent",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.66",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Downcity Agent 运行时 — 单 Agent 执行壳与 HTTP 服务能力",
|
|
6
6
|
"main": "./bin/index.js",
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
}
|
|
17
17
|
},
|
|
18
18
|
"dependencies": {
|
|
19
|
+
"@ai-sdk/openai-compatible": "^2.0.48",
|
|
19
20
|
"@larksuiteoapi/node-sdk": "^1.66.0",
|
|
20
21
|
"ai": "^6.0.193",
|
|
21
22
|
"commander": "^15.0.0",
|
|
@@ -28,7 +29,7 @@
|
|
|
28
29
|
"node-cron": "^4.2.1",
|
|
29
30
|
"ws": "^8.21.0",
|
|
30
31
|
"zod": "^4.4.3",
|
|
31
|
-
"@downcity/type": "0.1.
|
|
32
|
+
"@downcity/type": "0.1.9"
|
|
32
33
|
},
|
|
33
34
|
"devDependencies": {
|
|
34
35
|
"@types/fs-extra": "^11.0.4",
|
|
@@ -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) => {
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
import test from "node:test";
|
|
10
10
|
import assert from "node:assert/strict";
|
|
11
11
|
|
|
12
|
-
import { SessionPromptRuntime } from "../bin/
|
|
12
|
+
import { SessionPromptRuntime } from "../bin/session/runtime/SessionPromptRuntime.js";
|
|
13
13
|
|
|
14
14
|
function createDeferred() {
|
|
15
15
|
let resolve;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file 验证 Session 标题会进入 subscribe 事件与 history session 信息。
|
|
3
|
+
*
|
|
4
|
+
* 关键点(中文)
|
|
5
|
+
* - 这里走编译后的公开 SDK,锁住调用方实际可见行为。
|
|
6
|
+
* - 使用 appendUserMessage 触发 fallback 标题,避免测试依赖真实模型。
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import test from "node:test";
|
|
10
|
+
import assert from "node:assert/strict";
|
|
11
|
+
import os from "node:os";
|
|
12
|
+
import path from "node:path";
|
|
13
|
+
import fs from "node:fs/promises";
|
|
14
|
+
|
|
15
|
+
import { Agent } from "../bin/index.js";
|
|
16
|
+
|
|
17
|
+
test("Session publishes title event and exposes title in history", async () => {
|
|
18
|
+
const agent_path = await fs.mkdtemp(
|
|
19
|
+
path.join(os.tmpdir(), "downcity-agent-session-title-"),
|
|
20
|
+
);
|
|
21
|
+
const agent = new Agent({
|
|
22
|
+
id: "title_agent",
|
|
23
|
+
path: agent_path,
|
|
24
|
+
});
|
|
25
|
+
const session = await agent.createSession();
|
|
26
|
+
const events = [];
|
|
27
|
+
const unsubscribe = session.subscribe((event) => {
|
|
28
|
+
events.push(event);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
try {
|
|
32
|
+
await session.appendUserMessage({
|
|
33
|
+
text: "Use shell tools to inspect the current workspace",
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
const title_event = events.find((event) => event.type === "session-title");
|
|
37
|
+
assert.deepEqual(title_event, {
|
|
38
|
+
type: "session-title",
|
|
39
|
+
sessionId: session.id,
|
|
40
|
+
title: "Use shell tools to inspect the current workspace",
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
const history = await session.history();
|
|
44
|
+
assert.equal(
|
|
45
|
+
history.session.title,
|
|
46
|
+
"Use shell tools to inspect the current workspace",
|
|
47
|
+
);
|
|
48
|
+
} finally {
|
|
49
|
+
unsubscribe();
|
|
50
|
+
}
|
|
51
|
+
});
|