@actiondock/core 2.7.0 → 2.8.0

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 (236) hide show
  1. package/README.md +61 -68
  2. package/dist/catalog/action-catalog.d.ts +56 -0
  3. package/dist/catalog/action-catalog.js +202 -0
  4. package/dist/catalog/discovery.d.ts +60 -0
  5. package/dist/catalog/discovery.js +119 -0
  6. package/dist/catalog/graph.d.ts +190 -0
  7. package/dist/catalog/graph.js +589 -0
  8. package/dist/catalog/index.d.ts +5 -5
  9. package/dist/catalog/index.js +5 -5
  10. package/dist/catalog/resolve-action.d.ts +43 -0
  11. package/dist/catalog/resolve-action.js +119 -0
  12. package/dist/catalog/resolve-playbook.d.ts +26 -0
  13. package/dist/catalog/resolve-playbook.js +114 -0
  14. package/dist/doctor/checks/project.js +27 -8
  15. package/dist/doctor/checks/registry.js +2 -2
  16. package/dist/doctor/checks/runtime.js +34 -1
  17. package/dist/errors.d.ts +102 -10
  18. package/dist/errors.js +103 -10
  19. package/dist/execution/service.d.ts +18 -16
  20. package/dist/execution/service.js +183 -252
  21. package/dist/execution/types.d.ts +25 -69
  22. package/dist/execution/types.js +2 -1
  23. package/dist/filter/intent.d.ts +8 -9
  24. package/dist/filter/intent.js +49 -10
  25. package/dist/graph/index.d.ts +4 -0
  26. package/dist/graph/index.js +4 -0
  27. package/dist/host/host.d.ts +74 -32
  28. package/dist/host/host.js +585 -336
  29. package/dist/host/routing.d.ts +15 -48
  30. package/dist/host/routing.js +90 -153
  31. package/dist/host/types.d.ts +50 -17
  32. package/dist/index.d.ts +21 -24
  33. package/dist/index.js +19 -23
  34. package/dist/input/advice.d.ts +5 -1
  35. package/dist/input/advice.js +163 -214
  36. package/dist/input/describe.d.ts +1 -1
  37. package/dist/input/flat-errors.d.ts +5 -6
  38. package/dist/input/flat-errors.js +6 -6
  39. package/dist/input/flat-materializer.js +42 -44
  40. package/dist/invocation/index.d.ts +2 -0
  41. package/dist/invocation/index.js +2 -0
  42. package/dist/invocation/policy.d.ts +157 -0
  43. package/dist/invocation/policy.js +231 -0
  44. package/dist/invocation/types.d.ts +172 -0
  45. package/dist/invocation/types.js +97 -0
  46. package/dist/ipc/diagnostic.d.ts +0 -12
  47. package/dist/ipc/diagnostic.js +0 -21
  48. package/dist/ipc/host.d.ts +4 -4
  49. package/dist/ipc/host.js +44 -47
  50. package/dist/ipc/index.d.ts +1 -1
  51. package/dist/ipc/index.js +1 -1
  52. package/dist/ipc/service.d.ts +40 -0
  53. package/dist/ipc/service.js +349 -0
  54. package/dist/ipc/types.d.ts +21 -15
  55. package/dist/node/index.d.ts +1 -0
  56. package/dist/node/index.js +1 -0
  57. package/dist/node/module-loader.d.ts +63 -0
  58. package/dist/node/module-loader.js +112 -0
  59. package/dist/package/index.d.ts +23 -0
  60. package/dist/package/index.js +18 -0
  61. package/dist/{app/app.d.ts → package/runtime.d.ts} +41 -20
  62. package/dist/{app/app.js → package/runtime.js} +140 -95
  63. package/dist/{app → package}/static-index.js +58 -41
  64. package/dist/package/storage-view.d.ts +13 -0
  65. package/dist/package/storage-view.js +22 -0
  66. package/dist/{app → package}/types.d.ts +76 -57
  67. package/dist/package/types.js +22 -0
  68. package/dist/platform/index.d.ts +1 -1
  69. package/dist/platform/index.js +1 -1
  70. package/dist/platform/node.d.ts +48 -0
  71. package/dist/platform/node.js +96 -0
  72. package/dist/platform/types.d.ts +0 -9
  73. package/dist/process/control-arbiter.d.ts +2 -1
  74. package/dist/process/control-arbiter.js +1 -2
  75. package/dist/process/driver.d.ts +9 -8
  76. package/dist/process/driver.js +11 -22
  77. package/dist/process/index.d.ts +2 -0
  78. package/dist/process/index.js +2 -0
  79. package/dist/process/managed-record.d.ts +2 -2
  80. package/dist/process/process-driver.d.ts +109 -0
  81. package/dist/process/process-driver.js +650 -0
  82. package/dist/process/process-executor.d.ts +47 -0
  83. package/dist/process/process-executor.js +296 -0
  84. package/dist/process/process-manager.js +48 -26
  85. package/dist/process/run-executor.js +12 -12
  86. package/dist/profile/client-actions.js +2 -2
  87. package/dist/profile/client-health.js +2 -2
  88. package/dist/profile/client-query.js +5 -6
  89. package/dist/profile/client-transport.d.ts +19 -17
  90. package/dist/profile/client-transport.js +47 -80
  91. package/dist/profile/client.d.ts +2 -2
  92. package/dist/profile/client.js +1 -1
  93. package/dist/profile/index.d.ts +1 -0
  94. package/dist/profile/index.js +1 -0
  95. package/dist/profile/manager.d.ts +5 -0
  96. package/dist/profile/manager.js +41 -6
  97. package/dist/project/closure.js +2 -2
  98. package/dist/project/index.d.ts +8 -1
  99. package/dist/project/index.js +10 -1
  100. package/dist/project/init.js +2 -2
  101. package/dist/project/loader.d.ts +1 -1
  102. package/dist/project/loader.js +26 -29
  103. package/dist/project/transactions.js +5 -10
  104. package/dist/registry/index.d.ts +3 -1
  105. package/dist/registry/index.js +2 -1
  106. package/dist/registry/lock.d.ts +28 -7
  107. package/dist/registry/lock.js +136 -16
  108. package/dist/registry/registry.d.ts +1 -5
  109. package/dist/registry/registry.js +1 -53
  110. package/dist/registry/scan.d.ts +0 -15
  111. package/dist/registry/scan.js +0 -59
  112. package/dist/registry/store.d.ts +42 -0
  113. package/dist/registry/store.js +43 -0
  114. package/dist/registry/types.d.ts +0 -24
  115. package/dist/runtime/action-collection.d.ts +1 -1
  116. package/dist/runtime/action-registry.d.ts +0 -4
  117. package/dist/runtime/action-registry.js +0 -7
  118. package/dist/runtime/context.js +5 -11
  119. package/dist/runtime/events.js +3 -6
  120. package/dist/runtime/identity.d.ts +22 -0
  121. package/dist/runtime/identity.js +10 -0
  122. package/dist/runtime/index.d.ts +1 -2
  123. package/dist/runtime/index.js +1 -2
  124. package/dist/runtime/module-loader.d.ts +2 -34
  125. package/dist/runtime/module-loader.js +1 -16
  126. package/dist/runtime/process.d.ts +6 -24
  127. package/dist/runtime/process.js +1 -118
  128. package/dist/runtime/run-persistence.js +2 -5
  129. package/dist/runtime/runner.d.ts +33 -93
  130. package/dist/runtime/runner.js +132 -364
  131. package/dist/runtime/standalone.d.ts +23 -8
  132. package/dist/runtime/standalone.js +100 -147
  133. package/dist/server/body.d.ts +16 -8
  134. package/dist/server/body.js +33 -16
  135. package/dist/server/dispatcher.d.ts +6 -10
  136. package/dist/server/dispatcher.js +24 -32
  137. package/dist/server/http-server.d.ts +87 -0
  138. package/dist/server/http-server.js +304 -0
  139. package/dist/server/index.d.ts +11 -0
  140. package/dist/server/index.js +10 -0
  141. package/dist/server/mcp-endpoint.d.ts +61 -0
  142. package/dist/server/mcp-endpoint.js +118 -0
  143. package/dist/server/routes/actions.js +34 -28
  144. package/dist/server/routes/common.d.ts +10 -14
  145. package/dist/server/routes/common.js +32 -56
  146. package/dist/server/routes/config.js +54 -64
  147. package/dist/server/routes/doctor.d.ts +1 -1
  148. package/dist/server/routes/doctor.js +10 -18
  149. package/dist/server/routes/health.d.ts +1 -1
  150. package/dist/server/routes/health.js +2 -3
  151. package/dist/server/routes/info.d.ts +2 -2
  152. package/dist/server/routes/info.js +12 -13
  153. package/dist/server/routes/playbooks.js +16 -29
  154. package/dist/server/routes/runs.js +95 -117
  155. package/dist/server/routes/state.js +63 -62
  156. package/dist/server/server.d.ts +2 -2
  157. package/dist/server/server.js +43 -227
  158. package/dist/server/types.d.ts +8 -19
  159. package/dist/service/factory.d.ts +12 -0
  160. package/dist/service/factory.js +85 -0
  161. package/dist/{target → service}/index.d.ts +1 -1
  162. package/dist/{target → service}/index.js +1 -1
  163. package/dist/service/local.d.ts +27 -0
  164. package/dist/service/local.js +138 -0
  165. package/dist/{target → service}/remote-errors.d.ts +3 -6
  166. package/dist/service/remote-errors.js +38 -0
  167. package/dist/{target → service}/remote-polling.d.ts +2 -2
  168. package/dist/{target → service}/remote-polling.js +8 -8
  169. package/dist/service/remote.d.ts +34 -0
  170. package/dist/service/remote.js +634 -0
  171. package/dist/service/sse-stream.d.ts +24 -0
  172. package/dist/service/sse-stream.js +139 -0
  173. package/dist/service/types.d.ts +272 -0
  174. package/dist/service/types.js +32 -0
  175. package/dist/storage/data-dir-lock.js +10 -5
  176. package/dist/storage/driver.d.ts +2 -1
  177. package/dist/storage/driver.js +3 -52
  178. package/dist/storage/index.d.ts +1 -0
  179. package/dist/storage/index.js +1 -0
  180. package/dist/storage/lock-core.d.ts +2 -6
  181. package/dist/storage/lock-core.js +4 -17
  182. package/dist/storage/params.d.ts +2 -8
  183. package/dist/storage/params.js +2 -8
  184. package/dist/storage/sqlite-driver.d.ts +40 -0
  185. package/dist/storage/sqlite-driver.js +116 -0
  186. package/dist/storage/sqlite.js +21 -6
  187. package/dist/storage/types.d.ts +4 -4
  188. package/dist/storage/types.js +2 -2
  189. package/dist/utils/index.d.ts +53 -0
  190. package/dist/utils/index.js +99 -4
  191. package/dist/utils/process.d.ts +10 -0
  192. package/dist/utils/process.js +21 -0
  193. package/dist/version.d.ts +1 -1
  194. package/dist/version.js +1 -1
  195. package/package.json +41 -3
  196. package/dist/app/index.d.ts +0 -2
  197. package/dist/app/index.js +0 -2
  198. package/dist/app/types.js +0 -1
  199. package/dist/catalog/action-index.d.ts +0 -9
  200. package/dist/catalog/action-index.js +0 -95
  201. package/dist/catalog/action-resolver.d.ts +0 -20
  202. package/dist/catalog/action-resolver.js +0 -95
  203. package/dist/catalog/location-registry.d.ts +0 -15
  204. package/dist/catalog/location-registry.js +0 -138
  205. package/dist/catalog/package-catalog.d.ts +0 -7
  206. package/dist/catalog/package-catalog.js +0 -77
  207. package/dist/catalog/types.d.ts +0 -68
  208. package/dist/catalog/types.js +0 -1
  209. package/dist/export/index.d.ts +0 -1
  210. package/dist/export/index.js +0 -1
  211. package/dist/export/templates.d.ts +0 -71
  212. package/dist/export/templates.js +0 -635
  213. package/dist/ipc/target.d.ts +0 -74
  214. package/dist/ipc/target.js +0 -365
  215. package/dist/platform/default.d.ts +0 -38
  216. package/dist/platform/default.js +0 -78
  217. package/dist/project/resolver.d.ts +0 -116
  218. package/dist/project/resolver.js +0 -376
  219. package/dist/registry/resolve.d.ts +0 -62
  220. package/dist/registry/resolve.js +0 -195
  221. package/dist/runtime/execution-manager.d.ts +0 -48
  222. package/dist/runtime/execution-manager.js +0 -66
  223. package/dist/runtime/package-runner-factory.d.ts +0 -82
  224. package/dist/runtime/package-runner-factory.js +0 -186
  225. package/dist/target/local.d.ts +0 -53
  226. package/dist/target/local.js +0 -349
  227. package/dist/target/remote-errors.js +0 -51
  228. package/dist/target/remote.d.ts +0 -86
  229. package/dist/target/remote.js +0 -818
  230. package/dist/target/target.d.ts +0 -6
  231. package/dist/target/target.js +0 -37
  232. package/dist/target/types.d.ts +0 -261
  233. package/dist/target/types.js +0 -35
  234. /package/dist/{app → package}/static-index.d.ts +0 -0
  235. /package/dist/{target → service}/sse-parser.d.ts +0 -0
  236. /package/dist/{target → service}/sse-parser.js +0 -0
@@ -1,19 +1,24 @@
1
1
  import { existsSync } from "node:fs";
2
2
  import { join } from "node:path";
3
3
  import { DefaultExecutionService } from "../execution/service.js";
4
- import { createDefaultPlatform } from "../platform/index.js";
4
+ import { createNodePlatform } from "../platform/index.js";
5
5
  import { findProjectRoot, loadProjectConfig } from "../project/loader.js";
6
6
  import { RuntimeConfig } from "../runtime/context.js";
7
7
  import { normalizeActionCollection } from "../runtime/action-collection.js";
8
- import { createGlobalStorage, createLazyStorage, createStorage } from "../storage/index.js";
8
+ import { createLazyStorage } from "../storage/index.js";
9
9
  import { isSecretConfigKey, sanitizeConfigDefinitions } from "../storage/mask.js";
10
10
  import { decodeStateKey, SqliteRuntimeStorage } from "../storage/sqlite.js";
11
+ import { createPackageIdentity } from "../runtime/identity.js";
12
+ import { ACTION_NOT_FOUND, ActionDockError, CAPABILITY_UNAVAILABLE, INVALID_ARGUMENT, INVOCATION_UNSUPPORTED, NOT_FOUND, } from "../errors.js";
13
+ import { createRootInvocationContext } from "../invocation/types.js";
11
14
  import { buildStaticActionMap, buildStaticPlaybookMap } from "./static-index.js";
15
+ import { applyActionSummaryFilters } from "./types.js";
12
16
  /**
13
- * ActionDock 统一应用默认实现。
17
+ * ActionDock 统一包运行时默认实现。
14
18
  * 封装并管理单个 Action Package 的执行引擎、静态元数据索引、配置与状态存储生命周期。
15
19
  */
16
- export class DefaultActionDockApp {
20
+ export class DefaultPackageRuntime {
21
+ identity;
17
22
  packageId;
18
23
  packageInstanceId;
19
24
  generationId;
@@ -27,6 +32,7 @@ export class DefaultActionDockApp {
27
32
  options;
28
33
  runtimeConfig;
29
34
  isClosed = false;
35
+ injectedGlobalStorage = false;
30
36
  /** 静态清单索引缓存:包静态事实(根目录、配置、内存注入集合)在实例生命周期内不变,解析结果同实例内复用 */
31
37
  staticActionIndex;
32
38
  staticPlaybookIndex;
@@ -58,13 +64,18 @@ export class DefaultActionDockApp {
58
64
  }
59
65
  this.packageRoot = packageRoot;
60
66
  this.projectConfig = projectConfig;
61
- this.packageId = projectConfig.id;
62
- this.packageInstanceId = options.packageInstanceId || projectConfig.packageInstanceId || this.packageId;
63
- this.generationId = options.generationId || projectConfig.generationId || "1";
67
+ this.identity = options.identity || createPackageIdentity({
68
+ id: projectConfig.id,
69
+ instanceId: options?.packageInstanceId || projectConfig.packageInstanceId,
70
+ generation: options?.generationId || projectConfig.generationId,
71
+ });
72
+ this.packageId = this.identity.id;
73
+ this.packageInstanceId = this.identity.instanceId;
74
+ this.generationId = this.identity.generation;
64
75
  // 2. 转换 Action 集合:委托归一化单一入口
65
76
  this.actionsMap = normalizeActionCollection(options.actions).actionsMap;
66
77
  // 3. 确定平台适配层
67
- this.platform = options.platform ?? createDefaultPlatform();
78
+ this.platform = options.platform ?? createNodePlatform();
68
79
  // 4. 确定并初始化存储实例
69
80
  if (options.storage) {
70
81
  this.storage = options.storage;
@@ -79,20 +90,8 @@ export class DefaultActionDockApp {
79
90
  // CLI 查询旁观视图显式置 recoverOrphans: false 跳过收割
80
91
  recoverOrphans: options.recoverOrphans !== false,
81
92
  };
82
- const storageFactory = this.platform.storage;
83
93
  const initStorage = () => {
84
- if (typeof storageFactory === "function") {
85
- return storageFactory(this.packageId, storageOpts);
86
- }
87
- else if (storageFactory && typeof storageFactory.createStorage === "function") {
88
- return storageFactory.createStorage(this.packageId, storageOpts);
89
- }
90
- else if (storageFactory && typeof storageFactory.create === "function") {
91
- return storageFactory.create(this.packageId, storageOpts);
92
- }
93
- else {
94
- return createStorage(this.packageId, storageOpts);
95
- }
94
+ return this.platform.storage.createStorage(this.packageId, storageOpts);
96
95
  };
97
96
  if (options.inMemory) {
98
97
  this.storage = initStorage();
@@ -104,12 +103,14 @@ export class DefaultActionDockApp {
104
103
  // 5. 确定全局存储实例
105
104
  if (options.globalStorage) {
106
105
  this.globalStorage = options.globalStorage;
106
+ this.injectedGlobalStorage = true;
107
107
  }
108
108
  else if (options.inMemory) {
109
109
  this.globalStorage = new SqliteRuntimeStorage({
110
110
  dbPath: ":memory:",
111
111
  packageId: "__global__",
112
112
  });
113
+ this.injectedGlobalStorage = false;
113
114
  }
114
115
  else {
115
116
  const globalOpts = {
@@ -119,20 +120,15 @@ export class DefaultActionDockApp {
119
120
  // 与主存储保持同侧收割语义(全局库 runs 表为空集,收割无实际副作用)
120
121
  recoverOrphans: options.recoverOrphans !== false,
121
122
  };
122
- const storageFactory = this.platform.storage;
123
123
  const initGlobalStorage = () => {
124
- if (storageFactory && typeof storageFactory.createGlobalStorage === "function") {
125
- return storageFactory.createGlobalStorage(globalOpts);
126
- }
127
- return createGlobalStorage(globalOpts);
124
+ return this.platform.storage.createGlobalStorage(globalOpts);
128
125
  };
129
126
  this.globalStorage = createLazyStorage(initGlobalStorage);
127
+ this.injectedGlobalStorage = false;
130
128
  }
131
129
  // 6. 初始化唯一执行协调服务
132
130
  this.executionService = new DefaultExecutionService({
133
- packageId: this.packageId,
134
- packageInstanceId: this.packageInstanceId,
135
- generationId: this.generationId,
131
+ identity: this.identity,
136
132
  hostSessionId: options.hostSessionId,
137
133
  storage: this.storage,
138
134
  globalStorage: this.globalStorage,
@@ -145,14 +141,18 @@ export class DefaultActionDockApp {
145
141
  logger: options.logger,
146
142
  eventSink: options.eventSink ?? this.platform?.eventSink,
147
143
  maxActiveRuns: options.maxActiveRuns,
148
- maxCallDepth: options.maxCallDepth,
149
- maxSubRuns: options.maxSubRuns,
150
144
  ownerId: options.ownerId,
151
145
  actionResolver: options.actionResolver,
152
- packageContextResolver: options.packageContextResolver,
153
146
  customHome: options.customHome,
154
- platform: this.platform,
147
+ moduleLoader: this.platform.modules,
148
+ actionInvoker: options.actionInvoker,
155
149
  });
150
+ if (!options.actionInvoker) {
151
+ const unsupportedInvoker = async () => {
152
+ throw new ActionDockError(INVOCATION_UNSUPPORTED, "Cascaded action invocation (ctx.actions.invoke) is not supported in standalone PackageRuntime. Actions must be executed within an ActionDock Host.");
153
+ };
154
+ this.executionService.setActionInvoker?.(unsupportedInvoker);
155
+ }
156
156
  // 7. 初始化配置解析器
157
157
  this.runtimeConfig = new RuntimeConfig(this.storage, options.configOverrides, this.projectConfig, this.globalStorage);
158
158
  }
@@ -209,7 +209,7 @@ export class DefaultActionDockApp {
209
209
  }
210
210
  async listActions(options) {
211
211
  const map = this.getStaticActionMap();
212
- let summaries = Array.from(map.values()).map((spec) => ({
212
+ const summaries = Array.from(map.values()).map((spec) => ({
213
213
  id: spec.id,
214
214
  packageId: this.packageId,
215
215
  description: spec.description,
@@ -220,18 +220,7 @@ export class DefaultActionDockApp {
220
220
  inputSchema: spec.inputSchema,
221
221
  outputSchema: spec.outputSchema,
222
222
  }));
223
- if (options?.tags && options.tags.length > 0) {
224
- summaries = summaries.filter((s) => options.tags.every((t) => s.tags?.includes(t)));
225
- }
226
- if (options?.query) {
227
- const q = options.query.toLowerCase();
228
- summaries = summaries.filter((s) => s.id.toLowerCase().includes(q) ||
229
- (s.description && s.description.toLowerCase().includes(q)));
230
- }
231
- if (options?.prefix) {
232
- summaries = summaries.filter((s) => s.id.startsWith(options.prefix));
233
- }
234
- return summaries;
223
+ return applyActionSummaryFilters(summaries, options);
235
224
  }
236
225
  async describeAction(id) {
237
226
  const map = this.getStaticActionMap();
@@ -253,14 +242,14 @@ export class DefaultActionDockApp {
253
242
  this.executionService.getAction(id) ||
254
243
  (spec ? this.executionService.getAction(spec.id) : undefined);
255
244
  }
256
- if (!liveAction && this.executionService.runner?.resolveAction) {
245
+ if (!liveAction && this.executionService.resolveAction) {
257
246
  try {
258
247
  // 仅在本包范围内解析动态动作:限定 packageId 前缀,避免全局兜底搜索把
259
248
  // 其他包的动作误计为本包提供者(host 层据此统计候选导致 AMBIGUOUS_ACTION_REF 误报)。
260
249
  const qualifiedId = id.includes("/") ? id : `${this.packageId}/${id}`;
261
- const resolution = await this.executionService.runner.resolveAction(qualifiedId);
262
- if (resolution.status === "found") {
263
- liveAction = resolution.action;
250
+ const resolution = await this.executionService.resolveAction(qualifiedId);
251
+ if (resolution) {
252
+ liveAction = resolution;
264
253
  }
265
254
  }
266
255
  catch {
@@ -283,7 +272,7 @@ export class DefaultActionDockApp {
283
272
  };
284
273
  }
285
274
  if (!spec) {
286
- throw new Error(`Action '${id}' not found in package '${this.packageId}'`);
275
+ throw new ActionDockError(ACTION_NOT_FOUND, `Action '${id}' not found in package '${this.packageId}'`);
287
276
  }
288
277
  return {
289
278
  ...spec,
@@ -305,13 +294,28 @@ export class DefaultActionDockApp {
305
294
  const cleanId = id.replace(/\.md$/, "");
306
295
  const spec = map.get(cleanId) || map.get(id);
307
296
  if (!spec) {
308
- throw new Error(`Playbook '${id}' not found in package '${this.packageId}'`);
297
+ throw new ActionDockError(NOT_FOUND, `Playbook '${id}' not found in package '${this.packageId}'`);
309
298
  }
310
299
  return {
311
300
  ...spec,
312
301
  packageId: this.packageId,
313
302
  };
314
303
  }
304
+ buildRootInvocationContext(actionId, options) {
305
+ const rootKey = `${this.packageId}/${actionId}`;
306
+ return createRootInvocationContext({
307
+ targetPackage: this.identity,
308
+ callStack: [rootKey],
309
+ signal: options?.signal,
310
+ timeoutMs: options?.timeoutMs,
311
+ config: options?.config,
312
+ requestId: options?.requestId,
313
+ hostSessionId: this.options.hostSessionId,
314
+ maxCallDepth: this.options.maxCallDepth,
315
+ logger: this.options.logger,
316
+ process: this.options.process ?? this.platform.process,
317
+ });
318
+ }
315
319
  async runAction(id, input, options) {
316
320
  let actionId = id;
317
321
  if (actionId.includes("/")) {
@@ -319,10 +323,11 @@ export class DefaultActionDockApp {
319
323
  actionId = actionId.slice(this.packageId.length + 1);
320
324
  }
321
325
  else {
322
- throw new Error(`ActionDockApp only accepts local action short ID '${id}'. Cross-package invocations must be dispatched via Host invoker.`);
326
+ throw new ActionDockError(INVOCATION_UNSUPPORTED, `PackageRuntime only accepts local action short ID '${id}'. Cross-package invocations must be dispatched via Host invoker.`);
323
327
  }
324
328
  }
325
- return this.executionService.execute(actionId, input, options);
329
+ const context = this.buildRootInvocationContext(actionId, options);
330
+ return this.executionService.execute(actionId, input, context);
326
331
  }
327
332
  async startAction(id, input, options) {
328
333
  let actionId = id;
@@ -331,10 +336,18 @@ export class DefaultActionDockApp {
331
336
  actionId = actionId.slice(this.packageId.length + 1);
332
337
  }
333
338
  else {
334
- throw new Error(`ActionDockApp only accepts local action short ID '${id}'. Cross-package invocations must be dispatched via Host invoker.`);
339
+ throw new ActionDockError(INVOCATION_UNSUPPORTED, `PackageRuntime only accepts local action short ID '${id}'. Cross-package invocations must be dispatched via Host invoker.`);
335
340
  }
336
341
  }
337
- return this.executionService.start(actionId, input, options);
342
+ const context = this.buildRootInvocationContext(actionId, options);
343
+ return this.executionService.start(actionId, input, context);
344
+ }
345
+ async startInvocation(actionId, input, context) {
346
+ let targetId = actionId;
347
+ if (targetId.startsWith(`${this.packageId}/`)) {
348
+ targetId = targetId.slice(this.packageId.length + 1);
349
+ }
350
+ return this.executionService.start(targetId, input, context);
338
351
  }
339
352
  async getRun(runId) {
340
353
  return this.executionService.get(runId);
@@ -345,6 +358,16 @@ export class DefaultActionDockApp {
345
358
  events(runId, options) {
346
359
  return this.executionService.events(runId, options);
347
360
  }
361
+ setActionInvoker(invoker) {
362
+ if (this.executionService && typeof this.executionService.setActionInvoker === "function") {
363
+ this.executionService.setActionInvoker(invoker);
364
+ }
365
+ }
366
+ recoverDeadSessionRuns(sessionId) {
367
+ if (typeof this.storage.recoverDeadSessionRuns === "function") {
368
+ this.storage.recoverDeadSessionRuns(sessionId);
369
+ }
370
+ }
348
371
  async listConfig() {
349
372
  const declared = this.projectConfig.config || {};
350
373
  const stored = this.storage.listConfig();
@@ -383,36 +406,41 @@ export class DefaultActionDockApp {
383
406
  /**
384
407
  * 状态作用域统一解析辅助函数(单一事实源)。
385
408
  *
386
- * 完成重载消歧与 actionId 冲突校验,返回最终生效的命名空间:
387
- * - actionId 存在时拼 接 `${actionId}:${namespace}` 或直接 actionId;
388
- * - 无 actionId 时返回显式 namespace 或空字符串(包级扁平状态)。
409
+ * 完成重载消歧后委托 mergeStateScope 计算最终生效的命名空间:
410
+ * - (actionId, key, options) 形态:位置 actionId 参与冲突校验;
411
+ * - (key, options) 扁平形态:首参是状态键,options.actionId 属合法显式指定。
389
412
  */
390
413
  resolveStateScope(actionIdOrKey, keyOrOptions, options) {
391
- let actionId;
392
- let resolvedOpts;
393
414
  // 消歧规则(与原始重载语义严格一致):仅当第二参为字符串时认定为
394
415
  // (actionId, key, options) 形态;否则(undefined 或选项对象)认定为
395
416
  // (key, options) 扁平调用形态,首参是状态键而非 actionId
396
417
  if (typeof keyOrOptions === "string") {
397
- actionId = actionIdOrKey;
398
- resolvedOpts = options;
399
- if (resolvedOpts?.actionId && resolvedOpts.actionId !== actionId) {
400
- throw new Error(`Conflicting actionId specified: positional '${actionId}' vs options.actionId '${resolvedOpts.actionId}'`);
418
+ return { ns: this.mergeStateScope(actionIdOrKey, options), opts: options };
419
+ }
420
+ return { ns: this.mergeStateScope(undefined, keyOrOptions), opts: keyOrOptions };
421
+ }
422
+ /**
423
+ * 合并位置 actionId 与 options.actionId 并计算最终命名空间(单一事实源)。
424
+ *
425
+ * - positionalActionId 非 undefined:调用形态携带位置 actionId,
426
+ * options.actionId 与其冲突时抛出异常(空串同样参与冲突校验);
427
+ * - positionalActionId 为 undefined:无位置 actionId 的扁平调用形态,
428
+ * options.actionId 属合法显式指定,直接采纳。
429
+ */
430
+ mergeStateScope(positionalActionId, opts) {
431
+ let actionId;
432
+ if (positionalActionId !== undefined) {
433
+ actionId = positionalActionId;
434
+ if (opts?.actionId && opts.actionId !== actionId) {
435
+ throw new ActionDockError(INVALID_ARGUMENT, `Conflicting actionId specified: positional '${actionId}' vs options.actionId '${opts.actionId}'`);
401
436
  }
402
437
  }
403
438
  else {
404
- // (key, options) 扁平调用:首参是状态键,无位置 actionId;
405
- // options.actionId 属于合法的显式指定,直接采纳而非判为冲突
406
- actionId = "";
407
- resolvedOpts = keyOrOptions;
408
- }
409
- if (!actionId && resolvedOpts?.actionId) {
410
- actionId = resolvedOpts.actionId;
439
+ actionId = opts?.actionId ?? "";
411
440
  }
412
- const ns = actionId
413
- ? (resolvedOpts?.namespace ? `${actionId}:${resolvedOpts.namespace}` : actionId)
414
- : (resolvedOpts?.namespace ?? "");
415
- return { ns, opts: resolvedOpts };
441
+ return actionId
442
+ ? (opts?.namespace ? `${actionId}:${opts.namespace}` : actionId)
443
+ : (opts?.namespace ?? "");
416
444
  }
417
445
  async getState(actionIdOrKey, keyOrOptions, options) {
418
446
  const { ns, opts } = this.resolveStateScope(actionIdOrKey, keyOrOptions, options);
@@ -431,18 +459,16 @@ export class DefaultActionDockApp {
431
459
  let key;
432
460
  let value;
433
461
  let opts;
434
- let actionId;
462
+ // 携带位置 actionId 的调用形态标记:undefined 表示扁平 (key, value[, options]) 形态
463
+ let positionalActionId;
464
+ // 消歧规则(与原始重载语义严格一致):按实参个数区分三形态
435
465
  if (arguments.length >= 4) {
436
- if (options?.actionId && options.actionId !== actionIdOrKey) {
437
- throw new Error(`Conflicting actionId specified: positional '${actionIdOrKey}' vs options.actionId '${options.actionId}'`);
438
- }
439
- actionId = actionIdOrKey;
466
+ positionalActionId = actionIdOrKey;
440
467
  key = keyOrValue;
441
468
  value = valueOrOptions;
442
469
  opts = options;
443
470
  }
444
471
  else if (arguments.length === 2) {
445
- actionId = "";
446
472
  key = actionIdOrKey;
447
473
  value = keyOrValue;
448
474
  opts = undefined;
@@ -450,16 +476,14 @@ export class DefaultActionDockApp {
450
476
  else {
451
477
  if (valueOrOptions !== undefined &&
452
478
  (typeof valueOrOptions !== "object" || valueOrOptions === null || Array.isArray(valueOrOptions))) {
453
- throw new Error("Invalid options provided to setState. Use setActionState(actionId, key, value, options) or 4-argument setState for action state.");
479
+ throw new ActionDockError(INVALID_ARGUMENT, "Invalid options provided to setState. Use setActionState(actionId, key, value, options) or 4-argument setState for action state.");
454
480
  }
455
481
  key = actionIdOrKey;
456
482
  value = keyOrValue;
457
483
  opts = valueOrOptions;
458
- actionId = opts?.actionId ?? "";
459
484
  }
460
- const ns = actionId
461
- ? (opts?.namespace ? `${actionId}:${opts.namespace}` : actionId)
462
- : (opts?.namespace ?? "");
485
+ // 作用域解析与命名空间计算统一委托 mergeStateScope 单一事实源
486
+ const ns = this.mergeStateScope(positionalActionId, opts);
463
487
  if (ns) {
464
488
  await this.storage.setState(ns, key, value, opts?.ttl);
465
489
  return;
@@ -546,6 +570,25 @@ export class DefaultActionDockApp {
546
570
  all: opts?.all,
547
571
  });
548
572
  }
573
+ async listRuns(options) {
574
+ return this.storage.listRuns({
575
+ actionId: options?.actionId,
576
+ status: options?.status,
577
+ limit: options?.limit,
578
+ });
579
+ }
580
+ async clearRuns(options) {
581
+ return this.storage.clearRuns({
582
+ actionId: options?.actionId,
583
+ status: options?.status,
584
+ });
585
+ }
586
+ async listStateEntries(options) {
587
+ if (typeof this.storage.listStateEntries === "function") {
588
+ return this.storage.listStateEntries(options);
589
+ }
590
+ throw new ActionDockError(CAPABILITY_UNAVAILABLE, `CAPABILITY_UNAVAILABLE: listStateEntries is not supported by package '${this.packageId}' storage`);
591
+ }
549
592
  async close(options) {
550
593
  if (this.isClosed)
551
594
  return;
@@ -561,19 +604,21 @@ export class DefaultActionDockApp {
561
604
  // 忽略存储重复关闭异常
562
605
  }
563
606
  finally {
564
- try {
565
- this.globalStorage?.close();
566
- }
567
- catch {
568
- // 忽略全局存储关闭异常
607
+ if (!this.injectedGlobalStorage) {
608
+ try {
609
+ this.globalStorage?.close();
610
+ }
611
+ catch {
612
+ // 忽略全局存储关闭异常
613
+ }
569
614
  }
570
615
  }
571
616
  }
572
617
  }
573
618
  }
574
619
  /**
575
- * 工厂函数:创建并初始化 ActionDockApp 实例。
620
+ * 工厂函数:创建并初始化 PackageRuntime 实例。
576
621
  */
577
- export async function createActionDockApp(options = {}) {
578
- return new DefaultActionDockApp(options);
622
+ export async function createPackageRuntime(options = {}) {
623
+ return new DefaultPackageRuntime(options);
579
624
  }
@@ -2,6 +2,44 @@ import { existsSync, readFileSync } from "node:fs";
2
2
  import { join, resolve } from "node:path";
3
3
  import { loadPlaybooks } from "../project/loader.js";
4
4
  import { loadManifest, MANIFEST_FILE_NAME } from "../project/manifest.js";
5
+ /**
6
+ * 将增量声明合并到既有 Action 规范之上(纯函数,单一事实源)。
7
+ *
8
+ * 优先级语义由调用顺序表达:后层调用时以先层结果作为 base,
9
+ * 声明值(patch)存在时覆盖,缺省字段回退 base 既有值。
10
+ */
11
+ function mergeSpec(base, id, packageId, patch, packageRoot, policy = {}) {
12
+ const { arrayCopy = true, arrayFields = [], deriveFilePath = false, preserveEntry = false } = policy;
13
+ const merged = {
14
+ id,
15
+ packageId,
16
+ description: patch.description ?? base?.description,
17
+ inputSchema: patch.inputSchema ?? base?.inputSchema,
18
+ outputSchema: patch.outputSchema ?? base?.outputSchema,
19
+ annotations: patch.annotations ?? base?.annotations,
20
+ };
21
+ for (const field of arrayFields) {
22
+ merged[field] = patch[field]
23
+ ? (arrayCopy ? [...patch[field]] : patch[field])
24
+ : base?.[field];
25
+ }
26
+ if (deriveFilePath) {
27
+ merged.entry = patch.entry ?? base?.entry;
28
+ merged.filePath =
29
+ patch.entry && packageRoot
30
+ ? resolve(packageRoot, patch.entry)
31
+ : base?.filePath;
32
+ }
33
+ else if (preserveEntry) {
34
+ merged.entry = base?.entry;
35
+ merged.filePath = base?.filePath;
36
+ }
37
+ else {
38
+ merged.entry = patch.entry ?? base?.entry;
39
+ merged.filePath = base?.filePath;
40
+ }
41
+ return merged;
42
+ }
5
43
  /**
6
44
  * 静态读取并聚合指定包的 Action 规范索引。
7
45
  * 不产生全量模块导入与执行副作用。
@@ -28,18 +66,15 @@ export function buildStaticActionMap(input) {
28
66
  const manifest = loadManifest(packageRoot);
29
67
  if (manifest?.actions) {
30
68
  for (const [id, item] of Object.entries(manifest.actions)) {
31
- map.set(id, {
32
- id,
33
- packageId,
34
- description: item.description,
35
- inputSchema: item.inputSchema,
36
- outputSchema: item.outputSchema,
37
- tags: item.tags ? [...item.tags] : [],
38
- annotations: item.annotations,
39
- uses: item.uses ? [...item.uses] : [],
40
- entry: item.entry,
41
- filePath: item.entry ? resolve(packageRoot, item.entry) : undefined,
42
- });
69
+ // 磁盘清单为最底层来源:无更低层可回退,tags 与 uses 缺省时归一为空数组
70
+ map.set(id, mergeSpec(undefined, id, packageId, {
71
+ ...item,
72
+ tags: item.tags ?? [],
73
+ uses: item.uses ?? [],
74
+ }, packageRoot, {
75
+ arrayFields: ["tags", "uses"],
76
+ deriveFilePath: true,
77
+ }));
43
78
  }
44
79
  }
45
80
  }
@@ -53,21 +88,12 @@ export function buildStaticActionMap(input) {
53
88
  const rawActions = projectConfig.actions;
54
89
  if (typeof rawActions === "object" && rawActions !== null) {
55
90
  for (const [id, item] of Object.entries(rawActions)) {
56
- const existing = map.get(id);
57
- map.set(id, {
58
- id,
59
- packageId,
60
- description: item.description ?? existing?.description,
61
- inputSchema: item.inputSchema ?? existing?.inputSchema,
62
- outputSchema: item.outputSchema ?? existing?.outputSchema,
63
- tags: item.tags ?? existing?.tags,
64
- annotations: item.annotations ?? existing?.annotations,
65
- uses: item.uses ?? existing?.uses,
66
- entry: item.entry ?? existing?.entry,
67
- filePath: item.entry && packageRoot
68
- ? resolve(packageRoot, item.entry)
69
- : existing?.filePath,
70
- });
91
+ // 配置声明层:覆盖磁盘清单层,entry 变化时重算物理路径;数组字段直传引用
92
+ map.set(id, mergeSpec(map.get(id), id, packageId, item, packageRoot, {
93
+ arrayFields: ["tags", "uses"],
94
+ arrayCopy: false,
95
+ deriveFilePath: true,
96
+ }));
71
97
  }
72
98
  }
73
99
  }
@@ -80,20 +106,11 @@ export function buildStaticActionMap(input) {
80
106
  continue;
81
107
  }
82
108
  }
83
- const existing = map.get(id);
84
- const actObj = act;
85
- map.set(id, {
86
- id,
87
- packageId,
88
- description: actObj.description ?? existing?.description,
89
- inputSchema: actObj.inputSchema ?? existing?.inputSchema,
90
- outputSchema: actObj.outputSchema ?? existing?.outputSchema,
91
- tags: actObj.tags ? [...actObj.tags] : existing?.tags,
92
- annotations: actObj.annotations ?? existing?.annotations,
93
- uses: actObj.uses ? [...actObj.uses] : existing?.uses,
94
- entry: existing?.entry,
95
- filePath: existing?.filePath,
96
- });
109
+ // 内存注入层:保留磁盘层解析出的 entry 与 filePath,仅覆盖动态定义相关字段
110
+ map.set(id, mergeSpec(map.get(id), id, packageId, act, packageRoot, {
111
+ arrayFields: ["tags", "uses"],
112
+ preserveEntry: true,
113
+ }));
97
114
  }
98
115
  return map;
99
116
  }
@@ -0,0 +1,13 @@
1
+ import type { RuntimeStorage } from "../storage/types.js";
2
+ /**
3
+ * 构造外部注入 storage 的非接管视图。
4
+ *
5
+ * 外部注入的 storage 生命周期由注入方管理,本视图仅委托读写而不接管关闭:
6
+ * 所有成员函数与属性原样转发到原始实例并绑定原 this,仅 close 收敛为
7
+ * 显式声明的无操作边界,确保 service.close() 级联关闭时不会误伤外部实例。
8
+ *
9
+ * 单一事实源:mcp 适配层与 testing 运行时共同引用,严禁再各自复制 Proxy 包装实现。
10
+ *
11
+ * @param storage 外部注入的存储实例
12
+ */
13
+ export declare function createNonClosingStorageView<T extends RuntimeStorage>(storage: T): T;
@@ -0,0 +1,22 @@
1
+ /**
2
+ * 构造外部注入 storage 的非接管视图。
3
+ *
4
+ * 外部注入的 storage 生命周期由注入方管理,本视图仅委托读写而不接管关闭:
5
+ * 所有成员函数与属性原样转发到原始实例并绑定原 this,仅 close 收敛为
6
+ * 显式声明的无操作边界,确保 service.close() 级联关闭时不会误伤外部实例。
7
+ *
8
+ * 单一事实源:mcp 适配层与 testing 运行时共同引用,严禁再各自复制 Proxy 包装实现。
9
+ *
10
+ * @param storage 外部注入的存储实例
11
+ */
12
+ export function createNonClosingStorageView(storage) {
13
+ return new Proxy(storage, {
14
+ get(target, prop) {
15
+ if (prop === "close") {
16
+ return () => undefined;
17
+ }
18
+ const value = Reflect.get(target, prop, target);
19
+ return typeof value === "function" ? value.bind(target) : value;
20
+ },
21
+ });
22
+ }