@mickorz/opencode-agentic-workflow 0.3.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.
- package/LICENSE +21 -0
- package/README.md +406 -0
- package/dist/metrics/collector.d.ts +98 -0
- package/dist/metrics/collector.js +246 -0
- package/dist/metrics/collector.js.map +1 -0
- package/dist/observability/events.d.ts +99 -0
- package/dist/observability/events.js +55 -0
- package/dist/observability/events.js.map +1 -0
- package/dist/observability/observe.d.ts +7 -0
- package/dist/observability/observe.js +29 -0
- package/dist/observability/observe.js.map +1 -0
- package/dist/observability/trace.d.ts +26 -0
- package/dist/observability/trace.js +52 -0
- package/dist/observability/trace.js.map +1 -0
- package/dist/plugin/checkpoint-rpc.d.ts +74 -0
- package/dist/plugin/checkpoint-rpc.js +73 -0
- package/dist/plugin/checkpoint-rpc.js.map +1 -0
- package/dist/plugin/index.d.ts +24 -0
- package/dist/plugin/index.js +271 -0
- package/dist/plugin/index.js.map +1 -0
- package/dist/plugin/interactive-checkpoint-gate.d.ts +35 -0
- package/dist/plugin/interactive-checkpoint-gate.js +86 -0
- package/dist/plugin/interactive-checkpoint-gate.js.map +1 -0
- package/dist/plugin/opencode-v2-executor.d.ts +55 -0
- package/dist/plugin/opencode-v2-executor.js +92 -0
- package/dist/plugin/opencode-v2-executor.js.map +1 -0
- package/dist/plugin/policy-checkpoint-gate.d.ts +18 -0
- package/dist/plugin/policy-checkpoint-gate.js +27 -0
- package/dist/plugin/policy-checkpoint-gate.js.map +1 -0
- package/dist/plugin/price-table.d.ts +46 -0
- package/dist/plugin/price-table.js +44 -0
- package/dist/plugin/price-table.js.map +1 -0
- package/dist/plugin/tui.d.ts +12 -0
- package/dist/plugin/tui.js +53 -0
- package/dist/plugin/tui.js.map +1 -0
- package/dist/quality/check.d.ts +30 -0
- package/dist/quality/check.js +51 -0
- package/dist/quality/check.js.map +1 -0
- package/dist/quality/checkpoint.d.ts +44 -0
- package/dist/quality/checkpoint.js +55 -0
- package/dist/quality/checkpoint.js.map +1 -0
- package/dist/quality/json.d.ts +10 -0
- package/dist/quality/json.js +31 -0
- package/dist/quality/json.js.map +1 -0
- package/dist/quality/predicates.d.ts +17 -0
- package/dist/quality/predicates.js +44 -0
- package/dist/quality/predicates.js.map +1 -0
- package/dist/quality/verify.d.ts +50 -0
- package/dist/quality/verify.js +110 -0
- package/dist/quality/verify.js.map +1 -0
- package/dist/registry/definition.d.ts +66 -0
- package/dist/registry/definition.js +19 -0
- package/dist/registry/definition.js.map +1 -0
- package/dist/registry/errors.d.ts +31 -0
- package/dist/registry/errors.js +48 -0
- package/dist/registry/errors.js.map +1 -0
- package/dist/registry/index.d.ts +9 -0
- package/dist/registry/index.js +9 -0
- package/dist/registry/index.js.map +1 -0
- package/dist/registry/registry.d.ts +45 -0
- package/dist/registry/registry.js +117 -0
- package/dist/registry/registry.js.map +1 -0
- package/dist/registry/runner.d.ts +56 -0
- package/dist/registry/runner.js +199 -0
- package/dist/registry/runner.js.map +1 -0
- package/dist/registry/schema.d.ts +18 -0
- package/dist/registry/schema.js +73 -0
- package/dist/registry/schema.js.map +1 -0
- package/dist/runtime/command.d.ts +38 -0
- package/dist/runtime/command.js +61 -0
- package/dist/runtime/command.js.map +1 -0
- package/dist/runtime/engine.d.ts +17 -0
- package/dist/runtime/engine.js +31 -0
- package/dist/runtime/engine.js.map +1 -0
- package/dist/runtime/errors.d.ts +56 -0
- package/dist/runtime/errors.js +88 -0
- package/dist/runtime/errors.js.map +1 -0
- package/dist/runtime/executor.d.ts +46 -0
- package/dist/runtime/executor.js +16 -0
- package/dist/runtime/executor.js.map +1 -0
- package/dist/runtime/semaphore.d.ts +24 -0
- package/dist/runtime/semaphore.js +57 -0
- package/dist/runtime/semaphore.js.map +1 -0
- package/dist/state/file-store.d.ts +25 -0
- package/dist/state/file-store.js +138 -0
- package/dist/state/file-store.js.map +1 -0
- package/dist/state/journal.d.ts +83 -0
- package/dist/state/journal.js +59 -0
- package/dist/state/journal.js.map +1 -0
- package/dist/state/recorder.d.ts +63 -0
- package/dist/state/recorder.js +138 -0
- package/dist/state/recorder.js.map +1 -0
- package/dist/state/store.d.ts +23 -0
- package/dist/state/store.js +14 -0
- package/dist/state/store.js.map +1 -0
- package/dist/workflow/agent.d.ts +8 -0
- package/dist/workflow/agent.js +38 -0
- package/dist/workflow/agent.js.map +1 -0
- package/dist/workflow/fallback.d.ts +15 -0
- package/dist/workflow/fallback.js +32 -0
- package/dist/workflow/fallback.js.map +1 -0
- package/dist/workflow/parallel.d.ts +14 -0
- package/dist/workflow/parallel.js +62 -0
- package/dist/workflow/parallel.js.map +1 -0
- package/dist/workflow/phase.d.ts +7 -0
- package/dist/workflow/phase.js +12 -0
- package/dist/workflow/phase.js.map +1 -0
- package/dist/workflow/reliable.d.ts +50 -0
- package/dist/workflow/reliable.js +92 -0
- package/dist/workflow/reliable.js.map +1 -0
- package/dist/workflow/retry.d.ts +28 -0
- package/dist/workflow/retry.js +53 -0
- package/dist/workflow/retry.js.map +1 -0
- package/dist/workflow/sequence.d.ts +40 -0
- package/dist/workflow/sequence.js +100 -0
- package/dist/workflow/sequence.js.map +1 -0
- package/dist/workflow/smoke.d.ts +17 -0
- package/dist/workflow/smoke.js +38 -0
- package/dist/workflow/smoke.js.map +1 -0
- package/dist/workflows/artifact.d.ts +24 -0
- package/dist/workflows/artifact.js +71 -0
- package/dist/workflows/artifact.js.map +1 -0
- package/dist/workflows/index.d.ts +19 -0
- package/dist/workflows/index.js +40 -0
- package/dist/workflows/index.js.map +1 -0
- package/dist/workflows/reliable.d.ts +18 -0
- package/dist/workflows/reliable.js +32 -0
- package/dist/workflows/reliable.js.map +1 -0
- package/dist/workflows/smoke.d.ts +13 -0
- package/dist/workflows/smoke.js +26 -0
- package/dist/workflows/smoke.js.map +1 -0
- package/dist/workspace/ambient.d.ts +12 -0
- package/dist/workspace/ambient.js +17 -0
- package/dist/workspace/ambient.js.map +1 -0
- package/dist/workspace/git-worktree.d.ts +29 -0
- package/dist/workspace/git-worktree.js +110 -0
- package/dist/workspace/git-worktree.js.map +1 -0
- package/dist/workspace/index.d.ts +6 -0
- package/dist/workspace/index.js +6 -0
- package/dist/workspace/index.js.map +1 -0
- package/dist/workspace/provider.d.ts +61 -0
- package/dist/workspace/provider.js +15 -0
- package/dist/workspace/provider.js.map +1 -0
- package/package.json +71 -0
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AgentExecutor —— Workflow Core 与宿主(OpenCode V2)之间唯一的边界抽象
|
|
3
|
+
*
|
|
4
|
+
* Workflow Core 只认识本文件中的接口:
|
|
5
|
+
* workflow -> AgentExecutor -> OpenCodeV2Executor -> ctx.session
|
|
6
|
+
*
|
|
7
|
+
* 架构约束:本文件禁止 import 任何 OpenCode API。
|
|
8
|
+
* 未来可新增 ClaudeCodeExecutor / CodexExecutor 等实现,Core 不感知。
|
|
9
|
+
*/
|
|
10
|
+
export interface AgentTask {
|
|
11
|
+
/** 发给子 agent 的提示词 */
|
|
12
|
+
prompt: string;
|
|
13
|
+
/**
|
|
14
|
+
* 子 agent 的工作目录(P2.7 隔离):启用 workspace 时为 worktree 根,
|
|
15
|
+
* executor 应将其绑定为子会话 cwd;未启用时不传。
|
|
16
|
+
*/
|
|
17
|
+
cwd?: string;
|
|
18
|
+
}
|
|
19
|
+
/** token 用量(结构对齐宿主 API 的 usage 形状,但为 Core 自有类型) */
|
|
20
|
+
export interface TokenUsage {
|
|
21
|
+
input: number;
|
|
22
|
+
output: number;
|
|
23
|
+
reasoning: number;
|
|
24
|
+
/** 思维链 token 已含在 output 中时为 0 */
|
|
25
|
+
cache: {
|
|
26
|
+
read: number;
|
|
27
|
+
write: number;
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
export interface AgentResult {
|
|
31
|
+
/** 子 agent 的最终文本输出 */
|
|
32
|
+
output: string;
|
|
33
|
+
/** token 用量(宿主能提供时;如 OpenCode V2 的 assistant 消息 tokens 字段) */
|
|
34
|
+
usage?: TokenUsage;
|
|
35
|
+
/** 本次调用的美元成本(宿主直接给出时;如 V2 的 cost 字段) */
|
|
36
|
+
costUSD?: number;
|
|
37
|
+
/** 实际使用的模型 "providerID/id"(宿主能提供时) */
|
|
38
|
+
model?: string;
|
|
39
|
+
}
|
|
40
|
+
export interface AgentExecutor {
|
|
41
|
+
execute(task: AgentTask): Promise<AgentResult>;
|
|
42
|
+
}
|
|
43
|
+
/** MockExecutor:完全脱离 OpenCode,用于测试 Workflow Core 本身 */
|
|
44
|
+
export declare class MockExecutor implements AgentExecutor {
|
|
45
|
+
execute(task: AgentTask): Promise<AgentResult>;
|
|
46
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AgentExecutor —— Workflow Core 与宿主(OpenCode V2)之间唯一的边界抽象
|
|
3
|
+
*
|
|
4
|
+
* Workflow Core 只认识本文件中的接口:
|
|
5
|
+
* workflow -> AgentExecutor -> OpenCodeV2Executor -> ctx.session
|
|
6
|
+
*
|
|
7
|
+
* 架构约束:本文件禁止 import 任何 OpenCode API。
|
|
8
|
+
* 未来可新增 ClaudeCodeExecutor / CodexExecutor 等实现,Core 不感知。
|
|
9
|
+
*/
|
|
10
|
+
/** MockExecutor:完全脱离 OpenCode,用于测试 Workflow Core 本身 */
|
|
11
|
+
export class MockExecutor {
|
|
12
|
+
async execute(task) {
|
|
13
|
+
return { output: `[mock] ${task.prompt}` };
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=executor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"executor.js","sourceRoot":"","sources":["../../src/runtime/executor.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAoCH,uDAAuD;AACvD,MAAM,OAAO,YAAY;IACvB,KAAK,CAAC,OAAO,CAAC,IAAe;QAC3B,OAAO,EAAE,MAAM,EAAE,UAAU,IAAI,CAAC,MAAM,EAAE,EAAE,CAAA;IAC5C,CAAC;CACF"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Semaphore —— 并发上限控制
|
|
3
|
+
*
|
|
4
|
+
* 环境约束:模型 API 最多支持 3 个并发请求。
|
|
5
|
+
* parallel() 本身不限流(保持 Core 语义纯净),限流在执行器包装层完成:
|
|
6
|
+
* withConcurrencyLimit(executor, 3)
|
|
7
|
+
*/
|
|
8
|
+
import type { AgentExecutor } from "./executor.js";
|
|
9
|
+
export declare class Semaphore {
|
|
10
|
+
private readonly limit;
|
|
11
|
+
private active;
|
|
12
|
+
private readonly queue;
|
|
13
|
+
constructor(limit: number);
|
|
14
|
+
run<T>(fn: () => Promise<T>): Promise<T>;
|
|
15
|
+
private acquire;
|
|
16
|
+
private release;
|
|
17
|
+
}
|
|
18
|
+
/** 默认并发上限:模型 API 最多支持 3 个并发 */
|
|
19
|
+
export declare const DEFAULT_MAX_CONCURRENCY = 3;
|
|
20
|
+
/**
|
|
21
|
+
* 包装一个 AgentExecutor,限制同时执行的 agent 任务数。
|
|
22
|
+
* 超出上限的任务排队等待(FIFO),不拒绝。
|
|
23
|
+
*/
|
|
24
|
+
export declare function withConcurrencyLimit(executor: AgentExecutor, limit?: number): AgentExecutor;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Semaphore —— 并发上限控制
|
|
3
|
+
*
|
|
4
|
+
* 环境约束:模型 API 最多支持 3 个并发请求。
|
|
5
|
+
* parallel() 本身不限流(保持 Core 语义纯净),限流在执行器包装层完成:
|
|
6
|
+
* withConcurrencyLimit(executor, 3)
|
|
7
|
+
*/
|
|
8
|
+
export class Semaphore {
|
|
9
|
+
limit;
|
|
10
|
+
active = 0;
|
|
11
|
+
queue = [];
|
|
12
|
+
constructor(limit) {
|
|
13
|
+
this.limit = limit;
|
|
14
|
+
if (limit < 1)
|
|
15
|
+
throw new Error(`Semaphore limit must be >= 1, got ${limit}`);
|
|
16
|
+
}
|
|
17
|
+
async run(fn) {
|
|
18
|
+
await this.acquire();
|
|
19
|
+
try {
|
|
20
|
+
return await fn();
|
|
21
|
+
}
|
|
22
|
+
finally {
|
|
23
|
+
this.release();
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
acquire() {
|
|
27
|
+
if (this.active < this.limit) {
|
|
28
|
+
this.active += 1;
|
|
29
|
+
return Promise.resolve();
|
|
30
|
+
}
|
|
31
|
+
return new Promise((resolve) => this.queue.push(resolve));
|
|
32
|
+
}
|
|
33
|
+
release() {
|
|
34
|
+
const next = this.queue.shift();
|
|
35
|
+
if (next) {
|
|
36
|
+
next(); // 保持 active 不变:槽位直接移交给下一个等待者
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
this.active -= 1;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
/** 默认并发上限:模型 API 最多支持 3 个并发 */
|
|
44
|
+
export const DEFAULT_MAX_CONCURRENCY = 3;
|
|
45
|
+
/**
|
|
46
|
+
* 包装一个 AgentExecutor,限制同时执行的 agent 任务数。
|
|
47
|
+
* 超出上限的任务排队等待(FIFO),不拒绝。
|
|
48
|
+
*/
|
|
49
|
+
export function withConcurrencyLimit(executor, limit = DEFAULT_MAX_CONCURRENCY) {
|
|
50
|
+
const semaphore = new Semaphore(limit);
|
|
51
|
+
return {
|
|
52
|
+
async execute(task) {
|
|
53
|
+
return semaphore.run(() => executor.execute(task));
|
|
54
|
+
},
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=semaphore.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"semaphore.js","sourceRoot":"","sources":["../../src/runtime/semaphore.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,MAAM,OAAO,SAAS;IAIS,KAAK;IAH1B,MAAM,GAAG,CAAC,CAAA;IACD,KAAK,GAAsB,EAAE,CAAA;IAE9C,YAA6B,KAAa;qBAAb,KAAK;QAChC,IAAI,KAAK,GAAG,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,qCAAqC,KAAK,EAAE,CAAC,CAAA;IAC9E,CAAC;IAED,KAAK,CAAC,GAAG,CAAI,EAAoB;QAC/B,MAAM,IAAI,CAAC,OAAO,EAAE,CAAA;QACpB,IAAI,CAAC;YACH,OAAO,MAAM,EAAE,EAAE,CAAA;QACnB,CAAC;gBAAS,CAAC;YACT,IAAI,CAAC,OAAO,EAAE,CAAA;QAChB,CAAC;IACH,CAAC;IAEO,OAAO;QACb,IAAI,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,KAAK,EAAE,CAAC;YAC7B,IAAI,CAAC,MAAM,IAAI,CAAC,CAAA;YAChB,OAAO,OAAO,CAAC,OAAO,EAAE,CAAA;QAC1B,CAAC;QACD,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAA;IAC3D,CAAC;IAEO,OAAO;QACb,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAA;QAC/B,IAAI,IAAI,EAAE,CAAC;YACT,IAAI,EAAE,CAAA,CAAC,6BAA6B;QACtC,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,MAAM,IAAI,CAAC,CAAA;QAClB,CAAC;IACH,CAAC;CACF;AAED,+BAA+B;AAC/B,MAAM,CAAC,MAAM,uBAAuB,GAAG,CAAC,CAAA;AAExC;;;GAGG;AACH,MAAM,UAAU,oBAAoB,CAClC,QAAuB,EACvB,KAAK,GAAW,uBAAuB;IAEvC,MAAM,SAAS,GAAG,IAAI,SAAS,CAAC,KAAK,CAAC,CAAA;IACtC,OAAO;QACL,KAAK,CAAC,OAAO,CAAC,IAAe;YAC3B,OAAO,SAAS,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAA;QACpD,CAAC;KACF,CAAA;AACH,CAAC"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* FileExecutionStore —— 基于 JSON 文件的执行记录存储(P2.1)
|
|
3
|
+
*
|
|
4
|
+
* 布局:<baseDir>/<runId>.json(每 run 一个文件,全量快照)
|
|
5
|
+
* 原子性:先写同目录临时文件再 rename(POSIX 同文件系统 rename 原子),
|
|
6
|
+
* 读者永远不会读到半截 JSON。
|
|
7
|
+
* 安全:runId 白名单校验(防路径穿越)。
|
|
8
|
+
* 序列化:safeSerialize 兜底处理函数/BigInt/symbol/Error/循环引用,
|
|
9
|
+
* 保证任何 in-memory run 都能落盘(占位描述代替不可 JSON 化的值)。
|
|
10
|
+
*/
|
|
11
|
+
import { type WorkflowRun } from "./journal.js";
|
|
12
|
+
import type { ExecutionStore } from "./store.js";
|
|
13
|
+
export declare class FileExecutionStore implements ExecutionStore {
|
|
14
|
+
private readonly baseDir;
|
|
15
|
+
constructor(baseDir: string);
|
|
16
|
+
private filePath;
|
|
17
|
+
private ensureDir;
|
|
18
|
+
private exists;
|
|
19
|
+
/** 原子写:同目录 tmp -> rename(隐藏名,避免被 listRuns 扫到) */
|
|
20
|
+
private writeFileAtomic;
|
|
21
|
+
createRun(run: WorkflowRun): Promise<void>;
|
|
22
|
+
saveRun(run: WorkflowRun): Promise<void>;
|
|
23
|
+
getRun(runId: string): Promise<WorkflowRun | undefined>;
|
|
24
|
+
listRuns(workflowId?: string): Promise<WorkflowRun[]>;
|
|
25
|
+
}
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* FileExecutionStore —— 基于 JSON 文件的执行记录存储(P2.1)
|
|
3
|
+
*
|
|
4
|
+
* 布局:<baseDir>/<runId>.json(每 run 一个文件,全量快照)
|
|
5
|
+
* 原子性:先写同目录临时文件再 rename(POSIX 同文件系统 rename 原子),
|
|
6
|
+
* 读者永远不会读到半截 JSON。
|
|
7
|
+
* 安全:runId 白名单校验(防路径穿越)。
|
|
8
|
+
* 序列化:safeSerialize 兜底处理函数/BigInt/symbol/Error/循环引用,
|
|
9
|
+
* 保证任何 in-memory run 都能落盘(占位描述代替不可 JSON 化的值)。
|
|
10
|
+
*/
|
|
11
|
+
import { promises as fs } from "node:fs";
|
|
12
|
+
import path from "node:path";
|
|
13
|
+
import { isValidRunId } from "./journal.js";
|
|
14
|
+
/** 不可 JSON 化的值 -> 占位描述 */
|
|
15
|
+
function toPlaceholder(value) {
|
|
16
|
+
if (typeof value === "function")
|
|
17
|
+
return "[Function]";
|
|
18
|
+
if (typeof value === "bigint")
|
|
19
|
+
return value.toString();
|
|
20
|
+
if (typeof value === "symbol")
|
|
21
|
+
return String(value);
|
|
22
|
+
if (value instanceof Error)
|
|
23
|
+
return { name: value.name, message: value.message };
|
|
24
|
+
return value;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* 安全序列化:
|
|
28
|
+
* 1) 常规路径:基础替换器(重复引用保持原语义,完整复制两份)
|
|
29
|
+
* 2) 循环引用退路:WeakSet 标记(可能把重复引用误标为 [Circular],可接受——
|
|
30
|
+
* 只有出现真正循环时才会走到这条路)
|
|
31
|
+
*/
|
|
32
|
+
function safeSerialize(run) {
|
|
33
|
+
try {
|
|
34
|
+
return JSON.stringify(run, (_key, value) => toPlaceholder(value), 2) + "\n";
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
const seen = new WeakSet();
|
|
38
|
+
return (JSON.stringify(run, (_key, value) => {
|
|
39
|
+
if (typeof value === "object" && value !== null) {
|
|
40
|
+
if (seen.has(value))
|
|
41
|
+
return "[Circular]";
|
|
42
|
+
seen.add(value);
|
|
43
|
+
}
|
|
44
|
+
return toPlaceholder(value);
|
|
45
|
+
}, 2) + "\n");
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
export class FileExecutionStore {
|
|
49
|
+
baseDir;
|
|
50
|
+
constructor(baseDir) {
|
|
51
|
+
this.baseDir = baseDir;
|
|
52
|
+
}
|
|
53
|
+
filePath(runId) {
|
|
54
|
+
if (!isValidRunId(runId)) {
|
|
55
|
+
throw new Error(`[agentic-workflow] invalid runId: ${JSON.stringify(runId)}`);
|
|
56
|
+
}
|
|
57
|
+
return path.join(this.baseDir, `${runId}.json`);
|
|
58
|
+
}
|
|
59
|
+
async ensureDir() {
|
|
60
|
+
await fs.mkdir(this.baseDir, { recursive: true });
|
|
61
|
+
}
|
|
62
|
+
async exists(file) {
|
|
63
|
+
try {
|
|
64
|
+
await fs.access(file);
|
|
65
|
+
return true;
|
|
66
|
+
}
|
|
67
|
+
catch {
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
/** 原子写:同目录 tmp -> rename(隐藏名,避免被 listRuns 扫到) */
|
|
72
|
+
async writeFileAtomic(file, content) {
|
|
73
|
+
const tmp = path.join(path.dirname(file), `.${path.basename(file)}.${Date.now()}.${Math.random().toString(36).slice(2, 8)}.tmp`);
|
|
74
|
+
await fs.writeFile(tmp, content, "utf8");
|
|
75
|
+
await fs.rename(tmp, file);
|
|
76
|
+
}
|
|
77
|
+
async createRun(run) {
|
|
78
|
+
const file = this.filePath(run.runId);
|
|
79
|
+
await this.ensureDir();
|
|
80
|
+
if (await this.exists(file)) {
|
|
81
|
+
throw new Error(`[agentic-workflow] run already exists: ${run.runId}`);
|
|
82
|
+
}
|
|
83
|
+
await this.writeFileAtomic(file, safeSerialize(run));
|
|
84
|
+
}
|
|
85
|
+
async saveRun(run) {
|
|
86
|
+
const file = this.filePath(run.runId);
|
|
87
|
+
await this.ensureDir();
|
|
88
|
+
await this.writeFileAtomic(file, safeSerialize(run));
|
|
89
|
+
}
|
|
90
|
+
async getRun(runId) {
|
|
91
|
+
const file = this.filePath(runId);
|
|
92
|
+
let raw;
|
|
93
|
+
try {
|
|
94
|
+
raw = await fs.readFile(file, "utf8");
|
|
95
|
+
}
|
|
96
|
+
catch (error) {
|
|
97
|
+
if (error.code === "ENOENT")
|
|
98
|
+
return undefined;
|
|
99
|
+
throw error;
|
|
100
|
+
}
|
|
101
|
+
try {
|
|
102
|
+
return JSON.parse(raw);
|
|
103
|
+
}
|
|
104
|
+
catch (error) {
|
|
105
|
+
throw new Error(`[agentic-workflow] corrupt run file ${file}: ` +
|
|
106
|
+
`${error instanceof Error ? error.message : String(error)}`);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
async listRuns(workflowId) {
|
|
110
|
+
let entries;
|
|
111
|
+
try {
|
|
112
|
+
entries = await fs.readdir(this.baseDir);
|
|
113
|
+
}
|
|
114
|
+
catch (error) {
|
|
115
|
+
if (error.code === "ENOENT")
|
|
116
|
+
return [];
|
|
117
|
+
throw error;
|
|
118
|
+
}
|
|
119
|
+
const runs = [];
|
|
120
|
+
for (const entry of entries) {
|
|
121
|
+
if (!entry.endsWith(".json"))
|
|
122
|
+
continue;
|
|
123
|
+
const file = path.join(this.baseDir, entry);
|
|
124
|
+
try {
|
|
125
|
+
const run = JSON.parse(await fs.readFile(file, "utf8"));
|
|
126
|
+
if (workflowId && run.workflow?.id !== workflowId)
|
|
127
|
+
continue;
|
|
128
|
+
runs.push(run);
|
|
129
|
+
}
|
|
130
|
+
catch {
|
|
131
|
+
console.log(`[agentic-workflow] skipping corrupt run file: ${file}`);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
runs.sort((a, b) => b.startedAt - a.startedAt);
|
|
135
|
+
return runs;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
//# sourceMappingURL=file-store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"file-store.js","sourceRoot":"","sources":["../../src/state/file-store.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Workflow Journal 模型(P2.1,P2.5 升级版本身份)
|
|
3
|
+
*
|
|
4
|
+
* 一个 WorkflowRun 是某次 workflow 执行的持久化描述,是 P2 后续
|
|
5
|
+
* resume(P2.2)、trace(P2.4)、TUI(P2.3)、registry(P2.5)的共同基础。
|
|
6
|
+
*
|
|
7
|
+
* 形状:
|
|
8
|
+
* WorkflowRun
|
|
9
|
+
* ├─ runId / workflow { id, version } ← P2.5:版本身份(安全 resume 的依据)
|
|
10
|
+
* ├─ status / currentStep
|
|
11
|
+
* ├─ startedAt / completedAt
|
|
12
|
+
* └─ steps[] { input, output, status, error, timestamps }
|
|
13
|
+
*
|
|
14
|
+
* 可序列化纪律:run 及 steps 的 input/output/error 必须可 JSON 化
|
|
15
|
+
* (不可 JSON 化的值在落盘时被替换为占位描述,见 file-store 的 replacer;
|
|
16
|
+
* 需要被 resume 消费的值应保持 JSON-safe,如 AgentResult { output: string })。
|
|
17
|
+
*/
|
|
18
|
+
import type { WorkspaceIdentity } from "../workspace/provider.js";
|
|
19
|
+
export type RunStatus = "running" | "completed" | "failed" | "aborted";
|
|
20
|
+
export type StepStatus = "pending" | "running" | "completed" | "failed" | "skipped";
|
|
21
|
+
export interface StepErrorRecord {
|
|
22
|
+
/** 错误名(如 WorkflowCheckError) */
|
|
23
|
+
name: string;
|
|
24
|
+
message: string;
|
|
25
|
+
}
|
|
26
|
+
/** workflow 版本身份:resume 必须经 registry 解析到精确版本,绝不隐式取最新 */
|
|
27
|
+
export interface WorkflowIdentity {
|
|
28
|
+
id: string;
|
|
29
|
+
/** 语义化版本(与 WorkflowDefinition.version 一致) */
|
|
30
|
+
version: string;
|
|
31
|
+
}
|
|
32
|
+
export interface StepRecord {
|
|
33
|
+
/** 步骤序号(0 起,对应 sequence 下标) */
|
|
34
|
+
index: number;
|
|
35
|
+
/** 步骤名(可选,便于定位) */
|
|
36
|
+
name?: string;
|
|
37
|
+
status: StepStatus;
|
|
38
|
+
/** 步骤输入(序列化后;通常是上一步 output) */
|
|
39
|
+
input?: unknown;
|
|
40
|
+
/** 步骤输出(序列化后;resume 时作为后续步骤的 prev) */
|
|
41
|
+
output?: unknown;
|
|
42
|
+
/** 失败信息 */
|
|
43
|
+
error?: StepErrorRecord;
|
|
44
|
+
startedAt?: number;
|
|
45
|
+
completedAt?: number;
|
|
46
|
+
}
|
|
47
|
+
export interface WorkflowRun {
|
|
48
|
+
/** 全局唯一 run 标识(run_<time>_<rand>) */
|
|
49
|
+
runId: string;
|
|
50
|
+
/** workflow 版本身份(P2.5:安全 resume 依据,见 registry.resolve) */
|
|
51
|
+
workflow: WorkflowIdentity;
|
|
52
|
+
status: RunStatus;
|
|
53
|
+
/** 当前/最后尝试的步骤序号;未开始为 -1 */
|
|
54
|
+
currentStep: number;
|
|
55
|
+
/** epoch ms(排序与耗时计算用) */
|
|
56
|
+
startedAt: number;
|
|
57
|
+
completedAt?: number;
|
|
58
|
+
/** 执行参数(重建执行所需的最小上下文,如 topic;由 argsSchema 声明契约) */
|
|
59
|
+
args?: unknown;
|
|
60
|
+
/**
|
|
61
|
+
* 隔离工作区身份(P2.7):resume 必须附着原 workspace——
|
|
62
|
+
* durable resume = journal 状态 + 文件系统状态同时恢复。
|
|
63
|
+
* 清理完成后置 undefined(见 RunJournal.clearWorkspace)。
|
|
64
|
+
*/
|
|
65
|
+
workspace?: WorkspaceIdentity;
|
|
66
|
+
/** 失败/中断原因(run 级别) */
|
|
67
|
+
failure?: StepErrorRecord;
|
|
68
|
+
steps: StepRecord[];
|
|
69
|
+
}
|
|
70
|
+
/** 生成 runId:run_<epochms>_<rand8>(文件名安全字符) */
|
|
71
|
+
export declare function generateRunId(): string;
|
|
72
|
+
/** runId 白名单(防止路径穿越;文件名安全字符集) */
|
|
73
|
+
export declare function isValidRunId(runId: string): boolean;
|
|
74
|
+
/** Error -> StepErrorRecord */
|
|
75
|
+
export declare function toErrorRecord(error: unknown): StepErrorRecord;
|
|
76
|
+
/** 创建初始 run(全部步骤 pending) */
|
|
77
|
+
export declare function createRun(input: {
|
|
78
|
+
runId?: string;
|
|
79
|
+
workflow: WorkflowIdentity;
|
|
80
|
+
args?: unknown;
|
|
81
|
+
stepNames?: string[];
|
|
82
|
+
stepCount: number;
|
|
83
|
+
}): WorkflowRun;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Workflow Journal 模型(P2.1,P2.5 升级版本身份)
|
|
3
|
+
*
|
|
4
|
+
* 一个 WorkflowRun 是某次 workflow 执行的持久化描述,是 P2 后续
|
|
5
|
+
* resume(P2.2)、trace(P2.4)、TUI(P2.3)、registry(P2.5)的共同基础。
|
|
6
|
+
*
|
|
7
|
+
* 形状:
|
|
8
|
+
* WorkflowRun
|
|
9
|
+
* ├─ runId / workflow { id, version } ← P2.5:版本身份(安全 resume 的依据)
|
|
10
|
+
* ├─ status / currentStep
|
|
11
|
+
* ├─ startedAt / completedAt
|
|
12
|
+
* └─ steps[] { input, output, status, error, timestamps }
|
|
13
|
+
*
|
|
14
|
+
* 可序列化纪律:run 及 steps 的 input/output/error 必须可 JSON 化
|
|
15
|
+
* (不可 JSON 化的值在落盘时被替换为占位描述,见 file-store 的 replacer;
|
|
16
|
+
* 需要被 resume 消费的值应保持 JSON-safe,如 AgentResult { output: string })。
|
|
17
|
+
*/
|
|
18
|
+
/** 生成 runId:run_<epochms>_<rand8>(文件名安全字符) */
|
|
19
|
+
export function generateRunId() {
|
|
20
|
+
const rand = Math.random().toString(36).slice(2, 10);
|
|
21
|
+
return `run_${Date.now()}_${rand}`;
|
|
22
|
+
}
|
|
23
|
+
/** runId 白名单(防止路径穿越;文件名安全字符集) */
|
|
24
|
+
export function isValidRunId(runId) {
|
|
25
|
+
return /^[A-Za-z0-9][A-Za-z0-9_.-]{0,127}$/.test(runId);
|
|
26
|
+
}
|
|
27
|
+
/** Error -> StepErrorRecord */
|
|
28
|
+
export function toErrorRecord(error) {
|
|
29
|
+
if (error instanceof Error) {
|
|
30
|
+
return { name: error.name, message: error.message };
|
|
31
|
+
}
|
|
32
|
+
return { name: "Error", message: String(error) };
|
|
33
|
+
}
|
|
34
|
+
/** 创建初始 run(全部步骤 pending) */
|
|
35
|
+
export function createRun(input) {
|
|
36
|
+
if (!Number.isInteger(input.stepCount) || input.stepCount < 0) {
|
|
37
|
+
throw new Error(`stepCount must be a non-negative integer, got ${input.stepCount}`);
|
|
38
|
+
}
|
|
39
|
+
if (!input.workflow.id || typeof input.workflow.id !== "string") {
|
|
40
|
+
throw new Error("workflow.id must be a non-empty string");
|
|
41
|
+
}
|
|
42
|
+
if (!input.workflow.version || typeof input.workflow.version !== "string") {
|
|
43
|
+
throw new Error("workflow.version must be a non-empty string");
|
|
44
|
+
}
|
|
45
|
+
return {
|
|
46
|
+
runId: input.runId ?? generateRunId(),
|
|
47
|
+
workflow: input.workflow,
|
|
48
|
+
status: "running",
|
|
49
|
+
currentStep: -1,
|
|
50
|
+
startedAt: Date.now(),
|
|
51
|
+
args: input.args,
|
|
52
|
+
steps: Array.from({ length: input.stepCount }, (_, index) => ({
|
|
53
|
+
index,
|
|
54
|
+
name: input.stepNames?.[index],
|
|
55
|
+
status: "pending",
|
|
56
|
+
})),
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
//# sourceMappingURL=journal.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"journal.js","sourceRoot":"","sources":["../../src/state/journal.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AA2DH,8CAA8C;AAC9C,MAAM,UAAU,aAAa;IAC3B,MAAM,IAAI,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAA;IACpD,OAAO,OAAO,IAAI,CAAC,GAAG,EAAE,IAAI,IAAI,EAAE,CAAA;AACpC,CAAC;AAED,iCAAiC;AACjC,MAAM,UAAU,YAAY,CAAC,KAAa;IACxC,OAAO,oCAAoC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;AACzD,CAAC;AAED,+BAA+B;AAC/B,MAAM,UAAU,aAAa,CAAC,KAAc;IAC1C,IAAI,KAAK,YAAY,KAAK,EAAE,CAAC;QAC3B,OAAO,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,EAAE,CAAA;IACrD,CAAC;IACD,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,CAAC,KAAK,CAAC,EAAE,CAAA;AAClD,CAAC;AAED,6BAA6B;AAC7B,MAAM,UAAU,SAAS,CAAC,KAMzB;IACC,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,SAAS,CAAC,IAAI,KAAK,CAAC,SAAS,GAAG,CAAC,EAAE,CAAC;QAC9D,MAAM,IAAI,KAAK,CAAC,iDAAiD,KAAK,CAAC,SAAS,EAAE,CAAC,CAAA;IACrF,CAAC;IACD,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,EAAE,IAAI,OAAO,KAAK,CAAC,QAAQ,CAAC,EAAE,KAAK,QAAQ,EAAE,CAAC;QAChE,MAAM,IAAI,KAAK,CAAC,wCAAwC,CAAC,CAAA;IAC3D,CAAC;IACD,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,OAAO,IAAI,OAAO,KAAK,CAAC,QAAQ,CAAC,OAAO,KAAK,QAAQ,EAAE,CAAC;QAC1E,MAAM,IAAI,KAAK,CAAC,6CAA6C,CAAC,CAAA;IAChE,CAAC;IACD,OAAO;QACL,KAAK,EAAE,KAAK,CAAC,KAAK,IAAI,aAAa,EAAE;QACrC,QAAQ,EAAE,KAAK,CAAC,QAAQ;QACxB,MAAM,EAAE,SAAS;QACjB,WAAW,EAAE,CAAC,CAAC;QACf,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;QACrB,IAAI,EAAE,KAAK,CAAC,IAAI;QAChB,KAAK,EAAE,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC;YAC5D,KAAK;YACL,IAAI,EAAE,KAAK,CAAC,SAAS,EAAE,CAAC,KAAK,CAAC;YAC9B,MAAM,EAAE,SAAkB;SAC3B,CAAC,CAAC;KACJ,CAAA;AACH,CAAC"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RunJournal —— run 生命周期记录器(P2.1)
|
|
3
|
+
*
|
|
4
|
+
* 包装 ExecutionStore:每次状态变更后全量落盘(journal = 一串原子快照)。
|
|
5
|
+
* P2.2 的 sequence resume 将基于这些记录重建执行(attach -> 读已完成步骤 -> 续跑)。
|
|
6
|
+
*
|
|
7
|
+
* 典型生命周期:
|
|
8
|
+
* start() -> run 创建并落盘(全部步骤 pending)
|
|
9
|
+
* stepStarted(i) -> 步骤开始(input 记录)
|
|
10
|
+
* stepCompleted(i) -> 步骤成功(output 记录,resume 时的续跑依据)
|
|
11
|
+
* stepFailed(i, e) -> 步骤失败(error 记录)
|
|
12
|
+
* complete()/fail(e) -> run 收口(fail 时仍为 pending 的步骤标记 skipped)
|
|
13
|
+
*
|
|
14
|
+
* status=aborted 预留给中断场景(交互拒绝/人工打断,P2.3+)。
|
|
15
|
+
*/
|
|
16
|
+
import { type WorkflowIdentity, type WorkflowRun } from "./journal.js";
|
|
17
|
+
import type { ExecutionStore } from "./store.js";
|
|
18
|
+
import type { WorkspaceIdentity } from "../workspace/provider.js";
|
|
19
|
+
export interface JournalStartInput {
|
|
20
|
+
workflow: WorkflowIdentity;
|
|
21
|
+
/** 执行参数(重建执行所需的最小上下文) */
|
|
22
|
+
args?: unknown;
|
|
23
|
+
/** 步骤名(可选) */
|
|
24
|
+
stepNames?: string[];
|
|
25
|
+
stepCount: number;
|
|
26
|
+
/** 显式 runId(缺省自动生成) */
|
|
27
|
+
runId?: string;
|
|
28
|
+
}
|
|
29
|
+
export declare class RunJournal {
|
|
30
|
+
private readonly store;
|
|
31
|
+
readonly run: WorkflowRun;
|
|
32
|
+
private constructor();
|
|
33
|
+
/** 创建新 run 并持久化初始状态 */
|
|
34
|
+
static start(store: ExecutionStore, input: JournalStartInput): Promise<RunJournal>;
|
|
35
|
+
/** 附加到已有 run(resume 场景:读取历史 journal 继续记录);不存在返回 undefined */
|
|
36
|
+
static attach(store: ExecutionStore, runId: string): Promise<RunJournal | undefined>;
|
|
37
|
+
private assertOpen;
|
|
38
|
+
private step;
|
|
39
|
+
stepStarted(index: number, input?: unknown): Promise<void>;
|
|
40
|
+
stepCompleted(index: number, output?: unknown): Promise<void>;
|
|
41
|
+
stepFailed(index: number, error: unknown): Promise<void>;
|
|
42
|
+
/** run 成功收口 */
|
|
43
|
+
complete(): Promise<void>;
|
|
44
|
+
/** run 失败收口:仍为 pending 的步骤标记 skipped(fail-fast 下它们不会被执行) */
|
|
45
|
+
fail(error: unknown): Promise<void>;
|
|
46
|
+
/**
|
|
47
|
+
* 记录 workspace 身份(P2.7 隔离启用时,resume attach 的依据)。
|
|
48
|
+
* 仅限 run 进行中(收口后不可再写)。
|
|
49
|
+
*/
|
|
50
|
+
setWorkspace(identity: WorkspaceIdentity): Promise<void>;
|
|
51
|
+
/**
|
|
52
|
+
* 清除 workspace 身份(清理完成后调用)。
|
|
53
|
+
* 维护性操作:允许在 run 收口后执行——否则已清理的 run
|
|
54
|
+
* 在幂等 resume 时会试图 attach 一个已删除的 worktree。
|
|
55
|
+
*/
|
|
56
|
+
clearWorkspace(): Promise<void>;
|
|
57
|
+
/**
|
|
58
|
+
* 重新打开已收口的 run(resume 场景):
|
|
59
|
+
* failed/aborted -> running;未完成步骤重置 pending(连同 input/output/时间戳/错误),
|
|
60
|
+
* completed 步骤原样保留(resume 的续跑依据)。
|
|
61
|
+
*/
|
|
62
|
+
reopen(): Promise<void>;
|
|
63
|
+
}
|