@downcity/agent 1.1.113 → 1.1.118

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 (137) hide show
  1. package/bin/agent/local/Agent.d.ts +22 -0
  2. package/bin/agent/local/Agent.d.ts.map +1 -1
  3. package/bin/agent/local/Agent.js +31 -0
  4. package/bin/agent/local/Agent.js.map +1 -1
  5. package/bin/agent/local/services/AgentAssemblyService.d.ts +4 -0
  6. package/bin/agent/local/services/AgentAssemblyService.d.ts.map +1 -1
  7. package/bin/agent/local/services/AgentAssemblyService.js +18 -2
  8. package/bin/agent/local/services/AgentAssemblyService.js.map +1 -1
  9. package/bin/agent/local/services/AgentLifecycleService.d.ts +6 -0
  10. package/bin/agent/local/services/AgentLifecycleService.d.ts.map +1 -1
  11. package/bin/agent/local/services/AgentLifecycleService.js +4 -0
  12. package/bin/agent/local/services/AgentLifecycleService.js.map +1 -1
  13. package/bin/agent/remote/RemoteAgent.d.ts +18 -1
  14. package/bin/agent/remote/RemoteAgent.d.ts.map +1 -1
  15. package/bin/agent/remote/RemoteAgent.js +19 -1
  16. package/bin/agent/remote/RemoteAgent.js.map +1 -1
  17. package/bin/agent/remote/RemoteTransport.d.ts +11 -0
  18. package/bin/agent/remote/RemoteTransport.d.ts.map +1 -1
  19. package/bin/agent/remote/transports/HttpRemoteAgentTransport.d.ts +9 -0
  20. package/bin/agent/remote/transports/HttpRemoteAgentTransport.d.ts.map +1 -1
  21. package/bin/agent/remote/transports/HttpRemoteAgentTransport.js +28 -0
  22. package/bin/agent/remote/transports/HttpRemoteAgentTransport.js.map +1 -1
  23. package/bin/agent/remote/transports/RpcRemoteAgentTransport.d.ts +8 -0
  24. package/bin/agent/remote/transports/RpcRemoteAgentTransport.d.ts.map +1 -1
  25. package/bin/agent/remote/transports/RpcRemoteAgentTransport.js +9 -0
  26. package/bin/agent/remote/transports/RpcRemoteAgentTransport.js.map +1 -1
  27. package/bin/executor/Executor.d.ts.map +1 -1
  28. package/bin/executor/Executor.js +7 -1
  29. package/bin/executor/Executor.js.map +1 -1
  30. package/bin/index.d.ts +0 -1
  31. package/bin/index.d.ts.map +1 -1
  32. package/bin/index.js +0 -1
  33. package/bin/index.js.map +1 -1
  34. package/bin/rpc/Client.d.ts +13 -0
  35. package/bin/rpc/Client.d.ts.map +1 -1
  36. package/bin/rpc/Client.js +31 -0
  37. package/bin/rpc/Client.js.map +1 -1
  38. package/bin/rpc/server/InternalHandlers.d.ts.map +1 -1
  39. package/bin/rpc/server/InternalHandlers.js +30 -0
  40. package/bin/rpc/server/InternalHandlers.js.map +1 -1
  41. package/bin/rpc/server/ServerTypes.d.ts +5 -0
  42. package/bin/rpc/server/ServerTypes.d.ts.map +1 -1
  43. package/bin/types/agent/AgentOptions.d.ts +9 -0
  44. package/bin/types/agent/AgentOptions.d.ts.map +1 -1
  45. package/bin/types/config/DowncityConfig.d.ts +2 -2
  46. package/bin/types/config/DowncityConfig.d.ts.map +1 -1
  47. package/bin/types/rpc/RpcProtocol.d.ts +25 -0
  48. package/bin/types/rpc/RpcProtocol.d.ts.map +1 -1
  49. package/package.json +4 -3
  50. package/scripts/linux-bubblewrap-sandbox.test.mjs +1 -1
  51. package/scripts/shell-sandbox-preflight.test.mjs +1 -1
  52. package/src/agent/local/Agent.ts +38 -0
  53. package/src/agent/local/services/AgentAssemblyService.ts +23 -2
  54. package/src/agent/local/services/AgentLifecycleService.ts +11 -0
  55. package/src/agent/remote/RemoteAgent.ts +26 -1
  56. package/src/agent/remote/RemoteTransport.ts +10 -0
  57. package/src/agent/remote/transports/HttpRemoteAgentTransport.ts +45 -0
  58. package/src/agent/remote/transports/RpcRemoteAgentTransport.ts +16 -0
  59. package/src/executor/Executor.ts +12 -2
  60. package/src/index.ts +0 -1
  61. package/src/rpc/Client.ts +38 -0
  62. package/src/rpc/server/InternalHandlers.ts +31 -0
  63. package/src/rpc/server/ServerTypes.ts +5 -0
  64. package/src/types/agent/AgentOptions.ts +10 -0
  65. package/src/types/config/DowncityConfig.ts +2 -2
  66. package/src/types/rpc/RpcProtocol.ts +28 -0
  67. package/tsconfig.tsbuildinfo +1 -1
  68. package/bin/executor/tools/shell/ShellToolBridge.d.ts +0 -79
  69. package/bin/executor/tools/shell/ShellToolBridge.d.ts.map +0 -1
  70. package/bin/executor/tools/shell/ShellToolBridge.js +0 -320
  71. package/bin/executor/tools/shell/ShellToolBridge.js.map +0 -1
  72. package/bin/executor/tools/shell/ShellToolDefinition.d.ts +0 -116
  73. package/bin/executor/tools/shell/ShellToolDefinition.d.ts.map +0 -1
  74. package/bin/executor/tools/shell/ShellToolDefinition.js +0 -369
  75. package/bin/executor/tools/shell/ShellToolDefinition.js.map +0 -1
  76. package/bin/executor/tools/shell/ShellToolFormatting.d.ts +0 -17
  77. package/bin/executor/tools/shell/ShellToolFormatting.d.ts.map +0 -1
  78. package/bin/executor/tools/shell/ShellToolFormatting.js +0 -36
  79. package/bin/executor/tools/shell/ShellToolFormatting.js.map +0 -1
  80. package/bin/executor/tools/shell/ShellToolSchemas.d.ts +0 -61
  81. package/bin/executor/tools/shell/ShellToolSchemas.d.ts.map +0 -1
  82. package/bin/executor/tools/shell/ShellToolSchemas.js +0 -130
  83. package/bin/executor/tools/shell/ShellToolSchemas.js.map +0 -1
  84. package/bin/executor/tools/shell/types/Shell.d.ts +0 -115
  85. package/bin/executor/tools/shell/types/Shell.d.ts.map +0 -1
  86. package/bin/executor/tools/shell/types/Shell.js +0 -9
  87. package/bin/executor/tools/shell/types/Shell.js.map +0 -1
  88. package/bin/executor/tools/shell/types/ShellPlugin.d.ts +0 -258
  89. package/bin/executor/tools/shell/types/ShellPlugin.d.ts.map +0 -1
  90. package/bin/executor/tools/shell/types/ShellPlugin.js +0 -9
  91. package/bin/executor/tools/shell/types/ShellPlugin.js.map +0 -1
  92. package/bin/sandbox/LinuxBubblewrapSandbox.d.ts +0 -19
  93. package/bin/sandbox/LinuxBubblewrapSandbox.d.ts.map +0 -1
  94. package/bin/sandbox/LinuxBubblewrapSandbox.js +0 -186
  95. package/bin/sandbox/LinuxBubblewrapSandbox.js.map +0 -1
  96. package/bin/sandbox/MacOsSeatbeltSandbox.d.ts +0 -16
  97. package/bin/sandbox/MacOsSeatbeltSandbox.d.ts.map +0 -1
  98. package/bin/sandbox/MacOsSeatbeltSandbox.js +0 -154
  99. package/bin/sandbox/MacOsSeatbeltSandbox.js.map +0 -1
  100. package/bin/sandbox/SandboxConfigResolver.d.ts +0 -37
  101. package/bin/sandbox/SandboxConfigResolver.d.ts.map +0 -1
  102. package/bin/sandbox/SandboxConfigResolver.js +0 -130
  103. package/bin/sandbox/SandboxConfigResolver.js.map +0 -1
  104. package/bin/sandbox/SandboxPreflight.d.ts +0 -73
  105. package/bin/sandbox/SandboxPreflight.d.ts.map +0 -1
  106. package/bin/sandbox/SandboxPreflight.js +0 -122
  107. package/bin/sandbox/SandboxPreflight.js.map +0 -1
  108. package/bin/sandbox/SandboxRunner.d.ts +0 -61
  109. package/bin/sandbox/SandboxRunner.d.ts.map +0 -1
  110. package/bin/sandbox/SandboxRunner.js +0 -107
  111. package/bin/sandbox/SandboxRunner.js.map +0 -1
  112. package/bin/sandbox/UnrestrictedSandbox.d.ts +0 -16
  113. package/bin/sandbox/UnrestrictedSandbox.d.ts.map +0 -1
  114. package/bin/sandbox/UnrestrictedSandbox.js +0 -39
  115. package/bin/sandbox/UnrestrictedSandbox.js.map +0 -1
  116. package/bin/sandbox/types/Sandbox.d.ts +0 -130
  117. package/bin/sandbox/types/Sandbox.d.ts.map +0 -1
  118. package/bin/sandbox/types/Sandbox.js +0 -10
  119. package/bin/sandbox/types/Sandbox.js.map +0 -1
  120. package/bin/sandbox/types/SandboxRuntime.d.ts +0 -370
  121. package/bin/sandbox/types/SandboxRuntime.d.ts.map +0 -1
  122. package/bin/sandbox/types/SandboxRuntime.js +0 -10
  123. package/bin/sandbox/types/SandboxRuntime.js.map +0 -1
  124. package/src/executor/tools/shell/ShellToolBridge.ts +0 -412
  125. package/src/executor/tools/shell/ShellToolDefinition.ts +0 -524
  126. package/src/executor/tools/shell/ShellToolFormatting.ts +0 -34
  127. package/src/executor/tools/shell/ShellToolSchemas.ts +0 -139
  128. package/src/executor/tools/shell/types/Shell.ts +0 -123
  129. package/src/executor/tools/shell/types/ShellPlugin.ts +0 -278
  130. package/src/sandbox/LinuxBubblewrapSandbox.ts +0 -222
  131. package/src/sandbox/MacOsSeatbeltSandbox.ts +0 -191
  132. package/src/sandbox/SandboxConfigResolver.ts +0 -152
  133. package/src/sandbox/SandboxPreflight.ts +0 -205
  134. package/src/sandbox/SandboxRunner.ts +0 -151
  135. package/src/sandbox/UnrestrictedSandbox.ts +0 -53
  136. package/src/sandbox/types/Sandbox.ts +0 -144
  137. package/src/sandbox/types/SandboxRuntime.ts +0 -440
@@ -24,6 +24,11 @@ import type {
24
24
  AgentStartResult,
25
25
  AgentStopResult,
26
26
  } from "@/types/agent/AgentTypes.js";
27
+ import type {
28
+ ShellApprovalDecisionResult,
29
+ ShellApprovalView,
30
+ Shell,
31
+ } from "@downcity/shell";
27
32
  import { PluginRegistry } from "@/plugin/core/PluginRegistry.js";
28
33
  import { Logger } from "@/utils/logger/Logger.js";
29
34
  import { normalizeInstructionInput } from "@/agent/local/AgentInstructions.js";
@@ -53,6 +58,7 @@ export class Agent {
53
58
  private readonly SessionClass: AgentOptions["Session"];
54
59
  private readonly sessionManager: AgentSessionManager;
55
60
  private readonly lifecycleService: AgentLifecycleService;
61
+ private readonly shell?: AgentOptions["shell"];
56
62
 
57
63
  private instruction: string[];
58
64
 
@@ -88,6 +94,7 @@ export class Agent {
88
94
  this.config = assembly.config;
89
95
  this.env = assembly.env;
90
96
  this.instruction = assembly.instruction;
97
+ this.shell = assembly.shell;
91
98
 
92
99
  this.sessionManager = this.create_session_manager(assembly);
93
100
  session_manager_ref = this.sessionManager;
@@ -96,6 +103,7 @@ export class Agent {
96
103
  agent_context: this.agentContext,
97
104
  session_collection: this.sessionManager.get_session_collection(),
98
105
  get_runtime: () => this.runtime,
106
+ get_shell: () => this.shell,
99
107
  });
100
108
  }
101
109
 
@@ -136,6 +144,29 @@ export class Agent {
136
144
  return await this.lifecycleService.stop();
137
145
  }
138
146
 
147
+ /**
148
+ * 列出当前 shell pending approvals。
149
+ */
150
+ approvals(): ShellApprovalView[] {
151
+ return this.shell?.approvals() || [];
152
+ }
153
+
154
+ /**
155
+ * 批准当前 shell pending approval。
156
+ */
157
+ async approve(input: { approval_id: string }): Promise<ShellApprovalDecisionResult> {
158
+ if (!this.shell) throw new Error("Agent shell is not configured");
159
+ return await this.shell.approve(input);
160
+ }
161
+
162
+ /**
163
+ * 拒绝当前 shell pending approval。
164
+ */
165
+ async deny(input: { approval_id: string }): Promise<ShellApprovalDecisionResult> {
166
+ if (!this.shell) throw new Error("Agent shell is not configured");
167
+ return await this.shell.deny(input);
168
+ }
169
+
139
170
  /**
140
171
  * 更新当前 SDK Agent 的静态基础指令。
141
172
  */
@@ -178,6 +209,13 @@ export class Agent {
178
209
  return this.sessionManager.get_session_collection();
179
210
  }
180
211
 
212
+ /**
213
+ * 返回当前 agent 挂载的 Shell。
214
+ */
215
+ getShell(): Shell | undefined {
216
+ return this.shell;
217
+ }
218
+
181
219
  private create_session_manager(
182
220
  assembly: AgentAssemblyResult,
183
221
  ): AgentSessionManager {
@@ -28,13 +28,13 @@ import {
28
28
  createAgentContext,
29
29
  createAgentRuntime,
30
30
  } from "@/agent/local/AgentRuntimeFactory.js";
31
- import { setShellToolRuntime } from "@executor/tools/shell/ShellToolDefinition.js";
32
31
  import {
33
32
  plugin_tools,
34
33
  setPluginToolRuntime,
35
34
  } from "@executor/tools/plugin/PluginToolDefinition.js";
36
35
  import type { AgentManagedSession } from "@/types/agent/AgentTypes.js";
37
36
  import type { SessionPort } from "@/types/runtime/agent/AgentContext.js";
37
+ import type { AgentSessionEvent } from "@/types/sdk/AgentSessionEvent.js";
38
38
 
39
39
  type AgentAssemblyServiceOptions = {
40
40
  /**
@@ -123,6 +123,11 @@ export interface AgentAssemblyResult {
123
123
  * 当前 agent context。
124
124
  */
125
125
  agent_context: AgentContext;
126
+
127
+ /**
128
+ * 当前 agent 挂载的内建 shell。
129
+ */
130
+ shell?: AgentOptions["shell"];
126
131
  }
127
132
 
128
133
  /**
@@ -201,7 +206,22 @@ export class AgentAssemblyService {
201
206
  resolve_session_model: async (session_id) =>
202
207
  await this.resolve_session_model(session_id),
203
208
  });
204
- setShellToolRuntime(agent_context.invoke);
209
+ const shell = this.options.shell;
210
+ if (shell) {
211
+ shell.configure({
212
+ root_path: path,
213
+ env,
214
+ agent_id: id,
215
+ sandbox: config.sandbox,
216
+ logger,
217
+ emit_event: (event) => {
218
+ const session_id = String(event.session_id || "").trim();
219
+ if (!session_id) return;
220
+ agent_context.session.get(session_id).publishEvent(event as unknown as AgentSessionEvent);
221
+ },
222
+ });
223
+ Object.assign(tools, shell.tools);
224
+ }
205
225
  setPluginToolRuntime(plugins);
206
226
 
207
227
  return {
@@ -217,6 +237,7 @@ export class AgentAssemblyService {
217
237
  plugins,
218
238
  runtime,
219
239
  agent_context,
240
+ ...(shell ? { shell } : {}),
220
241
  };
221
242
  }
222
243
 
@@ -22,6 +22,7 @@ import { startActionScheduleRuntime } from "@/plugin/core/ActionScheduleRuntime.
22
22
  import { startAllPlugins, stopAllPlugins } from "@/plugin/core/Manager.js";
23
23
  import { startRpcServer } from "@/rpc/Server.js";
24
24
  import type { AgentRuntime } from "@/types/runtime/agent/AgentRuntime.js";
25
+ import type { Shell } from "@downcity/shell";
25
26
 
26
27
  type AgentLifecycleServiceOptions = {
27
28
  /**
@@ -43,6 +44,11 @@ type AgentLifecycleServiceOptions = {
43
44
  * 读取当前 agent runtime。
44
45
  */
45
46
  get_runtime: () => AgentRuntime;
47
+
48
+ /**
49
+ * 读取当前 agent 挂载的 Shell。
50
+ */
51
+ get_shell?: () => Shell | undefined;
46
52
  };
47
53
 
48
54
  /**
@@ -53,6 +59,7 @@ export class AgentLifecycleService {
53
59
  private readonly agent_context: AgentContext;
54
60
  private readonly session_collection: AgentSessionCollection;
55
61
  private readonly get_runtime: AgentLifecycleServiceOptions["get_runtime"];
62
+ private readonly get_shell: AgentLifecycleServiceOptions["get_shell"];
56
63
 
57
64
  private plugins_started = false;
58
65
  private action_schedule_runtime: ActionScheduleRuntimeHandle | null = null;
@@ -64,6 +71,7 @@ export class AgentLifecycleService {
64
71
  this.agent_context = options.agent_context;
65
72
  this.session_collection = options.session_collection;
66
73
  this.get_runtime = options.get_runtime;
74
+ this.get_shell = options.get_shell;
67
75
  }
68
76
 
69
77
  /**
@@ -112,6 +120,8 @@ export class AgentLifecycleService {
112
120
  await this.stop_rpc();
113
121
  }
114
122
 
123
+ await this.get_shell?.()?.dispose();
124
+
115
125
  this.start_promise = null;
116
126
 
117
127
  return {
@@ -168,6 +178,7 @@ export class AgentLifecycleService {
168
178
  sessionCollection: this.session_collection,
169
179
  getAgentContext: () => this.agent_context,
170
180
  getAgentRuntime: () => this.get_runtime(),
181
+ getShell: () => this.get_shell?.(),
171
182
  });
172
183
  this.rpc_binding = {
173
184
  url: `rpc://${host}:${port}`,
@@ -16,6 +16,10 @@ import type {
16
16
  RemoteAgentOptions,
17
17
  RemoteAgentSession,
18
18
  } from "@/types/agent/AgentTypes.js";
19
+ import type {
20
+ ShellApprovalDecisionResult,
21
+ ShellApprovalView,
22
+ } from "@downcity/shell";
19
23
  import type { RemoteAgentTransport } from "@/agent/remote/RemoteTransport.js";
20
24
  import { RemoteSession } from "@/agent/remote/RemoteSession.js";
21
25
  import { create_remote_agent_transport } from "@/agent/remote/TransportFactory.js";
@@ -70,7 +74,7 @@ export class RemoteAgent {
70
74
  *
71
75
  * 关键点(中文)
72
76
  * - 这是 RemoteAgent 顶层能力,不绑定某个 session。
73
- * - 前端收到 `tool-approval-request` 后,可以用它调用 `shell.approve` / `shell.deny`。
77
+ * - Shell approval 请使用 `approvals()` / `approve()` / `deny()`。
74
78
  */
75
79
  async runPluginAction(
76
80
  input: RemoteAgentPluginActionInput,
@@ -90,6 +94,27 @@ export class RemoteAgent {
90
94
  });
91
95
  }
92
96
 
97
+ /**
98
+ * 列出远程 Agent 的 shell pending approvals。
99
+ */
100
+ async approvals(): Promise<ShellApprovalView[]> {
101
+ return await this.transport.approvals();
102
+ }
103
+
104
+ /**
105
+ * 批准远程 Agent 的 shell approval。
106
+ */
107
+ async approve(input: { approval_id: string }): Promise<ShellApprovalDecisionResult> {
108
+ return await this.transport.approve(input);
109
+ }
110
+
111
+ /**
112
+ * 拒绝远程 Agent 的 shell approval。
113
+ */
114
+ async deny(input: { approval_id: string }): Promise<ShellApprovalDecisionResult> {
115
+ return await this.transport.deny(input);
116
+ }
117
+
93
118
  /**
94
119
  * 关闭远程 transport。
95
120
  *
@@ -20,6 +20,10 @@ import type {
20
20
  } from "@/types/agent/AgentTypes.js";
21
21
  import type { AgentSessionEvent } from "@/types/sdk/AgentSessionEvent.js";
22
22
  import type { AgentSessionPromptInput } from "@/types/sdk/AgentSessionPrompt.js";
23
+ import type {
24
+ ShellApprovalDecisionResult,
25
+ ShellApprovalView,
26
+ } from "@downcity/shell";
23
27
 
24
28
  /**
25
29
  * Transport 持有的事件订阅句柄。
@@ -72,6 +76,12 @@ export type RemoteAgentTransport = RemoteSessionTransport & {
72
76
  run_plugin_action(
73
77
  input: RemoteAgentPluginActionInput,
74
78
  ): Promise<RemoteAgentPluginActionResult>;
79
+ /** 列出 shell approvals。 */
80
+ approvals(): Promise<ShellApprovalView[]>;
81
+ /** 批准 shell approval。 */
82
+ approve(input: { approval_id: string }): Promise<ShellApprovalDecisionResult>;
83
+ /** 拒绝 shell approval。 */
84
+ deny(input: { approval_id: string }): Promise<ShellApprovalDecisionResult>;
75
85
  /** 关闭 transport 持有的长期连接。 */
76
86
  close?(): Promise<void>;
77
87
  };
@@ -24,6 +24,10 @@ import type {
24
24
  RemoteAgentTransport,
25
25
  TransportSubscription,
26
26
  } from "@/agent/remote/RemoteTransport.js";
27
+ import type {
28
+ ShellApprovalDecisionResult,
29
+ ShellApprovalView,
30
+ } from "@downcity/shell";
27
31
 
28
32
  type SdkEventsReadyFrame = {
29
33
  /** SDK HTTP events 连接内部 ready 标记。 */
@@ -264,6 +268,47 @@ export class HttpRemoteAgentTransport implements RemoteAgentTransport {
264
268
  }
265
269
  return payload;
266
270
  }
271
+
272
+ async approvals(): Promise<ShellApprovalView[]> {
273
+ const payload = await read_http_json<{
274
+ success?: boolean;
275
+ error?: string;
276
+ approvals?: ShellApprovalView[];
277
+ }>(`${this.base_url}/api/shell/approvals`, {
278
+ headers: this.headers(),
279
+ });
280
+ if (!payload.success || !Array.isArray(payload.approvals)) {
281
+ throw new Error(String(payload.error || "Remote shell approvals failed"));
282
+ }
283
+ return payload.approvals;
284
+ }
285
+
286
+ async approve(input: { approval_id: string }): Promise<ShellApprovalDecisionResult> {
287
+ return await this.run_shell_decision("approve", input.approval_id);
288
+ }
289
+
290
+ async deny(input: { approval_id: string }): Promise<ShellApprovalDecisionResult> {
291
+ return await this.run_shell_decision("deny", input.approval_id);
292
+ }
293
+
294
+ private async run_shell_decision(
295
+ action: "approve" | "deny",
296
+ approval_id: string,
297
+ ): Promise<ShellApprovalDecisionResult> {
298
+ const payload = await read_http_json<ShellApprovalDecisionResult & {
299
+ error?: string;
300
+ }>(`${this.base_url}/api/shell/${action}`, {
301
+ method: "POST",
302
+ headers: this.headers({
303
+ "Content-Type": "application/json",
304
+ }),
305
+ body: JSON.stringify({ approval_id }),
306
+ });
307
+ if (typeof payload.success !== "boolean") {
308
+ throw new Error(String(payload.error || `Remote shell ${action} failed`));
309
+ }
310
+ return payload;
311
+ }
267
312
  }
268
313
 
269
314
  async function read_http_json<T>(input: string, init?: RequestInit): Promise<T> {
@@ -25,6 +25,10 @@ import type {
25
25
  RemoteAgentTransport,
26
26
  TransportSubscription,
27
27
  } from "@/agent/remote/RemoteTransport.js";
28
+ import type {
29
+ ShellApprovalDecisionResult,
30
+ ShellApprovalView,
31
+ } from "@downcity/shell";
28
32
 
29
33
  /**
30
34
  * 本机 RPC transport。
@@ -113,6 +117,18 @@ export class RpcRemoteAgentTransport implements RemoteAgentTransport {
113
117
  });
114
118
  }
115
119
 
120
+ async approvals(): Promise<ShellApprovalView[]> {
121
+ return await this.client.list_shell_approvals();
122
+ }
123
+
124
+ async approve(input: { approval_id: string }): Promise<ShellApprovalDecisionResult> {
125
+ return await this.client.approve_shell_approval(input.approval_id);
126
+ }
127
+
128
+ async deny(input: { approval_id: string }): Promise<ShellApprovalDecisionResult> {
129
+ return await this.client.deny_shell_approval(input.approval_id);
130
+ }
131
+
116
132
  async close(): Promise<void> {
117
133
  await this.client.close();
118
134
  }
@@ -8,6 +8,7 @@
8
8
  */
9
9
 
10
10
  import { streamText, type LanguageModel, type Tool } from "ai";
11
+ import { withShellRunScope } from "@downcity/shell";
11
12
  import { SessionHistoryWriter } from "@executor/composer/history/SessionHistoryWriter.js";
12
13
  import type { SessionHistoryComposer } from "@executor/composer/history/SessionHistoryComposer.js";
13
14
  import type { SessionHistoryStore } from "@/executor/store/history/SessionHistoryStore.js";
@@ -267,7 +268,15 @@ export class Executor implements SessionExecutor {
267
268
  runContext: run_context,
268
269
  },
269
270
  async () =>
270
- await this.recovery_policy.run_with_retry({
271
+ await withShellRunScope(
272
+ {
273
+ run_context: {
274
+ session_id: run_context.sessionId,
275
+ ...(run_context.turnId ? { turn_id: run_context.turnId } : {}),
276
+ },
277
+ },
278
+ async () =>
279
+ await this.recovery_policy.run_with_retry({
271
280
  query,
272
281
  model: this.resolveModelOrThrow(),
273
282
  run_context,
@@ -293,7 +302,8 @@ export class Executor implements SessionExecutor {
293
302
  model,
294
303
  next_run_context,
295
304
  ),
296
- }),
305
+ }),
306
+ ),
297
307
  );
298
308
  return result;
299
309
  } finally {
package/src/index.ts CHANGED
@@ -105,7 +105,6 @@ export {
105
105
  loadStaticSystemPrompts,
106
106
  StaticPromptCatalog,
107
107
  } from "./executor/composer/system/default/StaticPromptCatalog.js";
108
- export { shellTools } from "./executor/tools/shell/ShellToolDefinition.js";
109
108
 
110
109
  // 通用 plugin 宿主工具
111
110
  export {
package/src/rpc/Client.ts CHANGED
@@ -37,6 +37,10 @@ import type {
37
37
  RpcSessionSubscription,
38
38
  RpcSystemPromptPayload,
39
39
  } from "@/types/rpc/RpcProtocol.js";
40
+ import type {
41
+ ShellApprovalDecisionResult,
42
+ ShellApprovalView,
43
+ } from "@downcity/shell";
40
44
 
41
45
  export type {
42
46
  RpcClientEndpoint,
@@ -358,6 +362,40 @@ export class RpcClient {
358
362
  });
359
363
  }
360
364
 
365
+ /**
366
+ * 列出 shell approvals。
367
+ */
368
+ async list_shell_approvals(): Promise<ShellApprovalView[]> {
369
+ const data = await this.request<{ approvals: ShellApprovalView[] }>({
370
+ method: "internal.shell.approvals",
371
+ });
372
+ return Array.isArray(data.approvals) ? data.approvals : [];
373
+ }
374
+
375
+ /**
376
+ * 批准 shell approval。
377
+ */
378
+ async approve_shell_approval(approval_id: string): Promise<ShellApprovalDecisionResult> {
379
+ return await this.request<ShellApprovalDecisionResult>({
380
+ method: "internal.shell.approve",
381
+ params: {
382
+ approvalId: approval_id,
383
+ },
384
+ });
385
+ }
386
+
387
+ /**
388
+ * 拒绝 shell approval。
389
+ */
390
+ async deny_shell_approval(approval_id: string): Promise<ShellApprovalDecisionResult> {
391
+ return await this.request<ShellApprovalDecisionResult>({
392
+ method: "internal.shell.deny",
393
+ params: {
394
+ approvalId: approval_id,
395
+ },
396
+ });
397
+ }
398
+
361
399
  /**
362
400
  * 关闭底层连接。
363
401
  */
@@ -154,11 +154,42 @@ export async function handleInternalRpcRequest(params: {
154
154
  });
155
155
  return true;
156
156
  }
157
+ case "internal.shell.approvals": {
158
+ const shell = requireShell(options);
159
+ write_success(request.id, {
160
+ approvals: shell.approvals(),
161
+ });
162
+ return true;
163
+ }
164
+ case "internal.shell.approve": {
165
+ const shell = requireShell(options);
166
+ const result = await shell.approve({
167
+ approval_id: String(request.params.approvalId || "").trim(),
168
+ });
169
+ write_success(request.id, result);
170
+ return true;
171
+ }
172
+ case "internal.shell.deny": {
173
+ const shell = requireShell(options);
174
+ const result = await shell.deny({
175
+ approval_id: String(request.params.approvalId || "").trim(),
176
+ });
177
+ write_success(request.id, result);
178
+ return true;
179
+ }
157
180
  default:
158
181
  return false;
159
182
  }
160
183
  }
161
184
 
185
+ function requireShell(options: RpcRequestHandlerOptions) {
186
+ const shell = options.getShell?.();
187
+ if (!shell) {
188
+ throw new Error("Agent RPC server was started without Shell");
189
+ }
190
+ return shell;
191
+ }
192
+
162
193
  function requireAgentContext(options: RpcRequestHandlerOptions): AgentContext {
163
194
  const context = options.getAgentContext?.();
164
195
  if (!context) {
@@ -10,6 +10,7 @@ import type { AgentSessionCollection } from "@/types/agent/AgentTypes.js";
10
10
  import type { AgentContext } from "@/types/runtime/agent/AgentContext.js";
11
11
  import type { AgentRuntime } from "@/types/runtime/agent/AgentRuntime.js";
12
12
  import type { RpcEventFrame } from "@/types/rpc/RpcProtocol.js";
13
+ import type { Shell } from "@downcity/shell";
13
14
 
14
15
  /**
15
16
  * RPC Server 启动参数。
@@ -25,6 +26,8 @@ export interface RpcServerStartOptions {
25
26
  getAgentContext?: () => AgentContext;
26
27
  /** Agent 运行态访问口。 */
27
28
  getAgentRuntime?: () => AgentRuntime;
29
+ /** Shell 访问口。 */
30
+ getShell?: () => Shell | undefined;
28
31
  }
29
32
 
30
33
  /**
@@ -37,6 +40,8 @@ export interface RpcRequestHandlerOptions {
37
40
  getAgentContext?: () => AgentContext;
38
41
  /** Agent 运行态访问口。 */
39
42
  getAgentRuntime?: () => AgentRuntime;
43
+ /** Shell 访问口。 */
44
+ getShell?: () => Shell | undefined;
40
45
  }
41
46
 
42
47
  /**
@@ -7,6 +7,7 @@
7
7
  */
8
8
 
9
9
  import type { Tool } from "ai";
10
+ import type { Shell } from "@downcity/shell";
10
11
  import type { BasePlugin } from "@/plugin/core/BasePlugin.js";
11
12
  import type { AgentModel } from "@/model/CityModelAdapter.js";
12
13
  import type { RpcServerInstance } from "@/rpc/Server.js";
@@ -51,6 +52,15 @@ export interface AgentOptions {
51
52
  */
52
53
  tools?: Record<string, Tool>;
53
54
 
55
+ /**
56
+ * 当前 agent 内建 shell 能力。
57
+ *
58
+ * 关键点(中文)
59
+ * - Shell 不是 plugin,而是 agent 直接挂载的内建工具对象。
60
+ * - 未传入时,Agent 不会自动注入 shell tools。
61
+ */
62
+ shell?: Shell;
63
+
54
64
  /**
55
65
  * 调用方显式传入的静态基础指令。
56
66
  *
@@ -8,7 +8,7 @@
8
8
  import type { LlmConfig } from "@/types/config/LlmConfig.js";
9
9
  import type { ExecutionBindingConfig } from "@/types/config/ExecutionBinding.js";
10
10
  import type { JsonObject } from "@/types/common/Json.js";
11
- import type { SandboxProjectConfig } from "@/sandbox/types/Sandbox.js";
11
+ import type { SandboxProjectConfig } from "@downcity/shell/sandbox/types/Sandbox.js";
12
12
 
13
13
  /**
14
14
  * 单个聊天渠道配置。
@@ -165,7 +165,7 @@ export interface DowncityConfig {
165
165
  * shell / CLI 执行 sandbox 配置。
166
166
  *
167
167
  * 关键点(中文)
168
- * - 当前只作用于 shell plugin 这条命令执行链。
168
+ * - 当前只作用于内建 shell 这条命令执行链。
169
169
  * - 这里不表达审批、用户授权与复杂策略系统,只表达最小边界。
170
170
  */
171
171
  sandbox?: SandboxProjectConfig;
@@ -231,6 +231,34 @@ export type RpcRequest =
231
231
  /** action payload。 */
232
232
  payload?: JsonValue;
233
233
  };
234
+ }
235
+ | {
236
+ /** 请求 id,用于匹配响应。 */
237
+ id: string;
238
+ /** 列出 shell approvals。 */
239
+ method: "internal.shell.approvals";
240
+ }
241
+ | {
242
+ /** 请求 id,用于匹配响应。 */
243
+ id: string;
244
+ /** 批准 shell approval。 */
245
+ method: "internal.shell.approve";
246
+ /** approval 参数。 */
247
+ params: {
248
+ /** approval id。 */
249
+ approvalId: string;
250
+ };
251
+ }
252
+ | {
253
+ /** 请求 id,用于匹配响应。 */
254
+ id: string;
255
+ /** 拒绝 shell approval。 */
256
+ method: "internal.shell.deny";
257
+ /** approval 参数。 */
258
+ params: {
259
+ /** approval id。 */
260
+ approvalId: string;
261
+ };
234
262
  };
235
263
 
236
264
  /**