@downcity/agent 1.1.21 → 1.1.23
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 -5
- package/bin/core/AgentContextTypes.d.ts +21 -2
- package/bin/core/AgentContextTypes.d.ts.map +1 -1
- package/bin/index.d.ts +5 -1
- package/bin/index.d.ts.map +1 -1
- package/bin/index.js.map +1 -1
- package/bin/runtime/server/http/Server.d.ts +1 -1
- package/bin/runtime/server/http/Server.d.ts.map +1 -1
- package/bin/runtime/server/http/Server.js +4 -0
- package/bin/runtime/server/http/Server.js.map +1 -1
- package/bin/runtime/server/http/control/ExecuteBySession.js +1 -1
- package/bin/runtime/server/http/control/ExecuteBySession.js.map +1 -1
- package/bin/runtime/server/http/control/StreamBySession.d.ts +3 -3
- package/bin/runtime/server/http/control/StreamBySession.js +6 -6
- package/bin/runtime/server/http/control/StreamBySession.js.map +1 -1
- package/bin/runtime/server/http/execute/execute.js +1 -1
- package/bin/runtime/server/http/execute/execute.js.map +1 -1
- package/bin/runtime/server/http/sdk/Router.d.ts +14 -0
- package/bin/runtime/server/http/sdk/Router.d.ts.map +1 -0
- package/bin/runtime/server/http/sdk/Router.js +18 -0
- package/bin/runtime/server/http/sdk/Router.js.map +1 -0
- package/bin/runtime/server/http/sdk/SessionRoutes.d.ts +15 -0
- package/bin/runtime/server/http/sdk/SessionRoutes.d.ts.map +1 -0
- package/bin/runtime/server/http/sdk/SessionRoutes.js +190 -0
- package/bin/runtime/server/http/sdk/SessionRoutes.js.map +1 -0
- package/bin/sdk/AgentSdkTypes.d.ts +1 -124
- package/bin/sdk/AgentSdkTypes.d.ts.map +1 -1
- package/bin/sdk/RemoteAgent.d.ts +27 -8
- package/bin/sdk/RemoteAgent.d.ts.map +1 -1
- package/bin/sdk/RemoteAgent.js +266 -38
- package/bin/sdk/RemoteAgent.js.map +1 -1
- package/bin/sdk/Session.d.ts +31 -10
- package/bin/sdk/Session.d.ts.map +1 -1
- package/bin/sdk/Session.js +181 -73
- package/bin/sdk/Session.js.map +1 -1
- package/bin/sdk/SessionEventMapper.d.ts +32 -0
- package/bin/sdk/SessionEventMapper.d.ts.map +1 -0
- package/bin/sdk/{StreamEvents.js → SessionEventMapper.js} +43 -28
- package/bin/sdk/SessionEventMapper.js.map +1 -0
- package/bin/sdk/session/ServicePort.d.ts +51 -4
- package/bin/sdk/session/ServicePort.d.ts.map +1 -1
- package/bin/sdk/session/ServicePort.js +17 -10
- package/bin/sdk/session/ServicePort.js.map +1 -1
- package/bin/sdk/session/runtime/SessionEventHub.d.ts +24 -0
- package/bin/sdk/session/runtime/SessionEventHub.d.ts.map +1 -0
- package/bin/sdk/session/runtime/SessionEventHub.js +39 -0
- package/bin/sdk/session/runtime/SessionEventHub.js.map +1 -0
- package/bin/sdk/session/runtime/SessionPromptRuntime.d.ts +68 -0
- package/bin/sdk/session/runtime/SessionPromptRuntime.d.ts.map +1 -0
- package/bin/sdk/session/runtime/SessionPromptRuntime.js +170 -0
- package/bin/sdk/session/runtime/SessionPromptRuntime.js.map +1 -0
- package/bin/service/builtins/chat/runtime/ChatQueueWorker.js +1 -1
- package/bin/service/builtins/chat/runtime/ChatQueueWorker.js.map +1 -1
- package/bin/service/builtins/chat/runtime/ChatSession.d.ts +1 -1
- package/bin/service/builtins/chat/runtime/ChatSession.js +1 -1
- package/bin/service/builtins/contact/runtime/ChatRuntime.js +1 -1
- package/bin/service/builtins/contact/runtime/ChatRuntime.js.map +1 -1
- package/bin/session/Executor.d.ts.map +1 -1
- package/bin/session/Executor.js +1 -1
- package/bin/session/Executor.js.map +1 -1
- package/bin/session/core-engine/CoreEngineSignals.d.ts +1 -1
- package/bin/session/core-engine/CoreEngineSignals.d.ts.map +1 -1
- package/bin/session/core-engine/CoreEngineSignals.js +1 -23
- package/bin/session/core-engine/CoreEngineSignals.js.map +1 -1
- package/bin/types/sdk/AgentSessionEvent.d.ts +160 -0
- package/bin/types/sdk/AgentSessionEvent.d.ts.map +1 -0
- package/bin/types/sdk/AgentSessionEvent.js +9 -0
- package/bin/types/sdk/AgentSessionEvent.js.map +1 -0
- package/bin/types/sdk/AgentSessionPrompt.d.ts +21 -0
- package/bin/types/sdk/AgentSessionPrompt.d.ts.map +1 -0
- package/bin/types/sdk/AgentSessionPrompt.js +9 -0
- package/bin/types/sdk/AgentSessionPrompt.js.map +1 -0
- package/bin/types/sdk/AgentSessionTurn.d.ts +59 -0
- package/bin/types/sdk/AgentSessionTurn.d.ts.map +1 -0
- package/bin/types/sdk/AgentSessionTurn.js +10 -0
- package/bin/types/sdk/AgentSessionTurn.js.map +1 -0
- package/bin/types/sdk/AgentUiChunkEvent.d.ts +100 -0
- package/bin/types/sdk/AgentUiChunkEvent.d.ts.map +1 -0
- package/bin/types/sdk/AgentUiChunkEvent.js +9 -0
- package/bin/types/sdk/AgentUiChunkEvent.js.map +1 -0
- package/package.json +2 -1
- package/scripts/session-prompt-runtime.test.mjs +167 -0
- package/src/core/AgentContextTypes.ts +24 -2
- package/src/index.ts +11 -3
- package/src/runtime/server/http/Server.ts +5 -1
- package/src/runtime/server/http/control/ExecuteBySession.ts +1 -1
- package/src/runtime/server/http/control/StreamBySession.ts +11 -11
- package/src/runtime/server/http/execute/execute.ts +1 -1
- package/src/runtime/server/http/sdk/Router.ts +22 -0
- package/src/runtime/server/http/sdk/SessionRoutes.ts +232 -0
- package/src/sdk/AgentSdkTypes.ts +1 -137
- package/src/sdk/RemoteAgent.ts +368 -49
- package/src/sdk/Session.ts +235 -86
- package/src/sdk/{StreamEvents.ts → SessionEventMapper.ts} +50 -40
- package/src/sdk/session/ServicePort.ts +74 -12
- package/src/sdk/session/runtime/SessionEventHub.ts +47 -0
- package/src/sdk/session/runtime/SessionPromptRuntime.ts +269 -0
- package/src/service/builtins/chat/runtime/ChatQueueWorker.ts +1 -1
- package/src/service/builtins/chat/runtime/ChatSession.ts +1 -1
- package/src/service/builtins/contact/runtime/ChatRuntime.ts +1 -1
- package/src/session/Executor.ts +1 -4
- package/src/session/README.md +8 -7
- package/src/session/core-engine/CoreEngineSignals.ts +0 -23
- package/src/types/sdk/AgentSessionEvent.ts +195 -0
- package/src/types/sdk/AgentSessionPrompt.ts +21 -0
- package/src/types/sdk/AgentSessionTurn.ts +65 -0
- package/src/types/sdk/AgentUiChunkEvent.ts +108 -0
- package/tsconfig.tsbuildinfo +1 -1
- package/bin/sdk/AsyncQueue.d.ts +0 -26
- package/bin/sdk/AsyncQueue.d.ts.map +0 -1
- package/bin/sdk/AsyncQueue.js +0 -75
- package/bin/sdk/AsyncQueue.js.map +0 -1
- package/bin/sdk/StreamEvents.d.ts +0 -36
- package/bin/sdk/StreamEvents.d.ts.map +0 -1
- package/bin/sdk/StreamEvents.js.map +0 -1
- package/src/sdk/AsyncQueue.ts +0 -92
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SessionEventHub:Session 级事件分发器。
|
|
3
|
+
*
|
|
4
|
+
* 关键点(中文)
|
|
5
|
+
* - 收口 `Session.subscribe()` 的订阅/取消订阅能力。
|
|
6
|
+
* - 只做未来事件广播,不做缓存、重放或背压管理。
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import type {
|
|
10
|
+
AgentSessionEvent,
|
|
11
|
+
AgentSessionSubscriber,
|
|
12
|
+
AgentSessionUnsubscribe,
|
|
13
|
+
} from "@/types/sdk/AgentSessionEvent.js";
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* SessionEventHub:最小事件总线实现。
|
|
17
|
+
*/
|
|
18
|
+
export class SessionEventHub {
|
|
19
|
+
private readonly subscribers = new Map<number, AgentSessionSubscriber>();
|
|
20
|
+
private nextSubscriberId = 1;
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* 注册一个新的 Session 事件订阅者。
|
|
24
|
+
*/
|
|
25
|
+
subscribe(subscriber: AgentSessionSubscriber): AgentSessionUnsubscribe {
|
|
26
|
+
const id = this.nextSubscriberId;
|
|
27
|
+
this.nextSubscriberId += 1;
|
|
28
|
+
this.subscribers.set(id, subscriber);
|
|
29
|
+
|
|
30
|
+
return () => {
|
|
31
|
+
this.subscribers.delete(id);
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* 广播一条 Session 事件。
|
|
37
|
+
*/
|
|
38
|
+
publish(event: AgentSessionEvent): void {
|
|
39
|
+
for (const subscriber of this.subscribers.values()) {
|
|
40
|
+
try {
|
|
41
|
+
subscriber(event);
|
|
42
|
+
} catch {
|
|
43
|
+
// ignore single subscriber failures
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SessionPromptRuntime:Session actor 模式下的最小 prompt 调度器。
|
|
3
|
+
*
|
|
4
|
+
* 关键点(中文)
|
|
5
|
+
* - `prompt()` 是唯一输入入口,内部决定并入当前 turn 还是排到下一 turn。
|
|
6
|
+
* - 不把调度逻辑塞进 Executor;Executor 继续只负责单次执行。
|
|
7
|
+
* - 这里不暴露历史或消息模型,只编排 prompt 队列与 turn 生命周期。
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { nanoid } from "nanoid";
|
|
11
|
+
import type { SessionUserMessageV1 } from "@/session/types/SessionMessages.js";
|
|
12
|
+
import type { AgentSessionEvent } from "@/types/sdk/AgentSessionEvent.js";
|
|
13
|
+
import type {
|
|
14
|
+
AgentSessionTurnHandle,
|
|
15
|
+
AgentSessionTurnResult,
|
|
16
|
+
} from "@/types/sdk/AgentSessionTurn.js";
|
|
17
|
+
|
|
18
|
+
type QueuedPrompt = {
|
|
19
|
+
/**
|
|
20
|
+
* 当前排队中的用户文本。
|
|
21
|
+
*/
|
|
22
|
+
query: string;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* 当前 prompt 对应的 turn handle Promise 控制器。
|
|
26
|
+
*/
|
|
27
|
+
deferredHandle: Deferred<AgentSessionTurnHandle>;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Promise 延迟控制器。
|
|
32
|
+
*/
|
|
33
|
+
interface Deferred<T> {
|
|
34
|
+
/**
|
|
35
|
+
* 延迟 Promise。
|
|
36
|
+
*/
|
|
37
|
+
promise: Promise<T>;
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* 兑现 Promise。
|
|
41
|
+
*/
|
|
42
|
+
resolve: (value: T) => void;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* 当前活跃 turn 的运行态。
|
|
47
|
+
*/
|
|
48
|
+
interface ActiveTurnState {
|
|
49
|
+
/**
|
|
50
|
+
* 当前 turnId。
|
|
51
|
+
*/
|
|
52
|
+
turnId: string;
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* 当前 turn 的最终结果快照。
|
|
56
|
+
*/
|
|
57
|
+
result: AgentSessionTurnResult | null;
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* 当前 turn 完成 Promise 控制器。
|
|
61
|
+
*/
|
|
62
|
+
deferredFinished: Deferred<AgentSessionTurnResult>;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* SessionPromptRuntime 构造参数。
|
|
67
|
+
*/
|
|
68
|
+
export interface SessionPromptRuntimeOptions {
|
|
69
|
+
/**
|
|
70
|
+
* 当前 session 标识。
|
|
71
|
+
*/
|
|
72
|
+
sessionId: string;
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* 广播 session 事件。
|
|
76
|
+
*/
|
|
77
|
+
publish: (event: AgentSessionEvent) => void;
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* 构造并持久化一条 user 消息。
|
|
81
|
+
*/
|
|
82
|
+
createAndPersistUserMessage: (query: string) => Promise<SessionUserMessageV1>;
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* 执行单轮 turn。
|
|
86
|
+
*/
|
|
87
|
+
executeTurn: (input: {
|
|
88
|
+
turnId: string;
|
|
89
|
+
query: string;
|
|
90
|
+
onStepMerge: () => Promise<SessionUserMessageV1[]>;
|
|
91
|
+
}) => Promise<{
|
|
92
|
+
text: string;
|
|
93
|
+
success: boolean;
|
|
94
|
+
error?: string;
|
|
95
|
+
}>;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* SessionPromptRuntime:最小 prompt 队列调度器。
|
|
100
|
+
*/
|
|
101
|
+
export class SessionPromptRuntime {
|
|
102
|
+
private readonly sessionId: string;
|
|
103
|
+
private readonly publish: SessionPromptRuntimeOptions["publish"];
|
|
104
|
+
private readonly createAndPersistUserMessage: SessionPromptRuntimeOptions["createAndPersistUserMessage"];
|
|
105
|
+
private readonly executeTurn: SessionPromptRuntimeOptions["executeTurn"];
|
|
106
|
+
private readonly queue: QueuedPrompt[] = [];
|
|
107
|
+
private processingPromise: Promise<void> | null = null;
|
|
108
|
+
|
|
109
|
+
constructor(options: SessionPromptRuntimeOptions) {
|
|
110
|
+
this.sessionId = String(options.sessionId || "").trim();
|
|
111
|
+
this.publish = options.publish;
|
|
112
|
+
this.createAndPersistUserMessage = options.createAndPersistUserMessage;
|
|
113
|
+
this.executeTurn = options.executeTurn;
|
|
114
|
+
if (!this.sessionId) {
|
|
115
|
+
throw new Error("SessionPromptRuntime requires a non-empty sessionId");
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* 追加一条新的 prompt。
|
|
121
|
+
*/
|
|
122
|
+
prompt(query: string): Promise<AgentSessionTurnHandle> {
|
|
123
|
+
const deferredHandle = createDeferred<AgentSessionTurnHandle>();
|
|
124
|
+
this.queue.push({
|
|
125
|
+
query: String(query || "").trim(),
|
|
126
|
+
deferredHandle,
|
|
127
|
+
});
|
|
128
|
+
this.ensureProcessing();
|
|
129
|
+
return deferredHandle.promise;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* 返回当前 actor prompt 调度器是否仍处于活跃态。
|
|
134
|
+
*
|
|
135
|
+
* 说明(中文)
|
|
136
|
+
* - 只要还有排队 prompt,或处理循环尚未结束,就视为活跃。
|
|
137
|
+
* - Session 会用它阻止内部 direct execution 与 actor 模式并发混用。
|
|
138
|
+
*/
|
|
139
|
+
isActive(): boolean {
|
|
140
|
+
return this.processingPromise !== null || this.queue.length > 0;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
private ensureProcessing(): void {
|
|
144
|
+
if (this.processingPromise) return;
|
|
145
|
+
this.processingPromise = this.processLoop().finally(() => {
|
|
146
|
+
this.processingPromise = null;
|
|
147
|
+
if (this.queue.length > 0) {
|
|
148
|
+
this.ensureProcessing();
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
private async processLoop(): Promise<void> {
|
|
154
|
+
while (this.queue.length > 0) {
|
|
155
|
+
const current = this.queue.shift();
|
|
156
|
+
if (!current) break;
|
|
157
|
+
|
|
158
|
+
const turnId = `turn:${this.sessionId}:${Date.now()}:${nanoid(6)}`;
|
|
159
|
+
const activeTurn = createActiveTurnState(turnId);
|
|
160
|
+
this.publish({
|
|
161
|
+
type: "turn-start",
|
|
162
|
+
turnId,
|
|
163
|
+
});
|
|
164
|
+
current.deferredHandle.resolve(createTurnHandle(activeTurn));
|
|
165
|
+
|
|
166
|
+
try {
|
|
167
|
+
await this.createAndPersistUserMessage(current.query);
|
|
168
|
+
const result = await this.executeTurn({
|
|
169
|
+
turnId,
|
|
170
|
+
query: current.query,
|
|
171
|
+
onStepMerge: async () => {
|
|
172
|
+
return await this.drainQueuedPromptsAsMessages(activeTurn);
|
|
173
|
+
},
|
|
174
|
+
});
|
|
175
|
+
const finalResult: AgentSessionTurnResult = {
|
|
176
|
+
turnId,
|
|
177
|
+
text: result.text,
|
|
178
|
+
success: result.success,
|
|
179
|
+
...(result.error ? { error: result.error } : {}),
|
|
180
|
+
};
|
|
181
|
+
activeTurn.result = finalResult;
|
|
182
|
+
this.publish({
|
|
183
|
+
type: "turn-finish",
|
|
184
|
+
turnId,
|
|
185
|
+
text: finalResult.text,
|
|
186
|
+
success: finalResult.success,
|
|
187
|
+
...(finalResult.error ? { error: finalResult.error } : {}),
|
|
188
|
+
});
|
|
189
|
+
activeTurn.deferredFinished.resolve(finalResult);
|
|
190
|
+
} catch (error) {
|
|
191
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
192
|
+
const finalResult: AgentSessionTurnResult = {
|
|
193
|
+
turnId,
|
|
194
|
+
text: "",
|
|
195
|
+
success: false,
|
|
196
|
+
error: message,
|
|
197
|
+
};
|
|
198
|
+
activeTurn.result = finalResult;
|
|
199
|
+
this.publish({
|
|
200
|
+
type: "error",
|
|
201
|
+
message,
|
|
202
|
+
});
|
|
203
|
+
this.publish({
|
|
204
|
+
type: "turn-finish",
|
|
205
|
+
turnId,
|
|
206
|
+
text: "",
|
|
207
|
+
success: false,
|
|
208
|
+
error: message,
|
|
209
|
+
});
|
|
210
|
+
activeTurn.deferredFinished.resolve(finalResult);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
private async drainQueuedPromptsAsMessages(
|
|
216
|
+
activeTurn: ActiveTurnState,
|
|
217
|
+
): Promise<SessionUserMessageV1[]> {
|
|
218
|
+
if (this.queue.length <= 0) return [];
|
|
219
|
+
const drained = this.queue.splice(0, this.queue.length);
|
|
220
|
+
const merged: SessionUserMessageV1[] = [];
|
|
221
|
+
|
|
222
|
+
for (let index = 0; index < drained.length; index += 1) {
|
|
223
|
+
const item = drained[index];
|
|
224
|
+
try {
|
|
225
|
+
const message = await this.createAndPersistUserMessage(item.query);
|
|
226
|
+
item.deferredHandle.resolve(createTurnHandle(activeTurn));
|
|
227
|
+
merged.push(message);
|
|
228
|
+
} catch {
|
|
229
|
+
// 关键点(中文):若某条消息持久化失败,把未处理部分重新放回队列头部,避免静默丢失。
|
|
230
|
+
const remaining = drained.slice(index);
|
|
231
|
+
this.queue.unshift(...remaining);
|
|
232
|
+
break;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
return merged;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
function createDeferred<T>(): Deferred<T> {
|
|
241
|
+
let resolve!: (value: T) => void;
|
|
242
|
+
const promise = new Promise<T>((innerResolve) => {
|
|
243
|
+
resolve = innerResolve;
|
|
244
|
+
});
|
|
245
|
+
return {
|
|
246
|
+
promise,
|
|
247
|
+
resolve,
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
function createActiveTurnState(turnId: string): ActiveTurnState {
|
|
252
|
+
return {
|
|
253
|
+
turnId,
|
|
254
|
+
result: null,
|
|
255
|
+
deferredFinished: createDeferred<AgentSessionTurnResult>(),
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
function createTurnHandle(
|
|
260
|
+
activeTurn: ActiveTurnState,
|
|
261
|
+
): AgentSessionTurnHandle {
|
|
262
|
+
return {
|
|
263
|
+
id: activeTurn.turnId,
|
|
264
|
+
get result() {
|
|
265
|
+
return activeTurn.result;
|
|
266
|
+
},
|
|
267
|
+
finished: activeTurn.deferredFinished.promise,
|
|
268
|
+
};
|
|
269
|
+
}
|
|
@@ -371,7 +371,7 @@ export class ChatQueueWorker {
|
|
|
371
371
|
const typing = this.startTypingHeartbeat(runItem);
|
|
372
372
|
let result: SessionRunResult;
|
|
373
373
|
try {
|
|
374
|
-
result = await serviceContext.
|
|
374
|
+
result = await serviceContext.execute({
|
|
375
375
|
query: runItem.text,
|
|
376
376
|
onStepCallback,
|
|
377
377
|
onAssistantStepCallback,
|
|
@@ -127,7 +127,7 @@ export class ChatSession extends Executor {
|
|
|
127
127
|
* 运行当前 chat session 的一次请求。
|
|
128
128
|
*
|
|
129
129
|
* 关键点(中文)
|
|
130
|
-
* -
|
|
130
|
+
* - 外层继续走统一的内部执行入口。
|
|
131
131
|
* - ChatSession 只是在 run 生命周期里临时绑定 turn 状态给自己的 composer。
|
|
132
132
|
*/
|
|
133
133
|
override async run(params: {
|
|
@@ -64,7 +64,7 @@ export async function receiveContactChatMessage(params: {
|
|
|
64
64
|
});
|
|
65
65
|
|
|
66
66
|
const sessionId = `contact_${contact.id}`;
|
|
67
|
-
const result = await params.context.session.get(sessionId).
|
|
67
|
+
const result = await params.context.session.get(sessionId).execute({
|
|
68
68
|
query: params.message,
|
|
69
69
|
});
|
|
70
70
|
const reply = extractMessageText(result.assistantMessage);
|
package/src/session/Executor.ts
CHANGED
|
@@ -617,10 +617,7 @@ export class Executor implements SessionExecutor {
|
|
|
617
617
|
textOnlyContinuationCount += 1;
|
|
618
618
|
incompleteResponseRecoveryCount = 0;
|
|
619
619
|
const continuationMessage = this.historyStore.userText({
|
|
620
|
-
text: buildTextOnlyContinuationNudge(
|
|
621
|
-
textOnlyContinuationCount,
|
|
622
|
-
textOnlyContinuationReason,
|
|
623
|
-
),
|
|
620
|
+
text: buildTextOnlyContinuationNudge(textOnlyContinuationCount),
|
|
624
621
|
metadata: {
|
|
625
622
|
sessionId,
|
|
626
623
|
extra: {
|
package/src/session/README.md
CHANGED
|
@@ -1,22 +1,23 @@
|
|
|
1
1
|
# Session Module
|
|
2
2
|
|
|
3
|
-
`session/` 是单 Agent 会话执行内核。它不直接面向普通 SDK 用户;普通用户通过 `sdk/Session.ts` 使用 `session.
|
|
3
|
+
`session/` 是单 Agent 会话执行内核。它不直接面向普通 SDK 用户;普通用户通过 `sdk/Session.ts` 使用 `session.prompt()`、`session.subscribe()`、`session.history()` 等 API。
|
|
4
4
|
|
|
5
5
|
## 核心概念
|
|
6
6
|
|
|
7
|
-
- `Session`:用户/SDK
|
|
8
|
-
- `Executor`:单个 session
|
|
7
|
+
- `Session`:用户/SDK 面向的完整会话对象,负责模型配置、历史入口、prompt 入口和分叉能力。
|
|
8
|
+
- `Executor`:单个 session 的内部单轮执行引擎,负责并发锁、执行 scope、Composer 编排、压缩重试、assistant step 持久化和 tool-loop 调用。
|
|
9
9
|
- `Composer`:执行前的材料组装协议,只负责把原材料组装成某一阶段输入,不直接执行模型,也没有公共运行时基类。
|
|
10
10
|
- `HistoryStore`:历史事实源,负责 JSONL 读写、meta、archive、lock、compact 与消息工厂。
|
|
11
11
|
- `HistoryComposer`:历史组装策略,只负责把 `HistoryStore` 中的历史组装成本轮模型输入。
|
|
12
12
|
- `CoreEngine`:Executor 内部的模型/tool-loop 核心机制,负责模型流、tool calls、续写和最终 assistant message 合并。
|
|
13
13
|
|
|
14
|
-
## 一轮
|
|
14
|
+
## 一轮 prompt 的调用链
|
|
15
15
|
|
|
16
16
|
```text
|
|
17
|
-
sdk/Session.
|
|
17
|
+
sdk/Session.prompt({ query })
|
|
18
|
+
-> SessionPromptRuntime 绑定 turn
|
|
18
19
|
-> append user message
|
|
19
|
-
-> session/Executor
|
|
20
|
+
-> session/Executor 执行当前 turn
|
|
20
21
|
-> ContextComposer.compose()
|
|
21
22
|
-> SystemComposer.resolve()
|
|
22
23
|
-> CompactionComposer.run()
|
|
@@ -25,7 +26,7 @@ sdk/Session.run({ query })
|
|
|
25
26
|
-> ai.streamText({ model, system, messages, tools })
|
|
26
27
|
-> return assistantMessage
|
|
27
28
|
-> append final assistant message
|
|
28
|
-
->
|
|
29
|
+
-> resolve turn.finished
|
|
29
30
|
```
|
|
30
31
|
|
|
31
32
|
## Composer 分工
|
|
@@ -63,18 +63,6 @@ const TEXT_ONLY_CONTINUATION_PATTERNS: ReadonlyArray<{
|
|
|
63
63
|
name: string;
|
|
64
64
|
pattern: RegExp;
|
|
65
65
|
}> = [
|
|
66
|
-
{
|
|
67
|
-
name: "pseudo_tool_protocol_dsml",
|
|
68
|
-
pattern: /<\s*||DSML||(?:tool_calls|invoke|parameter)\b/i,
|
|
69
|
-
},
|
|
70
|
-
{
|
|
71
|
-
name: "pseudo_tool_protocol_ascii",
|
|
72
|
-
pattern: /<\s*(?:tool_calls?|function_call|invoke)\b/i,
|
|
73
|
-
},
|
|
74
|
-
{
|
|
75
|
-
name: "pseudo_tool_protocol_json",
|
|
76
|
-
pattern: /"tool_calls"\s*:\s*\[/i,
|
|
77
|
-
},
|
|
78
66
|
{ name: "zh_start_now", pattern: /我现在开始/ },
|
|
79
67
|
{ name: "zh_next_will", pattern: /接下来我会/ },
|
|
80
68
|
{ name: "zh_will_do", pattern: /我会(?:先|继续|开始|基于|按)/ },
|
|
@@ -417,19 +405,8 @@ export function detectTextOnlyContinuationReason(
|
|
|
417
405
|
*/
|
|
418
406
|
export function buildTextOnlyContinuationNudge(
|
|
419
407
|
continuationIndex: number,
|
|
420
|
-
reason?: string | null,
|
|
421
408
|
): string {
|
|
422
409
|
const round = Math.max(1, continuationIndex);
|
|
423
|
-
const normalizedReason = String(reason || "").trim();
|
|
424
|
-
if (normalizedReason.startsWith("pseudo_tool_protocol")) {
|
|
425
|
-
return [
|
|
426
|
-
`系统续跑提醒(第 ${round} 次):上一轮输出了伪工具调用协议文本,但没有产生真实工具调用。`,
|
|
427
|
-
"不要把 DSML、XML、JSON tool_calls 或任何工具协议当作普通正文输出。",
|
|
428
|
-
"如果确实需要工具,请立刻使用当前运行时提供的原生工具重新发起调用;只能使用实际可用的工具名。",
|
|
429
|
-
"如果目标工具不可用,请明确说明不可用,并改用可用工具完成任务或说明受阻原因。",
|
|
430
|
-
"只有在任务真正完成、明确受阻、或必须等待用户提供信息时才停止。",
|
|
431
|
-
].join("\n");
|
|
432
|
-
}
|
|
433
410
|
return [
|
|
434
411
|
`系统续跑提醒(第 ${round} 次):继续执行当前任务。`,
|
|
435
412
|
"不要只描述计划、下一步或“我接下来会做什么”。",
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent Session 事件类型定义。
|
|
3
|
+
*
|
|
4
|
+
* 关键点(中文)
|
|
5
|
+
* - `subscribe()` 暴露的是 session 级长期事件序列,而不是单次执行的局部结果。
|
|
6
|
+
* - 事件只表达“当前 turn 正在发生什么”,不负责历史回放。
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import type { JsonValue } from "@/types/common/Json.js";
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* 单个 turn 开始事件。
|
|
13
|
+
*/
|
|
14
|
+
export interface AgentSessionTurnStartEvent {
|
|
15
|
+
/**
|
|
16
|
+
* 当前事件类型。
|
|
17
|
+
*/
|
|
18
|
+
type: "turn-start";
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* 当前 turn 的稳定标识。
|
|
22
|
+
*
|
|
23
|
+
* 说明(中文)
|
|
24
|
+
* - 同一个 turn 内的全部增量事件都会复用这个 turnId。
|
|
25
|
+
* - 当 Session 排队进入下一轮时,会生成新的 turnId。
|
|
26
|
+
*/
|
|
27
|
+
turnId: string;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* 普通文本增量事件。
|
|
32
|
+
*/
|
|
33
|
+
export interface AgentSessionTextDeltaEvent {
|
|
34
|
+
/**
|
|
35
|
+
* 当前事件类型。
|
|
36
|
+
*/
|
|
37
|
+
type: "text-delta";
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* 当前文本所属 turn。
|
|
41
|
+
*/
|
|
42
|
+
turnId: string;
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* 当前这次新增的可见文本片段。
|
|
46
|
+
*/
|
|
47
|
+
text: string;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* reasoning 文本增量事件。
|
|
52
|
+
*/
|
|
53
|
+
export interface AgentSessionReasoningDeltaEvent {
|
|
54
|
+
/**
|
|
55
|
+
* 当前事件类型。
|
|
56
|
+
*/
|
|
57
|
+
type: "reasoning-delta";
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* 当前 reasoning 所属 turn。
|
|
61
|
+
*/
|
|
62
|
+
turnId: string;
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* 当前这次新增的 reasoning 文本片段。
|
|
66
|
+
*/
|
|
67
|
+
text: string;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* 工具调用事件。
|
|
72
|
+
*/
|
|
73
|
+
export interface AgentSessionToolCallEvent {
|
|
74
|
+
/**
|
|
75
|
+
* 当前事件类型。
|
|
76
|
+
*/
|
|
77
|
+
type: "tool-call";
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* 当前工具调用所属 turn。
|
|
81
|
+
*/
|
|
82
|
+
turnId: string;
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* 当前工具调用唯一标识。
|
|
86
|
+
*/
|
|
87
|
+
toolCallId: string;
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* 当前工具名称。
|
|
91
|
+
*/
|
|
92
|
+
toolName: string;
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* 当前工具输入参数。
|
|
96
|
+
*/
|
|
97
|
+
args: JsonValue;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* 工具结果事件。
|
|
102
|
+
*/
|
|
103
|
+
export interface AgentSessionToolResultEvent {
|
|
104
|
+
/**
|
|
105
|
+
* 当前事件类型。
|
|
106
|
+
*/
|
|
107
|
+
type: "tool-result";
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* 当前工具结果所属 turn。
|
|
111
|
+
*/
|
|
112
|
+
turnId: string;
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* 当前工具调用唯一标识。
|
|
116
|
+
*/
|
|
117
|
+
toolCallId: string;
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* 当前工具名称。
|
|
121
|
+
*/
|
|
122
|
+
toolName: string;
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* 当前工具输出结果。
|
|
126
|
+
*/
|
|
127
|
+
result: JsonValue;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* 单个 turn 完成事件。
|
|
132
|
+
*/
|
|
133
|
+
export interface AgentSessionTurnFinishEvent {
|
|
134
|
+
/**
|
|
135
|
+
* 当前事件类型。
|
|
136
|
+
*/
|
|
137
|
+
type: "turn-finish";
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* 当前已完成 turn 的稳定标识。
|
|
141
|
+
*/
|
|
142
|
+
turnId: string;
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* 当前 turn 最终可见文本。
|
|
146
|
+
*/
|
|
147
|
+
text: string;
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* 当前 turn 是否成功结束。
|
|
151
|
+
*/
|
|
152
|
+
success: boolean;
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* 当前 turn 失败时的错误文本。
|
|
156
|
+
*/
|
|
157
|
+
error?: string;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Session 级错误事件。
|
|
162
|
+
*/
|
|
163
|
+
export interface AgentSessionErrorEvent {
|
|
164
|
+
/**
|
|
165
|
+
* 当前事件类型。
|
|
166
|
+
*/
|
|
167
|
+
type: "error";
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* 当前错误文本。
|
|
171
|
+
*/
|
|
172
|
+
message: string;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Session 订阅可见事件联合类型。
|
|
177
|
+
*/
|
|
178
|
+
export type AgentSessionEvent =
|
|
179
|
+
| AgentSessionTurnStartEvent
|
|
180
|
+
| AgentSessionTextDeltaEvent
|
|
181
|
+
| AgentSessionReasoningDeltaEvent
|
|
182
|
+
| AgentSessionToolCallEvent
|
|
183
|
+
| AgentSessionToolResultEvent
|
|
184
|
+
| AgentSessionTurnFinishEvent
|
|
185
|
+
| AgentSessionErrorEvent;
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Session 事件订阅回调。
|
|
189
|
+
*/
|
|
190
|
+
export type AgentSessionSubscriber = (event: AgentSessionEvent) => void;
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* 取消 Session 订阅的返回函数。
|
|
194
|
+
*/
|
|
195
|
+
export type AgentSessionUnsubscribe = () => void;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent Session prompt 输入类型定义。
|
|
3
|
+
*
|
|
4
|
+
* 关键点(中文)
|
|
5
|
+
* - `prompt()` 是 Session actor 模型下唯一的输入入口。
|
|
6
|
+
* - 首条输入、运行中补充输入、排队到下一轮的输入,调用侧都使用同一结构。
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Session prompt 输入。
|
|
11
|
+
*/
|
|
12
|
+
export interface AgentSessionPromptInput {
|
|
13
|
+
/**
|
|
14
|
+
* 当前这次要追加到 Session 的用户文本。
|
|
15
|
+
*
|
|
16
|
+
* 说明(中文)
|
|
17
|
+
* - 调用侧永远只传“新的用户输入”。
|
|
18
|
+
* - 它是否并入当前 turn,还是排到下一 turn,由 Session 内部决定。
|
|
19
|
+
*/
|
|
20
|
+
query: string;
|
|
21
|
+
}
|