@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,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Checkpoint RPC 契约(P2.3,server 与 TUI 双形态共享)
|
|
3
|
+
*
|
|
4
|
+
* 流向:
|
|
5
|
+
* server workflow -> checkpoint() -> InteractiveCheckpointGate
|
|
6
|
+
* --emit--> rpc.agentic-workflow.requested { requestId, label, message }
|
|
7
|
+
* <--call-- TUI: ui.dialog.confirm -> rpc "reply" { requestId, approved, reason? }
|
|
8
|
+
*
|
|
9
|
+
* 事件经 OpenCode 事件流广播(V2EventRpc),TUI 侧 ctx.data.on 订阅;
|
|
10
|
+
* 应答经 client.rpc(...)(或 rpc.call)回到 server 侧注册的 handler。
|
|
11
|
+
*/
|
|
12
|
+
import { Rpc } from "@opencode/plugin/rpc";
|
|
13
|
+
export const CheckpointRpc = Rpc.define({
|
|
14
|
+
id: "agentic-workflow",
|
|
15
|
+
methods: {
|
|
16
|
+
/** TUI -> server:应答审批请求 */
|
|
17
|
+
reply: {
|
|
18
|
+
input: {
|
|
19
|
+
type: "object",
|
|
20
|
+
properties: {
|
|
21
|
+
requestId: { type: "string" },
|
|
22
|
+
approved: { type: "boolean" },
|
|
23
|
+
reason: { type: "string" },
|
|
24
|
+
},
|
|
25
|
+
required: ["requestId", "approved"],
|
|
26
|
+
additionalProperties: false,
|
|
27
|
+
},
|
|
28
|
+
output: {
|
|
29
|
+
type: "object",
|
|
30
|
+
properties: {
|
|
31
|
+
ok: { type: "boolean" },
|
|
32
|
+
},
|
|
33
|
+
required: ["ok"],
|
|
34
|
+
additionalProperties: false,
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
events: {
|
|
39
|
+
/** server -> TUI:审批请求 */
|
|
40
|
+
requested: {
|
|
41
|
+
schema: {
|
|
42
|
+
type: "object",
|
|
43
|
+
properties: {
|
|
44
|
+
requestId: { type: "string" },
|
|
45
|
+
label: { type: "string" },
|
|
46
|
+
message: { type: "string" },
|
|
47
|
+
},
|
|
48
|
+
required: ["requestId", "label", "message"],
|
|
49
|
+
additionalProperties: false,
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
});
|
|
54
|
+
/** TUI 订阅用的事件名(字面量类型,保证 data.on 类型匹配;与 CheckpointRpc.id 的一致性由单测保证) */
|
|
55
|
+
export const CHECKPOINT_REQUESTED_EVENT = "rpc.agentic-workflow.requested";
|
|
56
|
+
/** 解析 TUI 应答(handler 入参为 unknown,需手动收窄) */
|
|
57
|
+
export function parseCheckpointReply(input) {
|
|
58
|
+
if (typeof input !== "object" || input === null)
|
|
59
|
+
return undefined;
|
|
60
|
+
const record = input;
|
|
61
|
+
if (typeof record.requestId !== "string" || typeof record.approved !== "boolean") {
|
|
62
|
+
return undefined;
|
|
63
|
+
}
|
|
64
|
+
if (record.reason !== undefined && typeof record.reason !== "string") {
|
|
65
|
+
return undefined;
|
|
66
|
+
}
|
|
67
|
+
return {
|
|
68
|
+
requestId: record.requestId,
|
|
69
|
+
approved: record.approved,
|
|
70
|
+
...(typeof record.reason === "string" ? { reason: record.reason } : {}),
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
//# sourceMappingURL=checkpoint-rpc.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"checkpoint-rpc.js","sourceRoot":"","sources":["../../src/plugin/checkpoint-rpc.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,GAAG,EAAE,MAAM,sBAAsB,CAAA;AAE1C,MAAM,CAAC,MAAM,aAAa,GAAG,GAAG,CAAC,MAAM,CAAC;IACtC,EAAE,EAAE,kBAAkB;IACtB,OAAO,EAAE;QACP,2BAA2B;QAC3B,KAAK,EAAE;YACL,KAAK,EAAE;gBACL,IAAI,EAAE,QAAQ;gBACd,UAAU,EAAE;oBACV,SAAS,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;oBAC7B,QAAQ,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE;oBAC7B,MAAM,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;iBAC3B;gBACD,QAAQ,EAAE,CAAC,WAAW,EAAE,UAAU,CAAC;gBACnC,oBAAoB,EAAE,KAAK;aAC5B;YACD,MAAM,EAAE;gBACN,IAAI,EAAE,QAAQ;gBACd,UAAU,EAAE;oBACV,EAAE,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE;iBACxB;gBACD,QAAQ,EAAE,CAAC,IAAI,CAAC;gBAChB,oBAAoB,EAAE,KAAK;aAC5B;SACF;KACF;IACD,MAAM,EAAE;QACN,yBAAyB;QACzB,SAAS,EAAE;YACT,MAAM,EAAE;gBACN,IAAI,EAAE,QAAQ;gBACd,UAAU,EAAE;oBACV,SAAS,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;oBAC7B,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;oBACzB,OAAO,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;iBAC5B;gBACD,QAAQ,EAAE,CAAC,WAAW,EAAE,OAAO,EAAE,SAAS,CAAC;gBAC3C,oBAAoB,EAAE,KAAK;aAC5B;SACF;KACF;CACF,CAAC,CAAA;AAEF,sEAAsE;AACtE,MAAM,CAAC,MAAM,0BAA0B,GAAG,gCAAyC,CAAA;AAEnF,2CAA2C;AAC3C,MAAM,UAAU,oBAAoB,CAClC,KAAc;IAEd,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,SAAS,CAAA;IACjE,MAAM,MAAM,GAAG,KAAsE,CAAA;IACrF,IAAI,OAAO,MAAM,CAAC,SAAS,KAAK,QAAQ,IAAI,OAAO,MAAM,CAAC,QAAQ,KAAK,SAAS,EAAE,CAAC;QACjF,OAAO,SAAS,CAAA;IAClB,CAAC;IACD,IAAI,MAAM,CAAC,MAAM,KAAK,SAAS,IAAI,OAAO,MAAM,CAAC,MAAM,KAAK,QAAQ,EAAE,CAAC;QACrE,OAAO,SAAS,CAAA;IAClB,CAAC;IACD,OAAO;QACL,SAAS,EAAE,MAAM,CAAC,SAAS;QAC3B,QAAQ,EAAE,MAAM,CAAC,QAAQ;QACzB,GAAG,CAAC,OAAO,MAAM,CAAC,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KACxE,CAAA;AACH,CAAC"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenCode V2 插件入口
|
|
3
|
+
*
|
|
4
|
+
* P0 接线:
|
|
5
|
+
* setup(ctx)
|
|
6
|
+
* -> 注册 OpenCodeV2Executor(绑定 ctx.session,经 withConcurrencyLimit 限流)
|
|
7
|
+
* -> ctx.tool.transform 注册 workflow tool
|
|
8
|
+
* -> Main Session 调用 workflow tool -> Workflow Core -> 子会话
|
|
9
|
+
*
|
|
10
|
+
* 插件 options(经 opencode.json 的 plugins[].options 传入):
|
|
11
|
+
* {
|
|
12
|
+
* "package": "<路径>",
|
|
13
|
+
* "options": {
|
|
14
|
+
* "model": { "providerID": "glm", "id": "glm-5.3-flash" },
|
|
15
|
+
* "agent": "build",
|
|
16
|
+
* "concurrency": 3
|
|
17
|
+
* }
|
|
18
|
+
* }
|
|
19
|
+
*
|
|
20
|
+
* 架构约束:本目录(plugin 层)是仅有的允许触碰 OpenCode API 的位置。
|
|
21
|
+
*/
|
|
22
|
+
import { Plugin } from "@opencode/plugin";
|
|
23
|
+
declare const _default: Plugin.Plugin;
|
|
24
|
+
export default _default;
|
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenCode V2 插件入口
|
|
3
|
+
*
|
|
4
|
+
* P0 接线:
|
|
5
|
+
* setup(ctx)
|
|
6
|
+
* -> 注册 OpenCodeV2Executor(绑定 ctx.session,经 withConcurrencyLimit 限流)
|
|
7
|
+
* -> ctx.tool.transform 注册 workflow tool
|
|
8
|
+
* -> Main Session 调用 workflow tool -> Workflow Core -> 子会话
|
|
9
|
+
*
|
|
10
|
+
* 插件 options(经 opencode.json 的 plugins[].options 传入):
|
|
11
|
+
* {
|
|
12
|
+
* "package": "<路径>",
|
|
13
|
+
* "options": {
|
|
14
|
+
* "model": { "providerID": "glm", "id": "glm-5.3-flash" },
|
|
15
|
+
* "agent": "build",
|
|
16
|
+
* "concurrency": 3
|
|
17
|
+
* }
|
|
18
|
+
* }
|
|
19
|
+
*
|
|
20
|
+
* 架构约束:本目录(plugin 层)是仅有的允许触碰 OpenCode API 的位置。
|
|
21
|
+
*/
|
|
22
|
+
import { Plugin } from "@opencode/plugin";
|
|
23
|
+
import path from "node:path";
|
|
24
|
+
import { setCheckpointGate } from "../quality/checkpoint.js";
|
|
25
|
+
import { setExecutor } from "../runtime/engine.js";
|
|
26
|
+
import { withConcurrencyLimit } from "../runtime/semaphore.js";
|
|
27
|
+
import { createFileTraceSink } from "../observability/trace.js";
|
|
28
|
+
import { MetricsCollector, formatMetrics } from "../metrics/collector.js";
|
|
29
|
+
import { WorkflowRegistry } from "../registry/registry.js";
|
|
30
|
+
import { startWorkflow, resumeWorkflow, runWorkflowInline } from "../registry/runner.js";
|
|
31
|
+
import { WorkflowExecutionError } from "../registry/errors.js";
|
|
32
|
+
import { FileExecutionStore } from "../state/file-store.js";
|
|
33
|
+
import { reliableWorkflow } from "../workflows/reliable.js";
|
|
34
|
+
import { smokeWorkflow } from "../workflows/smoke.js";
|
|
35
|
+
import { artifactWorkflow } from "../workflows/artifact.js";
|
|
36
|
+
import { GitWorktreeProvider } from "../workspace/index.js";
|
|
37
|
+
import { InteractiveCheckpointGate, } from "./interactive-checkpoint-gate.js";
|
|
38
|
+
import { OpenCodeV2Executor } from "./opencode-v2-executor.js";
|
|
39
|
+
import { buildPriceTable, estimateCostUSD } from "./price-table.js";
|
|
40
|
+
import { PolicyCheckpointGate } from "./policy-checkpoint-gate.js";
|
|
41
|
+
export default Plugin.define({
|
|
42
|
+
id: "agentic-workflow",
|
|
43
|
+
async setup(ctx) {
|
|
44
|
+
console.log(`[agentic-workflow] loaded: ${ctx.location.directory}`);
|
|
45
|
+
const options = ctx.options;
|
|
46
|
+
// P2.6 成本估算兜底:宿主价目表(ctx.model.list,USD/M tokens)。
|
|
47
|
+
// 宿主消息未带精确 cost(或记账为 0)时按 token 用量估算。
|
|
48
|
+
let priceTable = buildPriceTable((await ctx.model.list()).data);
|
|
49
|
+
const wantedModel = options.model
|
|
50
|
+
? `${options.model.providerID}/${options.model.id}`
|
|
51
|
+
: undefined;
|
|
52
|
+
if (wantedModel && !priceTable.has(wantedModel)) {
|
|
53
|
+
// 价目缓存缺目标模型(实测:本地 models-dev 缓存可能滞后于线上):
|
|
54
|
+
// 尝试重同步一次,失败则保持原表(成本退回宿主上报值)
|
|
55
|
+
try {
|
|
56
|
+
await ctx.model.reload();
|
|
57
|
+
priceTable = buildPriceTable((await ctx.model.list()).data);
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
/* 保持原表 */
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
// 用户价格覆盖(最高优先)
|
|
64
|
+
if (options.prices) {
|
|
65
|
+
for (const [model, price] of Object.entries(options.prices)) {
|
|
66
|
+
priceTable.set(model, price);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
if (wantedModel) {
|
|
70
|
+
const price = priceTable.get(wantedModel);
|
|
71
|
+
console.log(`[agentic-workflow] price table: ${priceTable.size} models` +
|
|
72
|
+
(price
|
|
73
|
+
? `; ${wantedModel}: in $${price.input}/M out $${price.output}/M cache r $${price.cacheRead}/M`
|
|
74
|
+
: `; ${wantedModel}: no pricing (cost will be host-reported only)`));
|
|
75
|
+
}
|
|
76
|
+
const executor = withConcurrencyLimit(new OpenCodeV2Executor({
|
|
77
|
+
session: ctx.session,
|
|
78
|
+
model: options.model,
|
|
79
|
+
agent: options.agent,
|
|
80
|
+
estimateCost: (model, usage) => estimateCostUSD(priceTable, model, usage),
|
|
81
|
+
}), options.concurrency);
|
|
82
|
+
setExecutor(executor);
|
|
83
|
+
// checkpoint 审批门:
|
|
84
|
+
// auto-approve / auto-reject(默认策略门,headless 友好)
|
|
85
|
+
// interactive(P2.3:TUI 双形态 + RPC,真正的人工审批)
|
|
86
|
+
if (options.checkpoint?.mode === "interactive") {
|
|
87
|
+
const gate = new InteractiveCheckpointGate(ctx.rpc, {
|
|
88
|
+
timeoutMs: options.checkpoint.timeoutMs,
|
|
89
|
+
onTimeout: options.checkpoint.onTimeout,
|
|
90
|
+
});
|
|
91
|
+
await gate.bind();
|
|
92
|
+
setCheckpointGate(gate);
|
|
93
|
+
console.log("[agentic-workflow] checkpoint gate: interactive (TUI dialog via RPC)");
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
setCheckpointGate(new PolicyCheckpointGate(options.checkpoint?.mode));
|
|
97
|
+
}
|
|
98
|
+
// P2.6 metrics collector:纯事件总线消费者(token/cost/时长/计数聚合)
|
|
99
|
+
const metrics = new MetricsCollector();
|
|
100
|
+
// observability(P2.4):可选 JSONL 事件 trace 落盘
|
|
101
|
+
// 注意:相对路径以项目目录(ctx.location.directory)为基准——
|
|
102
|
+
// 插件运行在 opencode service 进程内,其 cwd 不是项目目录
|
|
103
|
+
if (options.traceDir) {
|
|
104
|
+
const traceDir = path.isAbsolute(options.traceDir)
|
|
105
|
+
? options.traceDir
|
|
106
|
+
: path.join(ctx.location.directory, options.traceDir);
|
|
107
|
+
const traceFile = path.join(traceDir, "events.jsonl");
|
|
108
|
+
createFileTraceSink(traceFile);
|
|
109
|
+
console.log(`[agentic-workflow] event trace sink: ${traceFile}`);
|
|
110
|
+
// P2.6 metrics:workflow 结束时把聚合快照落盘(查询/取证用,写失败只记录)
|
|
111
|
+
metrics.subscribeFileSink(path.join(traceDir, "metrics.json"));
|
|
112
|
+
}
|
|
113
|
+
// P2.5 workflow registry:新增 workflow = 新增定义 + 在此注册,
|
|
114
|
+
// 工具描述/枚举/路由全部由注册表驱动
|
|
115
|
+
const registry = new WorkflowRegistry()
|
|
116
|
+
.register(smokeWorkflow())
|
|
117
|
+
.register(reliableWorkflow({ checkCommand: options.checkCommand }))
|
|
118
|
+
.register(artifactWorkflow());
|
|
119
|
+
// P2.5 durable journal:配置 journalDir 后,run 经 startWorkflow/resumeWorkflow
|
|
120
|
+
// 走持久化链路(journal 记录 workflow {id, version} + args + steps,可恢复)
|
|
121
|
+
let store;
|
|
122
|
+
if (options.journalDir) {
|
|
123
|
+
const journalDir = path.isAbsolute(options.journalDir)
|
|
124
|
+
? options.journalDir
|
|
125
|
+
: path.join(ctx.location.directory, options.journalDir);
|
|
126
|
+
store = new FileExecutionStore(journalDir);
|
|
127
|
+
console.log(`[agentic-workflow] journal store: ${journalDir}`);
|
|
128
|
+
}
|
|
129
|
+
// P2.7 workspace 隔离:GitWorktreeProvider(startDir = 项目目录,
|
|
130
|
+
// 内部解析到 git 仓库根;worktree 落在仓库同级目录,不污染仓库)
|
|
131
|
+
let workspaceBinding;
|
|
132
|
+
if (options.isolation?.mode === "git-worktree") {
|
|
133
|
+
const dir = options.isolation.dir
|
|
134
|
+
? path.isAbsolute(options.isolation.dir)
|
|
135
|
+
? options.isolation.dir
|
|
136
|
+
: path.join(ctx.location.directory, options.isolation.dir)
|
|
137
|
+
: undefined;
|
|
138
|
+
workspaceBinding = {
|
|
139
|
+
provider: new GitWorktreeProvider({ startDir: ctx.location.directory, dir }),
|
|
140
|
+
...(options.isolation.baseRef ? { options: { baseRef: options.isolation.baseRef } } : {}),
|
|
141
|
+
cleanup: options.isolation.cleanup,
|
|
142
|
+
};
|
|
143
|
+
console.log(`[agentic-workflow] workspace isolation: git-worktree ` +
|
|
144
|
+
`(cleanup=${options.isolation.cleanup ?? "on-success"}${dir ? `, dir=${dir}` : ""})`);
|
|
145
|
+
}
|
|
146
|
+
// 递归防护:workflow 运行期间,子会话里的 agent 也可能看到并调用 workflow 工具,
|
|
147
|
+
// 形成递归 workflow;叠加并发信号量后会自饿死死锁(实测卡死)。
|
|
148
|
+
// P0 策略:运行中直接拒绝嵌套调用,让子 agent 用自身能力直接完成任务。
|
|
149
|
+
let workflowDepth = 0;
|
|
150
|
+
ctx.tool.transform((editor) => {
|
|
151
|
+
editor.add({
|
|
152
|
+
name: "workflow",
|
|
153
|
+
description: "Run a registered agentic workflow (flow = workflow id). Available workflows:\n" +
|
|
154
|
+
`${registry.summarize()}\n` +
|
|
155
|
+
"Returns the workflow's final output. " +
|
|
156
|
+
"Do NOT call this tool from inside a workflow; do the work directly instead.",
|
|
157
|
+
input: {
|
|
158
|
+
type: "object",
|
|
159
|
+
properties: {
|
|
160
|
+
topic: {
|
|
161
|
+
type: "string",
|
|
162
|
+
description: "The topic to analyze (not needed when resuming)",
|
|
163
|
+
},
|
|
164
|
+
flow: {
|
|
165
|
+
type: "string",
|
|
166
|
+
enum: registry.listLatest().map((definition) => definition.id),
|
|
167
|
+
description: "Workflow id, default smoke",
|
|
168
|
+
},
|
|
169
|
+
resumeRunId: {
|
|
170
|
+
type: "string",
|
|
171
|
+
description: "Resume a previous durable run (from journal); overrides flow/topic. " +
|
|
172
|
+
"Use the runId reported by a failed workflow call",
|
|
173
|
+
},
|
|
174
|
+
},
|
|
175
|
+
},
|
|
176
|
+
// V2 强校验:Tool.Result 使用 output 字段必须声明 output schema,
|
|
177
|
+
// 否则报 "Tool result declared output without an output schema"
|
|
178
|
+
output: {
|
|
179
|
+
type: "string",
|
|
180
|
+
},
|
|
181
|
+
async execute(input) {
|
|
182
|
+
const parsed = input ?? {};
|
|
183
|
+
if (workflowDepth > 0) {
|
|
184
|
+
return {
|
|
185
|
+
output: "[agentic-workflow] nested workflow calls are not allowed: " +
|
|
186
|
+
"another workflow is running. Complete the task directly yourself.",
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
workflowDepth += 1;
|
|
190
|
+
try {
|
|
191
|
+
// resume 分支:journal -> registry 精确版本解析 -> 续跑
|
|
192
|
+
if (typeof parsed.resumeRunId === "string" && parsed.resumeRunId.length > 0) {
|
|
193
|
+
if (!store) {
|
|
194
|
+
return {
|
|
195
|
+
output: "[agentic-workflow] resume requires the journalDir plugin option to be configured",
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
const resumed = await resumeWorkflow(registry, store, parsed.resumeRunId, {
|
|
199
|
+
workspace: workspaceBinding,
|
|
200
|
+
});
|
|
201
|
+
return {
|
|
202
|
+
output: `[resumed ${resumed.workflow.id}@${resumed.workflow.version} ` +
|
|
203
|
+
`runId=${resumed.runId}]\n${resumed.output}`,
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
const workflowId = typeof parsed.flow === "string" && parsed.flow.length > 0
|
|
207
|
+
? parsed.flow
|
|
208
|
+
: "smoke";
|
|
209
|
+
if (store) {
|
|
210
|
+
const started = await startWorkflow(registry, store, workflowId, {
|
|
211
|
+
topic: parsed.topic,
|
|
212
|
+
}, { workspace: workspaceBinding });
|
|
213
|
+
return {
|
|
214
|
+
output: `[${started.workflow.id}@${started.workflow.version} ` +
|
|
215
|
+
`runId=${started.runId}]\n${started.output}`,
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
// 未配置 journalDir:直跑(不持久化、不可恢复)
|
|
219
|
+
const inline = await runWorkflowInline(registry, workflowId, {
|
|
220
|
+
topic: parsed.topic,
|
|
221
|
+
});
|
|
222
|
+
return { output: inline.output };
|
|
223
|
+
}
|
|
224
|
+
catch (error) {
|
|
225
|
+
// 抛错会诱发主 agent 无限重试工具调用(实测 22 轮重试耗尽配额)。
|
|
226
|
+
// 失败信息以结果文本返回,让主 agent 停止重试并向用户报告。
|
|
227
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
228
|
+
const resumeHint = error instanceof WorkflowExecutionError
|
|
229
|
+
? ` (runId: ${error.runId}; 可用 resumeRunId="${error.runId}" 恢复本次执行)`
|
|
230
|
+
: "";
|
|
231
|
+
return {
|
|
232
|
+
output: `[agentic-workflow] workflow failed: ${message}${resumeHint}`,
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
finally {
|
|
236
|
+
workflowDepth -= 1;
|
|
237
|
+
}
|
|
238
|
+
},
|
|
239
|
+
});
|
|
240
|
+
// P2.6 metrics 查询工具:读取聚合快照(纯只读,不执行任何 workflow)
|
|
241
|
+
editor.add({
|
|
242
|
+
name: "workflow_metrics",
|
|
243
|
+
description: "Query cumulative execution metrics of agentic workflows in this service: " +
|
|
244
|
+
"agent calls, token usage (input/output/reasoning/cache), cost in USD, " +
|
|
245
|
+
"per-model breakdown, workflow durations and quality-gate counters " +
|
|
246
|
+
"(check/verify/checkpoint). Read-only; call after running workflows to report cost & usage.",
|
|
247
|
+
input: {
|
|
248
|
+
type: "object",
|
|
249
|
+
properties: {
|
|
250
|
+
format: {
|
|
251
|
+
type: "string",
|
|
252
|
+
enum: ["text", "json"],
|
|
253
|
+
description: "Output format, default text",
|
|
254
|
+
},
|
|
255
|
+
},
|
|
256
|
+
},
|
|
257
|
+
output: {
|
|
258
|
+
type: "string",
|
|
259
|
+
},
|
|
260
|
+
async execute(input) {
|
|
261
|
+
const parsed = input ?? {};
|
|
262
|
+
const format = parsed.format === "json" ? "json" : "text";
|
|
263
|
+
const snapshot = metrics.snapshot();
|
|
264
|
+
const output = format === "json" ? JSON.stringify(snapshot, null, 2) : formatMetrics(snapshot);
|
|
265
|
+
return { output };
|
|
266
|
+
},
|
|
267
|
+
});
|
|
268
|
+
});
|
|
269
|
+
},
|
|
270
|
+
});
|
|
271
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/plugin/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* InteractiveCheckpointGate —— server 侧交互审批门(P2.3)
|
|
3
|
+
*
|
|
4
|
+
* 实现 CheckpointGate(Core 抽象不变,checkpoint() 零改动):
|
|
5
|
+
* ask(request)
|
|
6
|
+
* -> emit rpc.agentic-workflow.requested 事件(TUI 订阅并弹确认框)
|
|
7
|
+
* -> 挂起等待 reply RPC 回调或超时
|
|
8
|
+
* -> 超时按 onTimeout 策略裁决(默认 reject——headless 无 TUI 应答时安全失败)
|
|
9
|
+
*
|
|
10
|
+
* 依赖注入:构造时传入 Plugin.Context["rpc"](RpcDomain),
|
|
11
|
+
* 仅使用 register 一项能力,便于单测用 fake 注入。
|
|
12
|
+
*/
|
|
13
|
+
import type { Plugin } from "@opencode/plugin";
|
|
14
|
+
import type { CheckpointDecision, CheckpointGate, CheckpointRequest } from "../quality/checkpoint.js";
|
|
15
|
+
type RpcDomain = Plugin.Context["rpc"];
|
|
16
|
+
export interface InteractiveCheckpointOptions {
|
|
17
|
+
/** 等待人工应答的超时毫秒数,默认 300000(5 分钟) */
|
|
18
|
+
timeoutMs?: number;
|
|
19
|
+
/** 超时裁决策略,默认 "reject"(无 TUI 应答时安全失败) */
|
|
20
|
+
onTimeout?: "reject" | "approve";
|
|
21
|
+
}
|
|
22
|
+
export declare class InteractiveCheckpointGate implements CheckpointGate {
|
|
23
|
+
private readonly rpc;
|
|
24
|
+
private readonly timeoutMs;
|
|
25
|
+
private readonly onTimeout;
|
|
26
|
+
private registration;
|
|
27
|
+
private readonly pending;
|
|
28
|
+
constructor(rpc: RpcDomain, options?: InteractiveCheckpointOptions);
|
|
29
|
+
/** 注册 reply handler(插件 setup 时调用;成功后才可作为 gate 生效) */
|
|
30
|
+
bind(): Promise<void>;
|
|
31
|
+
ask(request: CheckpointRequest): Promise<CheckpointDecision>;
|
|
32
|
+
/** 清理:未决请求全部按拒绝收口(插件卸载/热重载时调用) */
|
|
33
|
+
dispose(): void;
|
|
34
|
+
}
|
|
35
|
+
export {};
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* InteractiveCheckpointGate —— server 侧交互审批门(P2.3)
|
|
3
|
+
*
|
|
4
|
+
* 实现 CheckpointGate(Core 抽象不变,checkpoint() 零改动):
|
|
5
|
+
* ask(request)
|
|
6
|
+
* -> emit rpc.agentic-workflow.requested 事件(TUI 订阅并弹确认框)
|
|
7
|
+
* -> 挂起等待 reply RPC 回调或超时
|
|
8
|
+
* -> 超时按 onTimeout 策略裁决(默认 reject——headless 无 TUI 应答时安全失败)
|
|
9
|
+
*
|
|
10
|
+
* 依赖注入:构造时传入 Plugin.Context["rpc"](RpcDomain),
|
|
11
|
+
* 仅使用 register 一项能力,便于单测用 fake 注入。
|
|
12
|
+
*/
|
|
13
|
+
import { randomUUID } from "node:crypto";
|
|
14
|
+
import { CheckpointRpc, parseCheckpointReply } from "./checkpoint-rpc.js";
|
|
15
|
+
export class InteractiveCheckpointGate {
|
|
16
|
+
rpc;
|
|
17
|
+
timeoutMs;
|
|
18
|
+
onTimeout;
|
|
19
|
+
registration;
|
|
20
|
+
pending = new Map();
|
|
21
|
+
constructor(rpc, options) {
|
|
22
|
+
this.rpc = rpc;
|
|
23
|
+
this.timeoutMs = options?.timeoutMs ?? 300_000;
|
|
24
|
+
this.onTimeout = options?.onTimeout ?? "reject";
|
|
25
|
+
}
|
|
26
|
+
/** 注册 reply handler(插件 setup 时调用;成功后才可作为 gate 生效) */
|
|
27
|
+
async bind() {
|
|
28
|
+
this.registration = await this.rpc.register(CheckpointRpc, {
|
|
29
|
+
reply: async (input) => {
|
|
30
|
+
const reply = parseCheckpointReply(input);
|
|
31
|
+
if (!reply) {
|
|
32
|
+
return { ok: false };
|
|
33
|
+
}
|
|
34
|
+
const resolve = this.pending.get(reply.requestId);
|
|
35
|
+
if (!resolve) {
|
|
36
|
+
// 已超时/已被处置的请求
|
|
37
|
+
return { ok: false };
|
|
38
|
+
}
|
|
39
|
+
this.pending.delete(reply.requestId);
|
|
40
|
+
resolve({
|
|
41
|
+
approved: reply.approved,
|
|
42
|
+
reason: reply.reason ??
|
|
43
|
+
(reply.approved ? undefined : "rejected by reviewer in TUI"),
|
|
44
|
+
});
|
|
45
|
+
return { ok: true };
|
|
46
|
+
},
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
async ask(request) {
|
|
50
|
+
if (!this.registration) {
|
|
51
|
+
throw new Error("[agentic-workflow] InteractiveCheckpointGate not bound: call bind() first");
|
|
52
|
+
}
|
|
53
|
+
const requestId = randomUUID();
|
|
54
|
+
await this.registration.events.emit("requested", {
|
|
55
|
+
requestId,
|
|
56
|
+
label: request.label,
|
|
57
|
+
message: request.message,
|
|
58
|
+
});
|
|
59
|
+
console.log(`[agentic-workflow] checkpoint waiting for interactive approval: ` +
|
|
60
|
+
`${request.label} (requestId=${requestId.slice(0, 8)}…)`);
|
|
61
|
+
return new Promise((resolve) => {
|
|
62
|
+
const timer = setTimeout(() => {
|
|
63
|
+
this.pending.delete(requestId);
|
|
64
|
+
resolve(this.onTimeout === "approve"
|
|
65
|
+
? { approved: true, reason: "auto-approved after interactive timeout" }
|
|
66
|
+
: {
|
|
67
|
+
approved: false,
|
|
68
|
+
reason: `no interactive reply within ${this.timeoutMs}ms ` +
|
|
69
|
+
"(no TUI attached?)",
|
|
70
|
+
});
|
|
71
|
+
}, this.timeoutMs);
|
|
72
|
+
this.pending.set(requestId, (decision) => {
|
|
73
|
+
clearTimeout(timer);
|
|
74
|
+
resolve(decision);
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
/** 清理:未决请求全部按拒绝收口(插件卸载/热重载时调用) */
|
|
79
|
+
dispose() {
|
|
80
|
+
for (const resolve of this.pending.values()) {
|
|
81
|
+
resolve({ approved: false, reason: "checkpoint gate disposed" });
|
|
82
|
+
}
|
|
83
|
+
this.pending.clear();
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
//# sourceMappingURL=interactive-checkpoint-gate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"interactive-checkpoint-gate.js","sourceRoot":"","sources":["../../src/plugin/interactive-checkpoint-gate.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenCodeV2Executor —— AgentExecutor 在 OpenCode V2 上的实现
|
|
3
|
+
*
|
|
4
|
+
* 调用链(全部基于 @opencode/plugin 2.0.22 已核实的类型):
|
|
5
|
+
* execute(task)
|
|
6
|
+
* -> session.create({ parentID, title, agent?, model? }) 创建子会话(挂在主会话下,TUI 可导航)
|
|
7
|
+
* -> session.prompt({ sessionID, text }) 投递用户消息(V2 为 inbox 异步模型,
|
|
8
|
+
* 返回 SessionInboxUser 而非 assistant 内容)
|
|
9
|
+
* -> session.wait({ sessionID }) 等待会话空闲(处理完成)
|
|
10
|
+
* -> session.context({ sessionID }) 拉取消息列表
|
|
11
|
+
* -> 取最后一条 assistant 消息的 text parts 拼接为 output
|
|
12
|
+
*
|
|
13
|
+
* 架构约束:本文件与 plugin/index.ts 是仅有的允许触碰 OpenCode API 的位置。
|
|
14
|
+
*/
|
|
15
|
+
import type { Plugin } from "@opencode/plugin";
|
|
16
|
+
import type { AgentExecutor, AgentResult, AgentTask, TokenUsage } from "../runtime/executor.js";
|
|
17
|
+
/** plugin context 的 session 域类型(避免在多处直接依赖 @opencode/plugin) */
|
|
18
|
+
export type SessionDomain = Plugin.Context["session"];
|
|
19
|
+
/** 子会话使用的模型引用(对应 models 列表中的 providerID/id) */
|
|
20
|
+
export interface ExecutorModelRef {
|
|
21
|
+
id: string;
|
|
22
|
+
providerID: string;
|
|
23
|
+
variant?: string;
|
|
24
|
+
}
|
|
25
|
+
export interface OpenCodeV2ExecutorOptions {
|
|
26
|
+
/** OpenCode V2 plugin context 提供的 session 域 */
|
|
27
|
+
session: SessionDomain;
|
|
28
|
+
/** 主会话 ID;子会话挂在其下(TUI 父子视图可导航) */
|
|
29
|
+
parentSessionId?: string;
|
|
30
|
+
/** 子会话标题前缀,默认 "workflow" */
|
|
31
|
+
titlePrefix?: string;
|
|
32
|
+
/**
|
|
33
|
+
* 子会话使用的模型。不指定时子会话会用默认 agent 的默认模型,
|
|
34
|
+
* 可能与主会话模型不一致(实测 CLI --model 不会传导到 session.create)。
|
|
35
|
+
*/
|
|
36
|
+
model?: ExecutorModelRef;
|
|
37
|
+
/** 子会话使用的 OpenCode agent 名(如 build / explore / plan) */
|
|
38
|
+
agent?: string;
|
|
39
|
+
/**
|
|
40
|
+
* 成本估算兜底(P2.6 metrics):宿主消息未带 cost 时,
|
|
41
|
+
* 由宿主价目表(ctx.model.list)按 token 用量估算 USD。
|
|
42
|
+
*/
|
|
43
|
+
estimateCost?: (model: string, usage: TokenUsage) => number | undefined;
|
|
44
|
+
}
|
|
45
|
+
export declare class OpenCodeV2Executor implements AgentExecutor {
|
|
46
|
+
private readonly session;
|
|
47
|
+
private readonly parentSessionId?;
|
|
48
|
+
private readonly titlePrefix;
|
|
49
|
+
private readonly model?;
|
|
50
|
+
private readonly agent?;
|
|
51
|
+
private readonly estimateCost?;
|
|
52
|
+
private counter;
|
|
53
|
+
constructor(options: OpenCodeV2ExecutorOptions);
|
|
54
|
+
execute(task: AgentTask): Promise<AgentResult>;
|
|
55
|
+
}
|