@downcity/agent 1.1.317 → 1.1.323
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/agent/Agent.d.ts.map +1 -1
- package/bin/agent/Agent.js +1 -0
- package/bin/agent/Agent.js.map +1 -1
- package/bin/agent/AgentSessions.d.ts +2 -0
- package/bin/agent/AgentSessions.d.ts.map +1 -1
- package/bin/agent/AgentSessions.js +9 -0
- package/bin/agent/AgentSessions.js.map +1 -1
- package/bin/executor/messages/SessionAttachmentMapper.d.ts +15 -7
- package/bin/executor/messages/SessionAttachmentMapper.d.ts.map +1 -1
- package/bin/executor/messages/SessionAttachmentMapper.js +22 -32
- package/bin/executor/messages/SessionAttachmentMapper.js.map +1 -1
- package/bin/executor/types/SessionHistoryMeta.d.ts +2 -0
- package/bin/executor/types/SessionHistoryMeta.d.ts.map +1 -1
- package/bin/index.d.ts +2 -1
- package/bin/index.d.ts.map +1 -1
- package/bin/index.js.map +1 -1
- package/bin/remote/RemoteSession.d.ts +2 -2
- package/bin/remote/RemoteSession.d.ts.map +1 -1
- package/bin/remote/RemoteSession.js +2 -2
- package/bin/remote/RemoteSession.js.map +1 -1
- package/bin/remote/RemoteTransport.d.ts +2 -2
- package/bin/remote/RemoteTransport.d.ts.map +1 -1
- package/bin/remote/transports/HttpRemoteAgentTransport.d.ts +2 -2
- package/bin/remote/transports/HttpRemoteAgentTransport.d.ts.map +1 -1
- package/bin/remote/transports/HttpRemoteAgentTransport.js +2 -2
- package/bin/remote/transports/HttpRemoteAgentTransport.js.map +1 -1
- package/bin/remote/transports/RpcClient.d.ts +2 -2
- package/bin/remote/transports/RpcClient.d.ts.map +1 -1
- package/bin/remote/transports/RpcClient.js +2 -2
- package/bin/remote/transports/RpcClient.js.map +1 -1
- package/bin/remote/transports/RpcRemoteAgentTransport.d.ts +2 -2
- package/bin/remote/transports/RpcRemoteAgentTransport.d.ts.map +1 -1
- package/bin/remote/transports/RpcRemoteAgentTransport.js +2 -2
- package/bin/remote/transports/RpcRemoteAgentTransport.js.map +1 -1
- package/bin/session/Session.d.ts +6 -4
- package/bin/session/Session.d.ts.map +1 -1
- package/bin/session/Session.js +56 -31
- package/bin/session/Session.js.map +1 -1
- package/bin/session/SessionLoop.d.ts.map +1 -1
- package/bin/session/SessionLoop.js +6 -4
- package/bin/session/SessionLoop.js.map +1 -1
- package/bin/session/SessionMessages.d.ts +9 -3
- package/bin/session/SessionMessages.d.ts.map +1 -1
- package/bin/session/SessionMessages.js +35 -21
- package/bin/session/SessionMessages.js.map +1 -1
- package/bin/session/SessionState.d.ts +23 -10
- package/bin/session/SessionState.d.ts.map +1 -1
- package/bin/session/SessionState.js +129 -51
- package/bin/session/SessionState.js.map +1 -1
- package/bin/session/SessionTitle.d.ts +4 -0
- package/bin/session/SessionTitle.d.ts.map +1 -1
- package/bin/session/SessionTitle.js +5 -1
- package/bin/session/SessionTitle.js.map +1 -1
- package/bin/session/messages/SessionAssistantMessageWriter.d.ts +1 -1
- package/bin/session/messages/SessionAssistantMessageWriter.d.ts.map +1 -1
- package/bin/session/messages/SessionAssistantMessageWriter.js +4 -4
- package/bin/session/messages/SessionAssistantMessageWriter.js.map +1 -1
- package/bin/session/runtime/SessionTitleTask.d.ts +35 -0
- package/bin/session/runtime/SessionTitleTask.d.ts.map +1 -0
- package/bin/session/runtime/SessionTitleTask.js +72 -0
- package/bin/session/runtime/SessionTitleTask.js.map +1 -0
- package/bin/session/storage/Metadata.d.ts.map +1 -1
- package/bin/session/storage/Metadata.js +7 -0
- package/bin/session/storage/Metadata.js.map +1 -1
- package/bin/types/agent/SessionActor.d.ts +3 -3
- package/bin/types/agent/SessionActor.d.ts.map +1 -1
- package/bin/types/agent/SessionTypes.d.ts +20 -0
- package/bin/types/agent/SessionTypes.d.ts.map +1 -1
- package/bin/types/rpc/RpcProtocol.d.ts +2 -1
- package/bin/types/rpc/RpcProtocol.d.ts.map +1 -1
- package/bin/types/sdk/AgentSessionPrompt.d.ts +22 -0
- package/bin/types/sdk/AgentSessionPrompt.d.ts.map +1 -1
- package/bin/types/sdk/AgentSessionPrompt.js.map +1 -1
- package/bin/types/session/SessionCommand.d.ts +2 -0
- package/bin/types/session/SessionCommand.d.ts.map +1 -1
- package/bin/types/session/SessionLocalState.d.ts +3 -0
- package/bin/types/session/SessionLocalState.d.ts.map +1 -1
- package/bin/types/session/SessionMessages.d.ts +5 -0
- package/bin/types/session/SessionMessages.d.ts.map +1 -1
- package/bin/types/session/SessionOptions.d.ts +2 -0
- package/bin/types/session/SessionOptions.d.ts.map +1 -1
- package/bin/types/store/SessionAttachmentStore.d.ts +29 -0
- package/bin/types/store/SessionAttachmentStore.d.ts.map +1 -0
- package/bin/types/store/SessionAttachmentStore.js +15 -0
- package/bin/types/store/SessionAttachmentStore.js.map +1 -0
- package/bin/types/store/SessionStore.d.ts +3 -0
- package/bin/types/store/SessionStore.d.ts.map +1 -1
- package/bin/workspace/store/LocalSessionAttachmentStore.d.ts +25 -0
- package/bin/workspace/store/LocalSessionAttachmentStore.d.ts.map +1 -0
- package/bin/workspace/store/LocalSessionAttachmentStore.js +72 -0
- package/bin/workspace/store/LocalSessionAttachmentStore.js.map +1 -0
- package/bin/workspace/store/LocalSessionStore.d.ts +3 -0
- package/bin/workspace/store/LocalSessionStore.d.ts.map +1 -1
- package/bin/workspace/store/LocalSessionStore.js +8 -1
- package/bin/workspace/store/LocalSessionStore.js.map +1 -1
- package/bin/workspace/store/LocalStorePaths.d.ts +6 -0
- package/bin/workspace/store/LocalStorePaths.d.ts.map +1 -1
- package/bin/workspace/store/LocalStorePaths.js +8 -0
- package/bin/workspace/store/LocalStorePaths.js.map +1 -1
- package/package.json +6 -6
- package/scripts/remote-session-reconnect.test.mjs +25 -0
- package/scripts/session-attachments.test.mjs +101 -0
- package/scripts/session-config-turn-boundary.test.mjs +185 -0
- package/scripts/session-list-title.test.mjs +6 -0
- package/scripts/session-title-event.test.mjs +67 -3
- package/scripts/session-turn-failure.test.mjs +34 -1
- package/src/agent/Agent.ts +1 -0
- package/src/agent/AgentSessions.ts +10 -0
- package/src/executor/messages/SessionAttachmentMapper.ts +24 -37
- package/src/executor/types/SessionHistoryMeta.ts +2 -0
- package/src/index.ts +2 -0
- package/src/remote/RemoteSession.ts +6 -2
- package/src/remote/RemoteTransport.ts +6 -1
- package/src/remote/transports/HttpRemoteAgentTransport.ts +3 -1
- package/src/remote/transports/RpcClient.ts +3 -1
- package/src/remote/transports/RpcRemoteAgentTransport.ts +3 -1
- package/src/session/Session.ts +65 -32
- package/src/session/SessionLoop.ts +6 -3
- package/src/session/SessionMessages.ts +61 -20
- package/src/session/SessionState.ts +150 -59
- package/src/session/SessionTitle.ts +13 -1
- package/src/session/messages/SessionAssistantMessageWriter.ts +7 -3
- package/src/session/runtime/SessionTitleTask.ts +86 -0
- package/src/session/storage/Metadata.ts +10 -0
- package/src/types/agent/SessionActor.ts +3 -2
- package/src/types/agent/SessionTypes.ts +22 -0
- package/src/types/rpc/RpcProtocol.ts +6 -1
- package/src/types/sdk/AgentSessionPrompt.ts +22 -0
- package/src/types/session/SessionCommand.ts +2 -0
- package/src/types/session/SessionLocalState.ts +4 -0
- package/src/types/session/SessionMessages.ts +5 -0
- package/src/types/session/SessionOptions.ts +3 -0
- package/src/types/store/SessionAttachmentStore.ts +29 -0
- package/src/types/store/SessionStore.ts +4 -0
- package/src/workspace/store/LocalSessionAttachmentStore.ts +96 -0
- package/src/workspace/store/LocalSessionStore.ts +13 -0
- package/src/workspace/store/LocalStorePaths.ts +16 -0
- package/tsconfig.tsbuildinfo +1 -1
package/src/session/Session.ts
CHANGED
|
@@ -21,6 +21,7 @@ import type {
|
|
|
21
21
|
AgentSessionInfo,
|
|
22
22
|
AgentSessionStatus,
|
|
23
23
|
AgentSessionSetInput,
|
|
24
|
+
AgentSessionSetOptions,
|
|
24
25
|
AgentSessionSystemBlock,
|
|
25
26
|
AgentSessionSystemSnapshot,
|
|
26
27
|
} from "@/types/agent/SessionTypes.js";
|
|
@@ -93,8 +94,6 @@ export class Session implements AgentSession {
|
|
|
93
94
|
private readonly events: SessionEventHub;
|
|
94
95
|
private readonly session_interactions: SessionInteractions;
|
|
95
96
|
private readonly shell_approval_adapter: SessionShellApprovalAdapter;
|
|
96
|
-
/** 已被 Session 接受、等待或已经在 Step 检查点提交的审批模式。 */
|
|
97
|
-
private configured_approval_mode: SessionApprovalMode = "ask";
|
|
98
97
|
private readonly local_state: SessionLocalState;
|
|
99
98
|
private readonly get_workspace_env: SessionOptions["get_workspace_env"];
|
|
100
99
|
private readonly get_agent_model: SessionOptions["get_agent_model"];
|
|
@@ -102,6 +101,8 @@ export class Session implements AgentSession {
|
|
|
102
101
|
private readonly get_instruction_system_blocks:
|
|
103
102
|
SessionOptions["get_instruction_system_blocks"];
|
|
104
103
|
private effective_instruction_system_blocks: AgentSessionSystemBlock[];
|
|
104
|
+
/** 当前 Session 的一次性初始化任务,避免缓存实例被重复恢复运行时状态。 */
|
|
105
|
+
private initialize_promise: Promise<void> | null = null;
|
|
105
106
|
private instruction_initialize_promise: Promise<void> | null = null;
|
|
106
107
|
private effective_workspace_env: Record<string, string>;
|
|
107
108
|
private effective_agent_plugins: AgentPluginExecutionRuntime;
|
|
@@ -149,6 +150,7 @@ export class Session implements AgentSession {
|
|
|
149
150
|
this.session_messages = new SessionMessages({
|
|
150
151
|
session_id: this.id,
|
|
151
152
|
store: this.store.messages,
|
|
153
|
+
attachment_store: this.store.attachments,
|
|
152
154
|
publish: (mutation) => {
|
|
153
155
|
this.events.publish(mutation);
|
|
154
156
|
},
|
|
@@ -199,11 +201,19 @@ export class Session implements AgentSession {
|
|
|
199
201
|
* 初始化当前 session。
|
|
200
202
|
*/
|
|
201
203
|
async initialize(): Promise<this> {
|
|
202
|
-
|
|
203
|
-
this.
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
204
|
+
if (!this.initialize_promise) {
|
|
205
|
+
this.initialize_promise = (async () => {
|
|
206
|
+
await Promise.all([
|
|
207
|
+
this.initialize_instruction(),
|
|
208
|
+
this.session_messages.initialize(),
|
|
209
|
+
this.state.initialize(),
|
|
210
|
+
]);
|
|
211
|
+
this.shell_approval_adapter.set_effective_mode(
|
|
212
|
+
this.state.get_approval_mode(),
|
|
213
|
+
);
|
|
214
|
+
})();
|
|
215
|
+
}
|
|
216
|
+
await this.initialize_promise;
|
|
207
217
|
return this;
|
|
208
218
|
}
|
|
209
219
|
|
|
@@ -257,7 +267,10 @@ export class Session implements AgentSession {
|
|
|
257
267
|
/**
|
|
258
268
|
* 写入当前 session 默认配置。
|
|
259
269
|
*/
|
|
260
|
-
async set(
|
|
270
|
+
async set(
|
|
271
|
+
input: AgentSessionSetInput,
|
|
272
|
+
options?: AgentSessionSetOptions,
|
|
273
|
+
): Promise<void> {
|
|
261
274
|
if (!input.model && !input.security) {
|
|
262
275
|
throw new Error("session.set requires model or security");
|
|
263
276
|
}
|
|
@@ -269,38 +282,49 @@ export class Session implements AgentSession {
|
|
|
269
282
|
) {
|
|
270
283
|
throw new Error("security.approval_mode must be ask or always-allow");
|
|
271
284
|
}
|
|
272
|
-
const
|
|
285
|
+
const persist_action = options?.persist_action !== false;
|
|
286
|
+
const publish_mutation =
|
|
287
|
+
options?.publish_mutation === undefined
|
|
288
|
+
? persist_action
|
|
289
|
+
: options.publish_mutation;
|
|
290
|
+
if (!persist_action && publish_mutation) {
|
|
291
|
+
throw new Error(
|
|
292
|
+
"session.set publish_mutation requires persist_action",
|
|
293
|
+
);
|
|
294
|
+
}
|
|
295
|
+
const model_result = input.model
|
|
273
296
|
? await this.state.set_model(input.model)
|
|
274
297
|
: undefined;
|
|
275
298
|
const next_approval_mode = requested_approval_mode;
|
|
276
|
-
const security_changed =
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
if (
|
|
280
|
-
this.configured_approval_mode = next_approval_mode;
|
|
281
|
-
}
|
|
282
|
-
if (!model_config && !security_changed) return;
|
|
299
|
+
const security_changed = next_approval_mode !== undefined
|
|
300
|
+
? await this.state.set_approval_mode(next_approval_mode)
|
|
301
|
+
: false;
|
|
302
|
+
if (!model_result && !security_changed) return;
|
|
283
303
|
const changed_fields = [
|
|
284
|
-
...(
|
|
285
|
-
? [`model: ${String(
|
|
304
|
+
...(model_result?.changed
|
|
305
|
+
? [`model: ${String(model_result.config.model_label || "configured")}`]
|
|
286
306
|
: []),
|
|
287
307
|
...(security_changed
|
|
288
308
|
? [`security.approval_mode: ${String(next_approval_mode)}`]
|
|
289
309
|
: []),
|
|
290
310
|
];
|
|
311
|
+
const completion = changed_fields.length > 0 && persist_action
|
|
312
|
+
? {
|
|
313
|
+
type: "action" as const,
|
|
314
|
+
id: `session-config:${this.id}:${Date.now()}:${generate_id()}`,
|
|
315
|
+
title: "Session configuration updated",
|
|
316
|
+
description: changed_fields.join("; "),
|
|
317
|
+
publish_mutation,
|
|
318
|
+
}
|
|
319
|
+
: undefined;
|
|
291
320
|
this.enqueue_command({
|
|
292
321
|
execute: async () => {
|
|
293
|
-
if (
|
|
322
|
+
if (model_result) this.state.apply_model_config(model_result.config);
|
|
294
323
|
if (security_changed && next_approval_mode) {
|
|
295
324
|
this.shell_approval_adapter.set_effective_mode(next_approval_mode);
|
|
296
325
|
}
|
|
297
326
|
},
|
|
298
|
-
completion: {
|
|
299
|
-
type: "action",
|
|
300
|
-
id: `session-config:${this.id}:${Date.now()}:${generate_id()}`,
|
|
301
|
-
title: "Session configuration updated",
|
|
302
|
-
description: changed_fields.join("; "),
|
|
303
|
-
},
|
|
327
|
+
...(completion ? { completion } : {}),
|
|
304
328
|
});
|
|
305
329
|
}
|
|
306
330
|
|
|
@@ -432,7 +456,7 @@ export class Session implements AgentSession {
|
|
|
432
456
|
state: active_turn_id || this.executor.is_executing() ? "running" : "idle",
|
|
433
457
|
...(active_turn_id ? { active_turn_id } : {}),
|
|
434
458
|
security: {
|
|
435
|
-
approval_mode: this.
|
|
459
|
+
approval_mode: this.state.get_approval_mode(),
|
|
436
460
|
effective_approval_mode: this.shell_approval_adapter.get_effective_mode(),
|
|
437
461
|
},
|
|
438
462
|
};
|
|
@@ -453,8 +477,8 @@ export class Session implements AgentSession {
|
|
|
453
477
|
text: String(input.text || "").trim(),
|
|
454
478
|
});
|
|
455
479
|
if (!appended) return;
|
|
456
|
-
|
|
457
|
-
|
|
480
|
+
this.state.touch_metadata_in_background();
|
|
481
|
+
this.state.schedule_title_generation();
|
|
458
482
|
}
|
|
459
483
|
|
|
460
484
|
/**
|
|
@@ -555,9 +579,12 @@ export class Session implements AgentSession {
|
|
|
555
579
|
await forked.initialize();
|
|
556
580
|
const session_config = this.state.get_config();
|
|
557
581
|
if (session_config.model) {
|
|
558
|
-
const
|
|
559
|
-
forked.state.apply_model_config(
|
|
582
|
+
const forked_model = await forked.state.set_model(session_config.model);
|
|
583
|
+
forked.state.apply_model_config(forked_model.config);
|
|
560
584
|
}
|
|
585
|
+
const approval_mode = this.state.get_approval_mode();
|
|
586
|
+
await forked.state.set_approval_mode(approval_mode);
|
|
587
|
+
forked.shell_approval_adapter.set_effective_mode(approval_mode);
|
|
561
588
|
await forked.session_messages.import_messages(fork_messages);
|
|
562
589
|
await this.emit_action_event({
|
|
563
590
|
id: action_id,
|
|
@@ -610,8 +637,8 @@ export class Session implements AgentSession {
|
|
|
610
637
|
message_params,
|
|
611
638
|
);
|
|
612
639
|
if (!appended) return;
|
|
613
|
-
|
|
614
|
-
|
|
640
|
+
this.state.touch_metadata_in_background();
|
|
641
|
+
this.state.schedule_title_generation();
|
|
615
642
|
},
|
|
616
643
|
append_assistant_message: async (message_params) => {
|
|
617
644
|
const appended = await this.session_messages.append_external_assistant_message({
|
|
@@ -629,6 +656,11 @@ export class Session implements AgentSession {
|
|
|
629
656
|
return this.runtime_port;
|
|
630
657
|
}
|
|
631
658
|
|
|
659
|
+
/** 取消并释放当前 Session 的标题后台任务。 */
|
|
660
|
+
dispose_title_generation(): void {
|
|
661
|
+
this.state.dispose_title_generation();
|
|
662
|
+
}
|
|
663
|
+
|
|
632
664
|
/**
|
|
633
665
|
* 在执行前确保 session 已完成初始化与宿主装配。
|
|
634
666
|
*/
|
|
@@ -710,6 +742,7 @@ export class Session implements AgentSession {
|
|
|
710
742
|
return {
|
|
711
743
|
session_config: {},
|
|
712
744
|
effective_session_config: {},
|
|
745
|
+
configured_approval_mode: "ask",
|
|
713
746
|
created_at: Date.now(),
|
|
714
747
|
timezone: resolve_system_timezone(),
|
|
715
748
|
initialize_promise: null,
|
|
@@ -279,6 +279,8 @@ export class SessionLoop {
|
|
|
279
279
|
session_id: this.session_id,
|
|
280
280
|
turn_id,
|
|
281
281
|
},
|
|
282
|
+
}, {
|
|
283
|
+
publish_mutation: completion.publish_mutation !== false,
|
|
282
284
|
});
|
|
283
285
|
} catch (error) {
|
|
284
286
|
try {
|
|
@@ -591,16 +593,17 @@ export class SessionLoop {
|
|
|
591
593
|
turn_id,
|
|
592
594
|
input_type,
|
|
593
595
|
});
|
|
594
|
-
|
|
595
|
-
|
|
596
|
+
this.state.touch_metadata_in_background();
|
|
597
|
+
this.state.schedule_title_generation();
|
|
596
598
|
return message;
|
|
597
599
|
}
|
|
598
600
|
|
|
599
601
|
/** 持久化 Executor Action,并同步刷新 Session metadata。 */
|
|
600
602
|
private async persist_action_event(
|
|
601
603
|
event: SessionActionRecordV1,
|
|
604
|
+
options?: { publish_mutation?: boolean },
|
|
602
605
|
): Promise<void> {
|
|
603
|
-
await this.messages.persist_action_record(event);
|
|
606
|
+
await this.messages.persist_action_record(event, options);
|
|
604
607
|
await this.state.touch_metadata();
|
|
605
608
|
}
|
|
606
609
|
|
|
@@ -50,7 +50,7 @@ import {
|
|
|
50
50
|
from_ui_user_parts,
|
|
51
51
|
to_executor_ui_message,
|
|
52
52
|
} from "@/session/messages/SessionMessageCodec.js";
|
|
53
|
-
import {
|
|
53
|
+
import { persist_user_prompt_file_parts } from "@executor/messages/SessionAttachmentMapper.js";
|
|
54
54
|
import type {
|
|
55
55
|
AppendCompletedAssistantMessageInput,
|
|
56
56
|
AppendExternalSessionAssistantMessageInput,
|
|
@@ -63,6 +63,7 @@ import type {
|
|
|
63
63
|
SessionMessagesOptions,
|
|
64
64
|
} from "@/types/session/SessionMessages.js";
|
|
65
65
|
import type { SessionMessageStore } from "@/types/store/SessionStore.js";
|
|
66
|
+
import type { SessionAttachmentStore } from "@/types/store/SessionAttachmentStore.js";
|
|
66
67
|
|
|
67
68
|
export { SessionAssistantMessageWriter } from "@/session/messages/SessionAssistantMessageWriter.js";
|
|
68
69
|
|
|
@@ -70,6 +71,7 @@ export { SessionAssistantMessageWriter } from "@/session/messages/SessionAssista
|
|
|
70
71
|
export class SessionMessages {
|
|
71
72
|
readonly session_id: string;
|
|
72
73
|
private readonly store: SessionMessageStore;
|
|
74
|
+
private readonly attachment_store: SessionAttachmentStore;
|
|
73
75
|
private readonly publish: SessionMessagesOptions["publish"];
|
|
74
76
|
private readonly messages_by_id = new Map<string, SessionMessage>();
|
|
75
77
|
/** 按 Assistant Message 隔离的完整写事务链。 */
|
|
@@ -81,6 +83,7 @@ export class SessionMessages {
|
|
|
81
83
|
constructor(options: SessionMessagesOptions) {
|
|
82
84
|
this.session_id = String(options.session_id || "").trim();
|
|
83
85
|
this.store = options.store;
|
|
86
|
+
this.attachment_store = options.attachment_store;
|
|
84
87
|
this.publish = options.publish;
|
|
85
88
|
if (!this.session_id) throw new Error("SessionMessages requires session_id");
|
|
86
89
|
this.interaction_writer = new SessionMessageInteractionWriter({
|
|
@@ -282,9 +285,9 @@ export class SessionMessages {
|
|
|
282
285
|
const query = input.prompt.query;
|
|
283
286
|
const ui_parts = typeof query === "string"
|
|
284
287
|
? [{ type: "text" as const, text: query.trim() }]
|
|
285
|
-
: await
|
|
288
|
+
: await persist_user_prompt_file_parts(
|
|
286
289
|
Array.isArray(query) ? query : [],
|
|
287
|
-
|
|
290
|
+
this.attachment_store,
|
|
288
291
|
);
|
|
289
292
|
const canonical = await this.append_user_message({
|
|
290
293
|
turn_id: input.turn_id,
|
|
@@ -315,7 +318,11 @@ export class SessionMessages {
|
|
|
315
318
|
}
|
|
316
319
|
|
|
317
320
|
/** 按稳定 Action ID 创建或更新 canonical Action Message。 */
|
|
318
|
-
async persist_action_record(
|
|
321
|
+
async persist_action_record(
|
|
322
|
+
event: SessionActionRecordV1,
|
|
323
|
+
options?: { publish_mutation?: boolean },
|
|
324
|
+
): Promise<void> {
|
|
325
|
+
const publish_mutation = options?.publish_mutation !== false;
|
|
319
326
|
const existing = this.get_message(event.id);
|
|
320
327
|
if (!existing) {
|
|
321
328
|
const writer = await this.open_action_message({
|
|
@@ -324,6 +331,7 @@ export class SessionMessages {
|
|
|
324
331
|
action_type: infer_action_type(event.id),
|
|
325
332
|
title: event.title,
|
|
326
333
|
description: event.description,
|
|
334
|
+
publish_mutation,
|
|
327
335
|
});
|
|
328
336
|
if (event.state === "completed") await writer.complete();
|
|
329
337
|
if (event.state === "failed") {
|
|
@@ -335,7 +343,7 @@ export class SessionMessages {
|
|
|
335
343
|
await this.update_action_message(event.id, event.state, {
|
|
336
344
|
title: event.title,
|
|
337
345
|
description: event.description,
|
|
338
|
-
});
|
|
346
|
+
}, { publish_mutation });
|
|
339
347
|
}
|
|
340
348
|
}
|
|
341
349
|
|
|
@@ -360,8 +368,12 @@ export class SessionMessages {
|
|
|
360
368
|
title: input.title,
|
|
361
369
|
...(input.description ? { description: input.description } : {}),
|
|
362
370
|
...(input.data ? { data: structuredClone(input.data) } : {}),
|
|
363
|
-
}))) as SessionActionMessage;
|
|
364
|
-
return new SessionActionMessageWriter(
|
|
371
|
+
}), false, input.publish_mutation !== false)) as SessionActionMessage;
|
|
372
|
+
return new SessionActionMessageWriter(
|
|
373
|
+
this,
|
|
374
|
+
message.message_id,
|
|
375
|
+
input.publish_mutation !== false,
|
|
376
|
+
);
|
|
365
377
|
}
|
|
366
378
|
|
|
367
379
|
/** 更新 Action 状态,同时保持 message_id 与 sequence 不变。 */
|
|
@@ -369,6 +381,7 @@ export class SessionMessages {
|
|
|
369
381
|
message_id: string,
|
|
370
382
|
status: "running" | "completed" | "failed",
|
|
371
383
|
changes?: { title?: string; description?: string; data?: JsonObject },
|
|
384
|
+
options?: { publish_mutation?: boolean },
|
|
372
385
|
): Promise<SessionActionMessage> {
|
|
373
386
|
const message = await this.store.append_message((state) => {
|
|
374
387
|
const current = require_message(state.messages, message_id, "action");
|
|
@@ -385,7 +398,7 @@ export class SessionMessages {
|
|
|
385
398
|
updated_at: created_at,
|
|
386
399
|
} satisfies SessionActionMessage;
|
|
387
400
|
});
|
|
388
|
-
this.accept_message(message);
|
|
401
|
+
this.accept_message(message, options?.publish_mutation !== false);
|
|
389
402
|
return message as SessionActionMessage;
|
|
390
403
|
}
|
|
391
404
|
|
|
@@ -711,9 +724,10 @@ export class SessionMessages {
|
|
|
711
724
|
async complete_assistant_message(
|
|
712
725
|
message_id: string,
|
|
713
726
|
status: "completed" | "stopped" | "failed",
|
|
727
|
+
error?: string,
|
|
714
728
|
): Promise<void> {
|
|
715
729
|
await this.enqueue_assistant_write(message_id, async () => {
|
|
716
|
-
await this.complete_assistant_message_serialized(message_id, status);
|
|
730
|
+
await this.complete_assistant_message_serialized(message_id, status, error);
|
|
717
731
|
});
|
|
718
732
|
}
|
|
719
733
|
|
|
@@ -721,6 +735,7 @@ export class SessionMessages {
|
|
|
721
735
|
private async complete_assistant_message_serialized(
|
|
722
736
|
message_id: string,
|
|
723
737
|
status: "completed" | "stopped" | "failed",
|
|
738
|
+
error?: string,
|
|
724
739
|
): Promise<void> {
|
|
725
740
|
const current = require_message([...this.messages_by_id.values()], message_id, "assistant");
|
|
726
741
|
require_streaming_assistant(current);
|
|
@@ -757,13 +772,18 @@ export class SessionMessages {
|
|
|
757
772
|
}
|
|
758
773
|
if (
|
|
759
774
|
part.type === "tool" &&
|
|
760
|
-
part.state
|
|
761
|
-
|
|
775
|
+
part.state !== "completed" &&
|
|
776
|
+
part.state !== "failed"
|
|
762
777
|
) {
|
|
763
778
|
return {
|
|
764
779
|
...part,
|
|
765
780
|
state: "failed" as const,
|
|
766
|
-
error:
|
|
781
|
+
error:
|
|
782
|
+
status === "stopped" &&
|
|
783
|
+
part.state === "waiting-user" &&
|
|
784
|
+
interrupted_tool_ids.has(part.tool_call_id)
|
|
785
|
+
? "Interaction cancelled"
|
|
786
|
+
: error || "Tool did not complete before Assistant Message closed",
|
|
767
787
|
};
|
|
768
788
|
}
|
|
769
789
|
return part;
|
|
@@ -776,6 +796,7 @@ export class SessionMessages {
|
|
|
776
796
|
private async create_message(
|
|
777
797
|
factory: (sequence: number, created_at: number) => SessionMessage,
|
|
778
798
|
draft = false,
|
|
799
|
+
publish_mutation = true,
|
|
779
800
|
): Promise<SessionMessage> {
|
|
780
801
|
await this.ensure_initialized();
|
|
781
802
|
if (draft) {
|
|
@@ -786,13 +807,13 @@ export class SessionMessages {
|
|
|
786
807
|
}
|
|
787
808
|
return candidate;
|
|
788
809
|
});
|
|
789
|
-
this.accept_message(message);
|
|
810
|
+
this.accept_message(message, publish_mutation);
|
|
790
811
|
return message;
|
|
791
812
|
}
|
|
792
813
|
const message = await this.store.append_message((state) =>
|
|
793
814
|
factory(state.message_sequence, Date.now()),
|
|
794
815
|
);
|
|
795
|
-
this.accept_message(message);
|
|
816
|
+
this.accept_message(message, publish_mutation);
|
|
796
817
|
return message;
|
|
797
818
|
}
|
|
798
819
|
|
|
@@ -923,7 +944,11 @@ export class SessionMessages {
|
|
|
923
944
|
} as SessionMessageSnapshotMutation;
|
|
924
945
|
}
|
|
925
946
|
|
|
926
|
-
private accept_message(message: SessionMessage): void {
|
|
947
|
+
private accept_message(message: SessionMessage, publish_mutation = true): void {
|
|
948
|
+
if (!publish_mutation) {
|
|
949
|
+
this.messages_by_id.set(message.message_id, structuredClone(message));
|
|
950
|
+
return;
|
|
951
|
+
}
|
|
927
952
|
this.accept_mutation(this.build_message_mutation(message), message);
|
|
928
953
|
}
|
|
929
954
|
|
|
@@ -941,26 +966,42 @@ export class SessionMessages {
|
|
|
941
966
|
export class SessionActionMessageWriter {
|
|
942
967
|
readonly message_id: string;
|
|
943
968
|
private readonly messages: SessionMessages;
|
|
969
|
+
private readonly publish_mutation: boolean;
|
|
944
970
|
private closed = false;
|
|
945
971
|
|
|
946
|
-
constructor(
|
|
972
|
+
constructor(
|
|
973
|
+
messages: SessionMessages,
|
|
974
|
+
message_id: string,
|
|
975
|
+
publish_mutation = true,
|
|
976
|
+
) {
|
|
947
977
|
this.messages = messages;
|
|
948
978
|
this.message_id = message_id;
|
|
979
|
+
this.publish_mutation = publish_mutation;
|
|
949
980
|
}
|
|
950
981
|
|
|
951
982
|
/** 把 Action 更新为 completed。 */
|
|
952
983
|
async complete(input?: { title?: string; description?: string; data?: JsonObject }): Promise<void> {
|
|
953
984
|
if (this.closed) return;
|
|
954
|
-
await this.messages.update_action_message(
|
|
985
|
+
await this.messages.update_action_message(
|
|
986
|
+
this.message_id,
|
|
987
|
+
"completed",
|
|
988
|
+
input,
|
|
989
|
+
{ publish_mutation: this.publish_mutation },
|
|
990
|
+
);
|
|
955
991
|
this.closed = true;
|
|
956
992
|
}
|
|
957
993
|
|
|
958
994
|
/** 把 Action 更新为 failed。 */
|
|
959
995
|
async fail(error: unknown): Promise<void> {
|
|
960
996
|
if (this.closed) return;
|
|
961
|
-
await this.messages.update_action_message(
|
|
962
|
-
|
|
963
|
-
|
|
997
|
+
await this.messages.update_action_message(
|
|
998
|
+
this.message_id,
|
|
999
|
+
"failed",
|
|
1000
|
+
{
|
|
1001
|
+
description: error instanceof Error ? error.message : String(error),
|
|
1002
|
+
},
|
|
1003
|
+
{ publish_mutation: this.publish_mutation },
|
|
1004
|
+
);
|
|
964
1005
|
this.closed = true;
|
|
965
1006
|
}
|
|
966
1007
|
}
|