@downcity/agent 1.1.198 → 1.1.200

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.
Files changed (143) hide show
  1. package/bin/executor/Executor.d.ts +2 -2
  2. package/bin/executor/Executor.d.ts.map +1 -1
  3. package/bin/executor/Executor.js +23 -24
  4. package/bin/executor/Executor.js.map +1 -1
  5. package/bin/executor/composer/compaction/SessionCompactionComposer.d.ts +5 -5
  6. package/bin/executor/composer/compaction/SessionCompactionComposer.d.ts.map +1 -1
  7. package/bin/executor/composer/compaction/jsonl/JsonlSessionCompactionComposer.js +1 -1
  8. package/bin/executor/composer/compaction/jsonl/JsonlSessionCompactionComposer.js.map +1 -1
  9. package/bin/executor/composer/compaction/jsonl/JsonlSessionCompactionExecutor.d.ts +2 -2
  10. package/bin/executor/composer/compaction/jsonl/JsonlSessionCompactionExecutor.d.ts.map +1 -1
  11. package/bin/executor/composer/compaction/jsonl/JsonlSessionCompactionExecutor.js +23 -30
  12. package/bin/executor/composer/compaction/jsonl/JsonlSessionCompactionExecutor.js.map +1 -1
  13. package/bin/executor/composer/context/LocalSessionContextComposer.d.ts +2 -2
  14. package/bin/executor/composer/context/LocalSessionContextComposer.d.ts.map +1 -1
  15. package/bin/executor/composer/context/LocalSessionContextComposer.js.map +1 -1
  16. package/bin/executor/composer/context/SessionContextComposer.d.ts +2 -2
  17. package/bin/executor/composer/context/SessionContextComposer.d.ts.map +1 -1
  18. package/bin/executor/composer/history/jsonl/JsonlSessionHistoryComposer.d.ts.map +1 -1
  19. package/bin/executor/composer/history/jsonl/JsonlSessionHistoryComposer.js +4 -3
  20. package/bin/executor/composer/history/jsonl/JsonlSessionHistoryComposer.js.map +1 -1
  21. package/bin/executor/core-engine/CoreEngineRunner.d.ts.map +1 -1
  22. package/bin/executor/core-engine/CoreEngineRunner.js.map +1 -1
  23. package/bin/executor/core-engine/CoreEngineSignals.d.ts +4 -4
  24. package/bin/executor/core-engine/CoreEngineSignals.d.ts.map +1 -1
  25. package/bin/executor/core-engine/CoreEngineSignals.js.map +1 -1
  26. package/bin/executor/core-engine/CoreEngineUiStreamCollector.d.ts +3 -3
  27. package/bin/executor/core-engine/CoreEngineUiStreamCollector.d.ts.map +1 -1
  28. package/bin/executor/core-engine/CoreEngineUiStreamCollector.js.map +1 -1
  29. package/bin/executor/messages/AssistantResultPersistence.d.ts +3 -3
  30. package/bin/executor/messages/AssistantResultPersistence.d.ts.map +1 -1
  31. package/bin/executor/messages/AssistantResultPersistence.js.map +1 -1
  32. package/bin/executor/messages/SessionAttachmentMapper.d.ts +3 -3
  33. package/bin/executor/messages/SessionAttachmentMapper.d.ts.map +1 -1
  34. package/bin/executor/messages/SessionAttachmentMapper.js.map +1 -1
  35. package/bin/executor/messages/SessionMessageCodec.d.ts +1 -1
  36. package/bin/executor/messages/SessionMessageCodec.d.ts.map +1 -1
  37. package/bin/executor/messages/SessionMessageCodec.js +5 -5
  38. package/bin/executor/messages/SessionMessageCodec.js.map +1 -1
  39. package/bin/executor/messages/SessionMessageLog.d.ts +3 -3
  40. package/bin/executor/messages/SessionMessageLog.d.ts.map +1 -1
  41. package/bin/executor/messages/SessionMessageLog.js.map +1 -1
  42. package/bin/executor/messages/SessionStepEventMapper.d.ts +2 -2
  43. package/bin/executor/messages/SessionStepEventMapper.d.ts.map +1 -1
  44. package/bin/executor/messages/SessionStepEventMapper.js.map +1 -1
  45. package/bin/executor/messages/UserVisibleText.d.ts +2 -2
  46. package/bin/executor/messages/UserVisibleText.d.ts.map +1 -1
  47. package/bin/executor/messages/UserVisibleText.js +5 -0
  48. package/bin/executor/messages/UserVisibleText.js.map +1 -1
  49. package/bin/executor/services/ExecutorInflightService.d.ts +2 -2
  50. package/bin/executor/services/ExecutorInflightService.d.ts.map +1 -1
  51. package/bin/executor/services/ExecutorInflightService.js +9 -5
  52. package/bin/executor/services/ExecutorInflightService.js.map +1 -1
  53. package/bin/executor/store/history/SessionHistoryStore.d.ts +11 -11
  54. package/bin/executor/store/history/SessionHistoryStore.d.ts.map +1 -1
  55. package/bin/executor/store/history/jsonl/JsonlSessionHistoryStore.d.ts +9 -7
  56. package/bin/executor/store/history/jsonl/JsonlSessionHistoryStore.d.ts.map +1 -1
  57. package/bin/executor/store/history/jsonl/JsonlSessionHistoryStore.js +98 -52
  58. package/bin/executor/store/history/jsonl/JsonlSessionHistoryStore.js.map +1 -1
  59. package/bin/executor/types/SessionMessages.d.ts +57 -18
  60. package/bin/executor/types/SessionMessages.d.ts.map +1 -1
  61. package/bin/executor/types/SessionMessages.js +33 -5
  62. package/bin/executor/types/SessionMessages.js.map +1 -1
  63. package/bin/executor/types/SessionRun.d.ts +2 -3
  64. package/bin/executor/types/SessionRun.d.ts.map +1 -1
  65. package/bin/index.d.ts +1 -1
  66. package/bin/index.d.ts.map +1 -1
  67. package/bin/index.js.map +1 -1
  68. package/bin/session/SessionTitle.d.ts.map +1 -1
  69. package/bin/session/SessionTitle.js +5 -0
  70. package/bin/session/SessionTitle.js.map +1 -1
  71. package/bin/session/browse/Browse.d.ts.map +1 -1
  72. package/bin/session/browse/Browse.js +36 -26
  73. package/bin/session/browse/Browse.js.map +1 -1
  74. package/bin/session/runtime/SessionPromptRuntime.d.ts +2 -2
  75. package/bin/session/runtime/SessionPromptRuntime.d.ts.map +1 -1
  76. package/bin/session/runtime/SessionPromptRuntime.js.map +1 -1
  77. package/bin/session/services/SessionStateService.d.ts +10 -10
  78. package/bin/session/services/SessionStateService.d.ts.map +1 -1
  79. package/bin/session/services/SessionStateService.js +44 -53
  80. package/bin/session/services/SessionStateService.js.map +1 -1
  81. package/bin/session/services/SessionTurnService.d.ts.map +1 -1
  82. package/bin/session/services/SessionTurnService.js +3 -3
  83. package/bin/session/services/SessionTurnService.js.map +1 -1
  84. package/bin/session/services/SessionViewService.d.ts.map +1 -1
  85. package/bin/session/services/SessionViewService.js +17 -27
  86. package/bin/session/services/SessionViewService.js.map +1 -1
  87. package/bin/session/storage/Persistence.d.ts +2 -2
  88. package/bin/session/storage/Persistence.d.ts.map +1 -1
  89. package/bin/session/storage/Persistence.js.map +1 -1
  90. package/bin/types/agent/SessionTypes.d.ts +10 -10
  91. package/bin/types/agent/SessionTypes.d.ts.map +1 -1
  92. package/bin/types/executor/SessionRunContext.d.ts +6 -6
  93. package/bin/types/executor/SessionRunContext.d.ts.map +1 -1
  94. package/bin/types/sdk/AgentSessionAction.d.ts +67 -0
  95. package/bin/types/sdk/AgentSessionAction.d.ts.map +1 -0
  96. package/bin/types/sdk/AgentSessionAction.js +10 -0
  97. package/bin/types/sdk/AgentSessionAction.js.map +1 -0
  98. package/bin/types/sdk/AgentSessionEvent.d.ts +2 -2
  99. package/bin/types/sdk/AgentSessionEvent.d.ts.map +1 -1
  100. package/bin/types/sdk/AgentSessionTurn.d.ts +2 -2
  101. package/bin/types/sdk/AgentSessionTurn.d.ts.map +1 -1
  102. package/package.json +3 -3
  103. package/scripts/{session-operation-message.test.mjs → session-action-message.test.mjs} +58 -41
  104. package/src/executor/Executor.ts +28 -29
  105. package/src/executor/composer/compaction/SessionCompactionComposer.ts +5 -5
  106. package/src/executor/composer/compaction/jsonl/JsonlSessionCompactionComposer.ts +1 -1
  107. package/src/executor/composer/compaction/jsonl/JsonlSessionCompactionExecutor.ts +32 -37
  108. package/src/executor/composer/context/LocalSessionContextComposer.ts +5 -2
  109. package/src/executor/composer/context/SessionContextComposer.ts +5 -2
  110. package/src/executor/composer/history/jsonl/JsonlSessionHistoryComposer.ts +7 -4
  111. package/src/executor/core-engine/CoreEngineRunner.ts +7 -4
  112. package/src/executor/core-engine/CoreEngineSignals.ts +7 -7
  113. package/src/executor/core-engine/CoreEngineUiStreamCollector.ts +5 -5
  114. package/src/executor/messages/AssistantResultPersistence.ts +6 -3
  115. package/src/executor/messages/SessionAttachmentMapper.ts +12 -9
  116. package/src/executor/messages/SessionMessageCodec.ts +5 -7
  117. package/src/executor/messages/SessionMessageLog.ts +5 -3
  118. package/src/executor/messages/SessionStepEventMapper.ts +5 -5
  119. package/src/executor/messages/UserVisibleText.ts +8 -2
  120. package/src/executor/services/ExecutorInflightService.ts +11 -7
  121. package/src/executor/store/history/SessionHistoryStore.ts +11 -10
  122. package/src/executor/store/history/jsonl/JsonlSessionHistoryStore.ts +120 -63
  123. package/src/executor/types/SessionMessages.ts +92 -22
  124. package/src/executor/types/SessionRun.ts +6 -3
  125. package/src/index.ts +5 -6
  126. package/src/session/SessionTitle.ts +3 -0
  127. package/src/session/browse/Browse.ts +49 -30
  128. package/src/session/runtime/SessionPromptRuntime.ts +6 -3
  129. package/src/session/services/SessionStateService.ts +56 -65
  130. package/src/session/services/SessionTurnService.ts +5 -4
  131. package/src/session/services/SessionViewService.ts +17 -27
  132. package/src/session/storage/Persistence.ts +5 -2
  133. package/src/types/agent/SessionTypes.ts +10 -10
  134. package/src/types/executor/SessionRunContext.ts +11 -11
  135. package/src/types/sdk/AgentSessionAction.ts +79 -0
  136. package/src/types/sdk/AgentSessionEvent.ts +2 -2
  137. package/src/types/sdk/AgentSessionTurn.ts +2 -2
  138. package/tsconfig.tsbuildinfo +1 -1
  139. package/bin/types/sdk/AgentSessionOperation.d.ts +0 -99
  140. package/bin/types/sdk/AgentSessionOperation.d.ts.map +0 -1
  141. package/bin/types/sdk/AgentSessionOperation.js +0 -10
  142. package/bin/types/sdk/AgentSessionOperation.js.map +0 -1
  143. package/src/types/sdk/AgentSessionOperation.ts +0 -125
@@ -3,23 +3,27 @@
3
3
  *
4
4
  * 关键点(中文)
5
5
  * - 这里统一描述 session 消息落盘格式与元信息结构。
6
- * - session 的唯一事实源是 `UIMessage[]`,不会再维护第二套平行消息结构。
6
+ * - session 的唯一事实源是消息 JSONL,模型消息继续使用 AI SDK `UIMessage`。
7
7
  * - 这些类型会被 history Store、history Composer、compact、control UI、task runtime 共同复用。
8
8
  */
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
+ import type {
13
+ AgentSessionActionRecord,
14
+ AgentSessionActionState,
15
+ } from "@/types/sdk/AgentSessionAction.js";
13
16
 
14
17
  /**
15
18
  * Session 消息:以 UIMessage[] 作为唯一事实源。
16
19
  *
17
20
  * 关键点(中文)
18
21
  * - 持久化存储在 `.downcity/agents/<encodedAgentId>/sessions/<encodedSessionId>/messages/messages.jsonl`
19
- * - 默认只存 `role=user|assistant`
22
+ * - 默认只存模型消息,或 `type=action` 的 UI 状态消息。
20
23
  * - compact 会把更早消息压缩为一条 `assistant` 摘要消息
24
+ * - action 只用于 UI timeline,不进入 LLM 输入
21
25
  */
22
- export type SessionMessageKind = "normal" | "summary" | "operation";
26
+ export type SessionMessageKind = "normal" | "summary";
23
27
 
24
28
  /**
25
29
  * Session 消息来源类型。
@@ -32,8 +36,7 @@ export type SessionMessageKind = "normal" | "summary" | "operation";
32
36
  export type SessionMessageSource =
33
37
  | "ingress"
34
38
  | "egress"
35
- | "compact"
36
- | "operation";
39
+ | "compact";
37
40
 
38
41
  /**
39
42
  * 入站消息细分类型。
@@ -73,14 +76,6 @@ export type SessionMetadataV1 = {
73
76
  archiveId?: string;
74
77
  /** compact 摘要所覆盖的原始消息范围。 */
75
78
  sourceRange?: SessionMessageSourceRangeV1;
76
- /**
77
- * operation message 对应的结构化操作记录。
78
- *
79
- * 说明(中文)
80
- * - 仅当 `kind=operation` 或 `source=operation` 时存在。
81
- * - 该字段供前端恢复时间线状态,不会进入 LLM 输入。
82
- */
83
- operation?: AgentSessionOperationRecord;
84
79
  /**
85
80
  * 扩展元信息。
86
81
  *
@@ -92,25 +87,100 @@ export type SessionMetadataV1 = {
92
87
  };
93
88
 
94
89
  /**
95
- * Session UI 消息结构。
90
+ * 模型可消费的 Session UI 消息结构。
91
+ */
92
+ export type SessionModelMessageV1 = UIMessage<SessionMetadataV1>;
93
+
94
+ /**
95
+ * action 消息元信息。
96
+ */
97
+ export type SessionActionMetadataV1 = {
98
+ /** 元信息 schema 版本号。 */
99
+ v: 1;
100
+ /** 当前 action 写入时的毫秒时间戳。 */
101
+ ts: number;
102
+ /** 当前 action 所属的 session ID。 */
103
+ sessionId: string;
104
+ /** 当前 action 关联的 turn 标识。 */
105
+ turnId?: string;
106
+ };
107
+
108
+ /**
109
+ * action 类型的 Session 消息结构。
110
+ *
111
+ * 关键点(中文)
112
+ * - `action` 不是 AI SDK 原生模型消息。
113
+ * - 它只存在于 JSONL history 与前端 timeline,进入 LLM 前必须过滤。
114
+ */
115
+ export type SessionActionMessageV1 = {
116
+ /** item 类型固定为 `action`。 */
117
+ type: "action";
118
+ /** 同一个 action 生命周期内稳定复用的 ID。 */
119
+ id: string;
120
+ /** 当前 action 标题。 */
121
+ title: string;
122
+ /** 当前 action 描述。 */
123
+ description?: string;
124
+ /** 当前 action 状态。 */
125
+ state: AgentSessionActionState;
126
+ /** action 元信息。 */
127
+ metadata: SessionActionMetadataV1;
128
+ };
129
+
130
+ /**
131
+ * Session 持久化消息结构。
96
132
  */
97
- export type SessionMessageV1 = UIMessage<SessionMetadataV1>;
133
+ export type SessionMessageV1 = SessionModelMessageV1 | SessionActionMessageV1;
98
134
 
99
135
  /**
100
136
  * user 角色的 Session 消息结构。
101
137
  */
102
- export type SessionUserMessageV1 = SessionMessageV1 & {
138
+ export type SessionUserMessageV1 = SessionModelMessageV1 & {
103
139
  /** 消息角色固定为 `user`。 */
104
140
  role: "user";
105
141
  };
106
142
 
107
143
  /**
108
- * 判断一条消息是否为 operation message。
144
+ * 判断一条消息是否为 action message。
145
+ */
146
+ export function isSessionActionMessage(
147
+ message: SessionMessageV1 | null | undefined,
148
+ ): message is SessionActionMessageV1 {
149
+ if (!message || typeof message !== "object") return false;
150
+ return (message as { type?: unknown }).type === "action";
151
+ }
152
+
153
+ /**
154
+ * 从订阅事件构造 action message。
155
+ */
156
+ export function toSessionActionMessage(
157
+ action: AgentSessionActionRecord,
158
+ session_id: string,
159
+ ): SessionActionMessageV1 {
160
+ const title = String(action.title || "").trim() || "Action";
161
+ const description = String(action.description || "").trim();
162
+ const id = String(action.id || "").trim() || `action:${session_id}:${Date.now()}`;
163
+ return {
164
+ type: "action",
165
+ id,
166
+ title,
167
+ ...(description ? { description } : {}),
168
+ state: action.state,
169
+ metadata: {
170
+ v: 1,
171
+ ts: Date.now(),
172
+ sessionId: session_id,
173
+ ...(action.turnId ? { turnId: action.turnId } : {}),
174
+ },
175
+ };
176
+ }
177
+
178
+ /**
179
+ * 判断一条消息是否为模型可消费的 UIMessage。
109
180
  */
110
- export function isSessionOperationMessage(
181
+ export function isSessionModelMessage(
111
182
  message: SessionMessageV1 | null | undefined,
112
- ): boolean {
183
+ ): message is SessionModelMessageV1 {
113
184
  if (!message || typeof message !== "object") return false;
114
- const metadata = (message.metadata || null) as SessionMetadataV1 | null;
115
- return metadata?.kind === "operation" || metadata?.source === "operation";
185
+ return !isSessionActionMessage(message);
116
186
  }
@@ -8,8 +8,11 @@
8
8
  */
9
9
 
10
10
  import type { Tool, UIMessageChunk } from "ai";
11
- import type { SessionMessageV1 } from "@/executor/types/SessionMessages.js";
12
- import type { SessionUserMessageV1 } from "@/executor/types/SessionMessages.js";
11
+ import type {
12
+ SessionMessageV1,
13
+ SessionModelMessageV1,
14
+ SessionUserMessageV1,
15
+ } from "@/executor/types/SessionMessages.js";
13
16
  import type { SessionSystemMessage } from "@/executor/types/SessionPrompts.js";
14
17
  import type { SessionRunContext } from "@/types/executor/SessionRunContext.js";
15
18
 
@@ -99,7 +102,7 @@ export interface SessionRunResult {
99
102
  * - stop/abort 且没有任何 assistant 内容时可以为空。
100
103
  * - turn 状态通过 `success` / `error` 表达,不应伪造成 assistant 正文。
101
104
  */
102
- assistantMessage?: SessionMessageV1 | null;
105
+ assistantMessage?: SessionModelMessageV1 | null;
103
106
 
104
107
  /**
105
108
  * 本轮执行结束后待写入长期历史的 user 消息。
package/src/index.ts CHANGED
@@ -56,12 +56,11 @@ export type {
56
56
  AgentSessionUnsubscribe,
57
57
  } from "./types/sdk/AgentSessionEvent.js";
58
58
  export type {
59
- AgentSessionOperationCallback,
60
- AgentSessionOperationEvent,
61
- AgentSessionOperationName,
62
- AgentSessionOperationRecord,
63
- AgentSessionOperationStatus,
64
- } from "./types/sdk/AgentSessionOperation.js";
59
+ AgentSessionActionCallback,
60
+ AgentSessionActionEvent,
61
+ AgentSessionActionRecord,
62
+ AgentSessionActionState,
63
+ } from "./types/sdk/AgentSessionAction.js";
65
64
  export type { AgentSessionPromptInput, SessionUserMessagePart } from "./types/sdk/AgentSessionPrompt.js";
66
65
  export type { AgentSessionStopResult } from "./types/sdk/AgentSessionStop.js";
67
66
  export type {
@@ -10,6 +10,7 @@
10
10
  import { generateText, type LanguageModel } from "ai";
11
11
  import type { SessionHistoryMetaV1 } from "@/executor/types/SessionHistoryMeta.js";
12
12
  import type { SessionMessageV1 } from "@/executor/types/SessionMessages.js";
13
+ import { isSessionModelMessage } from "@/executor/types/SessionMessages.js";
13
14
  import {
14
15
  normalizeSessionTitle,
15
16
  readSessionMetadata,
@@ -61,6 +62,7 @@ function truncateTitle(input: string, maxChars: number): string {
61
62
  }
62
63
 
63
64
  function extractTextFromMessage(message: SessionMessageV1): string {
65
+ if (!isSessionModelMessage(message)) return "";
64
66
  if (!Array.isArray(message.parts)) return "";
65
67
  const texts: string[] = [];
66
68
  for (const part of message.parts) {
@@ -76,6 +78,7 @@ function extractTextFromMessage(message: SessionMessageV1): string {
76
78
 
77
79
  function resolveFirstUserText(messages: SessionMessageV1[]): string {
78
80
  for (const message of messages) {
81
+ if (!isSessionModelMessage(message)) continue;
79
82
  if (message.role !== "user") continue;
80
83
  const text = extractTextFromMessage(message);
81
84
  if (text) return text;
@@ -26,10 +26,15 @@ import type {
26
26
  AgentSessionTimelineEvent,
27
27
  } from "@/types/agent/AgentTypes.js";
28
28
  import type {
29
+ SessionActionMessageV1,
29
30
  SessionMessageV1,
30
31
  SessionMetadataV1,
32
+ SessionModelMessageV1,
33
+ } from "@/executor/types/SessionMessages.js";
34
+ import {
35
+ isSessionActionMessage,
36
+ isSessionModelMessage,
31
37
  } from "@/executor/types/SessionMessages.js";
32
- import { isSessionOperationMessage } from "@/executor/types/SessionMessages.js";
33
38
  import type { SessionHistoryMetaV1 } from "@/executor/types/SessionHistoryMeta.js";
34
39
  import { pickLastSuccessfulChatSendText } from "@/executor/messages/UserVisibleText.js";
35
40
  import { getSdkAgentSessionMessagesPath } from "@/session/storage/Paths.js";
@@ -172,7 +177,7 @@ function extractMessageText(parts: unknown): string {
172
177
  return texts.join("\n").trim();
173
178
  }
174
179
 
175
- function extractAssistantToolSummary(message: SessionMessageV1): string {
180
+ function extractAssistantToolSummary(message: SessionModelMessageV1): string {
176
181
  if (!Array.isArray(message.parts)) return "";
177
182
  const toolNames = new Set<string>();
178
183
  for (const part of message.parts as AnyUiPart[]) {
@@ -189,6 +194,12 @@ function extractAssistantToolSummary(message: SessionMessageV1): string {
189
194
  * 解析单条 session 消息的用户可见预览文本。
190
195
  */
191
196
  export function resolveSessionMessagePreview(message: SessionMessageV1): string {
197
+ if (isSessionActionMessage(message)) {
198
+ return message.description
199
+ ? `${message.title}\n${message.description}`
200
+ : message.title;
201
+ }
202
+ if (!isSessionModelMessage(message)) return "";
192
203
  const plainText = extractMessageText(message.parts);
193
204
  if (plainText) return plainText;
194
205
  if (message.role !== "assistant") return "";
@@ -226,7 +237,7 @@ function extractToolResultOutput(part: ToolPartCompatShape): unknown {
226
237
  }
227
238
 
228
239
  function toTimelineEvent(params: {
229
- message: SessionMessageV1;
240
+ message: SessionModelMessageV1;
230
241
  role: AgentSessionTimelineEvent["role"];
231
242
  text: string;
232
243
  sequence: number;
@@ -241,15 +252,21 @@ function toTimelineEvent(params: {
241
252
  ...(typeof metadata?.source === "string" ? { source: metadata.source } : {}),
242
253
  text: params.text,
243
254
  ...(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
- : {}),
255
+ };
256
+ }
257
+
258
+ function toActionTimelineEvent(
259
+ message: SessionActionMessageV1,
260
+ ): AgentSessionTimelineEvent {
261
+ const metadata = message.metadata || null;
262
+ return {
263
+ id: `${String(message.id || "")}:0`,
264
+ role: "action",
265
+ ...(typeof metadata?.ts === "number" ? { ts: metadata.ts } : {}),
266
+ text: resolveSessionMessagePreview(message),
267
+ actionTitle: message.title,
268
+ ...(message.description ? { actionDescription: message.description } : {}),
269
+ actionState: message.state,
253
270
  };
254
271
  }
255
272
 
@@ -259,20 +276,11 @@ function toTimelineEvent(params: {
259
276
  export function toSessionTimelineEvents(
260
277
  message: SessionMessageV1,
261
278
  ): 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
- ];
279
+ if (isSessionActionMessage(message)) {
280
+ return [toActionTimelineEvent(message)];
274
281
  }
275
282
 
283
+ if (!isSessionModelMessage(message)) return [];
276
284
  if (message.role !== "assistant") {
277
285
  return [
278
286
  toTimelineEvent({
@@ -367,7 +375,14 @@ export async function loadSessionMessagesFromPath(
367
375
  try {
368
376
  const parsed = JSON.parse(line) as SessionMessageV1;
369
377
  if (!parsed || typeof parsed !== "object") continue;
370
- if (parsed.role !== "user" && parsed.role !== "assistant") continue;
378
+ const candidate = parsed as { type?: unknown; role?: unknown };
379
+ if (
380
+ candidate.type !== "action" &&
381
+ candidate.role !== "user" &&
382
+ candidate.role !== "assistant"
383
+ ) {
384
+ continue;
385
+ }
371
386
  messages.push(parsed);
372
387
  } catch {
373
388
  // 关键点(中文):单行损坏不影响整个 session 的可读性。
@@ -380,8 +395,7 @@ export async function loadSessionMessagesFromPath(
380
395
  try {
381
396
  const parsed = (await fs.readJson(inflight_path)) as SessionMessageV1;
382
397
  if (
383
- parsed &&
384
- typeof parsed === "object" &&
398
+ isSessionModelMessage(parsed) &&
385
399
  parsed.role === "assistant" &&
386
400
  Array.isArray(parsed.parts)
387
401
  ) {
@@ -410,10 +424,14 @@ export async function loadSessionArchiveMessagesFromPath(
410
424
  const items = Array.isArray(raw?.messages) ? raw.messages : [];
411
425
  return items.filter((item): item is SessionMessageV1 => {
412
426
  if (!item || typeof item !== "object") return false;
413
- const candidate = item as Partial<SessionMessageV1>;
427
+ const candidate = item as { type?: unknown; role?: unknown; parts?: unknown };
414
428
  return (
415
- (candidate.role === "user" || candidate.role === "assistant") &&
416
- Array.isArray(candidate.parts)
429
+ (
430
+ candidate.type === "action" ||
431
+ candidate.role === "user" ||
432
+ candidate.role === "assistant"
433
+ ) &&
434
+ (candidate.type === "action" || Array.isArray(candidate.parts))
417
435
  );
418
436
  });
419
437
  } catch {
@@ -422,6 +440,7 @@ export async function loadSessionArchiveMessagesFromPath(
422
440
  }
423
441
 
424
442
  function isCompactSummaryMessage(message: SessionMessageV1): boolean {
443
+ if (!isSessionModelMessage(message)) return false;
425
444
  const metadata = (message.metadata || null) as SessionMetadataV1 | null;
426
445
  return metadata?.source === "compact" || metadata?.kind === "summary";
427
446
  }
@@ -9,7 +9,10 @@
9
9
 
10
10
  import { nanoid } from "nanoid";
11
11
  import type { SessionUserMessageV1 } from "@/executor/types/SessionMessages.js";
12
- import type { SessionMessageV1 } from "@/executor/types/SessionMessages.js";
12
+ import type {
13
+ SessionMessageV1,
14
+ SessionModelMessageV1,
15
+ } from "@/executor/types/SessionMessages.js";
13
16
  import type { AgentSessionEvent } from "@/types/sdk/AgentSessionEvent.js";
14
17
  import type { AgentSessionPromptInput } from "@/types/sdk/AgentSessionPrompt.js";
15
18
  import type { AgentSessionStopResult } from "@/types/sdk/AgentSessionStop.js";
@@ -106,7 +109,7 @@ export interface SessionPromptRuntimeOptions {
106
109
  }) => Promise<{
107
110
  text: string;
108
111
  success: boolean;
109
- assistantMessage?: SessionMessageV1 | null;
112
+ assistantMessage?: SessionModelMessageV1 | null;
110
113
  error?: string;
111
114
  }>;
112
115
 
@@ -346,7 +349,7 @@ export class SessionPromptRuntime {
346
349
  function buildPromptRuntimeErrorAssistantMessage(input: {
347
350
  sessionId: string;
348
351
  message: string;
349
- }): SessionMessageV1 {
352
+ }): SessionModelMessageV1 {
350
353
  return {
351
354
  id: `a:${input.sessionId}:${Date.now()}:${nanoid(6)}`,
352
355
  role: "assistant",
@@ -31,12 +31,13 @@ import type {
31
31
  AgentSessionEvent,
32
32
  } from "@/types/sdk/AgentSessionEvent.js";
33
33
  import type {
34
- AgentSessionOperationEvent,
35
- AgentSessionOperationRecord,
36
- } from "@/types/sdk/AgentSessionOperation.js";
34
+ AgentSessionActionEvent,
35
+ AgentSessionActionRecord,
36
+ } from "@/types/sdk/AgentSessionAction.js";
37
37
  import type { AgentSessionPromptInput } from "@/types/sdk/AgentSessionPrompt.js";
38
38
  import type {
39
39
  SessionMessageV1,
40
+ SessionModelMessageV1,
40
41
  SessionUserMessageV1,
41
42
  } from "@/executor/types/SessionMessages.js";
42
43
  import type { SessionLocalState } from "@/types/session/SessionLocalState.js";
@@ -86,17 +87,17 @@ type SessionStateServiceOptions = {
86
87
 
87
88
  type SessionSetOptions = {
88
89
  /**
89
- * 是否写入并发布 model-switching operation
90
+ * 是否写入并发布 model-switching action
90
91
  *
91
92
  * 说明(中文)
92
93
  * - 公开 `session.set()` 应保持默认开启。
93
94
  * - fork 内部复制模型配置时关闭,避免污染 forked session 历史。
94
95
  */
95
- emit_operation?: boolean;
96
+ emit_action?: boolean;
96
97
  };
97
98
 
98
- type EmitOperationInput = Omit<AgentSessionOperationRecord, "operationId"> &
99
- Partial<Pick<AgentSessionOperationRecord, "operationId">>;
99
+ type EmitActionInput = Omit<AgentSessionActionRecord, "id"> &
100
+ Partial<Pick<AgentSessionActionRecord, "id">>;
100
101
 
101
102
  /**
102
103
  * 本地 Session 状态与持久化服务。
@@ -222,23 +223,21 @@ export class SessionStateService {
222
223
  * 写入当前 session 配置。
223
224
  */
224
225
  async set(input: AgentSessionSetInput, options?: SessionSetOptions): Promise<void> {
225
- const should_emit_operation = options?.emit_operation !== false;
226
+ const should_emit_action = options?.emit_action !== false;
226
227
  const previous_model_label = this.state.sessionConfig.modelLabel;
227
228
  const next_model_label = input.model
228
229
  ? inferAgentModelLabel(input.model)
229
230
  : 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
- },
231
+ const action_id = `model-switching:${this.session_id}:${Date.now()}:${generateId()}`;
232
+
233
+ if (input.model && should_emit_action) {
234
+ await this.emit_action_event({
235
+ id: action_id,
236
+ title: "Switching session model",
237
+ description: next_model_label
238
+ ? `Switching to ${next_model_label}.`
239
+ : undefined,
240
+ state: "running",
242
241
  });
243
242
  }
244
243
 
@@ -255,30 +254,25 @@ export class SessionStateService {
255
254
  model: this.state.sessionConfig.model,
256
255
  });
257
256
  } 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),
257
+ if (input.model && should_emit_action) {
258
+ await this.emit_action_event({
259
+ id: action_id,
260
+ title: "Session model switch failed",
261
+ description: error instanceof Error ? error.message : String(error),
262
+ state: "failed",
265
263
  });
266
264
  }
267
265
  throw error;
268
266
  }
269
267
 
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
- },
268
+ if (input.model && should_emit_action) {
269
+ await this.emit_action_event({
270
+ id: action_id,
271
+ title: "Session model switched",
272
+ description: this.state.sessionConfig.modelLabel
273
+ ? `Using ${this.state.sessionConfig.modelLabel}.`
274
+ : undefined,
275
+ state: "completed",
282
276
  });
283
277
  }
284
278
  }
@@ -355,7 +349,7 @@ export class SessionStateService {
355
349
  * 持久化最终 assistant 结果。
356
350
  */
357
351
  async persist_assistant_result(
358
- assistant_message?: SessionMessageV1 | null,
352
+ assistant_message?: SessionModelMessageV1 | null,
359
353
  ): Promise<void> {
360
354
  await persistSdkAssistantResult({
361
355
  projectRoot: this.project_root,
@@ -368,23 +362,18 @@ export class SessionStateService {
368
362
  }
369
363
 
370
364
  /**
371
- * 持久化一条 operation message。
365
+ * 持久化一条 action message。
372
366
  */
373
- async persist_operation_event(
374
- event: AgentSessionOperationEvent,
367
+ async persist_action_event(
368
+ event: AgentSessionActionEvent,
375
369
  ): Promise<void> {
376
- const message = this.history_store.operation({
377
- operation: {
378
- operationId: event.operationId,
379
- name: event.name,
380
- status: event.status,
370
+ const message = this.history_store.action({
371
+ action: {
372
+ id: event.id,
373
+ title: event.title,
374
+ state: event.state,
375
+ ...(event.description ? { description: event.description } : {}),
381
376
  ...(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
377
  },
389
378
  metadata: {
390
379
  sessionId: this.session_id,
@@ -395,22 +384,24 @@ export class SessionStateService {
395
384
  }
396
385
 
397
386
  /**
398
- * 写入并发布一条 operation
387
+ * 写入并发布一条 action
399
388
  */
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",
389
+ async emit_action_event(input: EmitActionInput): Promise<void> {
390
+ if (input.visible === false) return;
391
+ const action_id =
392
+ String(input.id || "").trim() ||
393
+ `action:${this.session_id}:${Date.now()}:${generateId()}`;
394
+ const { visible: _visible, ...public_input } = input;
395
+ const event: AgentSessionActionEvent = {
396
+ type: "action",
406
397
  sessionId: this.session_id,
407
- ...input,
408
- operationId: operation_id,
398
+ ...public_input,
399
+ id: action_id,
409
400
  };
410
401
  try {
411
- await this.persist_operation_event(event);
402
+ await this.persist_action_event(event);
412
403
  } catch {
413
- // operation 持久化失败不应阻断宿主操作。
404
+ // action 持久化失败不应阻断宿主操作。
414
405
  }
415
406
  this.publish_event(event);
416
407
  }
@@ -20,6 +20,7 @@ import type { AgentSessionStopResult } from "@/types/sdk/AgentSessionStop.js";
20
20
  import type { AgentSessionTurnHandle } from "@/types/sdk/AgentSessionTurn.js";
21
21
  import type {
22
22
  SessionMessageV1,
23
+ SessionModelMessageV1,
23
24
  SessionUserMessageV1,
24
25
  } from "@/executor/types/SessionMessages.js";
25
26
  import { mapAgentEventToSessionEvent, mapUiMessageChunkToAgentEvent } from "@/session/SessionEventMapper.js";
@@ -144,7 +145,7 @@ export class SessionTurnService {
144
145
  }): Promise<{
145
146
  text: string;
146
147
  success: boolean;
147
- assistantMessage?: SessionMessageV1 | null;
148
+ assistantMessage?: SessionModelMessageV1 | null;
148
149
  error?: string;
149
150
  }> {
150
151
  const tool_name_by_call_id = new Map<string, string>();
@@ -198,11 +199,11 @@ export class SessionTurnService {
198
199
  this.publish_event(session_event);
199
200
  }
200
201
  },
201
- onOperationCallback: async (event) => {
202
+ onActionCallback: async (event) => {
202
203
  try {
203
- await this.state_service.persist_operation_event(event);
204
+ await this.state_service.persist_action_event(event);
204
205
  } catch {
205
- // operation 持久化失败不应阻断当前 turn。
206
+ // action 持久化失败不应阻断当前 turn。
206
207
  }
207
208
  this.publish_event(event);
208
209
  },