@downcity/agent 1.1.222 → 1.1.224

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 (166) hide show
  1. package/bin/agent/local/Agent.d.ts +1 -34
  2. package/bin/agent/local/Agent.d.ts.map +1 -1
  3. package/bin/agent/local/Agent.js +1 -44
  4. package/bin/agent/local/Agent.js.map +1 -1
  5. package/bin/agent/local/services/AgentAssemblyService.d.ts.map +1 -1
  6. package/bin/agent/local/services/AgentAssemblyService.js +35 -2
  7. package/bin/agent/local/services/AgentAssemblyService.js.map +1 -1
  8. package/bin/agent/local/services/AgentSessions.d.ts +6 -0
  9. package/bin/agent/local/services/AgentSessions.d.ts.map +1 -1
  10. package/bin/agent/local/services/AgentSessions.js +42 -0
  11. package/bin/agent/local/services/AgentSessions.js.map +1 -1
  12. package/bin/agent/remote/RemoteAgent.d.ts +1 -35
  13. package/bin/agent/remote/RemoteAgent.d.ts.map +1 -1
  14. package/bin/agent/remote/RemoteAgent.js +1 -37
  15. package/bin/agent/remote/RemoteAgent.js.map +1 -1
  16. package/bin/agent/remote/RemoteSession.d.ts +24 -45
  17. package/bin/agent/remote/RemoteSession.d.ts.map +1 -1
  18. package/bin/agent/remote/RemoteSession.js +74 -121
  19. package/bin/agent/remote/RemoteSession.js.map +1 -1
  20. package/bin/agent/remote/RemoteTransport.d.ts +11 -27
  21. package/bin/agent/remote/RemoteTransport.d.ts.map +1 -1
  22. package/bin/agent/remote/transports/HttpRemoteAgentTransport.d.ts +7 -21
  23. package/bin/agent/remote/transports/HttpRemoteAgentTransport.d.ts.map +1 -1
  24. package/bin/agent/remote/transports/HttpRemoteAgentTransport.js +11 -47
  25. package/bin/agent/remote/transports/HttpRemoteAgentTransport.js.map +1 -1
  26. package/bin/agent/remote/transports/RpcRemoteAgentTransport.d.ts +7 -20
  27. package/bin/agent/remote/transports/RpcRemoteAgentTransport.d.ts.map +1 -1
  28. package/bin/agent/remote/transports/RpcRemoteAgentTransport.js +8 -17
  29. package/bin/agent/remote/transports/RpcRemoteAgentTransport.js.map +1 -1
  30. package/bin/executor/store/history/jsonl/JsonlSessionHistoryStore.js +2 -2
  31. package/bin/executor/store/history/jsonl/JsonlSessionHistoryStore.js.map +1 -1
  32. package/bin/executor/types/SessionHistoryPaths.d.ts +1 -1
  33. package/bin/index.d.ts +3 -3
  34. package/bin/index.d.ts.map +1 -1
  35. package/bin/index.js +1 -1
  36. package/bin/index.js.map +1 -1
  37. package/bin/rpc/Client.d.ts +7 -36
  38. package/bin/rpc/Client.d.ts.map +1 -1
  39. package/bin/rpc/Client.js +28 -78
  40. package/bin/rpc/Client.js.map +1 -1
  41. package/bin/session/Session.d.ts +16 -8
  42. package/bin/session/Session.d.ts.map +1 -1
  43. package/bin/session/Session.js +66 -21
  44. package/bin/session/Session.js.map +1 -1
  45. package/bin/session/browse/Browse.d.ts.map +1 -1
  46. package/bin/session/browse/Browse.js +49 -18
  47. package/bin/session/browse/Browse.js.map +1 -1
  48. package/bin/session/recorder/JsonlSessionMessageStore.d.ts +30 -24
  49. package/bin/session/recorder/JsonlSessionMessageStore.d.ts.map +1 -1
  50. package/bin/session/recorder/JsonlSessionMessageStore.js +163 -59
  51. package/bin/session/recorder/JsonlSessionMessageStore.js.map +1 -1
  52. package/bin/session/recorder/SessionMessageCodec.d.ts.map +1 -1
  53. package/bin/session/recorder/SessionMessageCodec.js +6 -1
  54. package/bin/session/recorder/SessionMessageCodec.js.map +1 -1
  55. package/bin/session/recorder/SessionRecorder.d.ts +20 -11
  56. package/bin/session/recorder/SessionRecorder.d.ts.map +1 -1
  57. package/bin/session/recorder/SessionRecorder.js +148 -103
  58. package/bin/session/recorder/SessionRecorder.js.map +1 -1
  59. package/bin/session/recorder/SessionRecorderCompaction.d.ts.map +1 -1
  60. package/bin/session/recorder/SessionRecorderCompaction.js +1 -0
  61. package/bin/session/recorder/SessionRecorderCompaction.js.map +1 -1
  62. package/bin/session/recorder/SessionRecorderHistoryStore.d.ts +5 -5
  63. package/bin/session/recorder/SessionRecorderHistoryStore.d.ts.map +1 -1
  64. package/bin/session/recorder/SessionRecorderHistoryStore.js +5 -5
  65. package/bin/session/recorder/SessionRecorderHistoryStore.js.map +1 -1
  66. package/bin/session/runtime/SessionEventHub.d.ts +12 -5
  67. package/bin/session/runtime/SessionEventHub.d.ts.map +1 -1
  68. package/bin/session/runtime/SessionEventHub.js +11 -6
  69. package/bin/session/runtime/SessionEventHub.js.map +1 -1
  70. package/bin/session/runtime/SessionPromptRuntime.d.ts +2 -2
  71. package/bin/session/runtime/SessionPromptRuntime.d.ts.map +1 -1
  72. package/bin/session/runtime/SessionPromptRuntime.js +35 -17
  73. package/bin/session/runtime/SessionPromptRuntime.js.map +1 -1
  74. package/bin/session/services/SessionStateService.d.ts +2 -2
  75. package/bin/session/services/SessionStateService.d.ts.map +1 -1
  76. package/bin/session/services/SessionStateService.js +6 -2
  77. package/bin/session/services/SessionStateService.js.map +1 -1
  78. package/bin/session/services/SessionTurnService.d.ts +2 -7
  79. package/bin/session/services/SessionTurnService.d.ts.map +1 -1
  80. package/bin/session/services/SessionTurnService.js +2 -8
  81. package/bin/session/services/SessionTurnService.js.map +1 -1
  82. package/bin/session/storage/Paths.d.ts +1 -1
  83. package/bin/session/storage/Paths.d.ts.map +1 -1
  84. package/bin/session/storage/Paths.js +2 -2
  85. package/bin/session/storage/Paths.js.map +1 -1
  86. package/bin/session/storage/RuntimeSessionPort.d.ts +3 -4
  87. package/bin/session/storage/RuntimeSessionPort.d.ts.map +1 -1
  88. package/bin/session/storage/RuntimeSessionPort.js +2 -2
  89. package/bin/session/storage/RuntimeSessionPort.js.map +1 -1
  90. package/bin/types/agent/SessionActor.d.ts +11 -7
  91. package/bin/types/agent/SessionActor.d.ts.map +1 -1
  92. package/bin/types/rpc/RpcProtocol.d.ts +35 -62
  93. package/bin/types/rpc/RpcProtocol.d.ts.map +1 -1
  94. package/bin/types/runtime/agent/AgentContext.d.ts +4 -4
  95. package/bin/types/runtime/agent/AgentContext.d.ts.map +1 -1
  96. package/bin/types/runtime/agent/AgentContext.js.map +1 -1
  97. package/bin/types/session/SessionApproval.d.ts +97 -0
  98. package/bin/types/session/SessionApproval.d.ts.map +1 -0
  99. package/bin/types/session/SessionApproval.js +7 -0
  100. package/bin/types/session/SessionApproval.js.map +1 -0
  101. package/bin/types/session/SessionMessage.d.ts +10 -4
  102. package/bin/types/session/SessionMessage.d.ts.map +1 -1
  103. package/bin/types/session/SessionMutation.d.ts +116 -0
  104. package/bin/types/session/SessionMutation.d.ts.map +1 -0
  105. package/bin/types/session/SessionMutation.js +19 -0
  106. package/bin/types/session/SessionMutation.js.map +1 -0
  107. package/bin/types/session/SessionOptions.d.ts +9 -0
  108. package/bin/types/session/SessionOptions.d.ts.map +1 -1
  109. package/package.json +3 -3
  110. package/scripts/remote-session-reconnect.test.mjs +7 -3
  111. package/scripts/session-history-inflight.test.mjs +2 -2
  112. package/scripts/session-prompt-runtime.test.mjs +3 -3
  113. package/scripts/session-recorder.test.mjs +100 -146
  114. package/src/agent/local/Agent.ts +1 -55
  115. package/src/agent/local/services/AgentAssemblyService.ts +39 -3
  116. package/src/agent/local/services/AgentSessions.ts +47 -0
  117. package/src/agent/remote/RemoteAgent.ts +1 -54
  118. package/src/agent/remote/RemoteSession.ts +105 -163
  119. package/src/agent/remote/RemoteTransport.ts +23 -34
  120. package/src/agent/remote/transports/HttpRemoteAgentTransport.ts +33 -90
  121. package/src/agent/remote/transports/RpcRemoteAgentTransport.ts +22 -39
  122. package/src/executor/store/history/jsonl/JsonlSessionHistoryStore.ts +2 -2
  123. package/src/executor/types/SessionHistoryPaths.ts +1 -1
  124. package/src/index.ts +20 -12
  125. package/src/rpc/Client.ts +48 -106
  126. package/src/session/Session.ts +91 -34
  127. package/src/session/browse/Browse.ts +51 -22
  128. package/src/session/recorder/JsonlSessionMessageStore.ts +170 -86
  129. package/src/session/recorder/SessionMessageCodec.ts +6 -1
  130. package/src/session/recorder/SessionRecorder.ts +183 -124
  131. package/src/session/recorder/SessionRecorderCompaction.ts +1 -0
  132. package/src/session/recorder/SessionRecorderHistoryStore.ts +5 -5
  133. package/src/session/runtime/SessionEventHub.ts +25 -12
  134. package/src/session/runtime/SessionPromptRuntime.ts +37 -19
  135. package/src/session/services/SessionStateService.ts +8 -6
  136. package/src/session/services/SessionTurnService.ts +3 -14
  137. package/src/session/storage/Paths.ts +2 -2
  138. package/src/session/storage/RuntimeSessionPort.ts +8 -11
  139. package/src/types/agent/SessionActor.ts +23 -20
  140. package/src/types/rpc/RpcProtocol.ts +34 -71
  141. package/src/types/runtime/agent/AgentContext.ts +7 -11
  142. package/src/types/session/SessionApproval.ts +108 -0
  143. package/src/types/session/SessionMessage.ts +10 -4
  144. package/src/types/session/SessionMutation.ts +141 -0
  145. package/src/types/session/SessionOptions.ts +25 -0
  146. package/tsconfig.tsbuildinfo +1 -1
  147. package/bin/session/recorder/SessionMessageReducer.d.ts +0 -12
  148. package/bin/session/recorder/SessionMessageReducer.d.ts.map +0 -1
  149. package/bin/session/recorder/SessionMessageReducer.js +0 -109
  150. package/bin/session/recorder/SessionMessageReducer.js.map +0 -1
  151. package/bin/session/runtime/SessionMessageEventHub.d.ts +0 -15
  152. package/bin/session/runtime/SessionMessageEventHub.d.ts.map +0 -1
  153. package/bin/session/runtime/SessionMessageEventHub.js +0 -28
  154. package/bin/session/runtime/SessionMessageEventHub.js.map +0 -1
  155. package/bin/types/sdk/AgentSessionEvent.d.ts +0 -303
  156. package/bin/types/sdk/AgentSessionEvent.d.ts.map +0 -1
  157. package/bin/types/sdk/AgentSessionEvent.js +0 -9
  158. package/bin/types/sdk/AgentSessionEvent.js.map +0 -1
  159. package/bin/types/session/SessionMessageMutation.d.ts +0 -92
  160. package/bin/types/session/SessionMessageMutation.d.ts.map +0 -1
  161. package/bin/types/session/SessionMessageMutation.js +0 -21
  162. package/bin/types/session/SessionMessageMutation.js.map +0 -1
  163. package/src/session/recorder/SessionMessageReducer.ts +0 -155
  164. package/src/session/runtime/SessionMessageEventHub.ts +0 -35
  165. package/src/types/sdk/AgentSessionEvent.ts +0 -372
  166. package/src/types/session/SessionMessageMutation.ts +0 -121
@@ -1,9 +1,7 @@
1
1
  /**
2
- * RemoteSession:远程 session actor 客户端。
2
+ * RemoteSession:统一 SessionMutation 协议的远程 Session 客户端。
3
3
  *
4
- * 关键点(中文)
5
- * - 这里只负责 session 级 API、turn lifecycle 与事件泵。
6
- * - 具体 HTTP / RPC 细节由 RemoteSessionTransport 提供。
4
+ * 事件泵只传输一种 Mutation;审批查询、模式和决策全部绑定当前 Session。
7
5
  */
8
6
 
9
7
  import type {
@@ -14,29 +12,29 @@ import type {
14
12
  AgentSessionSystemSnapshot,
15
13
  } from "@/types/agent/SessionTypes.js";
16
14
  import type { RemoteAgentSession } from "@/types/agent/SessionActor.js";
17
- import { isAgentSessionPromptInputEmpty } from "@/types/sdk/AgentSessionPrompt.js";
18
- import type { AgentSessionStopResult } from "@/types/sdk/AgentSessionStop.js";
19
15
  import type {
20
- AgentSessionEvent,
21
- AgentSessionSubscriber,
22
- AgentSessionUnsubscribe,
23
- } from "@/types/sdk/AgentSessionEvent.js";
24
- import type { AgentSessionPromptInput } from "@/types/sdk/AgentSessionPrompt.js";
16
+ ResolveSessionApprovalInput,
17
+ SessionApproval,
18
+ SessionApprovalModeSnapshot,
19
+ SessionApprovalResult,
20
+ SetSessionApprovalModeInput,
21
+ } from "@/types/session/SessionApproval.js";
25
22
  import type {
26
- AgentSessionTurnHandle,
27
- AgentSessionTurnResult,
28
- } from "@/types/sdk/AgentSessionTurn.js";
23
+ SessionMutation,
24
+ SessionMutationSubscriber,
25
+ SessionMutationUnsubscribe,
26
+ } from "@/types/session/SessionMutation.js";
29
27
  import type {
30
28
  ListSessionMessagesInput,
31
29
  SessionMessagePage,
32
30
  } from "@/types/session/SessionMessage.js";
33
- import {
34
- is_session_message_mutation,
35
- type ListSessionMessageChangesInput,
36
- type SessionMessageMutationPage,
37
- type SessionMessageMutationSubscriber,
38
- type SessionMessageMutationUnsubscribe,
39
- } from "@/types/session/SessionMessageMutation.js";
31
+ import { isAgentSessionPromptInputEmpty } from "@/types/sdk/AgentSessionPrompt.js";
32
+ import type { AgentSessionPromptInput } from "@/types/sdk/AgentSessionPrompt.js";
33
+ import type { AgentSessionStopResult } from "@/types/sdk/AgentSessionStop.js";
34
+ import type {
35
+ AgentSessionTurnHandle,
36
+ AgentSessionTurnResult,
37
+ } from "@/types/sdk/AgentSessionTurn.js";
40
38
  import { SessionEventHub } from "@/session/runtime/SessionEventHub.js";
41
39
  import type {
42
40
  RemoteSessionTransport,
@@ -46,29 +44,27 @@ import type {
46
44
  type Deferred<T> = {
47
45
  /** 当前延迟 Promise。 */
48
46
  promise: Promise<T>;
49
- /** 兑现 Promise。 */
47
+ /** 兑现当前 Promise。 */
50
48
  resolve: (value: T) => void;
51
49
  };
52
50
 
53
51
  type RemoteTurnLifecycle = {
54
- /** 当前 turnId。 */
55
- turnId: string;
56
- /** 当前 turn 的最终结果快照。 */
52
+ /** 当前 Turn 标识。 */
53
+ turn_id: string;
54
+ /** 当前 Turn 的最终结果;运行中为 null。 */
57
55
  result: AgentSessionTurnResult | null;
58
- /** 当前 turn 完成 Promise 控制器。 */
56
+ /** 当前 Turn 完成 Promise 控制器。 */
59
57
  deferred_finished: Deferred<AgentSessionTurnResult>;
60
58
  };
61
59
 
62
- /**
63
- * 远程 Session 客户端。
64
- */
60
+ /** 远程 Session 客户端。 */
65
61
  export class RemoteSession implements RemoteAgentSession {
66
62
  readonly id: string;
67
63
  readonly agentId: string;
68
64
  readonly config: AgentSessionConfigSnapshot;
69
65
 
70
66
  private readonly transport: RemoteSessionTransport;
71
- private readonly event_hub = new SessionEventHub();
67
+ private readonly event_hub: SessionEventHub;
72
68
  private readonly turns_by_id = new Map<string, RemoteTurnLifecycle>();
73
69
  private readonly completed_turn_ids: string[] = [];
74
70
  private event_pump_connect_promise: Promise<void> | null = null;
@@ -80,72 +76,60 @@ export class RemoteSession implements RemoteAgentSession {
80
76
  this.transport = transport;
81
77
  this.id = info.sessionId;
82
78
  this.agentId = info.agentId;
83
- // 远程 session 不暴露服务端模型实例,只保留可序列化配置。
84
79
  this.config = {
85
80
  ...(info.modelId ? { modelId: info.modelId } : {}),
86
81
  ...(info.modelLabel ? { modelLabel: info.modelLabel } : {}),
87
82
  };
83
+ this.event_hub = new SessionEventHub({
84
+ create_reply: (mutation) => ({
85
+ approval: async ({ decision }) => {
86
+ return await this.resolve_approval({
87
+ approval_id: require_approval_id(mutation),
88
+ decision,
89
+ });
90
+ },
91
+ }),
92
+ });
88
93
  }
89
94
 
90
95
  /** 按稳定模型 ID 更新远程 Session 模型。 */
91
96
  async set(input: AgentSessionSetInput): Promise<void> {
92
- if (input.model) {
93
- throw new Error("Remote session.set does not accept a local model instance.");
94
- }
97
+ if (input.model) throw new Error("Remote session.set does not accept a local model instance.");
95
98
  const model_id = String(input.modelId || "").trim();
96
- if (!model_id) {
97
- throw new Error("Remote session.set requires modelId.");
98
- }
99
+ if (!model_id) throw new Error("Remote session.set requires modelId.");
99
100
  const info = await this.transport.set(this.id, { modelId: model_id });
100
101
  this.config.modelId = info.modelId;
101
102
  this.config.modelLabel = info.modelLabel;
102
103
  }
103
104
 
104
- /**
105
- * 读取当前远程 session 详情。
106
- */
105
+ /** 读取当前远程 Session 详情。 */
107
106
  async get_info(): Promise<AgentSessionInfo> {
108
107
  return await this.transport.get_info(this.id);
109
108
  }
110
109
 
111
- /**
112
- * 向当前远程 session 追加一条新的 prompt。
113
- */
110
+ /** 向当前远程 Session 追加 Prompt。 */
114
111
  async prompt(input: AgentSessionPromptInput): Promise<AgentSessionTurnHandle> {
115
112
  if (isAgentSessionPromptInputEmpty(input)) {
116
113
  throw new Error("remote session.prompt requires a non-empty query");
117
114
  }
118
-
119
115
  await this.ensure_event_pump();
120
116
  const turn = await this.transport.prompt(this.id, input);
121
- const lifecycle = this.ensure_turn_lifecycle(turn.id);
122
- return create_turn_handle(lifecycle);
117
+ return create_turn_handle(this.ensure_turn_lifecycle(turn.id));
123
118
  }
124
119
 
125
- /**
126
- * 停止当前远程 session turn,并取消未吸收队列。
127
- */
120
+ /** 停止当前远程 Session Turn。 */
128
121
  async stop(): Promise<AgentSessionStopResult> {
129
122
  await this.ensure_event_pump();
130
123
  return await this.transport.stop(this.id);
131
124
  }
132
125
 
133
- /**
134
- * 订阅当前远程 session 的 future 事件。
135
- */
136
- subscribe(
137
- subscriber: SessionMessageMutationSubscriber,
138
- ): SessionMessageMutationUnsubscribe {
126
+ /** 订阅当前 Session 的全部未来 Mutation。 */
127
+ subscribe(subscriber: SessionMutationSubscriber): SessionMutationUnsubscribe {
139
128
  this.event_subscriber_count += 1;
140
129
  void this.ensure_event_pump().catch((error) => {
141
- this.event_hub.publish({
142
- type: "error",
143
- message: error instanceof Error ? error.message : String(error),
144
- });
145
- });
146
- const unsubscribe = this.event_hub.subscribe((event) => {
147
- if (is_session_message_mutation(event)) subscriber(event);
130
+ this.fail_pending_turns(error instanceof Error ? error.message : String(error));
148
131
  });
132
+ const unsubscribe = this.event_hub.subscribe(subscriber);
149
133
  return () => {
150
134
  unsubscribe();
151
135
  this.event_subscriber_count = Math.max(0, this.event_subscriber_count - 1);
@@ -153,47 +137,44 @@ export class RemoteSession implements RemoteAgentSession {
153
137
  };
154
138
  }
155
139
 
156
- /** 订阅 transport 使用的 Message lifecycle 事件。 */
157
- subscribe_transport(subscriber: AgentSessionSubscriber): AgentSessionUnsubscribe {
158
- return this.event_hub.subscribe(subscriber);
159
- }
160
-
161
- /**
162
- * 读取远程消息历史。
163
- */
140
+ /** 读取远程 Message 快照。 */
164
141
  async messages(input?: ListSessionMessagesInput): Promise<SessionMessagePage> {
165
142
  return await this.transport.messages(this.id, input);
166
143
  }
167
144
 
168
- /** 读取远程增量 Message Mutation。 */
169
- async message_changes(
170
- input: ListSessionMessageChangesInput,
171
- ): Promise<SessionMessageMutationPage> {
172
- return await this.transport.message_changes(this.id, input);
173
- }
174
-
175
- /**
176
- * 读取远程 session 当前生效的 system prompt 快照。
177
- */
145
+ /** 读取远程 Session System 快照。 */
178
146
  async system(): Promise<AgentSessionSystemSnapshot> {
179
147
  return await this.transport.system(this.id);
180
148
  }
181
149
 
182
- /**
183
- * 分叉远程 session。
184
- */
150
+ /** 列出当前远程 Session 的 pending 工具审批。 */
151
+ async approvals(): Promise<SessionApproval[]> {
152
+ return await this.transport.approvals(this.id);
153
+ }
154
+
155
+ /** 读取当前远程 Session 的工具审批模式。 */
156
+ async approval_mode(): Promise<SessionApprovalModeSnapshot> {
157
+ return await this.transport.approval_mode(this.id);
158
+ }
159
+
160
+ /** 更新当前远程 Session 的工具审批模式。 */
161
+ async set_approval_mode(input: SetSessionApprovalModeInput): Promise<SessionApprovalModeSnapshot> {
162
+ return await this.transport.set_approval_mode(this.id, input);
163
+ }
164
+
165
+ /** 处理当前远程 Session 的 pending 工具审批。 */
166
+ async resolve_approval(input: ResolveSessionApprovalInput): Promise<SessionApprovalResult> {
167
+ return await this.transport.resolve_approval(this.id, input);
168
+ }
169
+
170
+ /** 从当前远程 Session 创建分支。 */
185
171
  async fork(input?: AgentSessionForkInput | string): Promise<RemoteAgentSession> {
186
- const info = await this.transport.fork(this.id, input);
187
- return new RemoteSession(this.transport, info);
172
+ return new RemoteSession(this.transport, await this.transport.fork(this.id, input));
188
173
  }
189
174
 
190
175
  private async ensure_event_pump(): Promise<void> {
191
- if (this.event_pump_connect_promise) {
192
- await this.event_pump_connect_promise;
193
- return;
194
- }
176
+ if (this.event_pump_connect_promise) return await this.event_pump_connect_promise;
195
177
  if (this.event_pump_running) return;
196
-
197
178
  this.event_pump_connect_promise = (async () => {
198
179
  let resolved_ready = false;
199
180
  this.event_subscription = await this.transport.subscribe({
@@ -201,19 +182,12 @@ export class RemoteSession implements RemoteAgentSession {
201
182
  on_ready: () => {
202
183
  resolved_ready = true;
203
184
  },
204
- on_event: (event) => {
205
- this.handle_event(event);
206
- },
207
- on_close: (error) => {
208
- this.handle_event_pump_closed(error);
209
- },
185
+ on_event: (mutation) => this.handle_mutation(mutation),
186
+ on_close: (error) => this.handle_event_pump_closed(error),
210
187
  });
211
188
  this.event_pump_running = true;
212
- if (!resolved_ready) {
213
- throw new Error("Remote session events connection closed before ready");
214
- }
189
+ if (!resolved_ready) throw new Error("Remote session events connection closed before ready");
215
190
  })();
216
-
217
191
  try {
218
192
  await this.event_pump_connect_promise;
219
193
  } finally {
@@ -221,53 +195,38 @@ export class RemoteSession implements RemoteAgentSession {
221
195
  }
222
196
  }
223
197
 
224
- private handle_event(event: AgentSessionEvent): void {
225
- if (!is_session_message_mutation(event) && event.type === "error") {
226
- this.fail_pending_turns(event.message);
227
- }
228
- const turn_id = extract_turn_id(event);
229
- if (turn_id) {
230
- this.ensure_turn_lifecycle(turn_id);
231
- }
232
-
233
- if (event.type === "turn-finish") {
234
- const lifecycle = this.ensure_turn_lifecycle(event.turnId);
198
+ private handle_mutation(mutation: SessionMutation): void {
199
+ const turn_id = "turn_id" in mutation ? mutation.turn_id : undefined;
200
+ if (turn_id) this.ensure_turn_lifecycle(turn_id);
201
+ if (mutation.variant === "turn" && mutation.type === "finish") {
202
+ const lifecycle = this.ensure_turn_lifecycle(mutation.turn_id);
235
203
  const result: AgentSessionTurnResult = {
236
- turnId: event.turnId,
237
- text: event.text,
238
- success: event.success,
239
- ...(event.error ? { error: event.error } : {}),
204
+ turnId: mutation.turn_id,
205
+ text: mutation.text || "",
206
+ success: mutation.status === "completed",
207
+ ...(mutation.error ? { error: mutation.error } : {}),
240
208
  };
241
209
  lifecycle.result = result;
242
210
  lifecycle.deferred_finished.resolve(result);
243
- this.remember_completed_turn(event.turnId);
211
+ this.remember_completed_turn(mutation.turn_id);
244
212
  void this.maybe_stop_event_pump();
245
213
  }
246
-
247
- this.event_hub.publish(event);
214
+ this.event_hub.publish(mutation);
248
215
  }
249
216
 
250
- /**
251
- * 收口底层事件连接断开后的运行态。
252
- *
253
- * 关键点(中文)
254
- * - 连接已经结束时必须清除 running 标记,后续 prompt 才会重新订阅。
255
- * - 当前未完成 turn 立即失败,避免调用侧永久等待 finished。
256
- */
257
217
  private handle_event_pump_closed(error?: unknown): void {
258
218
  this.event_subscription = null;
259
219
  this.event_pump_running = false;
260
- const message = error instanceof Error
261
- ? error.message
262
- : String(error || "Remote session events connection closed");
263
- this.fail_pending_turns(message);
220
+ this.fail_pending_turns(
221
+ error instanceof Error ? error.message : String(error || "Remote session events connection closed"),
222
+ );
264
223
  }
265
224
 
266
225
  private ensure_turn_lifecycle(turn_id: string): RemoteTurnLifecycle {
267
226
  const cached = this.turns_by_id.get(turn_id);
268
227
  if (cached) return cached;
269
228
  const created: RemoteTurnLifecycle = {
270
- turnId: turn_id,
229
+ turn_id,
271
230
  result: null,
272
231
  deferred_finished: create_deferred<AgentSessionTurnResult>(),
273
232
  };
@@ -279,14 +238,14 @@ export class RemoteSession implements RemoteAgentSession {
279
238
  for (const lifecycle of this.turns_by_id.values()) {
280
239
  if (lifecycle.result) continue;
281
240
  const result: AgentSessionTurnResult = {
282
- turnId: lifecycle.turnId,
241
+ turnId: lifecycle.turn_id,
283
242
  text: "",
284
243
  success: false,
285
244
  error: message,
286
245
  };
287
246
  lifecycle.result = result;
288
247
  lifecycle.deferred_finished.resolve(result);
289
- this.remember_completed_turn(lifecycle.turnId);
248
+ this.remember_completed_turn(lifecycle.turn_id);
290
249
  }
291
250
  }
292
251
 
@@ -294,9 +253,7 @@ export class RemoteSession implements RemoteAgentSession {
294
253
  this.completed_turn_ids.push(turn_id);
295
254
  while (this.completed_turn_ids.length > 200) {
296
255
  const oldest_turn_id = this.completed_turn_ids.shift();
297
- if (oldest_turn_id) {
298
- this.turns_by_id.delete(oldest_turn_id);
299
- }
256
+ if (oldest_turn_id) this.turns_by_id.delete(oldest_turn_id);
300
257
  }
301
258
  }
302
259
 
@@ -308,9 +265,7 @@ export class RemoteSession implements RemoteAgentSession {
308
265
  this.event_pump_running = false;
309
266
  if (!current) return;
310
267
  await current.close().catch((error) => {
311
- this.fail_pending_turns(
312
- error instanceof Error ? error.message : String(error),
313
- );
268
+ this.fail_pending_turns(error instanceof Error ? error.message : String(error));
314
269
  });
315
270
  }
316
271
  }
@@ -320,17 +275,12 @@ function create_deferred<T>(): Deferred<T> {
320
275
  const promise = new Promise<T>((inner_resolve) => {
321
276
  resolve = inner_resolve;
322
277
  });
323
- return {
324
- promise,
325
- resolve,
326
- };
278
+ return { promise, resolve };
327
279
  }
328
280
 
329
- function create_turn_handle(
330
- lifecycle: RemoteTurnLifecycle,
331
- ): AgentSessionTurnHandle {
281
+ function create_turn_handle(lifecycle: RemoteTurnLifecycle): AgentSessionTurnHandle {
332
282
  return {
333
- id: lifecycle.turnId,
283
+ id: lifecycle.turn_id,
334
284
  get result() {
335
285
  return lifecycle.result;
336
286
  },
@@ -338,22 +288,14 @@ function create_turn_handle(
338
288
  };
339
289
  }
340
290
 
341
- function extract_turn_id(event: AgentSessionEvent): string | null {
342
- if (is_session_message_mutation(event)) {
343
- return event.turn_id || null;
344
- }
345
- switch (event.type) {
346
- case "turn-start":
347
- case "text-delta":
348
- case "reasoning-delta":
349
- case "tool-call":
350
- case "tool-result":
351
- case "assistant-step":
352
- case "turn-finish":
353
- return event.turnId;
354
- case "action":
355
- return event.metadata.turnId || null;
356
- default:
357
- return null;
291
+ function require_approval_id(mutation: SessionMutation): string {
292
+ if (
293
+ mutation.variant === "part" &&
294
+ mutation.type === "tool" &&
295
+ mutation.part.state === "approval-required" &&
296
+ mutation.part.approval_id
297
+ ) {
298
+ return mutation.part.approval_id;
358
299
  }
300
+ throw new Error("Current Session Mutation is not an approval-required Tool Part");
359
301
  }
@@ -24,25 +24,20 @@ import type {
24
24
  RemoteAgentPluginActionInput,
25
25
  RemoteAgentPluginActionResult,
26
26
  } from "@/types/agent/RemoteAgentPluginAction.js";
27
- import type { AgentSessionEvent } from "@/types/sdk/AgentSessionEvent.js";
27
+ import type { SessionMutation } from "@/types/session/SessionMutation.js";
28
+ import type {
29
+ ResolveSessionApprovalInput,
30
+ SessionApproval,
31
+ SessionApprovalModeSnapshot,
32
+ SessionApprovalResult,
33
+ SetSessionApprovalModeInput,
34
+ } from "@/types/session/SessionApproval.js";
28
35
  import type { AgentSessionPromptInput } from "@/types/sdk/AgentSessionPrompt.js";
29
36
  import type { AgentSessionStopResult } from "@/types/sdk/AgentSessionStop.js";
30
37
  import type {
31
38
  ListSessionMessagesInput,
32
39
  SessionMessagePage,
33
40
  } from "@/types/session/SessionMessage.js";
34
- import type {
35
- ListSessionMessageChangesInput,
36
- SessionMessageMutationPage,
37
- } from "@/types/session/SessionMessageMutation.js";
38
- import type {
39
- ShellApprovalMode,
40
- ShellApprovalDecisionResult,
41
- ShellApprovalModeUpdateResult,
42
- ShellApprovalModeOption,
43
- ShellSessionApprovalModeView,
44
- ShellApprovalView,
45
- } from "@downcity/shell";
46
41
 
47
42
  /**
48
43
  * Transport 持有的事件订阅句柄。
@@ -71,7 +66,7 @@ export type RemoteSessionTransport = {
71
66
  subscribe(params: {
72
67
  session_id: string;
73
68
  on_ready: () => void;
74
- on_event: (event: AgentSessionEvent) => void;
69
+ on_event: (mutation: SessionMutation) => void;
75
70
  /** 底层事件连接结束后的通知;主动关闭不触发。 */
76
71
  on_close: (error?: unknown) => void;
77
72
  }): Promise<TransportSubscription>;
@@ -80,11 +75,6 @@ export type RemoteSessionTransport = {
80
75
  session_id: string,
81
76
  input?: ListSessionMessagesInput,
82
77
  ): Promise<SessionMessagePage>;
83
- /** 读取增量 Message Mutation。 */
84
- message_changes(
85
- session_id: string,
86
- input: ListSessionMessageChangesInput,
87
- ): Promise<SessionMessageMutationPage>;
88
78
  /** 读取 system snapshot。 */
89
79
  system(session_id: string): Promise<AgentSessionSystemSnapshot>;
90
80
  /** 分叉 session。 */
@@ -92,6 +82,20 @@ export type RemoteSessionTransport = {
92
82
  session_id: string,
93
83
  input?: AgentSessionForkInput | string,
94
84
  ): Promise<AgentSessionInfo>;
85
+ /** 列出指定 Session 的 pending 工具审批。 */
86
+ approvals(session_id: string): Promise<SessionApproval[]>;
87
+ /** 读取指定 Session 的工具审批模式。 */
88
+ approval_mode(session_id: string): Promise<SessionApprovalModeSnapshot>;
89
+ /** 更新指定 Session 的工具审批模式。 */
90
+ set_approval_mode(
91
+ session_id: string,
92
+ input: SetSessionApprovalModeInput,
93
+ ): Promise<SessionApprovalModeSnapshot>;
94
+ /** 处理指定 Session 的 pending 工具审批。 */
95
+ resolve_approval(
96
+ session_id: string,
97
+ input: ResolveSessionApprovalInput,
98
+ ): Promise<SessionApprovalResult>;
95
99
  };
96
100
 
97
101
  /**
@@ -112,21 +116,6 @@ export type RemoteAgentTransport = RemoteSessionTransport & {
112
116
  run_plugin_action(
113
117
  input: RemoteAgentPluginActionInput,
114
118
  ): Promise<RemoteAgentPluginActionResult>;
115
- /** 列出 shell approvals。 */
116
- approvals(): Promise<ShellApprovalView[]>;
117
- /** 列出 shell approval modes。 */
118
- approval_modes(): Promise<ShellApprovalModeOption[]>;
119
- /** 读取 shell approval mode。 */
120
- approval_mode(input: { session_id: string }): Promise<ShellSessionApprovalModeView>;
121
- /** 设置 shell approval mode。 */
122
- set_approval_mode(input: {
123
- session_id: string;
124
- mode: ShellApprovalMode;
125
- }): Promise<ShellApprovalModeUpdateResult>;
126
- /** 批准 shell approval。 */
127
- approve(input: { approval_id: string }): Promise<ShellApprovalDecisionResult>;
128
- /** 拒绝 shell approval。 */
129
- deny(input: { approval_id: string }): Promise<ShellApprovalDecisionResult>;
130
119
  /** 关闭 transport 持有的长期连接。 */
131
120
  close?(): Promise<void>;
132
121
  };