@downcity/agent 1.1.195 → 1.1.196

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/bin/agent/local/Agent.d.ts.map +1 -1
  2. package/bin/agent/local/Agent.js +5 -2
  3. package/bin/agent/local/Agent.js.map +1 -1
  4. package/bin/agent/local/services/AgentAssemblyService.d.ts.map +1 -1
  5. package/bin/agent/local/services/AgentAssemblyService.js +2 -2
  6. package/bin/agent/local/services/AgentAssemblyService.js.map +1 -1
  7. package/bin/agent/local/services/AgentSessionManager.d.ts +5 -0
  8. package/bin/agent/local/services/AgentSessionManager.d.ts.map +1 -1
  9. package/bin/agent/local/services/AgentSessionManager.js +3 -0
  10. package/bin/agent/local/services/AgentSessionManager.js.map +1 -1
  11. package/bin/executor/tools/plugin/PluginToolBridge.d.ts +4 -8
  12. package/bin/executor/tools/plugin/PluginToolBridge.d.ts.map +1 -1
  13. package/bin/executor/tools/plugin/PluginToolBridge.js +6 -21
  14. package/bin/executor/tools/plugin/PluginToolBridge.js.map +1 -1
  15. package/bin/executor/tools/plugin/PluginToolDefinition.d.ts +7 -19
  16. package/bin/executor/tools/plugin/PluginToolDefinition.d.ts.map +1 -1
  17. package/bin/executor/tools/plugin/PluginToolDefinition.js +29 -18
  18. package/bin/executor/tools/plugin/PluginToolDefinition.js.map +1 -1
  19. package/bin/plugin/core/PluginRegistry.d.ts.map +1 -1
  20. package/bin/plugin/core/PluginRegistry.js +0 -4
  21. package/bin/plugin/core/PluginRegistry.js.map +1 -1
  22. package/bin/types/plugin/PluginToolRuntime.d.ts +75 -0
  23. package/bin/types/plugin/PluginToolRuntime.d.ts.map +1 -0
  24. package/bin/types/plugin/PluginToolRuntime.js +9 -0
  25. package/bin/types/plugin/PluginToolRuntime.js.map +1 -0
  26. package/package.json +1 -1
  27. package/scripts/agent-ready.test.mjs +168 -0
  28. package/scripts/plugin-tool-bridge.test.mjs +114 -10
  29. package/src/agent/local/Agent.ts +5 -2
  30. package/src/agent/local/services/AgentAssemblyService.ts +2 -3
  31. package/src/agent/local/services/AgentSessionManager.ts +8 -0
  32. package/src/executor/tools/plugin/PluginToolBridge.ts +10 -26
  33. package/src/executor/tools/plugin/PluginToolDefinition.ts +39 -22
  34. package/src/plugin/core/PluginRegistry.ts +0 -4
  35. package/src/types/plugin/PluginToolRuntime.ts +86 -0
  36. package/tsconfig.tsbuildinfo +1 -1
@@ -0,0 +1,75 @@
1
+ /**
2
+ * Plugin tool runtime 绑定类型。
3
+ *
4
+ * 关键点(中文)
5
+ * - plugin_call / plugin_read 必须绑定当前 Agent 自己的 plugin registry。
6
+ * - 这些类型描述 tool 工厂与 bridge 调用时需要的显式 runtime 依赖。
7
+ */
8
+ import type { Tool } from "ai";
9
+ import type { AgentPlugins } from "../../types/plugin/PluginRuntime.js";
10
+ import type { PluginCallInput, PluginReadInput } from "../../executor/tools/plugin/types/PluginTool.js";
11
+ /**
12
+ * 创建 plugin tools 的参数。
13
+ */
14
+ export interface CreatePluginToolsOptions {
15
+ /**
16
+ * 当前 Agent 自己的 plugin 调用面。
17
+ *
18
+ * 关键点(中文)
19
+ * - 该引用必须来自当前 Agent 装配出的 plugin registry。
20
+ * - tool 执行时只能通过它访问 plugin,避免跨 Agent 读到其他 registry。
21
+ */
22
+ plugins: AgentPlugins;
23
+ }
24
+ /**
25
+ * 当前 Agent 专属 plugin tools。
26
+ */
27
+ export interface AgentPluginTools {
28
+ /**
29
+ * 读取 plugin metadata 的模型工具。
30
+ *
31
+ * 关键点(中文)
32
+ * - 该 tool 通过闭包绑定当前 Agent 的 `plugins`。
33
+ * - 不读取任何模块级全局 runtime。
34
+ */
35
+ plugin_read: Tool;
36
+ /**
37
+ * 执行 plugin action 的模型工具。
38
+ *
39
+ * 关键点(中文)
40
+ * - 该 tool 通过闭包绑定当前 Agent 的 `plugins`。
41
+ * - 多 Agent 并发时,每个 Agent 都有自己的 tool 实例。
42
+ */
43
+ plugin_call: Tool;
44
+ }
45
+ /**
46
+ * 调用 plugin_call bridge 的参数。
47
+ */
48
+ export interface InvokePluginCallToolOptions {
49
+ /**
50
+ * 当前 Agent 自己的 plugin 调用面。
51
+ *
52
+ * 关键点(中文)
53
+ * - 由 Agent 级 tool 工厂注入。
54
+ * - 不能在 bridge 内通过全局变量读取。
55
+ */
56
+ plugins: AgentPlugins;
57
+ /**
58
+ * 模型提交给 plugin_call 的结构化输入。
59
+ */
60
+ input: PluginCallInput;
61
+ }
62
+ /**
63
+ * 调用 plugin_read bridge 的参数。
64
+ */
65
+ export interface InvokePluginReadToolOptions {
66
+ /**
67
+ * 当前 Agent 自己的 plugin 调用面。
68
+ */
69
+ plugins: AgentPlugins;
70
+ /**
71
+ * 模型提交给 plugin_read 的结构化输入。
72
+ */
73
+ input: PluginReadInput;
74
+ }
75
+ //# sourceMappingURL=PluginToolRuntime.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PluginToolRuntime.d.ts","sourceRoot":"","sources":["../../../src/types/plugin/PluginToolRuntime.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,IAAI,CAAC;AAC/B,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iCAAiC,CAAC;AACpE,OAAO,KAAK,EACV,eAAe,EACf,eAAe,EAChB,MAAM,6CAA6C,CAAC;AAErD;;GAEG;AACH,MAAM,WAAW,wBAAwB;IACvC;;;;;;OAMG;IACH,OAAO,EAAE,YAAY,CAAC;CACvB;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B;;;;;;OAMG;IACH,WAAW,EAAE,IAAI,CAAC;IAElB;;;;;;OAMG;IACH,WAAW,EAAE,IAAI,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,2BAA2B;IAC1C;;;;;;OAMG;IACH,OAAO,EAAE,YAAY,CAAC;IAEtB;;OAEG;IACH,KAAK,EAAE,eAAe,CAAC;CACxB;AAED;;GAEG;AACH,MAAM,WAAW,2BAA2B;IAC1C;;OAEG;IACH,OAAO,EAAE,YAAY,CAAC;IAEtB;;OAEG;IACH,KAAK,EAAE,eAAe,CAAC;CACxB"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Plugin tool runtime 绑定类型。
3
+ *
4
+ * 关键点(中文)
5
+ * - plugin_call / plugin_read 必须绑定当前 Agent 自己的 plugin registry。
6
+ * - 这些类型描述 tool 工厂与 bridge 调用时需要的显式 runtime 依赖。
7
+ */
8
+ export {};
9
+ //# sourceMappingURL=PluginToolRuntime.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PluginToolRuntime.js","sourceRoot":"","sources":["../../../src/types/plugin/PluginToolRuntime.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@downcity/agent",
3
- "version": "1.1.195",
3
+ "version": "1.1.196",
4
4
  "type": "module",
5
5
  "description": "Downcity Agent 运行时 — 单 Agent 执行壳与本机 RPC 能力",
6
6
  "main": "./bin/index.js",
@@ -0,0 +1,168 @@
1
+ /**
2
+ * @file 验证 session.prompt 会等待当前 Agent 后台能力 ready。
3
+ *
4
+ * 关键点(中文)
5
+ * - 这里走编译后的公开 SDK,覆盖宿主真实入口。
6
+ * - plugin lifecycle 未完成前,session.prompt 不应进入模型执行。
7
+ */
8
+
9
+ import test from "node:test";
10
+ import assert from "node:assert/strict";
11
+ import os from "node:os";
12
+ import path from "node:path";
13
+ import fs from "node:fs/promises";
14
+
15
+ import { MockLanguageModelV3 } from "ai/test";
16
+ import { Agent } from "../bin/index.js";
17
+ import { createPlugin } from "../bin/plugin/core/PluginActionFactory.js";
18
+
19
+ function create_deferred() {
20
+ let resolve;
21
+ const promise = new Promise((inner_resolve) => {
22
+ resolve = inner_resolve;
23
+ });
24
+ return {
25
+ promise,
26
+ resolve,
27
+ };
28
+ }
29
+
30
+ async function is_settled(promise) {
31
+ const marker = {};
32
+ const result = await Promise.race([
33
+ promise.then(
34
+ () => true,
35
+ () => true,
36
+ ),
37
+ new Promise((resolve) => setTimeout(() => resolve(marker), 0)),
38
+ ]);
39
+ return result !== marker;
40
+ }
41
+
42
+ function create_stream_text_result(text) {
43
+ return {
44
+ stream: new ReadableStream({
45
+ start(controller) {
46
+ controller.enqueue({
47
+ type: "stream-start",
48
+ warnings: [],
49
+ });
50
+ controller.enqueue({
51
+ type: "text-start",
52
+ id: "text_1",
53
+ });
54
+ controller.enqueue({
55
+ type: "text-delta",
56
+ id: "text_1",
57
+ delta: text,
58
+ });
59
+ controller.enqueue({
60
+ type: "text-end",
61
+ id: "text_1",
62
+ });
63
+ controller.enqueue({
64
+ type: "finish",
65
+ finishReason: {
66
+ unified: "stop",
67
+ raw: "stop",
68
+ },
69
+ usage: {
70
+ inputTokens: {
71
+ total: 0,
72
+ noCache: 0,
73
+ cacheRead: 0,
74
+ cacheWrite: 0,
75
+ },
76
+ outputTokens: {
77
+ total: 0,
78
+ text: 0,
79
+ reasoning: 0,
80
+ },
81
+ },
82
+ });
83
+ controller.close();
84
+ },
85
+ }),
86
+ };
87
+ }
88
+
89
+ test("session.prompt waits for agent background ready before model execution", async () => {
90
+ const agent_path = await fs.mkdtemp(
91
+ path.join(os.tmpdir(), "downcity-agent-ready-"),
92
+ );
93
+ const lifecycle_ready = create_deferred();
94
+ let model_stream_calls = 0;
95
+
96
+ const blocking_plugin = createPlugin({
97
+ name: "blocking",
98
+ title: "Blocking",
99
+ description: "Blocks lifecycle start until the test releases it",
100
+ lifecycle: {
101
+ start: async () => {
102
+ await lifecycle_ready.promise;
103
+ },
104
+ },
105
+ });
106
+ const model = new MockLanguageModelV3({
107
+ modelId: "agent-ready-model",
108
+ doStream: async () => {
109
+ model_stream_calls += 1;
110
+ return create_stream_text_result("ready");
111
+ },
112
+ doGenerate: async () => ({
113
+ content: [
114
+ {
115
+ type: "text",
116
+ text: "Ready title",
117
+ },
118
+ ],
119
+ finishReason: {
120
+ unified: "stop",
121
+ raw: "stop",
122
+ },
123
+ usage: {
124
+ inputTokens: {
125
+ total: 0,
126
+ noCache: 0,
127
+ cacheRead: 0,
128
+ cacheWrite: 0,
129
+ },
130
+ outputTokens: {
131
+ total: 0,
132
+ text: 0,
133
+ reasoning: 0,
134
+ },
135
+ },
136
+ warnings: [],
137
+ }),
138
+ });
139
+ const agent = new Agent({
140
+ id: "ready_agent",
141
+ path: agent_path,
142
+ plugins: [blocking_plugin],
143
+ model,
144
+ });
145
+
146
+ try {
147
+ const session = await agent.session_collection().create_session({
148
+ sessionId: "ready_session",
149
+ });
150
+ const prompt_promise = session.prompt({
151
+ query: "hello",
152
+ });
153
+
154
+ assert.equal(await is_settled(prompt_promise), false);
155
+ assert.equal(model_stream_calls, 0);
156
+
157
+ lifecycle_ready.resolve();
158
+ const turn = await prompt_promise;
159
+ const result = await turn.finished;
160
+
161
+ assert.equal(result.success, true);
162
+ assert.equal(model_stream_calls, 1);
163
+ } finally {
164
+ lifecycle_ready.resolve();
165
+ await agent.dispose();
166
+ }
167
+ });
168
+
@@ -15,8 +15,8 @@ import fs from "node:fs/promises";
15
15
  import {
16
16
  invokePluginCallTool,
17
17
  invokePluginReadTool,
18
- setPluginToolRuntime,
19
18
  } from "../bin/executor/tools/plugin/PluginToolBridge.js";
19
+ import { createPluginTools } from "../bin/executor/tools/plugin/PluginToolDefinition.js";
20
20
  import { withSessionRunScope } from "../bin/executor/SessionRunScope.js";
21
21
  import { createAction, createPlugin } from "../bin/plugin/core/PluginActionFactory.js";
22
22
  import { createAgentPluginRegistry } from "../bin/agent/local/AgentPluginFactory.js";
@@ -38,7 +38,7 @@ test("invokePluginCallTool returns absolute paths for materialized file parts",
38
38
  );
39
39
  const bytes = Buffer.from("png-bytes-for-plugin-tool", "utf8");
40
40
 
41
- setPluginToolRuntime({
41
+ const plugins = {
42
42
  list: () => [],
43
43
  read: () => ({ plugins: [] }),
44
44
  availability: async () => ({ enabled: true, available: true, reasons: [] }),
@@ -64,14 +64,17 @@ test("invokePluginCallTool returns absolute paths for materialized file parts",
64
64
  resolve: async () => {
65
65
  throw new Error("not implemented");
66
66
  },
67
- });
67
+ };
68
68
 
69
69
  const run_context = create_run_context(project_root);
70
70
  const result = await withSessionRunScope({ runContext: run_context }, () =>
71
71
  invokePluginCallTool({
72
- plugin: "image",
73
- action: "image_result",
74
- payload: { job_id: "img_1" },
72
+ plugins,
73
+ input: {
74
+ plugin: "image",
75
+ action: "image_result",
76
+ payload: { job_id: "img_1" },
77
+ },
75
78
  }),
76
79
  );
77
80
 
@@ -93,7 +96,7 @@ test("invokePluginCallTool returns absolute paths for materialized file parts",
93
96
  });
94
97
 
95
98
  test("invokePluginReadTool returns plugin action metadata", async () => {
96
- setPluginToolRuntime({
99
+ const plugins = {
97
100
  list: () => [],
98
101
  read: () => ({
99
102
  name: "image",
@@ -130,11 +133,14 @@ test("invokePluginReadTool returns plugin action metadata", async () => {
130
133
  resolve: async () => {
131
134
  throw new Error("not implemented");
132
135
  },
133
- });
136
+ };
134
137
 
135
138
  const result = await invokePluginReadTool({
136
- plugin: "image",
137
- action: "image_create",
139
+ plugins,
140
+ input: {
141
+ plugin: "image",
142
+ action: "image_create",
143
+ },
138
144
  });
139
145
 
140
146
  assert.equal(result.success, true);
@@ -174,6 +180,7 @@ test("PluginRegistry validates action payload with metadata schema", async () =>
174
180
  });
175
181
  const registry = createAgentPluginRegistry({
176
182
  plugins: [plugin],
183
+ plugin_instances: new Map(),
177
184
  get_context: () => ({ rootPath: process.cwd() }),
178
185
  });
179
186
 
@@ -197,3 +204,100 @@ test("PluginRegistry validates action payload with metadata schema", async () =>
197
204
  assert.equal(valid.success, true);
198
205
  assert.equal(valid.data.text, "hello");
199
206
  });
207
+
208
+ test("createPluginTools binds plugin_call to the current registry", async () => {
209
+ function create_owner_registry(owner) {
210
+ const plugin = createPlugin({
211
+ name: "skill",
212
+ title: `Skill ${owner}`,
213
+ description: "Owner scoped skill plugin",
214
+ actions: {
215
+ lookup: createAction({
216
+ description: "Return registry owner",
217
+ execute: async () => ({
218
+ success: true,
219
+ data: { owner },
220
+ message: owner,
221
+ }),
222
+ }),
223
+ },
224
+ });
225
+ return createAgentPluginRegistry({
226
+ plugins: [plugin],
227
+ plugin_instances: new Map(),
228
+ get_context: () => ({ rootPath: process.cwd() }),
229
+ });
230
+ }
231
+
232
+ const registry_a = create_owner_registry("agent_a");
233
+ const registry_b = create_owner_registry("agent_b");
234
+ const tools_a = createPluginTools({ plugins: registry_a });
235
+ const tools_b = createPluginTools({ plugins: registry_b });
236
+
237
+ const result_a = await tools_a.plugin_call.execute({
238
+ plugin: "skill",
239
+ action: "lookup",
240
+ payload: { name: "anything" },
241
+ });
242
+ const result_b = await tools_b.plugin_call.execute({
243
+ plugin: "skill",
244
+ action: "lookup",
245
+ payload: { name: "anything" },
246
+ });
247
+
248
+ assert.equal(result_a.success, true);
249
+ assert.equal(result_a.data.value.owner, "agent_a");
250
+ assert.equal(result_b.success, true);
251
+ assert.equal(result_b.data.value.owner, "agent_b");
252
+ });
253
+
254
+ test("PluginRegistry keeps plugin ready after action business failure", async () => {
255
+ let call_count = 0;
256
+ const plugin = createPlugin({
257
+ name: "skill",
258
+ title: "Skill",
259
+ description: "Retryable skill plugin",
260
+ actions: {
261
+ lookup: createAction({
262
+ description: "Fail once then succeed",
263
+ execute: async () => {
264
+ call_count += 1;
265
+ if (call_count === 1) {
266
+ return {
267
+ success: false,
268
+ error: "Skill not found: missing",
269
+ message: "Skill not found: missing",
270
+ };
271
+ }
272
+ return {
273
+ success: true,
274
+ data: { loaded: true },
275
+ message: "loaded",
276
+ };
277
+ },
278
+ }),
279
+ },
280
+ });
281
+ const registry = createAgentPluginRegistry({
282
+ plugins: [plugin],
283
+ plugin_instances: new Map(),
284
+ get_context: () => ({ rootPath: process.cwd() }),
285
+ });
286
+
287
+ const failed = await registry.runAction({
288
+ plugin: "skill",
289
+ action: "lookup",
290
+ payload: { name: "missing" },
291
+ });
292
+ assert.equal(failed.success, false);
293
+ assert.equal(registry.status("skill").state, "ready");
294
+
295
+ const retry = await registry.runAction({
296
+ plugin: "skill",
297
+ action: "lookup",
298
+ payload: { name: "exists" },
299
+ });
300
+ assert.equal(retry.success, true);
301
+ assert.equal(retry.data.loaded, true);
302
+ assert.equal(registry.status("skill").state, "ready");
303
+ });
@@ -90,14 +90,14 @@ export class Agent {
90
90
  this.instruction = assembly.instruction;
91
91
  this.shell = assembly.shell;
92
92
 
93
- this.sessionManager = this.create_session_manager(assembly);
94
- session_manager_ref = this.sessionManager;
95
93
  // 关键点(中文):构造完成即触发后台能力启动;调用方可 `await agent.ready()` 等待。
96
94
  this.backgroundService = new AgentBackgroundService({
97
95
  logger: this.logger,
98
96
  agent_context: this.agentContext,
99
97
  get_shell: () => this.shell,
100
98
  });
99
+ this.sessionManager = this.create_session_manager(assembly);
100
+ session_manager_ref = this.sessionManager;
101
101
  }
102
102
 
103
103
  /**
@@ -269,6 +269,9 @@ export class Agent {
269
269
  get_agent_context: () => this.agentContext ?? assembly.agent_context,
270
270
  get_instruction: () => this.instruction,
271
271
  plugin_instances: assembly.plugin_instances,
272
+ ensure_agent_ready: async () => {
273
+ await this.backgroundService.ready();
274
+ },
272
275
  default_model: this.defaultModel,
273
276
  SessionClass: this.SessionClass,
274
277
  });
@@ -28,8 +28,7 @@ import {
28
28
  createAgentPluginConfigRuntime,
29
29
  } from "@/agent/local/AgentRuntimeAssembly.js";
30
30
  import {
31
- plugin_tools,
32
- setPluginToolRuntime,
31
+ createPluginTools,
33
32
  } from "@executor/tools/plugin/PluginToolDefinition.js";
34
33
  import type { AgentManagedSession } from "@/types/agent/AgentTypes.js";
35
34
  import type { SessionPort } from "@/types/runtime/agent/AgentContext.js";
@@ -183,6 +182,7 @@ export class AgentAssemblyService {
183
182
  });
184
183
  const plugins = plugin_registry;
185
184
  if (this.should_register_plugin_call_tool(plugin_instances)) {
185
+ const plugin_tools = createPluginTools({ plugins });
186
186
  tools.plugin_read = tools.plugin_read || plugin_tools.plugin_read;
187
187
  tools.plugin_call = tools.plugin_call || plugin_tools.plugin_call;
188
188
  }
@@ -228,7 +228,6 @@ export class AgentAssemblyService {
228
228
  });
229
229
  Object.assign(tools, shell.tools);
230
230
  }
231
- setPluginToolRuntime(plugins);
232
231
 
233
232
  return {
234
233
  id,
@@ -87,6 +87,11 @@ type AgentSessionManagerOptions = {
87
87
  */
88
88
  plugin_instances: Map<string, Plugin>;
89
89
 
90
+ /**
91
+ * 等待当前 Agent 后台能力启动完成。
92
+ */
93
+ ensure_agent_ready: () => Promise<void>;
94
+
90
95
  /**
91
96
  * 当前默认模型实例。
92
97
  */
@@ -109,6 +114,7 @@ export class AgentSessionManager {
109
114
  private readonly get_agent_context: AgentSessionManagerOptions["get_agent_context"];
110
115
  private readonly get_instruction: AgentSessionManagerOptions["get_instruction"];
111
116
  private readonly plugin_instances: Map<string, Plugin>;
117
+ private readonly ensure_agent_ready: AgentSessionManagerOptions["ensure_agent_ready"];
112
118
  private readonly default_model?: AgentModel;
113
119
  private readonly SessionClass: AgentSessionConstructor;
114
120
  private readonly sessions_by_id = new Map<string, AgentManagedSession>();
@@ -123,6 +129,7 @@ export class AgentSessionManager {
123
129
  this.get_agent_context = options.get_agent_context;
124
130
  this.get_instruction = options.get_instruction;
125
131
  this.plugin_instances = options.plugin_instances;
132
+ this.ensure_agent_ready = options.ensure_agent_ready;
126
133
  this.default_model = options.default_model;
127
134
  this.SessionClass = options.SessionClass || Session;
128
135
  this.session_collection = {
@@ -350,6 +357,7 @@ export class AgentSessionManager {
350
357
  getManagedPluginSystemBlocks: async () => [],
351
358
  getPluginSystemBlocks: async () => await this.load_plugin_system_blocks(),
352
359
  ensureConfigured: async (session) => {
360
+ await this.ensure_agent_ready();
353
361
  await this.apply_session_defaults(session);
354
362
  },
355
363
  });
@@ -10,7 +10,6 @@
10
10
  import path from "node:path";
11
11
  import type { FileUIPart } from "ai";
12
12
  import type { JsonObject, JsonValue } from "@/types/common/Json.js";
13
- import type { AgentPlugins } from "@/plugin/types/Plugin.js";
14
13
  import type {
15
14
  PluginCallInput,
16
15
  PluginCallToolFileResult,
@@ -18,6 +17,10 @@ import type {
18
17
  PluginReadInput,
19
18
  PluginReadToolResult,
20
19
  } from "@/executor/tools/plugin/types/PluginTool.js";
20
+ import type {
21
+ InvokePluginCallToolOptions,
22
+ InvokePluginReadToolOptions,
23
+ } from "@/types/plugin/PluginToolRuntime.js";
21
24
  import {
22
25
  materializeAssistantFileParts,
23
26
  resolveAgentFilePath,
@@ -27,25 +30,6 @@ import {
27
30
  getSessionRunContext,
28
31
  } from "@executor/SessionRunScope.js";
29
32
 
30
- let plugin_tool_runtime: AgentPlugins | null = null;
31
-
32
- /**
33
- * 注入 plugin tool 运行时。
34
- */
35
- export function setPluginToolRuntime(next: AgentPlugins): void {
36
- plugin_tool_runtime = next;
37
- }
38
-
39
- /**
40
- * 读取已注入的 plugin tool 运行时。
41
- */
42
- function require_plugin_tool_runtime(): AgentPlugins {
43
- if (plugin_tool_runtime) return plugin_tool_runtime;
44
- throw new Error(
45
- "Plugin tool runtime is not initialized. Ensure agent assembly completed before using plugin_call.",
46
- );
47
- }
48
-
49
33
  /**
50
34
  * 判断值是否为普通对象。
51
35
  */
@@ -126,8 +110,9 @@ function summarize_action_data(data: JsonValue | undefined): JsonObject | undefi
126
110
  * 调用 plugin action 并桥接最终 assistant file parts。
127
111
  */
128
112
  export async function invokePluginCallTool(
129
- input: PluginCallInput,
113
+ params: InvokePluginCallToolOptions,
130
114
  ): Promise<PluginCallToolResult> {
115
+ const input: PluginCallInput = params.input;
131
116
  const plugin = String(input.plugin || "").trim();
132
117
  const action = String(input.action || "").trim();
133
118
  const payload = to_json_object(input.payload ?? {}) ?? {};
@@ -153,8 +138,7 @@ export async function invokePluginCallTool(
153
138
  }
154
139
 
155
140
  try {
156
- const runtime = require_plugin_tool_runtime();
157
- const result = await runtime.runAction({
141
+ const result = await params.plugins.runAction({
158
142
  plugin,
159
143
  action,
160
144
  payload,
@@ -203,11 +187,11 @@ export async function invokePluginCallTool(
203
187
  * 读取 plugin / action metadata。
204
188
  */
205
189
  export async function invokePluginReadTool(
206
- input: PluginReadInput,
190
+ params: InvokePluginReadToolOptions,
207
191
  ): Promise<PluginReadToolResult> {
192
+ const input: PluginReadInput = params.input;
208
193
  try {
209
- const runtime = require_plugin_tool_runtime();
210
- const data = runtime.read({
194
+ const data = params.plugins.read({
211
195
  plugin: typeof input.plugin === "string" ? input.plugin : undefined,
212
196
  action: typeof input.action === "string" ? input.action : undefined,
213
197
  });