@downcity/agent 1.1.22 → 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/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/README.md +8 -7
- 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
package/src/sdk/Session.ts
CHANGED
|
@@ -3,12 +3,12 @@
|
|
|
3
3
|
*
|
|
4
4
|
* 关键点(中文)
|
|
5
5
|
* - 面向 `new Agent(...)` 的本地会话使用场景。
|
|
6
|
-
* - 统一收口消息落盘、session 级模型配置、
|
|
6
|
+
* - 统一收口消息落盘、session 级模型配置、prompt/subscribe/fork 等高层 API。
|
|
7
7
|
* - 内部继续复用 `Executor` / `JsonlSessionHistoryStore` / Composer 体系。
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import { nanoid } from "nanoid";
|
|
11
|
-
import type {
|
|
11
|
+
import type { Tool } from "ai";
|
|
12
12
|
import { Executor } from "@session/Executor.js";
|
|
13
13
|
import { JsonlSessionHistoryComposer } from "@session/composer/history/jsonl/JsonlSessionHistoryComposer.js";
|
|
14
14
|
import { JsonlSessionHistoryStore } from "@/session/store/history/jsonl/JsonlSessionHistoryStore.js";
|
|
@@ -17,10 +17,7 @@ import type {
|
|
|
17
17
|
AgentSessionConfigSnapshot,
|
|
18
18
|
AgentSessionForkInput,
|
|
19
19
|
AgentSessionMetadata,
|
|
20
|
-
AgentSessionRunInput,
|
|
21
|
-
AgentSessionRunResult,
|
|
22
20
|
AgentSessionSetInput,
|
|
23
|
-
AgentSessionStreamEvent,
|
|
24
21
|
AgentSessionSystemBlock,
|
|
25
22
|
AgentSessionSystemSnapshot,
|
|
26
23
|
} from "@/sdk/AgentSdkTypes.js";
|
|
@@ -40,14 +37,29 @@ import {
|
|
|
40
37
|
getSdkAgentSessionArchiveDirPath,
|
|
41
38
|
getSdkAgentSessionDirPath,
|
|
42
39
|
} from "@/sdk/session/index.js";
|
|
43
|
-
import { AsyncQueue } from "@/sdk/AsyncQueue.js";
|
|
44
40
|
import type { SessionPort } from "@/core/AgentContextTypes.js";
|
|
45
|
-
import {
|
|
41
|
+
import {
|
|
42
|
+
mapAgentEventToSessionEvent,
|
|
43
|
+
mapUiMessageChunkToAgentEvent,
|
|
44
|
+
} from "@/sdk/SessionEventMapper.js";
|
|
46
45
|
import {
|
|
47
46
|
persistSdkAssistantResult,
|
|
48
47
|
touchSessionMetadata,
|
|
49
48
|
} from "@/sdk/session/index.js";
|
|
50
49
|
import { createSessionServicePort } from "@/sdk/session/index.js";
|
|
50
|
+
import type {
|
|
51
|
+
AgentSessionSubscriber,
|
|
52
|
+
AgentSessionUnsubscribe,
|
|
53
|
+
} from "@/types/sdk/AgentSessionEvent.js";
|
|
54
|
+
import type { AgentSessionPromptInput } from "@/types/sdk/AgentSessionPrompt.js";
|
|
55
|
+
import type { AgentSessionTurnHandle } from "@/types/sdk/AgentSessionTurn.js";
|
|
56
|
+
import type { SessionUserMessageV1 } from "@/session/types/SessionMessages.js";
|
|
57
|
+
import { SessionEventHub } from "@/sdk/session/runtime/SessionEventHub.js";
|
|
58
|
+
import { SessionPromptRuntime } from "@/sdk/session/runtime/SessionPromptRuntime.js";
|
|
59
|
+
import type {
|
|
60
|
+
SessionAssistantStepCallback,
|
|
61
|
+
SessionUiMessageChunkCallback,
|
|
62
|
+
} from "@/session/types/SessionRun.js";
|
|
51
63
|
|
|
52
64
|
type SessionOptions = {
|
|
53
65
|
/**
|
|
@@ -120,12 +132,15 @@ export class Session {
|
|
|
120
132
|
private readonly historyStore: JsonlSessionHistoryStore;
|
|
121
133
|
private readonly historyComposer: JsonlSessionHistoryComposer;
|
|
122
134
|
private readonly executor: Executor;
|
|
135
|
+
private readonly eventHub = new SessionEventHub();
|
|
136
|
+
private readonly promptRuntime: SessionPromptRuntime;
|
|
123
137
|
private sessionConfig: AgentSessionConfigSnapshot = {};
|
|
124
138
|
private createdAt = Date.now();
|
|
125
139
|
private timezone = resolveSystemTimezone();
|
|
126
140
|
private initializePromise: Promise<this> | null = null;
|
|
127
141
|
private ensureConfiguredPromise: Promise<void> | null = null;
|
|
128
142
|
private servicePort: SessionPort | null = null;
|
|
143
|
+
private directExecutionReserved = false;
|
|
129
144
|
|
|
130
145
|
constructor(options: SessionOptions) {
|
|
131
146
|
this.id = String(options.sessionId || "").trim();
|
|
@@ -189,6 +204,22 @@ export class Session {
|
|
|
189
204
|
}),
|
|
190
205
|
getTools: () => this.tools,
|
|
191
206
|
});
|
|
207
|
+
this.promptRuntime = new SessionPromptRuntime({
|
|
208
|
+
sessionId: this.id,
|
|
209
|
+
publish: (event) => {
|
|
210
|
+
this.eventHub.publish(event);
|
|
211
|
+
},
|
|
212
|
+
createAndPersistUserMessage: async (query) => {
|
|
213
|
+
return await this.createAndPersistUserPromptMessage(query);
|
|
214
|
+
},
|
|
215
|
+
executeTurn: async ({ turnId, query, onStepMerge }) => {
|
|
216
|
+
return await this.executePromptTurn({
|
|
217
|
+
turnId,
|
|
218
|
+
query,
|
|
219
|
+
onStepMerge,
|
|
220
|
+
});
|
|
221
|
+
},
|
|
222
|
+
});
|
|
192
223
|
}
|
|
193
224
|
|
|
194
225
|
/**
|
|
@@ -259,6 +290,34 @@ export class Session {
|
|
|
259
290
|
});
|
|
260
291
|
}
|
|
261
292
|
|
|
293
|
+
/**
|
|
294
|
+
* 追加一条新的 Session prompt。
|
|
295
|
+
*
|
|
296
|
+
* 关键点(中文)
|
|
297
|
+
* - 这是 Session actor 模型下唯一的输入入口。
|
|
298
|
+
* - 首条输入、运行中补充输入、排到下一轮的输入,调用方式完全一致。
|
|
299
|
+
*/
|
|
300
|
+
async prompt(input: AgentSessionPromptInput): Promise<AgentSessionTurnHandle> {
|
|
301
|
+
const query = String(input.query || "").trim();
|
|
302
|
+
if (!query) {
|
|
303
|
+
throw new Error("session.prompt requires a non-empty query");
|
|
304
|
+
}
|
|
305
|
+
await this.ensureRunnable();
|
|
306
|
+
this.assertCanPrompt();
|
|
307
|
+
return await this.promptRuntime.prompt(query);
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
/**
|
|
311
|
+
* 订阅当前 Session 的未来事件。
|
|
312
|
+
*
|
|
313
|
+
* 关键点(中文)
|
|
314
|
+
* - 只广播订阅之后产生的事件。
|
|
315
|
+
* - 不做历史回放;历史仍通过 `history()` 读取。
|
|
316
|
+
*/
|
|
317
|
+
subscribe(subscriber: AgentSessionSubscriber): AgentSessionUnsubscribe {
|
|
318
|
+
return this.eventHub.subscribe(subscriber);
|
|
319
|
+
}
|
|
320
|
+
|
|
262
321
|
/**
|
|
263
322
|
* 追加一条 user 文本消息。
|
|
264
323
|
*/
|
|
@@ -332,7 +391,11 @@ export class Session {
|
|
|
332
391
|
* 返回当前 session 是否正在执行。
|
|
333
392
|
*/
|
|
334
393
|
isExecuting(): boolean {
|
|
335
|
-
return
|
|
394
|
+
return (
|
|
395
|
+
this.directExecutionReserved ||
|
|
396
|
+
this.promptRuntime.isActive() ||
|
|
397
|
+
this.executor.isExecuting()
|
|
398
|
+
);
|
|
336
399
|
}
|
|
337
400
|
|
|
338
401
|
/**
|
|
@@ -342,83 +405,6 @@ export class Session {
|
|
|
342
405
|
this.executor.clearExecutor();
|
|
343
406
|
}
|
|
344
407
|
|
|
345
|
-
/**
|
|
346
|
-
* 执行一轮非流式请求。
|
|
347
|
-
*/
|
|
348
|
-
async run(input: AgentSessionRunInput): Promise<AgentSessionRunResult> {
|
|
349
|
-
const query = String(input.query || "").trim();
|
|
350
|
-
if (!query) {
|
|
351
|
-
throw new Error("session.run requires a non-empty query");
|
|
352
|
-
}
|
|
353
|
-
await this.ensureReadyForExecution();
|
|
354
|
-
if (!this.sessionConfig.model) {
|
|
355
|
-
throw new Error(
|
|
356
|
-
`Session "${this.id}" requires a configured model. Pass model to new Agent({ model }), call session.set({ model }) first, or let the host configure the session during creation.`,
|
|
357
|
-
);
|
|
358
|
-
}
|
|
359
|
-
await this.appendUserMessage({ text: query });
|
|
360
|
-
const result = await this.executor.run({
|
|
361
|
-
query,
|
|
362
|
-
});
|
|
363
|
-
await this.persistAssistantResult(result.assistantMessage);
|
|
364
|
-
return {
|
|
365
|
-
success: result.success,
|
|
366
|
-
...(result.error ? { error: result.error } : {}),
|
|
367
|
-
text: extractTextFromUiMessage(result.assistantMessage),
|
|
368
|
-
assistantMessage: result.assistantMessage,
|
|
369
|
-
};
|
|
370
|
-
}
|
|
371
|
-
|
|
372
|
-
/**
|
|
373
|
-
* 执行一轮流式请求。
|
|
374
|
-
*/
|
|
375
|
-
async *stream(
|
|
376
|
-
input: AgentSessionRunInput,
|
|
377
|
-
): AsyncIterable<AgentSessionStreamEvent> {
|
|
378
|
-
const query = String(input.query || "").trim();
|
|
379
|
-
if (!query) {
|
|
380
|
-
throw new Error("session.stream requires a non-empty query");
|
|
381
|
-
}
|
|
382
|
-
await this.ensureReadyForExecution();
|
|
383
|
-
if (!this.sessionConfig.model) {
|
|
384
|
-
throw new Error(
|
|
385
|
-
`Session "${this.id}" requires a configured model. Pass model to new Agent({ model }), call session.set({ model }) first, or let the host configure the session during creation.`,
|
|
386
|
-
);
|
|
387
|
-
}
|
|
388
|
-
const queue = new AsyncQueue<AgentSessionStreamEvent>();
|
|
389
|
-
const toolNameByCallId = new Map<string, string>();
|
|
390
|
-
await this.appendUserMessage({ text: query });
|
|
391
|
-
|
|
392
|
-
const runPromise = (async () => {
|
|
393
|
-
try {
|
|
394
|
-
const result = await this.executor.run({
|
|
395
|
-
query,
|
|
396
|
-
onUiMessageChunkCallback: async (chunk) => {
|
|
397
|
-
pushUiMessageChunkAsSdkEvent({
|
|
398
|
-
queue,
|
|
399
|
-
chunk,
|
|
400
|
-
toolNameByCallId,
|
|
401
|
-
});
|
|
402
|
-
},
|
|
403
|
-
});
|
|
404
|
-
await this.persistAssistantResult(result.assistantMessage);
|
|
405
|
-
} catch (error) {
|
|
406
|
-
queue.push({
|
|
407
|
-
type: "error",
|
|
408
|
-
error: error instanceof Error ? error.message : String(error),
|
|
409
|
-
});
|
|
410
|
-
} finally {
|
|
411
|
-
queue.close();
|
|
412
|
-
}
|
|
413
|
-
})();
|
|
414
|
-
|
|
415
|
-
for await (const event of queue) {
|
|
416
|
-
yield event;
|
|
417
|
-
}
|
|
418
|
-
|
|
419
|
-
await runPromise;
|
|
420
|
-
}
|
|
421
|
-
|
|
422
408
|
/**
|
|
423
409
|
* 从当前 session 创建一个分叉会话。
|
|
424
410
|
*/
|
|
@@ -490,7 +476,29 @@ export class Session {
|
|
|
490
476
|
if (this.servicePort) return this.servicePort;
|
|
491
477
|
this.servicePort = createSessionServicePort({
|
|
492
478
|
sessionId: this.id,
|
|
493
|
-
|
|
479
|
+
getExecutor: () => this.executor.getExecutor(),
|
|
480
|
+
executeDirect: async (runParams) => {
|
|
481
|
+
return await this.executeDirect(runParams);
|
|
482
|
+
},
|
|
483
|
+
prompt: async (input) => {
|
|
484
|
+
return await this.prompt(input);
|
|
485
|
+
},
|
|
486
|
+
subscribe: (subscriber) => {
|
|
487
|
+
return this.subscribe(subscriber);
|
|
488
|
+
},
|
|
489
|
+
clearExecutor: () => {
|
|
490
|
+
this.executor.clearExecutor();
|
|
491
|
+
},
|
|
492
|
+
afterSessionUpdatedAsync: async () => {
|
|
493
|
+
await this.executor.afterSessionUpdatedAsync();
|
|
494
|
+
},
|
|
495
|
+
appendUserMessage: async (messageParams) => {
|
|
496
|
+
await this.executor.appendUserMessage(messageParams);
|
|
497
|
+
},
|
|
498
|
+
appendAssistantMessage: async (messageParams) => {
|
|
499
|
+
await this.executor.appendAssistantMessage(messageParams);
|
|
500
|
+
},
|
|
501
|
+
isExecuting: () => this.isExecuting(),
|
|
494
502
|
historyStore: this.historyStore,
|
|
495
503
|
ensureReadyForExecution: async () => {
|
|
496
504
|
await this.ensureReadyForExecution();
|
|
@@ -523,6 +531,43 @@ export class Session {
|
|
|
523
531
|
}
|
|
524
532
|
}
|
|
525
533
|
|
|
534
|
+
private async ensureRunnable(): Promise<void> {
|
|
535
|
+
await this.ensureReadyForExecution();
|
|
536
|
+
if (!this.sessionConfig.model) {
|
|
537
|
+
throw new Error(
|
|
538
|
+
`Session "${this.id}" requires a configured model. Pass model to new Agent({ model }), call session.set({ model }) first, or let the host configure the session during creation.`,
|
|
539
|
+
);
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
private assertCanPrompt(): void {
|
|
544
|
+
if (!this.directExecutionReserved && !this.executor.isExecuting()) {
|
|
545
|
+
return;
|
|
546
|
+
}
|
|
547
|
+
if (this.promptRuntime.isActive()) {
|
|
548
|
+
return;
|
|
549
|
+
}
|
|
550
|
+
throw new Error(
|
|
551
|
+
"session.prompt cannot attach to an active direct session execution. Use prompt()/subscribe() as the public interactive API, or wait for the current execution to finish.",
|
|
552
|
+
);
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
private beginDirectExecution(): void {
|
|
556
|
+
if (this.promptRuntime.isActive()) {
|
|
557
|
+
throw new Error(
|
|
558
|
+
"Direct session execution cannot start while session.prompt() is active. Keep SDK-facing interactive sessions on prompt()/subscribe(), or wait for the actor turn to finish.",
|
|
559
|
+
);
|
|
560
|
+
}
|
|
561
|
+
if (this.directExecutionReserved || this.executor.isExecuting()) {
|
|
562
|
+
throw new Error("Direct session execution does not support concurrent execution on the same session.");
|
|
563
|
+
}
|
|
564
|
+
this.directExecutionReserved = true;
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
private endDirectExecution(): void {
|
|
568
|
+
this.directExecutionReserved = false;
|
|
569
|
+
}
|
|
570
|
+
|
|
526
571
|
private async touchMetadata(): Promise<void> {
|
|
527
572
|
await touchSessionMetadata({
|
|
528
573
|
projectRoot: this.projectRoot,
|
|
@@ -544,4 +589,108 @@ export class Session {
|
|
|
544
589
|
assistantMessage,
|
|
545
590
|
});
|
|
546
591
|
}
|
|
592
|
+
|
|
593
|
+
private async createAndPersistUserPromptMessage(
|
|
594
|
+
query: string,
|
|
595
|
+
): Promise<SessionUserMessageV1> {
|
|
596
|
+
const message = this.historyStore.userText({
|
|
597
|
+
text: String(query || "").trim(),
|
|
598
|
+
metadata: {
|
|
599
|
+
sessionId: this.id,
|
|
600
|
+
},
|
|
601
|
+
}) as SessionUserMessageV1;
|
|
602
|
+
await this.executor.appendUserMessage({
|
|
603
|
+
message,
|
|
604
|
+
});
|
|
605
|
+
await this.touchMetadata();
|
|
606
|
+
return message;
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
private async executePromptTurn(input: {
|
|
610
|
+
turnId: string;
|
|
611
|
+
query: string;
|
|
612
|
+
onStepMerge: () => Promise<SessionUserMessageV1[]>;
|
|
613
|
+
}): Promise<{
|
|
614
|
+
text: string;
|
|
615
|
+
success: boolean;
|
|
616
|
+
error?: string;
|
|
617
|
+
}> {
|
|
618
|
+
const toolNameByCallId = new Map<string, string>();
|
|
619
|
+
const result = await this.executor.run({
|
|
620
|
+
query: input.query,
|
|
621
|
+
onStepCallback: input.onStepMerge,
|
|
622
|
+
onUiMessageChunkCallback: async (chunk) => {
|
|
623
|
+
if (chunk.type === "tool-input-start") {
|
|
624
|
+
toolNameByCallId.set(chunk.toolCallId, chunk.toolName);
|
|
625
|
+
return;
|
|
626
|
+
}
|
|
627
|
+
const event = mapUiMessageChunkToAgentEvent(chunk);
|
|
628
|
+
if (!event) return;
|
|
629
|
+
const resolvedEvent =
|
|
630
|
+
(
|
|
631
|
+
event.type === "tool-result" ||
|
|
632
|
+
event.type === "tool-error"
|
|
633
|
+
) &&
|
|
634
|
+
event.toolName === "unknown"
|
|
635
|
+
? {
|
|
636
|
+
...event,
|
|
637
|
+
toolName:
|
|
638
|
+
toolNameByCallId.get(event.toolCallId) || event.toolName,
|
|
639
|
+
}
|
|
640
|
+
: event;
|
|
641
|
+
if (
|
|
642
|
+
resolvedEvent.type === "tool-call" ||
|
|
643
|
+
resolvedEvent.type === "tool-error"
|
|
644
|
+
) {
|
|
645
|
+
toolNameByCallId.set(
|
|
646
|
+
resolvedEvent.toolCallId,
|
|
647
|
+
resolvedEvent.toolName,
|
|
648
|
+
);
|
|
649
|
+
}
|
|
650
|
+
const sessionEvent = mapAgentEventToSessionEvent({
|
|
651
|
+
event: resolvedEvent,
|
|
652
|
+
turnId: input.turnId,
|
|
653
|
+
});
|
|
654
|
+
if (sessionEvent) {
|
|
655
|
+
this.eventHub.publish(sessionEvent);
|
|
656
|
+
}
|
|
657
|
+
},
|
|
658
|
+
});
|
|
659
|
+
await this.persistAssistantResult(result.assistantMessage);
|
|
660
|
+
return {
|
|
661
|
+
text: extractTextFromUiMessage(result.assistantMessage),
|
|
662
|
+
success: result.success,
|
|
663
|
+
...(result.error ? { error: result.error } : {}),
|
|
664
|
+
};
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
private async executeDirect(input: {
|
|
668
|
+
query: string;
|
|
669
|
+
onStepCallback?: () => Promise<SessionUserMessageV1[]>;
|
|
670
|
+
onAssistantStepCallback?: SessionAssistantStepCallback;
|
|
671
|
+
onUiMessageChunkCallback?: SessionUiMessageChunkCallback;
|
|
672
|
+
}) {
|
|
673
|
+
const query = String(input.query || "").trim();
|
|
674
|
+
if (!query) {
|
|
675
|
+
throw new Error("Session direct execution requires a non-empty query");
|
|
676
|
+
}
|
|
677
|
+
await this.ensureRunnable();
|
|
678
|
+
this.beginDirectExecution();
|
|
679
|
+
try {
|
|
680
|
+
return await this.executor.run({
|
|
681
|
+
query,
|
|
682
|
+
...(typeof input.onStepCallback === "function"
|
|
683
|
+
? { onStepCallback: input.onStepCallback }
|
|
684
|
+
: {}),
|
|
685
|
+
...(typeof input.onAssistantStepCallback === "function"
|
|
686
|
+
? { onAssistantStepCallback: input.onAssistantStepCallback }
|
|
687
|
+
: {}),
|
|
688
|
+
...(typeof input.onUiMessageChunkCallback === "function"
|
|
689
|
+
? { onUiMessageChunkCallback: input.onUiMessageChunkCallback }
|
|
690
|
+
: {}),
|
|
691
|
+
});
|
|
692
|
+
} finally {
|
|
693
|
+
this.endDirectExecution();
|
|
694
|
+
}
|
|
695
|
+
}
|
|
547
696
|
}
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Session 事件映射辅助。
|
|
3
3
|
*
|
|
4
4
|
* 关键点(中文)
|
|
5
|
-
* - 把底层 AI SDK `UIMessageChunk`
|
|
6
|
-
* -
|
|
5
|
+
* - 把底层 AI SDK `UIMessageChunk` 归一到内部 `AgentUiChunkEvent`。
|
|
6
|
+
* - 再把内部 chunk 事件转换为 `session.subscribe()` 可见的 Session 事件。
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
import type { UIMessageChunk } from "ai";
|
|
10
10
|
import type { JsonValue } from "@/types/common/Json.js";
|
|
11
|
-
import type {
|
|
12
|
-
import type {
|
|
11
|
+
import type { AgentSessionEvent } from "@/types/sdk/AgentSessionEvent.js";
|
|
12
|
+
import type { AgentUiChunkEvent } from "@/types/sdk/AgentUiChunkEvent.js";
|
|
13
13
|
|
|
14
14
|
function toJsonValue(value: unknown): JsonValue {
|
|
15
15
|
if (value === undefined) return null;
|
|
@@ -29,11 +29,11 @@ function toJsonValue(value: unknown): JsonValue {
|
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
/**
|
|
32
|
-
* 把单个 UI chunk
|
|
32
|
+
* 把单个 UI chunk 映射为内部 chunk 事件。
|
|
33
33
|
*/
|
|
34
|
-
export function
|
|
34
|
+
export function mapUiMessageChunkToAgentEvent(
|
|
35
35
|
chunk: UIMessageChunk,
|
|
36
|
-
):
|
|
36
|
+
): AgentUiChunkEvent | null {
|
|
37
37
|
switch (chunk.type) {
|
|
38
38
|
case "text-delta":
|
|
39
39
|
return {
|
|
@@ -88,7 +88,7 @@ export function mapUiMessageChunkToSdkEvent(
|
|
|
88
88
|
case "abort":
|
|
89
89
|
return {
|
|
90
90
|
type: "error",
|
|
91
|
-
error: String(chunk.reason || "
|
|
91
|
+
error: String(chunk.reason || "execution aborted"),
|
|
92
92
|
};
|
|
93
93
|
default:
|
|
94
94
|
return null;
|
|
@@ -96,43 +96,53 @@ export function mapUiMessageChunkToSdkEvent(
|
|
|
96
96
|
}
|
|
97
97
|
|
|
98
98
|
/**
|
|
99
|
-
*
|
|
99
|
+
* 把内部 chunk 事件映射为 Session actor 事件。
|
|
100
100
|
*
|
|
101
101
|
* 关键点(中文)
|
|
102
|
-
* -
|
|
103
|
-
* -
|
|
102
|
+
* - Session 级事件只保留 turn 维度所需的最小字段。
|
|
103
|
+
* - `finish` 与通用 `error` 由 turn 生命周期统一表达,不在这里直接透出。
|
|
104
104
|
*/
|
|
105
|
-
export function
|
|
105
|
+
export function mapAgentEventToSessionEvent(params: {
|
|
106
106
|
/**
|
|
107
|
-
*
|
|
107
|
+
* 当前内部 chunk 事件。
|
|
108
108
|
*/
|
|
109
|
-
|
|
109
|
+
event: AgentUiChunkEvent;
|
|
110
110
|
/**
|
|
111
|
-
*
|
|
111
|
+
* 当前 turn 标识。
|
|
112
112
|
*/
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
113
|
+
turnId: string;
|
|
114
|
+
}): AgentSessionEvent | null {
|
|
115
|
+
const { event, turnId } = params;
|
|
116
|
+
switch (event.type) {
|
|
117
|
+
case "text-delta":
|
|
118
|
+
return {
|
|
119
|
+
type: "text-delta",
|
|
120
|
+
turnId,
|
|
121
|
+
text: event.text,
|
|
122
|
+
};
|
|
123
|
+
case "reasoning-delta":
|
|
124
|
+
return {
|
|
125
|
+
type: "reasoning-delta",
|
|
126
|
+
turnId,
|
|
127
|
+
text: event.text,
|
|
128
|
+
};
|
|
129
|
+
case "tool-call":
|
|
130
|
+
return {
|
|
131
|
+
type: "tool-call",
|
|
132
|
+
turnId,
|
|
133
|
+
toolCallId: event.toolCallId,
|
|
134
|
+
toolName: event.toolName,
|
|
135
|
+
args: event.args,
|
|
136
|
+
};
|
|
137
|
+
case "tool-result":
|
|
138
|
+
return {
|
|
139
|
+
type: "tool-result",
|
|
140
|
+
turnId,
|
|
141
|
+
toolCallId: event.toolCallId,
|
|
142
|
+
toolName: event.toolName,
|
|
143
|
+
result: event.result,
|
|
144
|
+
};
|
|
145
|
+
default:
|
|
146
|
+
return null;
|
|
136
147
|
}
|
|
137
|
-
queue.push(event);
|
|
138
148
|
}
|
|
@@ -2,12 +2,24 @@
|
|
|
2
2
|
* SDK Session service 端口构造器。
|
|
3
3
|
*
|
|
4
4
|
* 关键点(中文)
|
|
5
|
-
* - 把 SDK 本地 session 适配成
|
|
6
|
-
* -
|
|
5
|
+
* - 把 SDK 本地 session 适配成 runtime / service 依赖的 `SessionPort`。
|
|
6
|
+
* - SDK 公开面只保留 `prompt()` / `subscribe()`;内部单轮执行原语仍通过这里暴露给 service/runtime。
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
import type { SessionPort } from "@/core/AgentContextTypes.js";
|
|
10
10
|
import type { SessionHistoryStore } from "@/session/store/history/SessionHistoryStore.js";
|
|
11
|
+
import type { SessionRunResult } from "@/session/types/SessionRun.js";
|
|
12
|
+
import type { SessionUserMessageV1 } from "@/session/types/SessionMessages.js";
|
|
13
|
+
import type {
|
|
14
|
+
SessionAssistantStepCallback,
|
|
15
|
+
SessionUiMessageChunkCallback,
|
|
16
|
+
} from "@/session/types/SessionRun.js";
|
|
17
|
+
import type { AgentSessionPromptInput } from "@/types/sdk/AgentSessionPrompt.js";
|
|
18
|
+
import type {
|
|
19
|
+
AgentSessionSubscriber,
|
|
20
|
+
AgentSessionUnsubscribe,
|
|
21
|
+
} from "@/types/sdk/AgentSessionEvent.js";
|
|
22
|
+
import type { AgentSessionTurnHandle } from "@/types/sdk/AgentSessionTurn.js";
|
|
11
23
|
|
|
12
24
|
/**
|
|
13
25
|
* 构造 SDK SessionPort 的参数。
|
|
@@ -18,9 +30,52 @@ export interface CreateSessionServicePortParams {
|
|
|
18
30
|
*/
|
|
19
31
|
sessionId: string;
|
|
20
32
|
/**
|
|
21
|
-
*
|
|
33
|
+
* 读取当前 session 底层执行端口。
|
|
22
34
|
*/
|
|
23
|
-
|
|
35
|
+
getExecutor: SessionPort["getExecutor"];
|
|
36
|
+
/**
|
|
37
|
+
* 运行一次内部 direct execution。
|
|
38
|
+
*
|
|
39
|
+
* 关键点(中文)
|
|
40
|
+
* - 这是 runtime / service 侧保留的内部原语。
|
|
41
|
+
* - 它不属于 SDK 用户推荐直接调用的公开模式。
|
|
42
|
+
*/
|
|
43
|
+
executeDirect: (params: {
|
|
44
|
+
query: string;
|
|
45
|
+
onStepCallback?: () => Promise<SessionUserMessageV1[]>;
|
|
46
|
+
onAssistantStepCallback?: SessionAssistantStepCallback;
|
|
47
|
+
onUiMessageChunkCallback?: SessionUiMessageChunkCallback;
|
|
48
|
+
}) => Promise<SessionRunResult>;
|
|
49
|
+
/**
|
|
50
|
+
* 追加一条新的 session prompt。
|
|
51
|
+
*/
|
|
52
|
+
prompt: (input: AgentSessionPromptInput) => Promise<AgentSessionTurnHandle>;
|
|
53
|
+
/**
|
|
54
|
+
* 订阅当前 session 的 future 事件。
|
|
55
|
+
*/
|
|
56
|
+
subscribe: (
|
|
57
|
+
subscriber: AgentSessionSubscriber,
|
|
58
|
+
) => AgentSessionUnsubscribe;
|
|
59
|
+
/**
|
|
60
|
+
* 清理当前 session executor 状态。
|
|
61
|
+
*/
|
|
62
|
+
clearExecutor: () => void;
|
|
63
|
+
/**
|
|
64
|
+
* session 更新后的异步通知回调。
|
|
65
|
+
*/
|
|
66
|
+
afterSessionUpdatedAsync: () => Promise<void>;
|
|
67
|
+
/**
|
|
68
|
+
* 追加 user 消息到底层历史。
|
|
69
|
+
*/
|
|
70
|
+
appendUserMessage: SessionPort["appendUserMessage"];
|
|
71
|
+
/**
|
|
72
|
+
* 追加 assistant 消息到底层历史。
|
|
73
|
+
*/
|
|
74
|
+
appendAssistantMessage: SessionPort["appendAssistantMessage"];
|
|
75
|
+
/**
|
|
76
|
+
* 返回当前 session 是否正在执行。
|
|
77
|
+
*/
|
|
78
|
+
isExecuting: () => boolean;
|
|
24
79
|
/**
|
|
25
80
|
* 当前 session 历史持久化端口。
|
|
26
81
|
*/
|
|
@@ -43,26 +98,33 @@ export function createSessionServicePort(
|
|
|
43
98
|
): SessionPort {
|
|
44
99
|
return {
|
|
45
100
|
sessionId: params.sessionId,
|
|
46
|
-
getExecutor: () => params.
|
|
101
|
+
getExecutor: () => params.getExecutor(),
|
|
47
102
|
getHistoryStore: () => params.historyStore,
|
|
48
|
-
|
|
103
|
+
execute: async (runParams) => {
|
|
104
|
+
await params.ensureReadyForExecution();
|
|
105
|
+
return await params.executeDirect(runParams);
|
|
106
|
+
},
|
|
107
|
+
prompt: async (input) => {
|
|
49
108
|
await params.ensureReadyForExecution();
|
|
50
|
-
return await params.
|
|
109
|
+
return await params.prompt(input);
|
|
110
|
+
},
|
|
111
|
+
subscribe: (subscriber) => {
|
|
112
|
+
return params.subscribe(subscriber);
|
|
51
113
|
},
|
|
52
114
|
clearExecutor: () => {
|
|
53
|
-
params.
|
|
115
|
+
params.clearExecutor();
|
|
54
116
|
},
|
|
55
117
|
afterSessionUpdatedAsync: async () => {
|
|
56
|
-
await params.
|
|
118
|
+
await params.afterSessionUpdatedAsync();
|
|
57
119
|
},
|
|
58
120
|
appendUserMessage: async (messageParams) => {
|
|
59
|
-
await params.
|
|
121
|
+
await params.appendUserMessage(messageParams);
|
|
60
122
|
await params.touchMetadata();
|
|
61
123
|
},
|
|
62
124
|
appendAssistantMessage: async (messageParams) => {
|
|
63
|
-
await params.
|
|
125
|
+
await params.appendAssistantMessage(messageParams);
|
|
64
126
|
await params.touchMetadata();
|
|
65
127
|
},
|
|
66
|
-
isExecuting: () => params.
|
|
128
|
+
isExecuting: () => params.isExecuting(),
|
|
67
129
|
};
|
|
68
130
|
}
|