@downcity/agent 1.1.189 → 1.1.196
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/local/Agent.d.ts.map +1 -1
- package/bin/agent/local/Agent.js +5 -2
- package/bin/agent/local/Agent.js.map +1 -1
- package/bin/agent/local/services/AgentAssemblyService.d.ts.map +1 -1
- package/bin/agent/local/services/AgentAssemblyService.js +2 -2
- package/bin/agent/local/services/AgentAssemblyService.js.map +1 -1
- package/bin/agent/local/services/AgentSessionManager.d.ts +5 -0
- package/bin/agent/local/services/AgentSessionManager.d.ts.map +1 -1
- package/bin/agent/local/services/AgentSessionManager.js +3 -0
- package/bin/agent/local/services/AgentSessionManager.js.map +1 -1
- package/bin/executor/Executor.d.ts +4 -0
- package/bin/executor/Executor.d.ts.map +1 -1
- package/bin/executor/Executor.js +40 -1
- package/bin/executor/Executor.js.map +1 -1
- package/bin/executor/composer/compaction/SessionCompactionComposer.d.ts +9 -0
- package/bin/executor/composer/compaction/SessionCompactionComposer.d.ts.map +1 -1
- package/bin/executor/composer/compaction/jsonl/JsonlSessionCompactionComposer.d.ts.map +1 -1
- package/bin/executor/composer/compaction/jsonl/JsonlSessionCompactionComposer.js +1 -0
- package/bin/executor/composer/compaction/jsonl/JsonlSessionCompactionComposer.js.map +1 -1
- package/bin/executor/composer/compaction/jsonl/JsonlSessionCompactionExecutor.d.ts +2 -0
- package/bin/executor/composer/compaction/jsonl/JsonlSessionCompactionExecutor.d.ts.map +1 -1
- package/bin/executor/composer/compaction/jsonl/JsonlSessionCompactionExecutor.js +49 -5
- package/bin/executor/composer/compaction/jsonl/JsonlSessionCompactionExecutor.js.map +1 -1
- package/bin/executor/composer/history/jsonl/JsonlSessionHistoryComposer.d.ts +1 -1
- package/bin/executor/composer/history/jsonl/JsonlSessionHistoryComposer.d.ts.map +1 -1
- package/bin/executor/composer/history/jsonl/JsonlSessionHistoryComposer.js +2 -0
- package/bin/executor/composer/history/jsonl/JsonlSessionHistoryComposer.js.map +1 -1
- package/bin/executor/messages/SessionMessageCodec.d.ts +1 -1
- package/bin/executor/messages/SessionMessageCodec.d.ts.map +1 -1
- package/bin/executor/messages/SessionMessageCodec.js +6 -1
- package/bin/executor/messages/SessionMessageCodec.js.map +1 -1
- package/bin/executor/store/history/SessionHistoryStore.d.ts +22 -0
- package/bin/executor/store/history/SessionHistoryStore.d.ts.map +1 -1
- package/bin/executor/store/history/jsonl/JsonlSessionHistoryStore.d.ts +7 -0
- package/bin/executor/store/history/jsonl/JsonlSessionHistoryStore.d.ts.map +1 -1
- package/bin/executor/store/history/jsonl/JsonlSessionHistoryStore.js +53 -0
- package/bin/executor/store/history/jsonl/JsonlSessionHistoryStore.js.map +1 -1
- package/bin/executor/tools/plugin/PluginToolBridge.d.ts +4 -8
- package/bin/executor/tools/plugin/PluginToolBridge.d.ts.map +1 -1
- package/bin/executor/tools/plugin/PluginToolBridge.js +6 -21
- package/bin/executor/tools/plugin/PluginToolBridge.js.map +1 -1
- package/bin/executor/tools/plugin/PluginToolDefinition.d.ts +7 -19
- package/bin/executor/tools/plugin/PluginToolDefinition.d.ts.map +1 -1
- package/bin/executor/tools/plugin/PluginToolDefinition.js +29 -18
- package/bin/executor/tools/plugin/PluginToolDefinition.js.map +1 -1
- package/bin/executor/types/SessionMessages.d.ts +15 -2
- package/bin/executor/types/SessionMessages.d.ts.map +1 -1
- package/bin/executor/types/SessionMessages.js +9 -1
- package/bin/executor/types/SessionMessages.js.map +1 -1
- package/bin/index.d.ts +1 -0
- package/bin/index.d.ts.map +1 -1
- package/bin/index.js.map +1 -1
- package/bin/plugin/core/PluginRegistry.d.ts.map +1 -1
- package/bin/plugin/core/PluginRegistry.js +0 -4
- package/bin/plugin/core/PluginRegistry.js.map +1 -1
- package/bin/session/browse/Browse.d.ts.map +1 -1
- package/bin/session/browse/Browse.js +22 -0
- package/bin/session/browse/Browse.js.map +1 -1
- package/bin/session/services/SessionStateService.d.ts +21 -1
- package/bin/session/services/SessionStateService.d.ts.map +1 -1
- package/bin/session/services/SessionStateService.js +101 -11
- package/bin/session/services/SessionStateService.js.map +1 -1
- package/bin/session/services/SessionTurnService.d.ts.map +1 -1
- package/bin/session/services/SessionTurnService.js +9 -0
- package/bin/session/services/SessionTurnService.js.map +1 -1
- package/bin/session/services/SessionViewService.d.ts.map +1 -1
- package/bin/session/services/SessionViewService.js +42 -8
- package/bin/session/services/SessionViewService.js.map +1 -1
- package/bin/types/agent/SessionTypes.d.ts +22 -1
- package/bin/types/agent/SessionTypes.d.ts.map +1 -1
- package/bin/types/executor/SessionRunContext.d.ts +10 -0
- package/bin/types/executor/SessionRunContext.d.ts.map +1 -1
- package/bin/types/plugin/PluginToolRuntime.d.ts +75 -0
- package/bin/types/plugin/PluginToolRuntime.d.ts.map +1 -0
- package/bin/types/plugin/PluginToolRuntime.js +9 -0
- package/bin/types/plugin/PluginToolRuntime.js.map +1 -0
- package/bin/types/sdk/AgentSessionEvent.d.ts +2 -1
- package/bin/types/sdk/AgentSessionEvent.d.ts.map +1 -1
- package/bin/types/sdk/AgentSessionOperation.d.ts +99 -0
- package/bin/types/sdk/AgentSessionOperation.d.ts.map +1 -0
- package/bin/types/sdk/AgentSessionOperation.js +10 -0
- package/bin/types/sdk/AgentSessionOperation.js.map +1 -0
- package/package.json +3 -3
- package/scripts/agent-ready.test.mjs +168 -0
- package/scripts/plugin-tool-bridge.test.mjs +114 -10
- package/scripts/session-operation-message.test.mjs +182 -0
- package/src/agent/local/Agent.ts +5 -2
- package/src/agent/local/services/AgentAssemblyService.ts +2 -3
- package/src/agent/local/services/AgentSessionManager.ts +8 -0
- package/src/executor/Executor.ts +49 -1
- package/src/executor/composer/compaction/SessionCompactionComposer.ts +10 -0
- package/src/executor/composer/compaction/jsonl/JsonlSessionCompactionComposer.ts +1 -0
- package/src/executor/composer/compaction/jsonl/JsonlSessionCompactionExecutor.ts +64 -5
- package/src/executor/composer/history/jsonl/JsonlSessionHistoryComposer.ts +5 -1
- package/src/executor/messages/SessionMessageCodec.ts +11 -2
- package/src/executor/store/history/SessionHistoryStore.ts +27 -0
- package/src/executor/store/history/jsonl/JsonlSessionHistoryStore.ts +66 -0
- package/src/executor/tools/plugin/PluginToolBridge.ts +10 -26
- package/src/executor/tools/plugin/PluginToolDefinition.ts +39 -22
- package/src/executor/types/SessionMessages.ts +26 -2
- package/src/index.ts +7 -0
- package/src/plugin/core/PluginRegistry.ts +0 -4
- package/src/session/browse/Browse.ts +24 -0
- package/src/session/services/SessionStateService.ts +125 -11
- package/src/session/services/SessionTurnService.ts +8 -0
- package/src/session/services/SessionViewService.ts +47 -10
- package/src/types/agent/SessionTypes.ts +22 -1
- package/src/types/executor/SessionRunContext.ts +20 -0
- package/src/types/plugin/PluginToolRuntime.ts +86 -0
- package/src/types/sdk/AgentSessionEvent.ts +2 -0
- package/src/types/sdk/AgentSessionOperation.ts +125 -0
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
|
|
10
10
|
import type { UIMessage } from "ai";
|
|
11
11
|
import type { JsonObject } from "@/types/common/Json.js";
|
|
12
|
+
import type { AgentSessionOperationRecord } from "@/types/sdk/AgentSessionOperation.js";
|
|
12
13
|
|
|
13
14
|
/**
|
|
14
15
|
* Session 消息:以 UIMessage[] 作为唯一事实源。
|
|
@@ -18,7 +19,7 @@ import type { JsonObject } from "@/types/common/Json.js";
|
|
|
18
19
|
* - 默认只存 `role=user|assistant`
|
|
19
20
|
* - compact 会把更早消息压缩为一条 `assistant` 摘要消息
|
|
20
21
|
*/
|
|
21
|
-
export type SessionMessageKind = "normal" | "summary";
|
|
22
|
+
export type SessionMessageKind = "normal" | "summary" | "operation";
|
|
22
23
|
|
|
23
24
|
/**
|
|
24
25
|
* Session 消息来源类型。
|
|
@@ -28,7 +29,11 @@ export type SessionMessageKind = "normal" | "summary";
|
|
|
28
29
|
* - `egress`:模型或 agent 输出写入的 assistant 消息。
|
|
29
30
|
* - `compact`:由 compact 过程生成的摘要消息。
|
|
30
31
|
*/
|
|
31
|
-
export type SessionMessageSource =
|
|
32
|
+
export type SessionMessageSource =
|
|
33
|
+
| "ingress"
|
|
34
|
+
| "egress"
|
|
35
|
+
| "compact"
|
|
36
|
+
| "operation";
|
|
32
37
|
|
|
33
38
|
/**
|
|
34
39
|
* 入站消息细分类型。
|
|
@@ -68,6 +73,14 @@ export type SessionMetadataV1 = {
|
|
|
68
73
|
archiveId?: string;
|
|
69
74
|
/** compact 摘要所覆盖的原始消息范围。 */
|
|
70
75
|
sourceRange?: SessionMessageSourceRangeV1;
|
|
76
|
+
/**
|
|
77
|
+
* operation message 对应的结构化操作记录。
|
|
78
|
+
*
|
|
79
|
+
* 说明(中文)
|
|
80
|
+
* - 仅当 `kind=operation` 或 `source=operation` 时存在。
|
|
81
|
+
* - 该字段供前端恢复时间线状态,不会进入 LLM 输入。
|
|
82
|
+
*/
|
|
83
|
+
operation?: AgentSessionOperationRecord;
|
|
71
84
|
/**
|
|
72
85
|
* 扩展元信息。
|
|
73
86
|
*
|
|
@@ -90,3 +103,14 @@ export type SessionUserMessageV1 = SessionMessageV1 & {
|
|
|
90
103
|
/** 消息角色固定为 `user`。 */
|
|
91
104
|
role: "user";
|
|
92
105
|
};
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* 判断一条消息是否为 operation message。
|
|
109
|
+
*/
|
|
110
|
+
export function isSessionOperationMessage(
|
|
111
|
+
message: SessionMessageV1 | null | undefined,
|
|
112
|
+
): boolean {
|
|
113
|
+
if (!message || typeof message !== "object") return false;
|
|
114
|
+
const metadata = (message.metadata || null) as SessionMetadataV1 | null;
|
|
115
|
+
return metadata?.kind === "operation" || metadata?.source === "operation";
|
|
116
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -55,6 +55,13 @@ export type {
|
|
|
55
55
|
AgentSessionSubscriber,
|
|
56
56
|
AgentSessionUnsubscribe,
|
|
57
57
|
} from "./types/sdk/AgentSessionEvent.js";
|
|
58
|
+
export type {
|
|
59
|
+
AgentSessionOperationCallback,
|
|
60
|
+
AgentSessionOperationEvent,
|
|
61
|
+
AgentSessionOperationName,
|
|
62
|
+
AgentSessionOperationRecord,
|
|
63
|
+
AgentSessionOperationStatus,
|
|
64
|
+
} from "./types/sdk/AgentSessionOperation.js";
|
|
58
65
|
export type { AgentSessionPromptInput, SessionUserMessagePart } from "./types/sdk/AgentSessionPrompt.js";
|
|
59
66
|
export type { AgentSessionStopResult } from "./types/sdk/AgentSessionStop.js";
|
|
60
67
|
export type {
|
|
@@ -522,12 +522,8 @@ export class PluginRegistry implements AgentPlugins {
|
|
|
522
522
|
pluginName: record.plugin.name,
|
|
523
523
|
actionName,
|
|
524
524
|
});
|
|
525
|
-
if (!result.success) {
|
|
526
|
-
update_record_state(record, "error", result.error || result.message);
|
|
527
|
-
}
|
|
528
525
|
return result;
|
|
529
526
|
} catch (error) {
|
|
530
|
-
update_record_state(record, "error", String(error));
|
|
531
527
|
return {
|
|
532
528
|
success: false,
|
|
533
529
|
error: String(error),
|
|
@@ -29,6 +29,7 @@ import type {
|
|
|
29
29
|
SessionMessageV1,
|
|
30
30
|
SessionMetadataV1,
|
|
31
31
|
} from "@/executor/types/SessionMessages.js";
|
|
32
|
+
import { isSessionOperationMessage } from "@/executor/types/SessionMessages.js";
|
|
32
33
|
import type { SessionHistoryMetaV1 } from "@/executor/types/SessionHistoryMeta.js";
|
|
33
34
|
import { pickLastSuccessfulChatSendText } from "@/executor/messages/UserVisibleText.js";
|
|
34
35
|
import { getSdkAgentSessionMessagesPath } from "@/session/storage/Paths.js";
|
|
@@ -240,6 +241,15 @@ function toTimelineEvent(params: {
|
|
|
240
241
|
...(typeof metadata?.source === "string" ? { source: metadata.source } : {}),
|
|
241
242
|
text: params.text,
|
|
242
243
|
...(params.toolName ? { toolName: params.toolName } : {}),
|
|
244
|
+
...(params.role === "operation" && metadata?.operation?.operationId
|
|
245
|
+
? { operationId: metadata.operation.operationId }
|
|
246
|
+
: {}),
|
|
247
|
+
...(params.role === "operation" && metadata?.operation?.name
|
|
248
|
+
? { operationName: metadata.operation.name }
|
|
249
|
+
: {}),
|
|
250
|
+
...(params.role === "operation" && metadata?.operation?.status
|
|
251
|
+
? { operationStatus: metadata.operation.status }
|
|
252
|
+
: {}),
|
|
243
253
|
};
|
|
244
254
|
}
|
|
245
255
|
|
|
@@ -249,6 +259,20 @@ function toTimelineEvent(params: {
|
|
|
249
259
|
export function toSessionTimelineEvents(
|
|
250
260
|
message: SessionMessageV1,
|
|
251
261
|
): AgentSessionTimelineEvent[] {
|
|
262
|
+
if (isSessionOperationMessage(message)) {
|
|
263
|
+
const metadata = (message.metadata || null) as SessionMetadataV1 | null;
|
|
264
|
+
return [
|
|
265
|
+
toTimelineEvent({
|
|
266
|
+
message,
|
|
267
|
+
role: "operation",
|
|
268
|
+
text:
|
|
269
|
+
String(metadata?.operation?.label || "").trim() ||
|
|
270
|
+
resolveSessionMessagePreview(message),
|
|
271
|
+
sequence: 0,
|
|
272
|
+
}),
|
|
273
|
+
];
|
|
274
|
+
}
|
|
275
|
+
|
|
252
276
|
if (message.role !== "assistant") {
|
|
253
277
|
return [
|
|
254
278
|
toTimelineEvent({
|
|
@@ -30,6 +30,10 @@ import type {
|
|
|
30
30
|
import type {
|
|
31
31
|
AgentSessionEvent,
|
|
32
32
|
} from "@/types/sdk/AgentSessionEvent.js";
|
|
33
|
+
import type {
|
|
34
|
+
AgentSessionOperationEvent,
|
|
35
|
+
AgentSessionOperationRecord,
|
|
36
|
+
} from "@/types/sdk/AgentSessionOperation.js";
|
|
33
37
|
import type { AgentSessionPromptInput } from "@/types/sdk/AgentSessionPrompt.js";
|
|
34
38
|
import type {
|
|
35
39
|
SessionMessageV1,
|
|
@@ -80,6 +84,20 @@ type SessionStateServiceOptions = {
|
|
|
80
84
|
publish_event: (event: AgentSessionEvent) => void;
|
|
81
85
|
};
|
|
82
86
|
|
|
87
|
+
type SessionSetOptions = {
|
|
88
|
+
/**
|
|
89
|
+
* 是否写入并发布 model-switching operation。
|
|
90
|
+
*
|
|
91
|
+
* 说明(中文)
|
|
92
|
+
* - 公开 `session.set()` 应保持默认开启。
|
|
93
|
+
* - fork 内部复制模型配置时关闭,避免污染 forked session 历史。
|
|
94
|
+
*/
|
|
95
|
+
emit_operation?: boolean;
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
type EmitOperationInput = Omit<AgentSessionOperationRecord, "operationId"> &
|
|
99
|
+
Partial<Pick<AgentSessionOperationRecord, "operationId">>;
|
|
100
|
+
|
|
83
101
|
/**
|
|
84
102
|
* 本地 Session 状态与持久化服务。
|
|
85
103
|
*/
|
|
@@ -203,18 +221,66 @@ export class SessionStateService {
|
|
|
203
221
|
/**
|
|
204
222
|
* 写入当前 session 配置。
|
|
205
223
|
*/
|
|
206
|
-
async set(input: AgentSessionSetInput): Promise<void> {
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
224
|
+
async set(input: AgentSessionSetInput, options?: SessionSetOptions): Promise<void> {
|
|
225
|
+
const should_emit_operation = options?.emit_operation !== false;
|
|
226
|
+
const previous_model_label = this.state.sessionConfig.modelLabel;
|
|
227
|
+
const next_model_label = input.model
|
|
228
|
+
? inferAgentModelLabel(input.model)
|
|
229
|
+
: undefined;
|
|
230
|
+
const operation_id = `model-switching:${this.session_id}:${Date.now()}:${generateId()}`;
|
|
231
|
+
|
|
232
|
+
if (input.model && should_emit_operation) {
|
|
233
|
+
await this.emit_operation_event({
|
|
234
|
+
operationId: operation_id,
|
|
235
|
+
name: "model-switching",
|
|
236
|
+
status: "started",
|
|
237
|
+
label: "Switching session model",
|
|
238
|
+
result: {
|
|
239
|
+
...(previous_model_label ? { previousModelLabel: previous_model_label } : {}),
|
|
240
|
+
...(next_model_label ? { modelLabel: next_model_label } : {}),
|
|
241
|
+
},
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
try {
|
|
246
|
+
if (input.model) {
|
|
247
|
+
this.state.sessionConfig.model = normalizeAgentModel(input.model);
|
|
248
|
+
this.state.sessionConfig.modelLabel = next_model_label;
|
|
249
|
+
this.executor.clearExecutor();
|
|
250
|
+
}
|
|
251
|
+
await patchSessionModelLabel({
|
|
252
|
+
projectRoot: this.project_root,
|
|
253
|
+
agentId: this.agent_id,
|
|
254
|
+
sessionId: this.session_id,
|
|
255
|
+
model: this.state.sessionConfig.model,
|
|
256
|
+
});
|
|
257
|
+
} catch (error) {
|
|
258
|
+
if (input.model && should_emit_operation) {
|
|
259
|
+
await this.emit_operation_event({
|
|
260
|
+
operationId: operation_id,
|
|
261
|
+
name: "model-switching",
|
|
262
|
+
status: "failed",
|
|
263
|
+
label: "Session model switch failed",
|
|
264
|
+
error: error instanceof Error ? error.message : String(error),
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
throw error;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
if (input.model && should_emit_operation) {
|
|
271
|
+
await this.emit_operation_event({
|
|
272
|
+
operationId: operation_id,
|
|
273
|
+
name: "model-switching",
|
|
274
|
+
status: "finished",
|
|
275
|
+
label: "Session model switched",
|
|
276
|
+
result: {
|
|
277
|
+
...(previous_model_label ? { previousModelLabel: previous_model_label } : {}),
|
|
278
|
+
...(this.state.sessionConfig.modelLabel
|
|
279
|
+
? { modelLabel: this.state.sessionConfig.modelLabel }
|
|
280
|
+
: {}),
|
|
281
|
+
},
|
|
282
|
+
});
|
|
211
283
|
}
|
|
212
|
-
await patchSessionModelLabel({
|
|
213
|
-
projectRoot: this.project_root,
|
|
214
|
-
agentId: this.agent_id,
|
|
215
|
-
sessionId: this.session_id,
|
|
216
|
-
model: this.state.sessionConfig.model,
|
|
217
|
-
});
|
|
218
284
|
}
|
|
219
285
|
|
|
220
286
|
/**
|
|
@@ -301,6 +367,54 @@ export class SessionStateService {
|
|
|
301
367
|
});
|
|
302
368
|
}
|
|
303
369
|
|
|
370
|
+
/**
|
|
371
|
+
* 持久化一条 operation message。
|
|
372
|
+
*/
|
|
373
|
+
async persist_operation_event(
|
|
374
|
+
event: AgentSessionOperationEvent,
|
|
375
|
+
): Promise<void> {
|
|
376
|
+
const message = this.history_store.operation({
|
|
377
|
+
operation: {
|
|
378
|
+
operationId: event.operationId,
|
|
379
|
+
name: event.name,
|
|
380
|
+
status: event.status,
|
|
381
|
+
...(event.turnId ? { turnId: event.turnId } : {}),
|
|
382
|
+
...(event.label ? { label: event.label } : {}),
|
|
383
|
+
...(event.reason ? { reason: event.reason } : {}),
|
|
384
|
+
...(typeof event.progress === "number" ? { progress: event.progress } : {}),
|
|
385
|
+
...(event.result !== undefined ? { result: event.result } : {}),
|
|
386
|
+
...(event.error ? { error: event.error } : {}),
|
|
387
|
+
...(event.visible === false ? { visible: false } : {}),
|
|
388
|
+
},
|
|
389
|
+
metadata: {
|
|
390
|
+
sessionId: this.session_id,
|
|
391
|
+
},
|
|
392
|
+
});
|
|
393
|
+
await this.history_store.append(message);
|
|
394
|
+
await this.touch_metadata();
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
/**
|
|
398
|
+
* 写入并发布一条 operation。
|
|
399
|
+
*/
|
|
400
|
+
async emit_operation_event(input: EmitOperationInput): Promise<void> {
|
|
401
|
+
const operation_id =
|
|
402
|
+
String(input.operationId || "").trim() ||
|
|
403
|
+
`${input.name}:${this.session_id}:${Date.now()}:${generateId()}`;
|
|
404
|
+
const event: AgentSessionOperationEvent = {
|
|
405
|
+
type: "operation",
|
|
406
|
+
sessionId: this.session_id,
|
|
407
|
+
...input,
|
|
408
|
+
operationId: operation_id,
|
|
409
|
+
};
|
|
410
|
+
try {
|
|
411
|
+
await this.persist_operation_event(event);
|
|
412
|
+
} catch {
|
|
413
|
+
// operation 持久化失败不应阻断宿主操作。
|
|
414
|
+
}
|
|
415
|
+
this.publish_event(event);
|
|
416
|
+
}
|
|
417
|
+
|
|
304
418
|
/**
|
|
305
419
|
* 构造并持久化一条 prompt user 消息。
|
|
306
420
|
*/
|
|
@@ -198,6 +198,14 @@ export class SessionTurnService {
|
|
|
198
198
|
this.publish_event(session_event);
|
|
199
199
|
}
|
|
200
200
|
},
|
|
201
|
+
onOperationCallback: async (event) => {
|
|
202
|
+
try {
|
|
203
|
+
await this.state_service.persist_operation_event(event);
|
|
204
|
+
} catch {
|
|
205
|
+
// operation 持久化失败不应阻断当前 turn。
|
|
206
|
+
}
|
|
207
|
+
this.publish_event(event);
|
|
208
|
+
},
|
|
201
209
|
injectedUserMessages: [],
|
|
202
210
|
deferredPersistedUserMessages: [],
|
|
203
211
|
pendingAssistantFileParts: [],
|
|
@@ -278,19 +278,56 @@ export class SessionViewService<TSession extends Pick<AgentSession, "set">> {
|
|
|
278
278
|
!message_id
|
|
279
279
|
? messages
|
|
280
280
|
: this.resolve_fork_messages(messages, message_id);
|
|
281
|
+
const operation_id = `history-forking:${this.session_id}:${Date.now()}:${nanoid(8)}`;
|
|
281
282
|
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
283
|
+
await this.state_service.emit_operation_event({
|
|
284
|
+
operationId: operation_id,
|
|
285
|
+
name: "history-forking",
|
|
286
|
+
status: "started",
|
|
287
|
+
label: "Forking session history",
|
|
288
|
+
result: {
|
|
289
|
+
messageCount: fork_messages.length,
|
|
290
|
+
...(message_id ? { messageId: message_id } : {}),
|
|
291
|
+
},
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
try {
|
|
295
|
+
const forked_bundle = await this.create_fork_session(
|
|
296
|
+
`fork-${Date.now()}-${nanoid(8)}`,
|
|
297
|
+
);
|
|
298
|
+
const forked = forked_bundle.session;
|
|
299
|
+
const session_config = this.state_service.get_config();
|
|
300
|
+
if (session_config.model) {
|
|
301
|
+
await forked_bundle.state_service.set(
|
|
302
|
+
{
|
|
303
|
+
model: session_config.model,
|
|
304
|
+
},
|
|
305
|
+
{ emit_operation: false },
|
|
306
|
+
);
|
|
307
|
+
}
|
|
308
|
+
await this.append_fork_messages(forked_bundle, fork_messages);
|
|
309
|
+
await this.state_service.emit_operation_event({
|
|
310
|
+
operationId: operation_id,
|
|
311
|
+
name: "history-forking",
|
|
312
|
+
status: "finished",
|
|
313
|
+
label: "Session history forked",
|
|
314
|
+
result: {
|
|
315
|
+
forkedSessionId: String((forked as { id?: unknown }).id || ""),
|
|
316
|
+
messageCount: fork_messages.length,
|
|
317
|
+
...(message_id ? { messageId: message_id } : {}),
|
|
318
|
+
},
|
|
319
|
+
});
|
|
320
|
+
return forked;
|
|
321
|
+
} catch (error) {
|
|
322
|
+
await this.state_service.emit_operation_event({
|
|
323
|
+
operationId: operation_id,
|
|
324
|
+
name: "history-forking",
|
|
325
|
+
status: "failed",
|
|
326
|
+
label: "Session history fork failed",
|
|
327
|
+
error: error instanceof Error ? error.message : String(error),
|
|
290
328
|
});
|
|
329
|
+
throw error;
|
|
291
330
|
}
|
|
292
|
-
await this.append_fork_messages(forked_bundle, fork_messages);
|
|
293
|
-
return forked;
|
|
294
331
|
}
|
|
295
332
|
|
|
296
333
|
private resolve_fork_messages(
|
|
@@ -99,7 +99,7 @@ export interface AgentSessionTimelineEvent {
|
|
|
99
99
|
* 说明(中文)
|
|
100
100
|
* - `tool-call` / `tool-result` 用于把 assistant 内部工具过程平铺给 UI。
|
|
101
101
|
*/
|
|
102
|
-
role: "user" | "assistant" | "tool-call" | "tool-result";
|
|
102
|
+
role: "user" | "assistant" | "tool-call" | "tool-result" | "operation";
|
|
103
103
|
/** 事件时间戳(毫秒)。 */
|
|
104
104
|
ts?: number;
|
|
105
105
|
/** 事件所属消息种类。 */
|
|
@@ -115,6 +115,27 @@ export interface AgentSessionTimelineEvent {
|
|
|
115
115
|
* - 仅 `tool-call` / `tool-result` 这类事件通常会携带该字段。
|
|
116
116
|
*/
|
|
117
117
|
toolName?: string;
|
|
118
|
+
/**
|
|
119
|
+
* 当前 operation ID。
|
|
120
|
+
*
|
|
121
|
+
* 说明(中文)
|
|
122
|
+
* - 仅 `role=operation` 的事件通常会携带该字段。
|
|
123
|
+
*/
|
|
124
|
+
operationId?: string;
|
|
125
|
+
/**
|
|
126
|
+
* 当前 operation 名称。
|
|
127
|
+
*
|
|
128
|
+
* 说明(中文)
|
|
129
|
+
* - 仅 `role=operation` 的事件通常会携带该字段。
|
|
130
|
+
*/
|
|
131
|
+
operationName?: string;
|
|
132
|
+
/**
|
|
133
|
+
* 当前 operation 状态。
|
|
134
|
+
*
|
|
135
|
+
* 说明(中文)
|
|
136
|
+
* - 仅 `role=operation` 的事件通常会携带该字段。
|
|
137
|
+
*/
|
|
138
|
+
operationStatus?: string;
|
|
118
139
|
}
|
|
119
140
|
|
|
120
141
|
/**
|
|
@@ -7,6 +7,11 @@
|
|
|
7
7
|
* - tool 深层回调等无法直接传参的场景,才通过 run scope 间接读取这个对象。
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
+
import type {
|
|
11
|
+
AgentSessionOperationCallback,
|
|
12
|
+
AgentSessionOperationEvent,
|
|
13
|
+
AgentSessionOperationRecord,
|
|
14
|
+
} from "@/types/sdk/AgentSessionOperation.js";
|
|
10
15
|
import type {
|
|
11
16
|
SessionAssistantStepCallback,
|
|
12
17
|
SessionUiMessageChunkCallback,
|
|
@@ -66,6 +71,15 @@ export interface SessionRunContext {
|
|
|
66
71
|
*/
|
|
67
72
|
onUiMessageChunkCallback?: SessionUiMessageChunkCallback;
|
|
68
73
|
|
|
74
|
+
/**
|
|
75
|
+
* operation 发布回调。
|
|
76
|
+
*
|
|
77
|
+
* 关键点(中文)
|
|
78
|
+
* - 用于把 compaction 等辅助操作转成 session event 与 operation message。
|
|
79
|
+
* - operation 不代表 assistant 正文,也不会进入 LLM 输入。
|
|
80
|
+
*/
|
|
81
|
+
onOperationCallback?: AgentSessionOperationCallback;
|
|
82
|
+
|
|
69
83
|
/**
|
|
70
84
|
* 当前 turn 的取消信号。
|
|
71
85
|
*
|
|
@@ -101,3 +115,9 @@ export interface SessionRunContext {
|
|
|
101
115
|
*/
|
|
102
116
|
pendingAssistantFileParts: FileUIPart[];
|
|
103
117
|
}
|
|
118
|
+
|
|
119
|
+
export type {
|
|
120
|
+
AgentSessionOperationCallback,
|
|
121
|
+
AgentSessionOperationEvent,
|
|
122
|
+
AgentSessionOperationRecord,
|
|
123
|
+
};
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Plugin tool runtime 绑定类型。
|
|
3
|
+
*
|
|
4
|
+
* 关键点(中文)
|
|
5
|
+
* - plugin_call / plugin_read 必须绑定当前 Agent 自己的 plugin registry。
|
|
6
|
+
* - 这些类型描述 tool 工厂与 bridge 调用时需要的显式 runtime 依赖。
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import type { Tool } from "ai";
|
|
10
|
+
import type { AgentPlugins } from "@/types/plugin/PluginRuntime.js";
|
|
11
|
+
import type {
|
|
12
|
+
PluginCallInput,
|
|
13
|
+
PluginReadInput,
|
|
14
|
+
} from "@/executor/tools/plugin/types/PluginTool.js";
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* 创建 plugin tools 的参数。
|
|
18
|
+
*/
|
|
19
|
+
export interface CreatePluginToolsOptions {
|
|
20
|
+
/**
|
|
21
|
+
* 当前 Agent 自己的 plugin 调用面。
|
|
22
|
+
*
|
|
23
|
+
* 关键点(中文)
|
|
24
|
+
* - 该引用必须来自当前 Agent 装配出的 plugin registry。
|
|
25
|
+
* - tool 执行时只能通过它访问 plugin,避免跨 Agent 读到其他 registry。
|
|
26
|
+
*/
|
|
27
|
+
plugins: AgentPlugins;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* 当前 Agent 专属 plugin tools。
|
|
32
|
+
*/
|
|
33
|
+
export interface AgentPluginTools {
|
|
34
|
+
/**
|
|
35
|
+
* 读取 plugin metadata 的模型工具。
|
|
36
|
+
*
|
|
37
|
+
* 关键点(中文)
|
|
38
|
+
* - 该 tool 通过闭包绑定当前 Agent 的 `plugins`。
|
|
39
|
+
* - 不读取任何模块级全局 runtime。
|
|
40
|
+
*/
|
|
41
|
+
plugin_read: Tool;
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* 执行 plugin action 的模型工具。
|
|
45
|
+
*
|
|
46
|
+
* 关键点(中文)
|
|
47
|
+
* - 该 tool 通过闭包绑定当前 Agent 的 `plugins`。
|
|
48
|
+
* - 多 Agent 并发时,每个 Agent 都有自己的 tool 实例。
|
|
49
|
+
*/
|
|
50
|
+
plugin_call: Tool;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* 调用 plugin_call bridge 的参数。
|
|
55
|
+
*/
|
|
56
|
+
export interface InvokePluginCallToolOptions {
|
|
57
|
+
/**
|
|
58
|
+
* 当前 Agent 自己的 plugin 调用面。
|
|
59
|
+
*
|
|
60
|
+
* 关键点(中文)
|
|
61
|
+
* - 由 Agent 级 tool 工厂注入。
|
|
62
|
+
* - 不能在 bridge 内通过全局变量读取。
|
|
63
|
+
*/
|
|
64
|
+
plugins: AgentPlugins;
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* 模型提交给 plugin_call 的结构化输入。
|
|
68
|
+
*/
|
|
69
|
+
input: PluginCallInput;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* 调用 plugin_read bridge 的参数。
|
|
74
|
+
*/
|
|
75
|
+
export interface InvokePluginReadToolOptions {
|
|
76
|
+
/**
|
|
77
|
+
* 当前 Agent 自己的 plugin 调用面。
|
|
78
|
+
*/
|
|
79
|
+
plugins: AgentPlugins;
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* 模型提交给 plugin_read 的结构化输入。
|
|
83
|
+
*/
|
|
84
|
+
input: PluginReadInput;
|
|
85
|
+
}
|
|
86
|
+
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
import type { JsonValue } from "@/types/common/Json.js";
|
|
10
10
|
import type { SessionAssistantStepVisibility } from "@/executor/types/SessionRun.js";
|
|
11
|
+
import type { AgentSessionOperationEvent } from "@/types/sdk/AgentSessionOperation.js";
|
|
11
12
|
|
|
12
13
|
/**
|
|
13
14
|
* 单个 turn 开始事件。
|
|
@@ -354,6 +355,7 @@ export type AgentSessionEvent =
|
|
|
354
355
|
| AgentSessionToolApprovalResultEvent
|
|
355
356
|
| AgentSessionAssistantStepEvent
|
|
356
357
|
| AgentSessionTitleEvent
|
|
358
|
+
| AgentSessionOperationEvent
|
|
357
359
|
| AgentSessionTurnFinishEvent
|
|
358
360
|
| AgentSessionErrorEvent;
|
|
359
361
|
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent Session operation 类型定义。
|
|
3
|
+
*
|
|
4
|
+
* 关键点(中文)
|
|
5
|
+
* - operation 描述 session 内部正在发生的辅助操作,不代表 assistant 正文。
|
|
6
|
+
* - operation 可以同时作为订阅事件发布,也可以作为 UIMessage 持久化到 JSONL。
|
|
7
|
+
* - operation message 只用于前端时间线与调试,不应进入 LLM 输入。
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import type { JsonValue } from "@/types/common/Json.js";
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Session operation 当前状态。
|
|
14
|
+
*/
|
|
15
|
+
export type AgentSessionOperationStatus =
|
|
16
|
+
| "started"
|
|
17
|
+
| "progress"
|
|
18
|
+
| "finished"
|
|
19
|
+
| "skipped"
|
|
20
|
+
| "failed";
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Session operation 名称。
|
|
24
|
+
*
|
|
25
|
+
* 说明(中文)
|
|
26
|
+
* - 内置名称用于 SDK 与前端约定常见辅助操作。
|
|
27
|
+
* - 字符串扩展允许 plugin 或宿主运行时声明自己的 operation。
|
|
28
|
+
*/
|
|
29
|
+
export type AgentSessionOperationName =
|
|
30
|
+
| "compacting"
|
|
31
|
+
| "model-switching"
|
|
32
|
+
| "history-forking"
|
|
33
|
+
| (string & {});
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Session operation 记录。
|
|
37
|
+
*/
|
|
38
|
+
export interface AgentSessionOperationRecord {
|
|
39
|
+
/**
|
|
40
|
+
* 同一个 operation 生命周期内稳定复用的 ID。
|
|
41
|
+
*/
|
|
42
|
+
operationId: string;
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* 当前 operation 名称。
|
|
46
|
+
*/
|
|
47
|
+
name: AgentSessionOperationName;
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* 当前 operation 状态。
|
|
51
|
+
*/
|
|
52
|
+
status: AgentSessionOperationStatus;
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* 当前 operation 关联的 turn 标识。
|
|
56
|
+
*
|
|
57
|
+
* 说明(中文)
|
|
58
|
+
* - turn 内触发的 operation 应写入该字段。
|
|
59
|
+
* - session 级 operation 可以省略。
|
|
60
|
+
*/
|
|
61
|
+
turnId?: string;
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* 面向用户展示的短文本。
|
|
65
|
+
*/
|
|
66
|
+
label?: string;
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* 机器可读原因。
|
|
70
|
+
*
|
|
71
|
+
* 示例(中文)
|
|
72
|
+
* - `under_budget`:未达到压缩阈值。
|
|
73
|
+
* - `summary_fallback`:摘要生成失败后使用降级摘要。
|
|
74
|
+
*/
|
|
75
|
+
reason?: string;
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* 当前 operation 进度,范围 0 到 1。
|
|
79
|
+
*
|
|
80
|
+
* 说明(中文)
|
|
81
|
+
* - 无法估算进度时省略。
|
|
82
|
+
*/
|
|
83
|
+
progress?: number;
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* operation 完成后产生的轻量结果。
|
|
87
|
+
*/
|
|
88
|
+
result?: JsonValue;
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* operation 失败时的错误文本。
|
|
92
|
+
*/
|
|
93
|
+
error?: string;
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* 是否建议前端展示。
|
|
97
|
+
*
|
|
98
|
+
* 说明(中文)
|
|
99
|
+
* - 默认视为 `true`。
|
|
100
|
+
* - 调试或审计类辅助事件可显式写入 `false`。
|
|
101
|
+
*/
|
|
102
|
+
visible?: boolean;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Session operation 订阅事件。
|
|
107
|
+
*/
|
|
108
|
+
export interface AgentSessionOperationEvent extends AgentSessionOperationRecord {
|
|
109
|
+
/**
|
|
110
|
+
* 当前事件类型。
|
|
111
|
+
*/
|
|
112
|
+
type: "operation";
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* 当前 session 唯一标识。
|
|
116
|
+
*/
|
|
117
|
+
sessionId: string;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Session operation 发布回调。
|
|
122
|
+
*/
|
|
123
|
+
export type AgentSessionOperationCallback = (
|
|
124
|
+
operation: AgentSessionOperationEvent,
|
|
125
|
+
) => Promise<void>;
|