@actiondock/sdk 2.0.1 → 2.0.3

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.
package/README.md CHANGED
@@ -1,121 +1,107 @@
1
1
  # @actiondock/sdk
2
2
 
3
- The lightweight, zero-dependency SDK for defining, orchestrating, and testing AI Agent Actions and Skills in ActionDock 2.0.
3
+ ActionDock 2.0 纯净核心开发者接口契约包。
4
4
 
5
- [![Bun](https://img.shields.io/badge/Bun-%3E%3D1.2-black?logo=bun)](https://bun.sh/)
5
+ [![Node.js](https://img.shields.io/badge/Node.js-%3E%3D22-green?logo=node.js)](https://nodejs.org/)
6
6
  [![TypeScript](https://img.shields.io/badge/TypeScript-5.0+-blue?logo=typescript)](https://www.typescriptlang.org/)
7
7
  [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)
8
8
 
9
- > **Runtime requirement**: [Bun](https://bun.sh/) >= 1.2.0 is required.
9
+ `@actiondock/sdk` 为开发者编写原子 Action 提供零外部依赖的纯净类型定义与核心契约。
10
10
 
11
11
  ---
12
12
 
13
- ## Installation
13
+ ## 安装方式
14
+
15
+ 使用标准包管理器添加依赖:
14
16
 
15
17
  ```bash
16
- bun add @actiondock/sdk
18
+ npm install @actiondock/sdk
17
19
  ```
18
20
 
19
21
  ---
20
22
 
21
- ## Quick Example
23
+ ## 核心契约与函数
24
+
25
+ ### defineAction 函数
22
26
 
23
- ### 1. Define an Action (`actions/greet.ts`)
27
+ 用于声明并严格校验单个 Action 的静态属性与执行函数:
24
28
 
25
29
  ```ts
26
30
  import { defineAction } from "@actiondock/sdk";
27
31
 
28
32
  export default defineAction({
29
- id: "sample.greet",
30
- description: "Greet a user and track greeting count in persistent state",
33
+ id: "calculator.add",
34
+ description: "计算两个数值之和",
31
35
 
32
36
  inputSchema: {
33
37
  type: "object",
34
38
  properties: {
35
- name: { type: "string", minLength: 1 },
39
+ a: { type: "number", description: "第一个加数" },
40
+ b: { type: "number", description: "第二个加数" },
36
41
  },
37
- required: ["name"],
42
+ required: ["a", "b"],
38
43
  },
39
44
 
40
- async run(input: { name: string }, ctx) {
41
- const greeting = ctx.config.get("GREETING_PREFIX", "Hello");
42
- const count = ((await ctx.state.get<number>(`greet:${input.name}`)) || 0) + 1;
43
- await ctx.state.set(`greet:${input.name}`, count);
44
- ctx.log.info(`Greeted ${input.name} ${count} time(s)`);
45
+ outputSchema: {
46
+ type: "object",
47
+ properties: {
48
+ result: { type: "number", description: "计算结果" },
49
+ },
50
+ required: ["result"],
51
+ },
45
52
 
53
+ async run(input, ctx) {
54
+ ctx.log.info(`计算加法: ${input.a} + ${input.b}`);
46
55
  return {
47
- message: `${greeting}, ${input.name}!`,
48
- count,
56
+ result: input.a + input.b,
49
57
  };
50
58
  },
51
59
  });
52
60
  ```
53
61
 
54
- ### 2. Test in Milliseconds (`tests/greet.test.ts`)
62
+ `defineAction` 支持的完整契约字段:
55
63
 
56
- ```ts
57
- import { describe, expect, it } from "bun:test";
58
- import { createTestRuntime } from "@actiondock/sdk";
59
- import greetAction from "../actions/greet";
60
-
61
- describe("sample.greet Action", () => {
62
- it("greets user and updates state counter", async () => {
63
- const runtime = createTestRuntime({
64
- config: { GREETING_PREFIX: "Welcome" },
65
- state: { "greet:Alice": 2 },
66
- });
67
-
68
- const result = await runtime.run(greetAction, { name: "Alice" });
69
- expect(result.message).toBe("Welcome, Alice!");
70
- expect(result.count).toBe(3);
71
- expect(await runtime.state.get("greet:Alice")).toBe(3);
72
- });
73
- });
74
- ```
64
+ - `id`:动作唯一标识符,格式通常为领域或命名空间前缀拼接名称。
65
+ - `description`:动作功能的人类可读说明。
66
+ - `inputSchema`:入参模式规范,采用标准 JSON Schema 格式。
67
+ - `outputSchema`:出参模式规范,采用标准 JSON Schema 格式。
68
+ - `uses`:静态声明当前 Action 所依赖的其他 Action 标识列表。
69
+ - `tags`:用于分类与检索的标签数组。
70
+ - `annotations`:面向协议适配器的扩展注解字典。
71
+ - `run`:业务执行入口函数,接收已通过校验的入参数据和运行时上下文对象。
75
72
 
76
73
  ---
77
74
 
78
- ## Core Capabilities
75
+ ## ActionContext 运行时上下文
79
76
 
80
- - `ActionContext`:
81
- - `ctx.config`: 5-tier configuration resolution (`override > sqlite > env > default > fallback`)
82
- - `ctx.state`: Embedded SQLite state store with namespace and TTL support
83
- - `ctx.actions`: Inter-action invocation with cycle detection
84
- - `ctx.log`: Isolated logging directed to `stderr`
85
- - `ctx.signal`: Cooperative cancellation via `AbortSignal`
86
- - `execCli`: Deadlock-safe, cross-platform synchronous CLI executor with Windows `.cmd` resolution, stdin streaming, timeout, and signal support.
87
- - `spawnDetached`: Safe asynchronous launcher for daemon-spawning CLI tools (e.g., `agent-browser open`), preventing pipe EOF hangs via fire-and-forget stdio decoupling and polling probe.
88
- - `createTestRuntime`: Fast, zero-dependency in-memory test harness for unit tests.
89
-
90
- ---
77
+ Action 执行时,宿主环境注入标准化上下文对象 `ActionContext`,提供受控的系统交互能力:
91
78
 
92
- ## API Summary
93
-
94
- | Export | Type | Description |
95
- |---|---|---|
96
- | `defineAction(def)` | Function | Defines and defensively validates an Action |
97
- | `createTestRuntime(opts)` | Function | Creates an in-memory test runner (`config`, `state`, `logger`, `run()`) |
98
- | `execCli(cmd, args, opts)` | Function | Synchronous, deadlock-safe CLI execution utility |
99
- | `spawnDetached(opts)` | Function | Safe asynchronous launcher & readiness prober for daemon-spawning CLI tools |
100
- | `MemoryConfig` | Class | In-memory `Config` provider for unit tests |
101
- | `MemoryStateStore` | Class | In-memory `StateStore` with TTL and namespace support |
102
- | `MemoryLogger` | Class | In-memory `Logger` collecting log entries for test assertions |
103
- | `ActionContext` | Interface | Runtime context provided to `action.run(input, ctx)` |
104
- | `ActionDefinition` | Interface | Schema and execution contract for an Action |
105
- | `ExecutionResult` | Type | Standard JSON envelope (`ok: true, data` or `ok: false, error`) |
79
+ - `ctx.process`:进程执行接口,提供 `exec` 与 `spawnDetached`,安全调用系统外部命令或启动后台守护进程,支持超时与输出截断保护。
80
+ - `ctx.log`:结构化日志输出接口,提供 `debug`、`info`、`warn`、`error` 级别日志。日志严格输出至标准错误流,彻底隔离标准输出流,杜绝污染协议报文。
81
+ - `ctx.progress`:进度报告器,支持在长时间运行的任务中通过 `report(current, total, message)` 上报当前阶段。
82
+ - `ctx.signal`:协作式取消信号,类型为标准 `AbortSignal`。当任务被外部客户端取消或超时时自动触发中止,业务逻辑需主动监听并响应。
83
+ - `ctx.run`:当前执行实例元数据,包含 `id`(本次运行标识)、`rootId`(根调用标识)和 `parentId`(父级调用标识),便于全链路追踪。
84
+ - `ctx.config`:分层配置读取接口,提供 `get` `has` 方法,支持优先级回退与类型强转。
85
+ - `ctx.state`:持久化状态接口,提供基于当前 Package 命名空间隔离的键值存储与存活时间控制。
86
+ - `ctx.actions`:动作相互调用接口,支持调用包内其他 Action,并内置调用栈环路死锁检测。
106
87
 
107
88
  ---
108
89
 
109
- ## 📖 Documentation
90
+ ## 快速单元测试
110
91
 
111
- For detailed guides, design principles, and exhaustive API references, see the [ActionDock Documentation Center](../../docs/README.md):
92
+ 结合 `@actiondock/testing` SDK 内置的测试辅助方法,可以在纯内存环境下毫秒级验证 Action:
112
93
 
113
- - [Action SDK API Reference](../../docs/reference/action-api.md)
114
- - [ActionContext Concept](../../docs/concepts/action-context.md)
115
- - [Testing & Verification Guide](../../docs/developer/testing.md)
94
+ ```ts
95
+ import { createTestRuntime } from "@actiondock/sdk";
96
+ import addAction from "../actions/add";
97
+
98
+ const runtime = createTestRuntime();
99
+ const result = await runtime.run(addAction, { a: 10, b: 20 });
100
+ console.log(result.result); // 输出 30
101
+ ```
116
102
 
117
103
  ---
118
104
 
119
- ## License
105
+ ## 开源协议
120
106
 
121
- [Apache-2.0](../../LICENSE) © team4u
107
+ 本项目采用 Apache-2.0 开源协议。
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@actiondock/sdk",
3
- "version": "2.0.1",
3
+ "version": "2.0.3",
4
4
  "description": "ActionDock SDK for defining and testing standalone AI Agent Actions",
5
5
  "type": "module",
6
6
  "main": "./src/index.ts",
@@ -17,7 +17,7 @@
17
17
  "README.md"
18
18
  ],
19
19
  "engines": {
20
- "bun": ">=1.2.0"
20
+ "node": ">=22.12.0"
21
21
  },
22
22
  "publishConfig": {
23
23
  "access": "public",
package/src/cli.ts CHANGED
@@ -77,6 +77,48 @@ export interface ExecCliResult {
77
77
  * @param options 运行选项(工作目录、环境变量、取消信号、超时、stdin、字符编码、抛错开关)
78
78
  * @returns ExecCliResult 执行结果结构体
79
79
  */
80
+ import { spawn, spawnSync } from "node:child_process";
81
+ import { existsSync } from "node:fs";
82
+ import { delimiter, join } from "node:path";
83
+
84
+ /**
85
+ * 跨运行时安全查找可执行文件绝对物理路径。
86
+ */
87
+ function findExecutable(command: string): string | null {
88
+ if (typeof (globalThis as any).Bun !== "undefined" && typeof (globalThis as any).Bun.which === "function") {
89
+ try {
90
+ const bPath = (globalThis as any).Bun.which(command);
91
+ if (bPath) return bPath;
92
+ } catch {
93
+ // 忽略 Bun.which 异常,进入通用解析
94
+ }
95
+ }
96
+
97
+ const hasPathSep = command.includes("/") || command.includes("\\");
98
+ if (hasPathSep) {
99
+ return existsSync(command) ? command : null;
100
+ }
101
+
102
+ const pathEnv = process.env.PATH || "";
103
+ const dirs = pathEnv.split(delimiter);
104
+ const isWindows = process.platform === "win32";
105
+ const pathext = isWindows
106
+ ? (process.env.PATHEXT || ".COM;.EXE;.BAT;.CMD").split(";")
107
+ : [""];
108
+
109
+ for (const dir of dirs) {
110
+ if (!dir) continue;
111
+ for (const ext of pathext) {
112
+ const candidate = join(dir, isWindows && !command.includes(".") ? command + ext : command);
113
+ if (existsSync(candidate)) {
114
+ return candidate;
115
+ }
116
+ }
117
+ }
118
+
119
+ return null;
120
+ }
121
+
80
122
  export function execCli(
81
123
  command: string,
82
124
  args: string[] = [],
@@ -100,11 +142,11 @@ export function execCli(
100
142
  return errRes;
101
143
  }
102
144
 
103
- // 2. 跨平台绝对路径解析(解决 Windows 下 npm 全局 .cmd shim 识别问题)
145
+ // 2. 跨平台绝对路径解析
104
146
  const hasPathSep = command.includes("/") || command.includes("\\");
105
- const binPath = hasPathSep ? command : (Bun.which(command) || command);
147
+ const binPath = hasPathSep ? (existsSync(command) ? command : null) : findExecutable(command);
106
148
 
107
- if (!hasPathSep && !Bun.which(command)) {
149
+ if (!binPath) {
108
150
  const errRes: ExecCliResult = {
109
151
  ok: false,
110
152
  exitCode: -1,
@@ -120,27 +162,26 @@ export function execCli(
120
162
  }
121
163
 
122
164
  // 3. 处理标准输入数据
123
- let stdinOption: Uint8Array | "ignore" | undefined = "ignore";
165
+ let stdinInput: Buffer | undefined;
124
166
  if (options.input !== undefined) {
125
167
  if (typeof options.input === "string") {
126
- stdinOption = new TextEncoder().encode(options.input);
168
+ stdinInput = Buffer.from(options.input);
127
169
  } else if (options.input instanceof Uint8Array) {
128
- stdinOption = options.input;
170
+ stdinInput = Buffer.from(options.input);
129
171
  }
130
172
  }
131
173
 
132
174
  try {
133
- const proc = Bun.spawnSync([binPath, ...args], {
175
+ const proc = spawnSync(binPath, args, {
134
176
  cwd: options.cwd || process.cwd(),
135
177
  env: options.env ? { ...process.env, ...options.env } : process.env,
136
- stdin: stdinOption,
137
- stdout: "pipe",
138
- stderr: "pipe",
178
+ input: stdinInput,
179
+ stdio: [stdinInput ? "pipe" : "ignore", "pipe", "pipe"],
139
180
  timeout: options.timeout,
140
181
  });
141
182
 
142
183
  const durationMs = Math.round(performance.now() - startTime);
143
- const timedOut = Boolean((proc as any).exitedDueToTimeout);
184
+ const timedOut = Boolean(proc.error && (proc.error as any).code === "ETIMEDOUT");
144
185
 
145
186
  // 4. 自定义字符集解码
146
187
  const decoder = new TextDecoder(options.encoding || "utf-8");
@@ -154,7 +195,7 @@ export function execCli(
154
195
  stderr = `Command '${command}' timed out after ${options.timeout}ms`;
155
196
  }
156
197
 
157
- const exitCode = timedOut ? -1 : (proc.exitCode ?? (proc.success ? 0 : -1));
198
+ const exitCode = timedOut ? -1 : (proc.status ?? (proc.error ? -1 : 0));
158
199
  const ok = !timedOut && exitCode === 0;
159
200
 
160
201
  const result: ExecCliResult = {
@@ -255,28 +296,36 @@ export async function spawnDetached(options: SpawnDetachedOptions): Promise<bool
255
296
  throw new Error("Command aborted before execution by signal");
256
297
  }
257
298
 
258
- // 2. 跨平台绝对路径解析(解决 Windows 下 npm 全局 .cmd shim 识别问题)
299
+ // 2. 跨平台绝对路径解析
259
300
  const hasPathSep = command.includes("/") || command.includes("\\");
260
- const binPath = hasPathSep ? command : (Bun.which(command) || command);
301
+ const binPath = hasPathSep ? (existsSync(command) ? command : null) : findExecutable(command);
261
302
 
262
- if (!hasPathSep && !Bun.which(command)) {
303
+ if (!binPath) {
263
304
  throw new Error(`Command '${command}' not found in PATH.`);
264
305
  }
265
306
 
266
307
  // 3. fire-and-forget:ignore 所有 stdio,unref 防止阻塞事件循环
267
- const child = Bun.spawn([binPath, ...args], {
308
+ const child = spawn(binPath, args, {
268
309
  cwd: cwd || process.cwd(),
269
310
  env: env ? { ...process.env, ...env } : process.env,
270
- stdin: "ignore",
271
- stdout: "ignore",
272
- stderr: "ignore",
311
+ stdio: "ignore",
312
+ detached: true,
273
313
  signal,
274
314
  });
275
315
  child.unref();
276
316
 
277
- // 4. 等待 CLI 前端进程自身退出(错开冷启动窗口,避免 probe 并发拉起两个 daemon)
317
+ // 4. 等待 CLI 前端进程自身退出
278
318
  try {
279
- await child.exited;
319
+ await new Promise<void>((resolveChild, rejectChild) => {
320
+ child.on("exit", () => resolveChild());
321
+ child.on("error", (err) => {
322
+ if (signal?.aborted) {
323
+ rejectChild(new Error("aborted"));
324
+ } else {
325
+ rejectChild(err);
326
+ }
327
+ });
328
+ });
280
329
  } catch (err) {
281
330
  if (signal?.aborted) {
282
331
  throw new Error("aborted");
package/src/index.ts CHANGED
@@ -1,11 +1,4 @@
1
1
  export { defineAction } from "./action";
2
- export {
3
- execCli,
4
- spawnDetached,
5
- type ExecCliOptions,
6
- type ExecCliResult,
7
- type SpawnDetachedOptions,
8
- } from "./cli";
9
2
  export {
10
3
  createTestRuntime,
11
4
  MemoryConfig,
@@ -16,16 +9,25 @@ export {
16
9
  } from "./test-runtime";
17
10
  export type {
18
11
  ActionContext,
12
+ ActionContract,
19
13
  ActionDefinition,
20
14
  ActionInvoker,
15
+ ActionRef,
21
16
  Config,
17
+ DetachedProcessOptions,
18
+ DetachedProcessResult,
19
+ ExecutionEvent,
22
20
  ExecutionResult,
23
21
  JsonSchema,
22
+ JsonValue,
24
23
  Logger,
24
+ ProcessAPI,
25
+ ProcessExecOptions,
26
+ ProcessResult,
27
+ ProgressReporter,
28
+ ResolvedActionRef,
25
29
  RuntimeError,
26
30
  RunRecord,
27
31
  RunStatus,
28
32
  StateStore,
29
33
  } from "./types";
30
-
31
-
@@ -1,9 +1,11 @@
1
+ import { execCli, spawnDetached } from "./cli";
1
2
  import type {
2
3
  ActionContext,
3
4
  ActionDefinition,
4
5
  ActionInvoker,
5
6
  Config,
6
7
  Logger,
8
+ ProcessResult,
7
9
  StateStore,
8
10
  } from "./types";
9
11
 
@@ -248,12 +250,76 @@ export function createTestRuntime(options: TestRuntimeOptions = {}): TestRuntime
248
250
  }
249
251
  callStack.push(action.id);
250
252
  try {
253
+ const runId = "test-" + Math.random().toString(36).slice(2, 10);
251
254
  const ctx: ActionContext = {
252
255
  config,
253
256
  state,
254
257
  actions: invoker,
258
+ process: {
259
+ async exec(command, args, options) {
260
+ const res = await execCli(command, args, {
261
+ cwd: options?.cwd,
262
+ env: options?.env,
263
+ input: options?.input,
264
+ timeout: options?.timeoutMs,
265
+ throwOnError: options?.throwOnError,
266
+ signal: options?.signal,
267
+ });
268
+ return {
269
+ ok: res.ok,
270
+ exitCode: res.exitCode,
271
+ stdout: res.stdout,
272
+ stderr: res.stderr,
273
+ raw: res.raw,
274
+ timedOut: res.timedOut ?? false,
275
+ cancelled: Boolean(options?.signal?.aborted),
276
+ durationMs: res.durationMs,
277
+ };
278
+ },
279
+ async spawnDetached(options) {
280
+ const probeFn = options.probe
281
+ ? async () => {
282
+ const fakeRes: ProcessResult = {
283
+ ok: true,
284
+ exitCode: 0,
285
+ stdout: "",
286
+ stderr: "",
287
+ raw: new Uint8Array(),
288
+ timedOut: false,
289
+ cancelled: false,
290
+ durationMs: 0,
291
+ };
292
+ return options.probe!(fakeRes);
293
+ }
294
+ : () => true;
295
+
296
+ const ready = await spawnDetached({
297
+ command: options.command,
298
+ args: options.args,
299
+ cwd: options.cwd,
300
+ env: options.env,
301
+ timeoutMs: options.timeoutMs ?? options.probeTimeoutMs,
302
+ intervalMs: options.probeIntervalMs,
303
+ signal: options.signal,
304
+ probe: probeFn,
305
+ });
306
+
307
+ return {
308
+ ok: ready,
309
+ ready,
310
+ durationMs: 0,
311
+ };
312
+ },
313
+ },
255
314
  log: logger,
315
+ progress: {
316
+ report() {},
317
+ },
256
318
  signal,
319
+ run: {
320
+ id: runId,
321
+ rootId: runId,
322
+ },
257
323
  };
258
324
  return await action.run(input, ctx);
259
325
  } finally {
package/src/types.ts CHANGED
@@ -1,6 +1,17 @@
1
+ /**
2
+ * 标准 JSON 数据域。
3
+ */
4
+ export type JsonValue =
5
+ | null
6
+ | boolean
7
+ | number
8
+ | string
9
+ | JsonValue[]
10
+ | { [key: string]: JsonValue };
11
+
1
12
  /**
2
13
  * 标准 JSON Schema 结构定义。
3
- * 支持对象模式(Object Schema)或布尔模式(Boolean Schema)。
14
+ * 支持对象模式或布尔模式。
4
15
  */
5
16
  export type JsonSchema = Record<string, unknown> | boolean;
6
17
 
@@ -8,25 +19,24 @@ export type JsonSchema = Record<string, unknown> | boolean;
8
19
  * ActionDock 标准运行时错误对象。
9
20
  */
10
21
  export interface RuntimeError {
11
- /** 机器可读的唯一错误码,例如 ACTION_NOT_FOUND, INPUT_VALIDATION_FAILED, ACTION_TIMEOUT 等 */
22
+ /** 机器可读的唯一错误码 */
12
23
  code: string;
13
24
  /** 人类可读的错误描述信息 */
14
25
  message: string;
15
- /** 结构化的附加错误详情(如 JSON Schema 校验失败的具体字段列表) */
26
+ /** 结构化的附加错误详情 */
16
27
  details?: unknown;
17
28
  /** 导致此错误的底层原始异常或原因 */
18
29
  cause?: unknown;
19
30
  }
20
31
 
21
32
  /**
22
- * 标准执行结果信封(JSON Envelope)。
23
- * ActionDock 在 CLI、独立二进制、HTTP Runner 和 MCP 等所有场景中均输出该格式。
33
+ * 标准执行结果信封。
24
34
  */
25
- export type ExecutionResult<T = unknown> =
35
+ export type ExecutionResult<T = JsonValue> =
26
36
  | {
27
37
  /** 执行是否成功 */
28
38
  ok: true;
29
- /** 本次执行的全局唯一运行 ID(UUIDv4) */
39
+ /** 本次执行的全局唯一运行标识 */
30
40
  runId: string;
31
41
  /** Action 执行返回的业务数据 */
32
42
  data: T;
@@ -34,15 +44,58 @@ export type ExecutionResult<T = unknown> =
34
44
  | {
35
45
  /** 执行是否失败 */
36
46
  ok: false;
37
- /** 本次执行的全局唯一运行 ID(UUIDv4) */
47
+ /** 本次执行的全局唯一运行标识 */
38
48
  runId: string;
39
49
  /** 运行时错误详情 */
40
50
  error: RuntimeError;
41
51
  };
42
52
 
43
53
  /**
44
- * 配置提供器接口(只读查询)。
45
- * 遵循 5 层优先级链:CLI 参数 > 本地 SQLite > 全局 SQLite > 环境变量 > 项目默认值 > 回退默认值。
54
+ * Action 逻辑引用。
55
+ */
56
+ export interface ActionRef {
57
+ /** 所属包标识 */
58
+ packageId?: string;
59
+ /** Action 动作标识 */
60
+ actionId: string;
61
+ }
62
+
63
+ /**
64
+ * 运行时完整解析后的 Action 引用。
65
+ */
66
+ export interface ResolvedActionRef {
67
+ /** 包逻辑标识 */
68
+ packageId: string;
69
+ /** 包物理实例标识 */
70
+ packageInstanceId: string;
71
+ /** Action 动作标识 */
72
+ actionId: string;
73
+ /** 运行时代码快照代次标识 */
74
+ generationId: string;
75
+ }
76
+
77
+ /**
78
+ * Action 声明契约。
79
+ */
80
+ export interface ActionContract {
81
+ /** Action 唯一标识 */
82
+ id: string;
83
+ /** Action 功能描述 */
84
+ description?: string;
85
+ /** 输入参数模式规范 */
86
+ inputSchema?: JsonSchema;
87
+ /** 输出结果模式规范 */
88
+ outputSchema?: JsonSchema;
89
+ /** 静态 Action 依赖列表 */
90
+ uses?: string[];
91
+ /** 检索与分类标签 */
92
+ tags?: string[];
93
+ /** 协议注解元数据 */
94
+ annotations?: Record<string, JsonValue>;
95
+ }
96
+
97
+ /**
98
+ * 配置提供器接口。
46
99
  */
47
100
  export interface Config {
48
101
  /**
@@ -57,7 +110,7 @@ export interface Config {
57
110
  */
58
111
  get<T = unknown>(key: string, defaultValue: T): T;
59
112
  /**
60
- * 检查指定键是否存在配置值(无论来源于哪一层优先级)
113
+ * 检查指定键是否存在配置值
61
114
  * @param key 配置键名
62
115
  */
63
116
  has(key: string): boolean;
@@ -65,7 +118,6 @@ export interface Config {
65
118
 
66
119
  /**
67
120
  * 共享状态持久化存储接口。
68
- * 提供跨 Action 调用的数据共享与持久化存储能力,支持命名空间与基于秒的 TTL 自动过期机制。
69
121
  */
70
122
  export interface StateStore {
71
123
  /**
@@ -74,31 +126,29 @@ export interface StateStore {
74
126
  */
75
127
  get<T = unknown>(key: string): Promise<T | undefined>;
76
128
  /**
77
- * 设置状态键值对,可选指定过期存活时间(TTL)
129
+ * 设置状态键值对,可选指定过期存活时间
78
130
  * @param key 状态键名
79
- * @param value 要存储的数据值(会自动进行深拷贝/序列化)
80
- * @param ttl 存活时间(单位:秒)。不传或 <= 0 表示永久有效
131
+ * @param value 要存储的数据值
132
+ * @param ttl 存活时间(单位:秒)。不传或小于等于 0 表示永久有效
81
133
  */
82
134
  set<T = unknown>(key: string, value: T, ttl?: number): Promise<void>;
83
135
  /**
84
- * 删除指定键的状态数据。
136
+ * 删除指定键的状态数据
85
137
  * @param key 状态键名
86
- * @returns 是否实际删除了数据(true: 存在并删除,false: 不存在)
87
138
  */
88
139
  delete(key: string): Promise<boolean>;
89
140
  /**
90
- * 清空当前命名空间(或指定前缀)下的所有状态数据
141
+ * 清空当前命名空间下的所有状态数据
91
142
  * @param prefix 可选的键名前缀过滤条件
92
- * @returns 实际清除的条目数量
93
143
  */
94
144
  clear(prefix?: string): Promise<number>;
95
145
  /**
96
- * 列出当前命名空间下所有匹配前缀的状态键名(已自动过滤已过期的键)
146
+ * 列出当前命名空间下所有匹配前缀的状态键名
97
147
  * @param prefix 键名前缀过滤条件
98
148
  */
99
149
  keys(prefix?: string): Promise<string[]>;
100
150
  /**
101
- * 创建一个具有独立命名空间隔离的子 StateStore 实例
151
+ * 创建具有独立命名空间隔离的子 StateStore 实例
102
152
  * @param namespace 命名空间标识
103
153
  */
104
154
  scope(namespace: string): StateStore;
@@ -106,7 +156,6 @@ export interface StateStore {
106
156
 
107
157
  /**
108
158
  * 结构化日志记录器接口。
109
- * 所有日志输出均定向到 stderr,确保不污染 stdout 中的 JSON 信封。
110
159
  */
111
160
  export interface Logger {
112
161
  /** 记录调试级别日志 */
@@ -119,86 +168,196 @@ export interface Logger {
119
168
  error(message: string, data?: unknown): void;
120
169
  }
121
170
 
171
+ /**
172
+ * 执行进度报告器接口。
173
+ */
174
+ export interface ProgressReporter {
175
+ /**
176
+ * 报告当前任务执行进度
177
+ * @param current 当前完成量
178
+ * @param total 任务总量
179
+ * @param message 当前进度说明
180
+ */
181
+ report(current: number, total?: number, message?: string): void;
182
+ }
183
+
184
+ /**
185
+ * 进程执行参数。
186
+ */
187
+ export interface ProcessExecOptions {
188
+ cwd?: string;
189
+ env?: Record<string, string>;
190
+ input?: string | Uint8Array;
191
+ timeoutMs?: number;
192
+ signal?: AbortSignal;
193
+ encoding?: string;
194
+ throwOnError?: boolean;
195
+ maxOutputBytes?: number;
196
+ }
197
+
198
+ /**
199
+ * 进程执行结果。
200
+ */
201
+ export interface ProcessResult {
202
+ ok: boolean;
203
+ exitCode: number | null;
204
+ signal?: string;
205
+ stdout: string;
206
+ stderr: string;
207
+ raw: Uint8Array;
208
+ timedOut: boolean;
209
+ cancelled: boolean;
210
+ durationMs: number;
211
+ error?: RuntimeError;
212
+ }
213
+
214
+ /**
215
+ * 后台守护进程启动选项。
216
+ */
217
+ export interface DetachedProcessOptions {
218
+ command: string;
219
+ args?: string[];
220
+ cwd?: string;
221
+ env?: Record<string, string>;
222
+ timeoutMs?: number;
223
+ signal?: AbortSignal;
224
+ probeIntervalMs?: number;
225
+ probeTimeoutMs?: number;
226
+ probe?: (result: ProcessResult) => boolean | Promise<boolean>;
227
+ }
228
+
229
+ /**
230
+ * 后台守护进程启动结果。
231
+ */
232
+ export interface DetachedProcessResult {
233
+ ok: boolean;
234
+ pid?: number;
235
+ ready: boolean;
236
+ durationMs: number;
237
+ error?: RuntimeError;
238
+ }
239
+
240
+ /**
241
+ * 统一进程操作接口。
242
+ */
243
+ export interface ProcessAPI {
244
+ /** 执行外部命令 */
245
+ exec(command: string, args?: string[], options?: ProcessExecOptions): Promise<ProcessResult>;
246
+ /** 启动脱离父进程的后台进程并探测就绪状态 */
247
+ spawnDetached(options: DetachedProcessOptions): Promise<DetachedProcessResult>;
248
+ }
249
+
122
250
  /**
123
251
  * Action 间相互调用的执行器接口。
124
- * 支持在 Action 内部安全调用同 Package 或其他 Action,内置调用栈防死循环环路检测。
125
252
  */
126
253
  export interface ActionInvoker {
127
254
  /**
128
255
  * 调用指定的 Action 并传入参数,返回其执行结果
129
- * @param action 目标 Action 定义对象
256
+ * @param action 目标 Action 定义对象、引用或标识符
130
257
  * @param input 传递给目标 Action 的输入参数
131
258
  */
132
- invoke<I, O>(
133
- action: ActionDefinition<I, O>,
134
- input: I
259
+ invoke<I = unknown, O = unknown>(
260
+ action: ActionDefinition<I, O> | ActionRef | string,
261
+ input?: I
135
262
  ): Promise<O>;
136
263
  }
137
264
 
138
265
  /**
139
- * 传递给 Action `run` 处理函数的运行时上下文对象。
266
+ * 传递给 Action 业务函数的运行时上下文对象。
140
267
  */
141
268
  export interface ActionContext {
142
- /** 配置读取接口,自动按多层优先级解析配置 */
269
+ /** 配置读取接口 */
143
270
  config: Config;
144
- /** 状态持久化存储接口,提供跨 Action 调用的数据存取与 TTL */
271
+ /** 状态持久化存储接口 */
145
272
  state: StateStore;
146
- /** Action 相互调用接口,支持模块化组合与复用 */
273
+ /** Action 相互调用接口 */
147
274
  actions: ActionInvoker;
148
- /** 结构化日志接口,输出定向至 stderr */
275
+ /** 进程执行接口 */
276
+ process: ProcessAPI;
277
+ /** 结构化日志接口 */
149
278
  log: Logger;
150
- /** 取消信号,用于感知客户端中断、超时或 SIGINT */
279
+ /** 进度报告接口 */
280
+ progress: ProgressReporter;
281
+ /** 取消信号 */
151
282
  signal: AbortSignal;
283
+ /** 当前执行信息 */
284
+ run: {
285
+ id: string;
286
+ rootId: string;
287
+ parentId?: string;
288
+ };
152
289
  }
153
290
 
154
291
  /**
155
292
  * Action 动作定义契约。
156
- * 通过 `defineAction({...})` 声明。
157
293
  */
158
- export interface ActionDefinition<I = unknown, O = unknown> {
159
- /** Action 唯一标识符(例如: "github.get-pr" 或 "sample.greet") */
160
- id: string;
161
- /** Action 功能描述,用于 CLI 帮助文档、Agent 发现以及 MCP Tool 描述 */
162
- description?: string;
163
- /** 输入参数的 JSON Schema 校验规范 */
164
- inputSchema?: JsonSchema;
165
- /** 输出结果的 JSON Schema 校验规范 */
166
- outputSchema?: JsonSchema;
294
+ export interface ActionDefinition<I = unknown, O = unknown> extends ActionContract {
167
295
  /**
168
296
  * Action 的核心业务执行函数
169
297
  * @param input 符合 inputSchema 契约的输入数据
170
- * @param ctx 运行时上下文对象(包含 config, state, actions, log, signal)
298
+ * @param ctx 运行时上下文对象
171
299
  */
172
300
  run(input: I, ctx: ActionContext): Promise<O> | O;
173
301
  }
174
302
 
175
303
  /**
176
- * 运行记录状态枚举。
304
+ * 运行记录状态。
177
305
  */
178
- export type RunStatus = "running" | "success" | "failed" | "cancelled";
306
+ export type RunStatus =
307
+ | "running"
308
+ | "success"
309
+ | "failed"
310
+ | "cancelled"
311
+ | "timed_out"
312
+ | "interrupted";
179
313
 
180
314
  /**
181
315
  * Action 执行运行历史记录。
182
316
  */
183
317
  export interface RunRecord {
184
- /** 全局唯一运行 ID (UUIDv4) */
318
+ /** 全局唯一运行标识 */
185
319
  id: string;
186
- /** 所属 Action Package 的唯一 ID */
320
+ /** 根调用运行标识 */
321
+ rootRunId: string;
322
+ /** 父级调用的运行标识 */
323
+ parentRunId?: string;
324
+ /** 所属 Action Package 的逻辑标识 */
187
325
  packageId: string;
188
- /** 所执行的 Action ID */
326
+ /** 包物理实例标识 */
327
+ packageInstanceId: string;
328
+ /** 所执行的 Action 标识 */
189
329
  actionId: string;
190
- /** 父级调用的运行 ID(若由其他 Action 嵌套调用触发) */
191
- parentRunId?: string;
330
+ /** 运行时代码快照代次标识 */
331
+ generationId: string;
332
+ /** 执行宿主所有者标识 */
333
+ ownerId: string;
192
334
  /** 运行生命周期状态 */
193
335
  status: RunStatus;
194
336
  /** 输入参数快照 */
195
- input: unknown;
196
- /** 执行成功时的输出结果 */
197
- output?: unknown;
337
+ input?: JsonValue;
338
+ /** 执行成功时的输出结果快照 */
339
+ output?: JsonValue;
198
340
  /** 执行失败时的错误信息 */
199
341
  error?: RuntimeError;
200
- /** 开始执行时间(ISO 8601 格式) */
342
+ /** 开始执行时间(UTC ISO 8601 格式) */
201
343
  startedAt: string;
202
- /** 结束执行时间(ISO 8601 格式,进行中时为 undefined) */
344
+ /** 结束执行时间(UTC ISO 8601 格式) */
203
345
  finishedAt?: string;
346
+ /** 运行耗时(单位:毫秒) */
347
+ durationMs?: number;
204
348
  }
349
+
350
+ /**
351
+ * 执行生命周期事件。
352
+ */
353
+ export type ExecutionEvent = {
354
+ runId: string;
355
+ rootRunId: string;
356
+ sequence: number;
357
+ timestamp: string;
358
+ } & (
359
+ | { type: "log"; level: "debug" | "info" | "warn" | "error"; message: string; data?: JsonValue }
360
+ | { type: "progress"; current?: number; total?: number; message?: string }
361
+ | { type: "status"; status: RunStatus }
362
+ | { type: "finish"; result: ExecutionResult }
363
+ );