@downcity/agent 1.1.175 → 1.1.179

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 (139) hide show
  1. package/bin/agent/local/AgentPluginFactory.d.ts +4 -2
  2. package/bin/agent/local/AgentPluginFactory.d.ts.map +1 -1
  3. package/bin/agent/local/AgentPluginFactory.js +4 -6
  4. package/bin/agent/local/AgentPluginFactory.js.map +1 -1
  5. package/bin/agent/local/AgentRuntimeAssembly.d.ts.map +1 -1
  6. package/bin/agent/local/AgentRuntimeAssembly.js +3 -8
  7. package/bin/agent/local/AgentRuntimeAssembly.js.map +1 -1
  8. package/bin/agent/local/ProjectSetup.d.ts +1 -1
  9. package/bin/agent/local/ProjectSetup.d.ts.map +1 -1
  10. package/bin/agent/local/ProjectSetup.js +2 -6
  11. package/bin/agent/local/ProjectSetup.js.map +1 -1
  12. package/bin/agent/local/services/AgentAssemblyService.d.ts +2 -3
  13. package/bin/agent/local/services/AgentAssemblyService.d.ts.map +1 -1
  14. package/bin/agent/local/services/AgentAssemblyService.js +9 -14
  15. package/bin/agent/local/services/AgentAssemblyService.js.map +1 -1
  16. package/bin/agent/local/services/AgentBackgroundService.d.ts.map +1 -1
  17. package/bin/agent/local/services/AgentBackgroundService.js +5 -6
  18. package/bin/agent/local/services/AgentBackgroundService.js.map +1 -1
  19. package/bin/agent/local/services/AgentSessionManager.d.ts +2 -2
  20. package/bin/agent/local/services/AgentSessionManager.d.ts.map +1 -1
  21. package/bin/agent/local/services/AgentSessionManager.js +1 -28
  22. package/bin/agent/local/services/AgentSessionManager.js.map +1 -1
  23. package/bin/config/AgentInitializer.d.ts +4 -4
  24. package/bin/config/AgentInitializer.d.ts.map +1 -1
  25. package/bin/config/AgentInitializer.js +7 -49
  26. package/bin/config/AgentInitializer.js.map +1 -1
  27. package/bin/config/DowncitySchema.d.ts +3 -4
  28. package/bin/config/DowncitySchema.d.ts.map +1 -1
  29. package/bin/config/DowncitySchema.js +1 -2
  30. package/bin/config/DowncitySchema.js.map +1 -1
  31. package/bin/executor/composer/system/default/SystemDomain.d.ts.map +1 -1
  32. package/bin/executor/composer/system/default/SystemDomain.js +1 -2
  33. package/bin/executor/composer/system/default/SystemDomain.js.map +1 -1
  34. package/bin/executor/composer/system/default/assets/core.prompt.d.ts +1 -1
  35. package/bin/executor/composer/system/default/assets/core.prompt.d.ts.map +1 -1
  36. package/bin/executor/composer/system/default/assets/core.prompt.js +1 -1
  37. package/bin/executor/composer/system/default/assets/core.prompt.js.map +1 -1
  38. package/bin/index.d.ts +0 -2
  39. package/bin/index.d.ts.map +1 -1
  40. package/bin/index.js +1 -4
  41. package/bin/index.js.map +1 -1
  42. package/bin/plugin/core/Activation.d.ts +6 -5
  43. package/bin/plugin/core/Activation.d.ts.map +1 -1
  44. package/bin/plugin/core/Activation.js +9 -9
  45. package/bin/plugin/core/Activation.js.map +1 -1
  46. package/bin/plugin/core/BasePlugin.d.ts +5 -4
  47. package/bin/plugin/core/BasePlugin.d.ts.map +1 -1
  48. package/bin/plugin/core/BasePlugin.js +7 -4
  49. package/bin/plugin/core/BasePlugin.js.map +1 -1
  50. package/bin/plugin/core/HookRegistry.d.ts +4 -0
  51. package/bin/plugin/core/HookRegistry.d.ts.map +1 -1
  52. package/bin/plugin/core/HookRegistry.js +40 -0
  53. package/bin/plugin/core/HookRegistry.js.map +1 -1
  54. package/bin/plugin/core/Manager.d.ts +1 -1
  55. package/bin/plugin/core/Manager.d.ts.map +1 -1
  56. package/bin/plugin/core/Manager.js +1 -1
  57. package/bin/plugin/core/Manager.js.map +1 -1
  58. package/bin/plugin/core/PluginActionRunner.d.ts +13 -7
  59. package/bin/plugin/core/PluginActionRunner.d.ts.map +1 -1
  60. package/bin/plugin/core/PluginActionRunner.js +46 -111
  61. package/bin/plugin/core/PluginActionRunner.js.map +1 -1
  62. package/bin/plugin/core/PluginCatalog.d.ts +1 -1
  63. package/bin/plugin/core/PluginCatalog.d.ts.map +1 -1
  64. package/bin/plugin/core/PluginCatalog.js +1 -21
  65. package/bin/plugin/core/PluginCatalog.js.map +1 -1
  66. package/bin/plugin/core/PluginLocalExecution.d.ts +12 -2
  67. package/bin/plugin/core/PluginLocalExecution.d.ts.map +1 -1
  68. package/bin/plugin/core/PluginLocalExecution.js +18 -24
  69. package/bin/plugin/core/PluginLocalExecution.js.map +1 -1
  70. package/bin/plugin/core/PluginRegistry.d.ts +60 -7
  71. package/bin/plugin/core/PluginRegistry.d.ts.map +1 -1
  72. package/bin/plugin/core/PluginRegistry.js +269 -42
  73. package/bin/plugin/core/PluginRegistry.js.map +1 -1
  74. package/bin/plugin/core/PluginStateController.d.ts +11 -36
  75. package/bin/plugin/core/PluginStateController.d.ts.map +1 -1
  76. package/bin/plugin/core/PluginStateController.js +45 -190
  77. package/bin/plugin/core/PluginStateController.js.map +1 -1
  78. package/bin/plugin/types/Plugin.d.ts +1 -1
  79. package/bin/plugin/types/Plugin.d.ts.map +1 -1
  80. package/bin/plugin/types/PluginState.d.ts +1 -1
  81. package/bin/plugin/types/PluginState.d.ts.map +1 -1
  82. package/bin/plugin/types/Plugins.d.ts +12 -12
  83. package/bin/plugin/types/Plugins.d.ts.map +1 -1
  84. package/bin/types/agent/AgentOptions.d.ts +12 -3
  85. package/bin/types/agent/AgentOptions.d.ts.map +1 -1
  86. package/bin/types/agent/AgentRuntimeAssembly.d.ts +1 -1
  87. package/bin/types/config/AgentProject.d.ts +6 -14
  88. package/bin/types/config/AgentProject.d.ts.map +1 -1
  89. package/bin/types/plugin/PluginRuntime.d.ts +23 -2
  90. package/bin/types/plugin/PluginRuntime.d.ts.map +1 -1
  91. package/bin/types/plugin/PluginState.d.ts +47 -36
  92. package/bin/types/plugin/PluginState.d.ts.map +1 -1
  93. package/bin/types/plugin/PluginState.js +4 -3
  94. package/bin/types/plugin/PluginState.js.map +1 -1
  95. package/bin/types/runtime/agent/AgentContext.d.ts +3 -3
  96. package/bin/types/runtime/agent/AgentContext.d.ts.map +1 -1
  97. package/bin/types/runtime/agent/AgentContext.js.map +1 -1
  98. package/package.json +3 -3
  99. package/src/agent/local/AgentPluginFactory.ts +8 -8
  100. package/src/agent/local/AgentRuntimeAssembly.ts +3 -9
  101. package/src/agent/local/ProjectSetup.ts +1 -8
  102. package/src/agent/local/services/AgentAssemblyService.ts +13 -23
  103. package/src/agent/local/services/AgentBackgroundService.ts +5 -6
  104. package/src/agent/local/services/AgentSessionManager.ts +4 -26
  105. package/src/config/AgentInitializer.ts +7 -57
  106. package/src/config/DowncitySchema.ts +3 -4
  107. package/src/executor/composer/system/default/SystemDomain.ts +1 -2
  108. package/src/executor/composer/system/default/assets/core.prompt.ts +1 -1
  109. package/src/executor/composer/system/default/assets/core.prompt.ts.txt +1 -1
  110. package/src/index.ts +1 -8
  111. package/src/plugin/core/Activation.ts +10 -10
  112. package/src/plugin/core/BasePlugin.ts +9 -6
  113. package/src/plugin/core/HookRegistry.ts +41 -0
  114. package/src/plugin/core/Manager.ts +0 -2
  115. package/src/plugin/core/PluginActionRunner.ts +52 -133
  116. package/src/plugin/core/PluginCatalog.ts +1 -23
  117. package/src/plugin/core/PluginLocalExecution.ts +30 -25
  118. package/src/plugin/core/PluginRegistry.ts +298 -44
  119. package/src/plugin/core/PluginStateController.ts +45 -235
  120. package/src/plugin/types/Plugin.ts +2 -1
  121. package/src/plugin/types/PluginState.ts +2 -1
  122. package/src/plugin/types/Plugins.ts +12 -12
  123. package/src/types/agent/AgentOptions.ts +13 -3
  124. package/src/types/agent/AgentRuntimeAssembly.ts +1 -1
  125. package/src/types/config/AgentProject.ts +7 -15
  126. package/src/types/plugin/PluginRuntime.ts +23 -2
  127. package/src/types/plugin/PluginState.ts +50 -43
  128. package/src/types/runtime/agent/AgentContext.ts +3 -3
  129. package/tsconfig.tsbuildinfo +1 -1
  130. package/bin/config/Defaults.d.ts +0 -21
  131. package/bin/config/Defaults.d.ts.map +0 -1
  132. package/bin/config/Defaults.js +0 -47
  133. package/bin/config/Defaults.js.map +0 -1
  134. package/bin/plugin/core/ProjectConfigStore.d.ts +0 -84
  135. package/bin/plugin/core/ProjectConfigStore.d.ts.map +0 -1
  136. package/bin/plugin/core/ProjectConfigStore.js +0 -89
  137. package/bin/plugin/core/ProjectConfigStore.js.map +0 -1
  138. package/src/config/Defaults.ts +0 -49
  139. package/src/plugin/core/ProjectConfigStore.ts +0 -155
@@ -1,12 +1,12 @@
1
1
  /**
2
- * Plugin 注册表。
2
+ * Agent plugin runtime。
3
3
  *
4
4
  * 关键点(中文)
5
- * - 统一管理 plugin 注册、可用性检查与显式 action 运行。
6
- * - Plugin 自身不维护独立状态机;可用性由 enabled 配置与 plugin 自定义 availability 决定。
5
+ * - Plugin 只属于 Agent:注册即生效,卸载即不可见。
6
+ * - 注册时自动启动 plugin lifecycle;卸载时自动停止 plugin lifecycle。
7
+ * - action、system、hook、resolve 都统一以“已注册且 ready”为生效边界。
7
8
  */
8
9
 
9
- import { isPluginEnabled } from "@/plugin/core/Activation.js";
10
10
  import { toPluginView } from "@/plugin/core/PluginCatalog.js";
11
11
  import type { HookRegistry } from "@/plugin/core/HookRegistry.js";
12
12
  import type {
@@ -18,42 +18,218 @@ import type {
18
18
  PluginReadView,
19
19
  PluginView,
20
20
  } from "@/plugin/types/Plugin.js";
21
+ import type { AgentSessionSystemBlock } from "@/types/agent/AgentTypes.js";
21
22
  import type { AgentContext } from "@/types/runtime/agent/AgentContext.js";
22
23
  import type { JsonValue } from "@/types/common/Json.js";
24
+ import type {
25
+ PluginRuntimeRecord,
26
+ PluginSnapshot,
27
+ } from "@/types/plugin/PluginState.js";
23
28
 
24
29
  type ContextResolver = () => AgentContext;
25
30
 
31
+ function now_ms(): number {
32
+ return Date.now();
33
+ }
34
+
35
+ function normalize_plugin_name(plugin_name: string): string {
36
+ return String(plugin_name || "").trim();
37
+ }
38
+
39
+ function create_record(plugin: Plugin): PluginRuntimeRecord {
40
+ const current_time = now_ms();
41
+ return {
42
+ plugin,
43
+ state: "ready",
44
+ registered_at: current_time,
45
+ updated_at: current_time,
46
+ chain: Promise.resolve(),
47
+ lifecycle_started: false,
48
+ };
49
+ }
50
+
51
+ function to_plugin_snapshot(record: PluginRuntimeRecord): PluginSnapshot {
52
+ const plugin = record.plugin;
53
+ const last_error = String(record.last_error || "").trim();
54
+ return {
55
+ name: plugin.name,
56
+ title: String(plugin.title || plugin.name || "").trim(),
57
+ description: String(plugin.description || "").trim(),
58
+ status: record.state,
59
+ state: record.state,
60
+ registered_at: record.registered_at,
61
+ updated_at: record.updated_at,
62
+ updatedAt: record.updated_at,
63
+ ...(last_error ? { last_error, lastError: last_error } : {}),
64
+ };
65
+ }
66
+
67
+ function update_record_state(
68
+ record: PluginRuntimeRecord,
69
+ state: PluginRuntimeRecord["state"],
70
+ error?: string,
71
+ ): void {
72
+ record.state = state;
73
+ record.updated_at = now_ms();
74
+ const normalized_error = String(error || "").trim();
75
+ if (normalized_error) {
76
+ record.last_error = normalized_error;
77
+ } else {
78
+ delete record.last_error;
79
+ }
80
+ }
81
+
82
+ async function run_serial(
83
+ record: PluginRuntimeRecord,
84
+ step: () => Promise<void> | void,
85
+ ): Promise<void> {
86
+ const next = record.chain.then(() => Promise.resolve(step()));
87
+ record.chain = next.then(
88
+ () => undefined,
89
+ () => undefined,
90
+ );
91
+ await next;
92
+ }
93
+
26
94
  /**
27
- * PluginRegistry:plugin 注册与调度实现。
95
+ * PluginRegistry:Agent plugin 注册、卸载与调用实现。
28
96
  */
29
97
  export class PluginRegistry implements AgentPlugins {
30
98
  private readonly contextResolver: ContextResolver;
31
99
 
32
100
  private readonly hookRegistry: HookRegistry;
33
101
 
34
- private readonly plugins = new Map<string, Plugin>();
102
+ private readonly pluginInstances: Map<string, Plugin>;
103
+
104
+ private readonly records = new Map<string, PluginRuntimeRecord>();
35
105
 
36
106
  constructor(params: {
37
107
  contextResolver: ContextResolver;
38
108
  hookRegistry: HookRegistry;
109
+ pluginInstances: Map<string, Plugin>;
39
110
  }) {
40
111
  this.contextResolver = params.contextResolver;
41
112
  this.hookRegistry = params.hookRegistry;
113
+ this.pluginInstances = params.pluginInstances;
42
114
  }
43
115
 
44
116
  /**
45
- * 注册单个 Plugin
117
+ * 注册单个 plugin
118
+ *
119
+ * 说明(中文)
120
+ * - 同名注册表示替换:先卸载旧实例,再注册并启动新实例。
121
+ * - 如果新实例启动失败,会自动回滚为未注册状态并抛错。
46
122
  */
47
- register(plugin: Plugin): void {
48
- const key = String(plugin.name || "").trim();
123
+ async register(plugin: Plugin): Promise<PluginSnapshot> {
124
+ const key = normalize_plugin_name(plugin.name);
49
125
  if (!key) {
50
126
  throw new Error("Plugin name is required");
51
127
  }
52
- if (this.plugins.has(key)) {
128
+ if (this.records.has(key)) {
129
+ await this.unregister(key);
130
+ }
131
+
132
+ const record = create_record(plugin);
133
+ this.records.set(key, record);
134
+ this.register_hooks(plugin);
135
+ this.pluginInstances.set(key, plugin);
136
+
137
+ try {
138
+ await this.start_record(record);
139
+ return to_plugin_snapshot(record);
140
+ } catch (error) {
141
+ this.unregister_hooks(key);
142
+ this.records.delete(key);
143
+ this.pluginInstances.delete(key);
144
+ throw error;
145
+ }
146
+ }
147
+
148
+ /**
149
+ * 同步挂载 plugin 元信息。
150
+ *
151
+ * 说明(中文)
152
+ * - 仅供 Agent 构造期使用,避免构造函数里 await。
153
+ * - 后续 `startAll()` 会统一启动这些初始 plugin。
154
+ */
155
+ mount(plugin: Plugin): PluginSnapshot {
156
+ const key = normalize_plugin_name(plugin.name);
157
+ if (!key) {
158
+ throw new Error("Plugin name is required");
159
+ }
160
+ if (this.records.has(key)) {
53
161
  throw new Error(`Plugin already registered: ${key}`);
54
162
  }
55
- this.plugins.set(key, plugin);
56
163
 
164
+ const record = create_record(plugin);
165
+ this.records.set(key, record);
166
+ this.register_hooks(plugin);
167
+ this.pluginInstances.set(key, plugin);
168
+ return to_plugin_snapshot(record);
169
+ }
170
+
171
+ /**
172
+ * 卸载指定 plugin。
173
+ */
174
+ async unregister(pluginName: string): Promise<boolean> {
175
+ const key = normalize_plugin_name(pluginName);
176
+ if (!key) return false;
177
+ const record = this.records.get(key);
178
+ if (!record) return false;
179
+
180
+ await this.stop_record(record);
181
+ this.unregister_hooks(key);
182
+ this.records.delete(key);
183
+ this.pluginInstances.delete(key);
184
+ return true;
185
+ }
186
+
187
+ /**
188
+ * 启动全部已挂载 plugin。
189
+ */
190
+ async startAll(): Promise<PluginSnapshot[]> {
191
+ const snapshots: PluginSnapshot[] = [];
192
+ for (const record of this.records.values()) {
193
+ await this.start_record(record);
194
+ snapshots.push(to_plugin_snapshot(record));
195
+ }
196
+ return snapshots;
197
+ }
198
+
199
+ /**
200
+ * 卸载全部 plugin。
201
+ */
202
+ async unregisterAll(): Promise<void> {
203
+ for (const name of Array.from(this.records.keys())) {
204
+ await this.unregister(name);
205
+ }
206
+ }
207
+
208
+ /**
209
+ * 判断 plugin 是否已注册且 ready。
210
+ */
211
+ isReady(pluginName: string): boolean {
212
+ const record = this.records.get(normalize_plugin_name(pluginName));
213
+ return Boolean(record && record.state === "ready");
214
+ }
215
+
216
+ /**
217
+ * 读取单个 plugin 快照。
218
+ */
219
+ status(pluginName: string): PluginSnapshot | null {
220
+ const record = this.records.get(normalize_plugin_name(pluginName));
221
+ return record ? to_plugin_snapshot(record) : null;
222
+ }
223
+
224
+ /**
225
+ * 判断 plugin 是否已注册。
226
+ */
227
+ has(pluginName: string): boolean {
228
+ return this.records.has(normalize_plugin_name(pluginName));
229
+ }
230
+
231
+ private register_hooks(plugin: Plugin): void {
232
+ const key = normalize_plugin_name(plugin.name);
57
233
  for (const [hookName, handlers] of Object.entries(
58
234
  plugin.hooks?.pipeline || {},
59
235
  )) {
@@ -83,6 +259,43 @@ export class PluginRegistry implements AgentPlugins {
83
259
  }
84
260
  }
85
261
 
262
+ private unregister_hooks(pluginName: string): void {
263
+ this.hookRegistry.unregisterPlugin(pluginName);
264
+ }
265
+
266
+ private async start_record(record: PluginRuntimeRecord): Promise<void> {
267
+ if (record.lifecycle_started) {
268
+ update_record_state(record, "ready");
269
+ return;
270
+ }
271
+ await run_serial(record, async () => {
272
+ if (record.lifecycle_started) {
273
+ update_record_state(record, "ready");
274
+ return;
275
+ }
276
+ try {
277
+ await record.plugin.lifecycle?.start?.(this.contextResolver());
278
+ record.lifecycle_started = true;
279
+ update_record_state(record, "ready");
280
+ } catch (error) {
281
+ update_record_state(record, "error", String(error));
282
+ throw error;
283
+ }
284
+ });
285
+ }
286
+
287
+ private async stop_record(record: PluginRuntimeRecord): Promise<void> {
288
+ await run_serial(record, async () => {
289
+ if (!record.lifecycle_started) return;
290
+ try {
291
+ await record.plugin.lifecycle?.stop?.(this.contextResolver());
292
+ } finally {
293
+ record.lifecycle_started = false;
294
+ record.updated_at = now_ms();
295
+ }
296
+ });
297
+ }
298
+
86
299
  /**
87
300
  * 运行 pipeline 点。
88
301
  */
@@ -118,15 +331,24 @@ export class PluginRegistry implements AgentPlugins {
118
331
  * 获取单个 plugin 定义。
119
332
  */
120
333
  get(pluginName: string): Plugin | null {
121
- return this.plugins.get(String(pluginName || "").trim()) || null;
334
+ return this.records.get(normalize_plugin_name(pluginName))?.plugin || null;
122
335
  }
123
336
 
124
337
  /**
125
338
  * 列出全部 plugin 概览视图。
126
339
  */
127
340
  list(): PluginView[] {
128
- return Array.from(this.plugins.values())
129
- .map((plugin) => toPluginView(plugin))
341
+ return Array.from(this.records.values())
342
+ .map((record) => toPluginView(record.plugin))
343
+ .sort((a, b) => a.name.localeCompare(b.name));
344
+ }
345
+
346
+ /**
347
+ * 列出全部 plugin 注册快照。
348
+ */
349
+ snapshots(): PluginSnapshot[] {
350
+ return Array.from(this.records.values())
351
+ .map((record) => to_plugin_snapshot(record))
130
352
  .sort((a, b) => a.name.localeCompare(b.name));
131
353
  }
132
354
 
@@ -158,7 +380,7 @@ export class PluginRegistry implements AgentPlugins {
158
380
  plugin?: string;
159
381
  action?: string;
160
382
  }): PluginReadView | { plugins: PluginView[] } {
161
- const pluginName = String(params.plugin || "").trim();
383
+ const pluginName = normalize_plugin_name(params.plugin || "");
162
384
  if (!pluginName) {
163
385
  return { plugins: this.list() };
164
386
  }
@@ -171,7 +393,7 @@ export class PluginRegistry implements AgentPlugins {
171
393
  actions: [],
172
394
  };
173
395
  }
174
- const actionName = String(params.action || "").trim();
396
+ const actionName = normalize_plugin_name(params.action || "");
175
397
  const actions = Object.entries(plugin.actions || {})
176
398
  .filter(([name]) => !actionName || name === actionName)
177
399
  .sort(([left], [right]) => left.localeCompare(right))
@@ -188,8 +410,9 @@ export class PluginRegistry implements AgentPlugins {
188
410
  * 检查 plugin 可用性。
189
411
  */
190
412
  async availability(pluginName: string): Promise<PluginAvailability> {
191
- const plugin = this.get(pluginName);
192
- if (!plugin) {
413
+ const key = normalize_plugin_name(pluginName);
414
+ const record = this.records.get(key);
415
+ if (!record) {
193
416
  return {
194
417
  enabled: false,
195
418
  available: false,
@@ -197,20 +420,18 @@ export class PluginRegistry implements AgentPlugins {
197
420
  };
198
421
  }
199
422
 
200
- if (plugin.availability) {
201
- return await plugin.availability(this.contextResolver());
202
- }
203
-
204
- const context = this.contextResolver();
205
- const enabled = isPluginEnabled({ plugin, context });
206
-
207
- if (!enabled) {
423
+ if (record.state !== "ready") {
208
424
  return {
209
- enabled: false,
425
+ enabled: true,
210
426
  available: false,
211
- reasons: [`Plugin "${plugin.name}" is disabled`],
427
+ reasons: [record.last_error || `Plugin "${record.plugin.name}" is not ready`],
212
428
  };
213
429
  }
430
+
431
+ if (record.plugin.availability) {
432
+ return await record.plugin.availability(this.contextResolver());
433
+ }
434
+
214
435
  return {
215
436
  enabled: true,
216
437
  available: true,
@@ -248,8 +469,9 @@ export class PluginRegistry implements AgentPlugins {
248
469
  action: string;
249
470
  payload?: JsonValue;
250
471
  }): Promise<PluginActionResult<JsonValue>> {
251
- const plugin = this.get(params.plugin);
252
- if (!plugin) {
472
+ const key = normalize_plugin_name(params.plugin);
473
+ const record = this.records.get(key);
474
+ if (!record) {
253
475
  return {
254
476
  success: false,
255
477
  error: `Unknown plugin: ${params.plugin}`,
@@ -257,7 +479,7 @@ export class PluginRegistry implements AgentPlugins {
257
479
  };
258
480
  }
259
481
 
260
- const actionName = String(params.action || "").trim();
482
+ const actionName = normalize_plugin_name(params.action);
261
483
  if (!actionName) {
262
484
  return {
263
485
  success: false,
@@ -266,28 +488,26 @@ export class PluginRegistry implements AgentPlugins {
266
488
  };
267
489
  }
268
490
 
269
- const action = plugin.actions?.[actionName];
270
- if (!action) {
491
+ if (record.state !== "ready") {
271
492
  return {
272
493
  success: false,
273
- error: `Plugin "${plugin.name}" does not implement action "${actionName}"`,
274
- message: `Plugin "${plugin.name}" does not implement action "${actionName}"`,
494
+ error: `Plugin "${record.plugin.name}" is not ready`,
495
+ message: `Plugin "${record.plugin.name}" is not ready`,
275
496
  };
276
497
  }
277
498
 
278
- const context = this.contextResolver();
279
- const enabled = isPluginEnabled({ plugin, context });
280
- if (!enabled && action.allowWhenDisabled !== true) {
499
+ const action = record.plugin.actions?.[actionName];
500
+ if (!action) {
281
501
  return {
282
502
  success: false,
283
- error: `Plugin "${plugin.name}" is disabled`,
284
- message: `Plugin "${plugin.name}" is disabled`,
503
+ error: `Plugin "${record.plugin.name}" does not implement action "${actionName}"`,
504
+ message: `Plugin "${record.plugin.name}" does not implement action "${actionName}"`,
285
505
  };
286
506
  }
287
507
 
288
508
  try {
289
509
  const parsed_payload = this.parseActionPayload({
290
- pluginName: plugin.name,
510
+ pluginName: record.plugin.name,
291
511
  actionName,
292
512
  payload: (params.payload ?? {}) as JsonValue,
293
513
  action,
@@ -295,14 +515,19 @@ export class PluginRegistry implements AgentPlugins {
295
515
  if (!("input" in parsed_payload)) {
296
516
  return parsed_payload;
297
517
  }
298
- return await action.execute({
299
- context,
518
+ const result = await action.execute({
519
+ context: this.contextResolver(),
300
520
  payload: parsed_payload.input,
301
521
  input: parsed_payload.input,
302
- pluginName: plugin.name,
522
+ pluginName: record.plugin.name,
303
523
  actionName,
304
524
  });
525
+ if (!result.success) {
526
+ update_record_state(record, "error", result.error || result.message);
527
+ }
528
+ return result;
305
529
  } catch (error) {
530
+ update_record_state(record, "error", String(error));
306
531
  return {
307
532
  success: false,
308
533
  error: String(error),
@@ -310,4 +535,33 @@ export class PluginRegistry implements AgentPlugins {
310
535
  };
311
536
  }
312
537
  }
538
+
539
+ /**
540
+ * 读取当前生效的 plugin system blocks。
541
+ */
542
+ async systemBlocks(): Promise<AgentSessionSystemBlock[]> {
543
+ const context = this.contextResolver();
544
+ const out: AgentSessionSystemBlock[] = [];
545
+ for (const record of this.records.values()) {
546
+ const plugin = record.plugin;
547
+ if (record.state !== "ready") continue;
548
+ if (typeof plugin.system !== "function") continue;
549
+ try {
550
+ if (typeof plugin.availability === "function") {
551
+ const availability = await plugin.availability(context);
552
+ if (!availability.available) continue;
553
+ }
554
+ const text = String(await plugin.system(context)).trim();
555
+ if (!text) continue;
556
+ out.push({
557
+ source: "plugin",
558
+ name: plugin.name,
559
+ content: text,
560
+ });
561
+ } catch {
562
+ // 单个 plugin system 失败不应阻断 session 主链路。
563
+ }
564
+ }
565
+ return out;
566
+ }
313
567
  }