@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.
Files changed (144) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +406 -0
  3. package/dist/metrics/collector.d.ts +98 -0
  4. package/dist/metrics/collector.js +246 -0
  5. package/dist/metrics/collector.js.map +1 -0
  6. package/dist/observability/events.d.ts +99 -0
  7. package/dist/observability/events.js +55 -0
  8. package/dist/observability/events.js.map +1 -0
  9. package/dist/observability/observe.d.ts +7 -0
  10. package/dist/observability/observe.js +29 -0
  11. package/dist/observability/observe.js.map +1 -0
  12. package/dist/observability/trace.d.ts +26 -0
  13. package/dist/observability/trace.js +52 -0
  14. package/dist/observability/trace.js.map +1 -0
  15. package/dist/plugin/checkpoint-rpc.d.ts +74 -0
  16. package/dist/plugin/checkpoint-rpc.js +73 -0
  17. package/dist/plugin/checkpoint-rpc.js.map +1 -0
  18. package/dist/plugin/index.d.ts +24 -0
  19. package/dist/plugin/index.js +271 -0
  20. package/dist/plugin/index.js.map +1 -0
  21. package/dist/plugin/interactive-checkpoint-gate.d.ts +35 -0
  22. package/dist/plugin/interactive-checkpoint-gate.js +86 -0
  23. package/dist/plugin/interactive-checkpoint-gate.js.map +1 -0
  24. package/dist/plugin/opencode-v2-executor.d.ts +55 -0
  25. package/dist/plugin/opencode-v2-executor.js +92 -0
  26. package/dist/plugin/opencode-v2-executor.js.map +1 -0
  27. package/dist/plugin/policy-checkpoint-gate.d.ts +18 -0
  28. package/dist/plugin/policy-checkpoint-gate.js +27 -0
  29. package/dist/plugin/policy-checkpoint-gate.js.map +1 -0
  30. package/dist/plugin/price-table.d.ts +46 -0
  31. package/dist/plugin/price-table.js +44 -0
  32. package/dist/plugin/price-table.js.map +1 -0
  33. package/dist/plugin/tui.d.ts +12 -0
  34. package/dist/plugin/tui.js +53 -0
  35. package/dist/plugin/tui.js.map +1 -0
  36. package/dist/quality/check.d.ts +30 -0
  37. package/dist/quality/check.js +51 -0
  38. package/dist/quality/check.js.map +1 -0
  39. package/dist/quality/checkpoint.d.ts +44 -0
  40. package/dist/quality/checkpoint.js +55 -0
  41. package/dist/quality/checkpoint.js.map +1 -0
  42. package/dist/quality/json.d.ts +10 -0
  43. package/dist/quality/json.js +31 -0
  44. package/dist/quality/json.js.map +1 -0
  45. package/dist/quality/predicates.d.ts +17 -0
  46. package/dist/quality/predicates.js +44 -0
  47. package/dist/quality/predicates.js.map +1 -0
  48. package/dist/quality/verify.d.ts +50 -0
  49. package/dist/quality/verify.js +110 -0
  50. package/dist/quality/verify.js.map +1 -0
  51. package/dist/registry/definition.d.ts +66 -0
  52. package/dist/registry/definition.js +19 -0
  53. package/dist/registry/definition.js.map +1 -0
  54. package/dist/registry/errors.d.ts +31 -0
  55. package/dist/registry/errors.js +48 -0
  56. package/dist/registry/errors.js.map +1 -0
  57. package/dist/registry/index.d.ts +9 -0
  58. package/dist/registry/index.js +9 -0
  59. package/dist/registry/index.js.map +1 -0
  60. package/dist/registry/registry.d.ts +45 -0
  61. package/dist/registry/registry.js +117 -0
  62. package/dist/registry/registry.js.map +1 -0
  63. package/dist/registry/runner.d.ts +56 -0
  64. package/dist/registry/runner.js +199 -0
  65. package/dist/registry/runner.js.map +1 -0
  66. package/dist/registry/schema.d.ts +18 -0
  67. package/dist/registry/schema.js +73 -0
  68. package/dist/registry/schema.js.map +1 -0
  69. package/dist/runtime/command.d.ts +38 -0
  70. package/dist/runtime/command.js +61 -0
  71. package/dist/runtime/command.js.map +1 -0
  72. package/dist/runtime/engine.d.ts +17 -0
  73. package/dist/runtime/engine.js +31 -0
  74. package/dist/runtime/engine.js.map +1 -0
  75. package/dist/runtime/errors.d.ts +56 -0
  76. package/dist/runtime/errors.js +88 -0
  77. package/dist/runtime/errors.js.map +1 -0
  78. package/dist/runtime/executor.d.ts +46 -0
  79. package/dist/runtime/executor.js +16 -0
  80. package/dist/runtime/executor.js.map +1 -0
  81. package/dist/runtime/semaphore.d.ts +24 -0
  82. package/dist/runtime/semaphore.js +57 -0
  83. package/dist/runtime/semaphore.js.map +1 -0
  84. package/dist/state/file-store.d.ts +25 -0
  85. package/dist/state/file-store.js +138 -0
  86. package/dist/state/file-store.js.map +1 -0
  87. package/dist/state/journal.d.ts +83 -0
  88. package/dist/state/journal.js +59 -0
  89. package/dist/state/journal.js.map +1 -0
  90. package/dist/state/recorder.d.ts +63 -0
  91. package/dist/state/recorder.js +138 -0
  92. package/dist/state/recorder.js.map +1 -0
  93. package/dist/state/store.d.ts +23 -0
  94. package/dist/state/store.js +14 -0
  95. package/dist/state/store.js.map +1 -0
  96. package/dist/workflow/agent.d.ts +8 -0
  97. package/dist/workflow/agent.js +38 -0
  98. package/dist/workflow/agent.js.map +1 -0
  99. package/dist/workflow/fallback.d.ts +15 -0
  100. package/dist/workflow/fallback.js +32 -0
  101. package/dist/workflow/fallback.js.map +1 -0
  102. package/dist/workflow/parallel.d.ts +14 -0
  103. package/dist/workflow/parallel.js +62 -0
  104. package/dist/workflow/parallel.js.map +1 -0
  105. package/dist/workflow/phase.d.ts +7 -0
  106. package/dist/workflow/phase.js +12 -0
  107. package/dist/workflow/phase.js.map +1 -0
  108. package/dist/workflow/reliable.d.ts +50 -0
  109. package/dist/workflow/reliable.js +92 -0
  110. package/dist/workflow/reliable.js.map +1 -0
  111. package/dist/workflow/retry.d.ts +28 -0
  112. package/dist/workflow/retry.js +53 -0
  113. package/dist/workflow/retry.js.map +1 -0
  114. package/dist/workflow/sequence.d.ts +40 -0
  115. package/dist/workflow/sequence.js +100 -0
  116. package/dist/workflow/sequence.js.map +1 -0
  117. package/dist/workflow/smoke.d.ts +17 -0
  118. package/dist/workflow/smoke.js +38 -0
  119. package/dist/workflow/smoke.js.map +1 -0
  120. package/dist/workflows/artifact.d.ts +24 -0
  121. package/dist/workflows/artifact.js +71 -0
  122. package/dist/workflows/artifact.js.map +1 -0
  123. package/dist/workflows/index.d.ts +19 -0
  124. package/dist/workflows/index.js +40 -0
  125. package/dist/workflows/index.js.map +1 -0
  126. package/dist/workflows/reliable.d.ts +18 -0
  127. package/dist/workflows/reliable.js +32 -0
  128. package/dist/workflows/reliable.js.map +1 -0
  129. package/dist/workflows/smoke.d.ts +13 -0
  130. package/dist/workflows/smoke.js +26 -0
  131. package/dist/workflows/smoke.js.map +1 -0
  132. package/dist/workspace/ambient.d.ts +12 -0
  133. package/dist/workspace/ambient.js +17 -0
  134. package/dist/workspace/ambient.js.map +1 -0
  135. package/dist/workspace/git-worktree.d.ts +29 -0
  136. package/dist/workspace/git-worktree.js +110 -0
  137. package/dist/workspace/git-worktree.js.map +1 -0
  138. package/dist/workspace/index.d.ts +6 -0
  139. package/dist/workspace/index.js +6 -0
  140. package/dist/workspace/index.js.map +1 -0
  141. package/dist/workspace/provider.d.ts +61 -0
  142. package/dist/workspace/provider.js +15 -0
  143. package/dist/workspace/provider.js.map +1 -0
  144. package/package.json +71 -0
@@ -0,0 +1,92 @@
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
+ export class OpenCodeV2Executor {
16
+ session;
17
+ parentSessionId;
18
+ titlePrefix;
19
+ model;
20
+ agent;
21
+ estimateCost;
22
+ counter = 0;
23
+ constructor(options) {
24
+ this.session = options.session;
25
+ this.parentSessionId = options.parentSessionId;
26
+ this.titlePrefix = options.titlePrefix ?? "workflow";
27
+ this.model = options.model;
28
+ this.agent = options.agent;
29
+ this.estimateCost = options.estimateCost;
30
+ }
31
+ async execute(task) {
32
+ this.counter += 1;
33
+ const created = await this.session.create({
34
+ parentID: this.parentSessionId,
35
+ title: `${this.titlePrefix}#${this.counter}`,
36
+ model: this.model,
37
+ agent: this.agent,
38
+ // P2.7 隔离:子会话 cwd 绑定到 workflow workspace(worktree 根)
39
+ ...(task.cwd ? { location: { directory: task.cwd } } : {}),
40
+ });
41
+ const sessionID = created.id;
42
+ await this.session.prompt({
43
+ sessionID,
44
+ text: task.prompt,
45
+ });
46
+ // V2 的 prompt 是 inbox 投递:等待会话处理完成后再读结果
47
+ await this.session.wait({ sessionID });
48
+ const messages = await this.session.context({ sessionID });
49
+ // 从后往前找最后一条 assistant 消息
50
+ for (let i = messages.length - 1; i >= 0; i--) {
51
+ const message = messages[i];
52
+ if (message && message.type === "assistant") {
53
+ const output = message.content
54
+ .filter((part) => part.type === "text")
55
+ .map((part) => part.text)
56
+ .join("\n")
57
+ .trim();
58
+ if (output.length > 0) {
59
+ // token 用量 / 成本 / 模型(V2 assistant 消息自带;P2.6 metrics 消费)
60
+ const usage = message.tokens && {
61
+ input: message.tokens.input,
62
+ output: message.tokens.output,
63
+ reasoning: message.tokens.reasoning,
64
+ cache: {
65
+ read: message.tokens.cache.read,
66
+ write: message.tokens.cache.write,
67
+ },
68
+ };
69
+ const model = message.model && `${message.model.providerID}/${message.model.id}`;
70
+ return {
71
+ output,
72
+ usage,
73
+ // 成本优先级:宿主正值(精确)> 价目表估算(宿主 0/缺失时兜底——
74
+ // 实测部分 provider 宿主记账为 0 但 models.dev 有实价)> 宿主原值
75
+ costUSD: message.cost && message.cost > 0
76
+ ? message.cost
77
+ : (usage && model ? this.estimateCost?.(model, usage) : undefined) ??
78
+ message.cost,
79
+ model,
80
+ };
81
+ }
82
+ // assistant 存在但无文本:带出底层错误信息(如 provider 限流),便于排查
83
+ const detail = message.error
84
+ ? `${message.error.type ?? "error"}: ${message.error.message ?? "unknown"}`
85
+ : "assistant message has no text parts";
86
+ throw new Error(`[agentic-workflow] session ${sessionID} failed: ${detail}`);
87
+ }
88
+ }
89
+ throw new Error(`[agentic-workflow] session ${sessionID} finished without assistant message`);
90
+ }
91
+ }
92
+ //# sourceMappingURL=opencode-v2-executor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"opencode-v2-executor.js","sourceRoot":"","sources":["../../src/plugin/opencode-v2-executor.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * PolicyCheckpointGate —— 按固定策略裁决的审批门(plugin 层,P1)
3
+ *
4
+ * 背景:server 侧插件域没有创建权限请求/表单的 API(permission.create
5
+ * 只在 HTTP client 上),交互式审批需要 TUI 双形态插件 + RPC(P2)。
6
+ * P1 先用策略门证明 checkpoint 的执行语义(headless e2e 可验收)。
7
+ *
8
+ * mode:
9
+ * "auto-approve" : 自动批准(默认;仅用于验收/演示,日志明确提示非人工)
10
+ * "auto-reject" : 自动拒绝(安全演练/测试拒绝路径)
11
+ */
12
+ import type { CheckpointDecision, CheckpointGate, CheckpointRequest } from "../quality/checkpoint.js";
13
+ export type CheckpointPolicy = "auto-approve" | "auto-reject";
14
+ export declare class PolicyCheckpointGate implements CheckpointGate {
15
+ private readonly mode;
16
+ constructor(mode?: CheckpointPolicy);
17
+ ask(request: CheckpointRequest): Promise<CheckpointDecision>;
18
+ }
@@ -0,0 +1,27 @@
1
+ /**
2
+ * PolicyCheckpointGate —— 按固定策略裁决的审批门(plugin 层,P1)
3
+ *
4
+ * 背景:server 侧插件域没有创建权限请求/表单的 API(permission.create
5
+ * 只在 HTTP client 上),交互式审批需要 TUI 双形态插件 + RPC(P2)。
6
+ * P1 先用策略门证明 checkpoint 的执行语义(headless e2e 可验收)。
7
+ *
8
+ * mode:
9
+ * "auto-approve" : 自动批准(默认;仅用于验收/演示,日志明确提示非人工)
10
+ * "auto-reject" : 自动拒绝(安全演练/测试拒绝路径)
11
+ */
12
+ export class PolicyCheckpointGate {
13
+ mode;
14
+ constructor(mode = "auto-approve") {
15
+ this.mode = mode;
16
+ }
17
+ async ask(request) {
18
+ console.log(`[agentic-workflow] checkpoint request (${this.mode}): ${request.label} -- ${request.message}`);
19
+ if (this.mode === "auto-approve") {
20
+ console.log("[agentic-workflow] NOTE: auto-approved by policy gate; " +
21
+ "interactive approval lands in P2 (TUI dialog gate)");
22
+ return { approved: true };
23
+ }
24
+ return { approved: false, reason: "rejected by policy gate (auto-reject)" };
25
+ }
26
+ }
27
+ //# sourceMappingURL=policy-checkpoint-gate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"policy-checkpoint-gate.js","sourceRoot":"","sources":["../../src/plugin/policy-checkpoint-gate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAUH,MAAM,OAAO,oBAAoB;IACd,IAAI,CAAkB;IAEvC,YAAY,IAAI,GAAqB,cAAc;QACjD,IAAI,CAAC,IAAI,GAAG,IAAI,CAAA;IAClB,CAAC;IAED,KAAK,CAAC,GAAG,CAAC,OAA0B;QAClC,OAAO,CAAC,GAAG,CACT,0CAA0C,IAAI,CAAC,IAAI,MAAM,OAAO,CAAC,KAAK,OAAO,OAAO,CAAC,OAAO,EAAE,CAC/F,CAAA;QACD,IAAI,IAAI,CAAC,IAAI,KAAK,cAAc,EAAE,CAAC;YACjC,OAAO,CAAC,GAAG,CACT,yDAAyD;gBACvD,oDAAoD,CACvD,CAAA;YACD,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAA;QAC3B,CAAC;QACD,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,EAAE,uCAAuC,EAAE,CAAA;IAC7E,CAAC;CACF"}
@@ -0,0 +1,46 @@
1
+ /**
2
+ * 模型价目表与成本估算(P2.6,插件层=宿主关注点)
3
+ *
4
+ * 数据源:OpenCode V2 `ctx.model.list()` 的 Model.Info.cost
5
+ * (USD / 百万 token,含 cache read/write 单价;结构子集本地声明,
6
+ * 不直接依赖 @opencode 类型,便于单测)。
7
+ *
8
+ * 优先级:宿主消息自带精确 cost > 价目表估算 > 不计。
9
+ */
10
+ import type { TokenUsage } from "../runtime/executor.js";
11
+ /** Model.Info.cost 条目的结构子集(结构兼容 @opencode/schema Model.Info) */
12
+ interface CostTier {
13
+ tier?: {
14
+ type: "context";
15
+ size: number;
16
+ };
17
+ input: number;
18
+ output: number;
19
+ cache: {
20
+ read: number;
21
+ write: number;
22
+ };
23
+ }
24
+ /** model.list() 元素的结构子集 */
25
+ interface ModelLike {
26
+ id: string;
27
+ providerID: string;
28
+ cost?: CostTier[];
29
+ }
30
+ export interface ModelPrice {
31
+ input: number;
32
+ output: number;
33
+ cacheRead: number;
34
+ cacheWrite: number;
35
+ }
36
+ /** 构建价目表:key = "providerID/id";基准价取无 tier 限制的条目(缺省第一个) */
37
+ export declare function buildPriceTable(models: readonly ModelLike[]): Map<string, ModelPrice>;
38
+ /**
39
+ * 估算成本(USD):
40
+ * input×in + cache.read×cacheRead + cache.write×cacheWrite
41
+ * + (output + reasoning)×out —— reasoning 按输出价计
42
+ * 单位:价目为 USD/百万 token。注意这是估算值(output 是否已含
43
+ * reasoning 因 provider 而异);宿主给出精确 cost 时不走本函数。
44
+ */
45
+ export declare function estimateCostUSD(table: Map<string, ModelPrice>, model: string, usage: TokenUsage): number | undefined;
46
+ export {};
@@ -0,0 +1,44 @@
1
+ /**
2
+ * 模型价目表与成本估算(P2.6,插件层=宿主关注点)
3
+ *
4
+ * 数据源:OpenCode V2 `ctx.model.list()` 的 Model.Info.cost
5
+ * (USD / 百万 token,含 cache read/write 单价;结构子集本地声明,
6
+ * 不直接依赖 @opencode 类型,便于单测)。
7
+ *
8
+ * 优先级:宿主消息自带精确 cost > 价目表估算 > 不计。
9
+ */
10
+ /** 构建价目表:key = "providerID/id";基准价取无 tier 限制的条目(缺省第一个) */
11
+ export function buildPriceTable(models) {
12
+ const table = new Map();
13
+ for (const model of models) {
14
+ const tiers = model.cost ?? [];
15
+ const base = tiers.find((tier) => !tier.tier) ?? tiers[0];
16
+ if (!base)
17
+ continue;
18
+ table.set(`${model.providerID}/${model.id}`, {
19
+ input: base.input,
20
+ output: base.output,
21
+ cacheRead: base.cache.read,
22
+ cacheWrite: base.cache.write,
23
+ });
24
+ }
25
+ return table;
26
+ }
27
+ /**
28
+ * 估算成本(USD):
29
+ * input×in + cache.read×cacheRead + cache.write×cacheWrite
30
+ * + (output + reasoning)×out —— reasoning 按输出价计
31
+ * 单位:价目为 USD/百万 token。注意这是估算值(output 是否已含
32
+ * reasoning 因 provider 而异);宿主给出精确 cost 时不走本函数。
33
+ */
34
+ export function estimateCostUSD(table, model, usage) {
35
+ const price = table.get(model);
36
+ if (!price)
37
+ return undefined;
38
+ return ((usage.input * price.input +
39
+ usage.cache.read * price.cacheRead +
40
+ usage.cache.write * price.cacheWrite +
41
+ (usage.output + usage.reasoning) * price.output) /
42
+ 1_000_000);
43
+ }
44
+ //# sourceMappingURL=price-table.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"price-table.js","sourceRoot":"","sources":["../../src/plugin/price-table.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AA0BH,0DAA0D;AAC1D,MAAM,UAAU,eAAe,CAAC,MAA4B;IAC1D,MAAM,KAAK,GAAG,IAAI,GAAG,EAAsB,CAAA;IAC3C,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,IAAI,EAAE,CAAA;QAC9B,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,CAAA;QACzD,IAAI,CAAC,IAAI;YAAE,SAAQ;QACnB,KAAK,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC,UAAU,IAAI,KAAK,CAAC,EAAE,EAAE,EAAE;YAC3C,KAAK,EAAE,IAAI,CAAC,KAAK;YACjB,MAAM,EAAE,IAAI,CAAC,MAAM;YACnB,SAAS,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI;YAC1B,UAAU,EAAE,IAAI,CAAC,KAAK,CAAC,KAAK;SAC7B,CAAC,CAAA;IACJ,CAAC;IACD,OAAO,KAAK,CAAA;AACd,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,eAAe,CAC7B,KAA8B,EAC9B,KAAa,EACb,KAAiB;IAEjB,MAAM,KAAK,GAAG,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,CAAA;IAC9B,IAAI,CAAC,KAAK;QAAE,OAAO,SAAS,CAAA;IAC5B,OAAO,CACL,CAAC,KAAK,CAAC,KAAK,GAAG,KAAK,CAAC,KAAK;QACxB,KAAK,CAAC,KAAK,CAAC,IAAI,GAAG,KAAK,CAAC,SAAS;QAClC,KAAK,CAAC,KAAK,CAAC,KAAK,GAAG,KAAK,CAAC,UAAU;QACpC,CAAC,KAAK,CAAC,MAAM,GAAG,KAAK,CAAC,SAAS,CAAC,GAAG,KAAK,CAAC,MAAM,CAAC;QAClD,SAAS,CACV,CAAA;AACH,CAAC"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * OpenCode V2 TUI 插件入口(P2.3 双形态的 TUI 侧)
3
+ *
4
+ * 加载约定:package.json exports["./tui"] -> 本文件(default export Plugin.define)。
5
+ * 职责单一:订阅 server 侧 checkpoint 审批请求事件,弹出 TUI 确认框,
6
+ * 把人工决定经 RPC 应答回 server(InteractiveCheckpointGate 挂起处)。
7
+ *
8
+ * 注意:本文件只在 TUI 宿主内加载;server 宿主只加载 "." 入口(src/plugin/index.ts)。
9
+ */
10
+ import { Plugin } from "@opencode/plugin/tui";
11
+ declare const _default: Plugin.Definition;
12
+ export default _default;
@@ -0,0 +1,53 @@
1
+ /**
2
+ * OpenCode V2 TUI 插件入口(P2.3 双形态的 TUI 侧)
3
+ *
4
+ * 加载约定:package.json exports["./tui"] -> 本文件(default export Plugin.define)。
5
+ * 职责单一:订阅 server 侧 checkpoint 审批请求事件,弹出 TUI 确认框,
6
+ * 把人工决定经 RPC 应答回 server(InteractiveCheckpointGate 挂起处)。
7
+ *
8
+ * 注意:本文件只在 TUI 宿主内加载;server 宿主只加载 "." 入口(src/plugin/index.ts)。
9
+ */
10
+ import { Plugin } from "@opencode/plugin/tui";
11
+ import { CHECKPOINT_REQUESTED_EVENT, CheckpointRpc } from "./checkpoint-rpc.js";
12
+ export default Plugin.define({
13
+ id: "agentic-workflow-tui",
14
+ setup(ctx) {
15
+ return ctx.data.on(CHECKPOINT_REQUESTED_EVENT, (event) => {
16
+ // 事件 data 为 unknown 形状(V2EventRpc.data),手动收窄
17
+ void handleCheckpointRequest(ctx, event.data);
18
+ });
19
+ },
20
+ });
21
+ async function handleCheckpointRequest(ctx, data) {
22
+ if (typeof data !== "object" || data === null)
23
+ return;
24
+ const record = data;
25
+ if (typeof record.requestId !== "string")
26
+ return;
27
+ const label = typeof record.label === "string" ? record.label : "checkpoint";
28
+ const message = typeof record.message === "string" ? record.message : "";
29
+ // dialog.confirm: true=确认 / false=取消 / undefined=关闭(全部按「拒绝/批准」二值裁决)
30
+ const approved = await ctx.ui.dialog.confirm({
31
+ title: `Agentic Workflow: ${label}`,
32
+ message,
33
+ label: { confirm: "批准继续", cancel: "拒绝中止" },
34
+ });
35
+ const reason = approved === undefined
36
+ ? "dialog dismissed (escaped)"
37
+ : approved
38
+ ? undefined
39
+ : "rejected by reviewer in TUI";
40
+ try {
41
+ await ctx.client.rpc(CheckpointRpc).reply({
42
+ requestId: record.requestId,
43
+ approved: approved === true,
44
+ reason,
45
+ });
46
+ }
47
+ catch (error) {
48
+ // server 侧已超时收口等情况;仅记录,不影响 TUI
49
+ console.log(`[agentic-workflow-tui] checkpoint reply failed (requestId=${record.requestId}): ` +
50
+ `${error instanceof Error ? error.message : String(error)}`);
51
+ }
52
+ }
53
+ //# sourceMappingURL=tui.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tui.js","sourceRoot":"","sources":["../../src/plugin/tui.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAA;AAE7C,OAAO,EAAE,0BAA0B,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAA;AAI/E,eAAe,MAAM,CAAC,MAAM,CAAC;IAC3B,EAAE,EAAE,sBAAsB;IAC1B,KAAK,CAAC,GAAe;QACnB,OAAO,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,0BAA0B,EAAE,CAAC,KAAK,EAAE,EAAE;YACvD,6CAA6C;YAC7C,KAAK,uBAAuB,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,CAAC,CAAA;QAC/C,CAAC,CAAC,CAAA;IACJ,CAAC;CACF,CAAC,CAAA;AAEF,KAAK,UAAU,uBAAuB,CAAC,GAAe,EAAE,IAAa;IACnE,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,IAAI;QAAE,OAAM;IACrD,MAAM,MAAM,GAAG,IAAmE,CAAA;IAClF,IAAI,OAAO,MAAM,CAAC,SAAS,KAAK,QAAQ;QAAE,OAAM;IAChD,MAAM,KAAK,GAAG,OAAO,MAAM,CAAC,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,YAAY,CAAA;IAC5E,MAAM,OAAO,GAAG,OAAO,MAAM,CAAC,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAA;IAExE,oEAAoE;IACpE,MAAM,QAAQ,GAAG,MAAM,GAAG,CAAC,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC;QAC3C,KAAK,EAAE,qBAAqB,KAAK,EAAE;QACnC,OAAO;QACP,KAAK,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE;KAC3C,CAAC,CAAA;IAEF,MAAM,MAAM,GACV,QAAQ,KAAK,SAAS;QACpB,CAAC,CAAC,4BAA4B;QAC9B,CAAC,CAAC,QAAQ;YACR,CAAC,CAAC,SAAS;YACX,CAAC,CAAC,6BAA6B,CAAA;IAErC,IAAI,CAAC;QACH,MAAM,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC,KAAK,CAAC;YACxC,SAAS,EAAE,MAAM,CAAC,SAAS;YAC3B,QAAQ,EAAE,QAAQ,KAAK,IAAI;YAC3B,MAAM;SACP,CAAC,CAAA;IACJ,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,+BAA+B;QAC/B,OAAO,CAAC,GAAG,CACT,6DAA6D,MAAM,CAAC,SAAS,KAAK;YAChF,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAC9D,CAAA;IACH,CAAC;AACH,CAAC"}
@@ -0,0 +1,30 @@
1
+ /**
2
+ * check / assert —— 确定性验证节点(P1.1)
3
+ *
4
+ * 职责边界:
5
+ * agent = 做事(LLM,非确定性)
6
+ * check = 确定性验证(谓词求值,可重复)
7
+ *
8
+ * 两个 API:
9
+ * check(predicate, label) -> 永不抛错,返回 CheckResult(ok 可能为 false)
10
+ * assert(predicate, label) -> 失败抛 WorkflowCheckError(fail-fast 基石,
11
+ * 完整失败语义在 P1.3 定义)
12
+ */
13
+ export interface CheckResult {
14
+ /** 本次检查的说明,例如 "tests must pass" */
15
+ label: string;
16
+ ok: boolean;
17
+ /** 失败原因(谓词抛错时为错误信息;成功时通常为空) */
18
+ detail?: string;
19
+ }
20
+ import { WorkflowError } from "../runtime/errors.js";
21
+ /** assert 失败时抛出的错误,携带完整 CheckResult 供上层失败策略消费 */
22
+ export declare class WorkflowCheckError extends WorkflowError {
23
+ readonly result: CheckResult;
24
+ constructor(result: CheckResult);
25
+ }
26
+ export type CheckPredicate = () => boolean | Promise<boolean>;
27
+ /** 求值谓词:抛错视为不通过,异常信息进入 detail */
28
+ export declare function check(predicate: CheckPredicate, label: string): Promise<CheckResult>;
29
+ /** check 的严格版:失败抛 WorkflowCheckError,成功原样返回 CheckResult */
30
+ export declare function assert(predicate: CheckPredicate, label: string): Promise<CheckResult>;
@@ -0,0 +1,51 @@
1
+ /**
2
+ * check / assert —— 确定性验证节点(P1.1)
3
+ *
4
+ * 职责边界:
5
+ * agent = 做事(LLM,非确定性)
6
+ * check = 确定性验证(谓词求值,可重复)
7
+ *
8
+ * 两个 API:
9
+ * check(predicate, label) -> 永不抛错,返回 CheckResult(ok 可能为 false)
10
+ * assert(predicate, label) -> 失败抛 WorkflowCheckError(fail-fast 基石,
11
+ * 完整失败语义在 P1.3 定义)
12
+ */
13
+ import { WorkflowError } from "../runtime/errors.js";
14
+ import { emitEvent } from "../observability/events.js";
15
+ /** assert 失败时抛出的错误,携带完整 CheckResult 供上层失败策略消费 */
16
+ export class WorkflowCheckError extends WorkflowError {
17
+ result;
18
+ constructor(result) {
19
+ super(`[workflow] check failed: ${result.label}` +
20
+ (result.detail ? ` (${result.detail})` : ""));
21
+ this.name = "WorkflowCheckError";
22
+ this.result = result;
23
+ }
24
+ }
25
+ /** 求值谓词:抛错视为不通过,异常信息进入 detail */
26
+ export async function check(predicate, label) {
27
+ let ok = false;
28
+ let detail;
29
+ try {
30
+ ok = (await predicate()) === true;
31
+ }
32
+ catch (error) {
33
+ ok = false;
34
+ detail = error instanceof Error ? error.message : String(error);
35
+ }
36
+ const result = { label, ok };
37
+ if (!ok && detail)
38
+ result.detail = detail;
39
+ console.log(`[agentic-workflow] check ${ok ? "ok" : "FAIL"}: ${label}`);
40
+ emitEvent({ type: "check.completed", label, ok });
41
+ return result;
42
+ }
43
+ /** check 的严格版:失败抛 WorkflowCheckError,成功原样返回 CheckResult */
44
+ export async function assert(predicate, label) {
45
+ const result = await check(predicate, label);
46
+ if (!result.ok) {
47
+ throw new WorkflowCheckError(result);
48
+ }
49
+ return result;
50
+ }
51
+ //# sourceMappingURL=check.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"check.js","sourceRoot":"","sources":["../../src/quality/check.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAUH,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAA;AACpD,OAAO,EAAE,SAAS,EAAE,MAAM,4BAA4B,CAAA;AAEtD,iDAAiD;AACjD,MAAM,OAAO,kBAAmB,SAAQ,aAAa;IAC1C,MAAM,CAAa;IAE5B,YAAY,MAAmB;QAC7B,KAAK,CACH,4BAA4B,MAAM,CAAC,KAAK,EAAE;YACxC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAC/C,CAAA;QACD,IAAI,CAAC,IAAI,GAAG,oBAAoB,CAAA;QAChC,IAAI,CAAC,MAAM,GAAG,MAAM,CAAA;IACtB,CAAC;CACF;AAID,iCAAiC;AACjC,MAAM,CAAC,KAAK,UAAU,KAAK,CACzB,SAAyB,EACzB,KAAa;IAEb,IAAI,EAAE,GAAG,KAAK,CAAA;IACd,IAAI,MAA0B,CAAA;IAC9B,IAAI,CAAC;QACH,EAAE,GAAG,CAAC,MAAM,SAAS,EAAE,CAAC,KAAK,IAAI,CAAA;IACnC,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,EAAE,GAAG,KAAK,CAAA;QACV,MAAM,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;IACjE,CAAC;IACD,MAAM,MAAM,GAAgB,EAAE,KAAK,EAAE,EAAE,EAAE,CAAA;IACzC,IAAI,CAAC,EAAE,IAAI,MAAM;QAAE,MAAM,CAAC,MAAM,GAAG,MAAM,CAAA;IACzC,OAAO,CAAC,GAAG,CAAC,4BAA4B,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,KAAK,KAAK,EAAE,CAAC,CAAA;IACvE,SAAS,CAAC,EAAE,IAAI,EAAE,iBAAiB,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC,CAAA;IACjD,OAAO,MAAM,CAAA;AACf,CAAC;AAED,2DAA2D;AAC3D,MAAM,CAAC,KAAK,UAAU,MAAM,CAC1B,SAAyB,EACzB,KAAa;IAEb,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC,SAAS,EAAE,KAAK,CAAC,CAAA;IAC5C,IAAI,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC;QACf,MAAM,IAAI,kBAAkB,CAAC,MAAM,CAAC,CAAA;IACtC,CAAC;IACD,OAAO,MAAM,CAAA;AACf,CAAC"}
@@ -0,0 +1,44 @@
1
+ /**
2
+ * checkpoint —— Human-in-the-loop 审批节点(P1.5)
3
+ *
4
+ * 执行语义(本文件负责证明的部分):
5
+ * workflow 执行到 checkpoint(message) 时阻塞,等待人工批准:
6
+ * approved=true -> 继续(返回 undefined,可参与 sequence 传值)
7
+ * approved=false -> 抛 WorkflowCheckpointError,链路中断
8
+ *
9
+ * 宿主交互抽象:CheckpointGate。Core 不知道「人工」在哪里:
10
+ * - PolicyCheckpointGate(plugin 层):按策略自动批准/拒绝(P1 headless 验收用)
11
+ * - P2:交互式门(TUI dialog / 权限请求),经 setCheckpointGate 注入同一抽象
12
+ */
13
+ import { WorkflowError } from "../runtime/errors.js";
14
+ export interface CheckpointRequest {
15
+ label: string;
16
+ message: string;
17
+ }
18
+ export interface CheckpointDecision {
19
+ approved: boolean;
20
+ /** 拒绝原因(可选,进入错误信息) */
21
+ reason?: string;
22
+ }
23
+ export interface CheckpointGate {
24
+ ask(request: CheckpointRequest): Promise<CheckpointDecision>;
25
+ }
26
+ /** checkpoint 被拒绝(或按策略判定不通过)时抛出 */
27
+ export declare class WorkflowCheckpointError extends WorkflowError {
28
+ readonly label: string;
29
+ readonly reason?: string;
30
+ constructor(label: string, reason?: string);
31
+ }
32
+ export interface CheckpointOptions {
33
+ /** 节点标签,默认 "checkpoint" */
34
+ label?: string;
35
+ }
36
+ /** 注入 CheckpointGate(plugin 初始化时调用) */
37
+ export declare function setCheckpointGate(value: CheckpointGate): void;
38
+ /** 获取当前 gate;未注入时抛错 */
39
+ export declare function requireCheckpointGate(): CheckpointGate;
40
+ /**
41
+ * 人工审批节点:阻塞等待批准;拒绝抛 WorkflowCheckpointError。
42
+ * 批准时返回 undefined,不改变 sequence 的传值流。
43
+ */
44
+ export declare function checkpoint(message: string, options?: CheckpointOptions): Promise<void>;
@@ -0,0 +1,55 @@
1
+ /**
2
+ * checkpoint —— Human-in-the-loop 审批节点(P1.5)
3
+ *
4
+ * 执行语义(本文件负责证明的部分):
5
+ * workflow 执行到 checkpoint(message) 时阻塞,等待人工批准:
6
+ * approved=true -> 继续(返回 undefined,可参与 sequence 传值)
7
+ * approved=false -> 抛 WorkflowCheckpointError,链路中断
8
+ *
9
+ * 宿主交互抽象:CheckpointGate。Core 不知道「人工」在哪里:
10
+ * - PolicyCheckpointGate(plugin 层):按策略自动批准/拒绝(P1 headless 验收用)
11
+ * - P2:交互式门(TUI dialog / 权限请求),经 setCheckpointGate 注入同一抽象
12
+ */
13
+ import { WorkflowError } from "../runtime/errors.js";
14
+ import { emitEvent } from "../observability/events.js";
15
+ import { phase } from "../workflow/phase.js";
16
+ /** checkpoint 被拒绝(或按策略判定不通过)时抛出 */
17
+ export class WorkflowCheckpointError extends WorkflowError {
18
+ label;
19
+ reason;
20
+ constructor(label, reason) {
21
+ super(`checkpoint rejected: ${label}` + (reason ? ` (${reason})` : ""));
22
+ this.name = "WorkflowCheckpointError";
23
+ this.label = label;
24
+ this.reason = reason;
25
+ }
26
+ }
27
+ /** ---- gate 注册(与 engine 的 executor 注入同构) ---- */
28
+ let gate;
29
+ /** 注入 CheckpointGate(plugin 初始化时调用) */
30
+ export function setCheckpointGate(value) {
31
+ gate = value;
32
+ }
33
+ /** 获取当前 gate;未注入时抛错 */
34
+ export function requireCheckpointGate() {
35
+ if (!gate) {
36
+ throw new Error("[agentic-workflow] no checkpoint gate bound: call setCheckpointGate() first");
37
+ }
38
+ return gate;
39
+ }
40
+ /**
41
+ * 人工审批节点:阻塞等待批准;拒绝抛 WorkflowCheckpointError。
42
+ * 批准时返回 undefined,不改变 sequence 的传值流。
43
+ */
44
+ export async function checkpoint(message, options) {
45
+ const label = options?.label ?? "checkpoint";
46
+ phase(`Checkpoint(${label})`);
47
+ emitEvent({ type: "checkpoint.waiting", label, message });
48
+ const decision = await requireCheckpointGate().ask({ label, message });
49
+ console.log(`[agentic-workflow] checkpoint ${label}: ${decision.approved ? "approved" : "rejected"}`);
50
+ emitEvent({ type: "checkpoint.completed", label, approved: decision.approved });
51
+ if (!decision.approved) {
52
+ throw new WorkflowCheckpointError(label, decision.reason);
53
+ }
54
+ }
55
+ //# sourceMappingURL=checkpoint.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"checkpoint.js","sourceRoot":"","sources":["../../src/quality/checkpoint.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAA;AACpD,OAAO,EAAE,SAAS,EAAE,MAAM,4BAA4B,CAAA;AACtD,OAAO,EAAE,KAAK,EAAE,MAAM,sBAAsB,CAAA;AAiB5C,mCAAmC;AACnC,MAAM,OAAO,uBAAwB,SAAQ,aAAa;IAC/C,KAAK,CAAQ;IACb,MAAM,CAAS;IAExB,YAAY,KAAa,EAAE,MAAe;QACxC,KAAK,CACH,wBAAwB,KAAK,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,MAAM,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CACjE,CAAA;QACD,IAAI,CAAC,IAAI,GAAG,yBAAyB,CAAA;QACrC,IAAI,CAAC,KAAK,GAAG,KAAK,CAAA;QAClB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAA;IACtB,CAAC;CACF;AAOD,kDAAkD;AAElD,IAAI,IAAgC,CAAA;AAEpC,uCAAuC;AACvC,MAAM,UAAU,iBAAiB,CAAC,KAAqB;IACrD,IAAI,GAAG,KAAK,CAAA;AACd,CAAC;AAED,uBAAuB;AACvB,MAAM,UAAU,qBAAqB;IACnC,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,MAAM,IAAI,KAAK,CACb,6EAA6E,CAC9E,CAAA;IACH,CAAC;IACD,OAAO,IAAI,CAAA;AACb,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAC9B,OAAe,EACf,OAA2B;IAE3B,MAAM,KAAK,GAAG,OAAO,EAAE,KAAK,IAAI,YAAY,CAAA;IAC5C,KAAK,CAAC,cAAc,KAAK,GAAG,CAAC,CAAA;IAE7B,SAAS,CAAC,EAAE,IAAI,EAAE,oBAAoB,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,CAAA;IAEzD,MAAM,QAAQ,GAAG,MAAM,qBAAqB,EAAE,CAAC,GAAG,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,CAAA;IAEtE,OAAO,CAAC,GAAG,CACT,iCAAiC,KAAK,KAAK,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,UAAU,EAAE,CACzF,CAAA;IAED,SAAS,CAAC,EAAE,IAAI,EAAE,sBAAsB,EAAE,KAAK,EAAE,QAAQ,EAAE,QAAQ,CAAC,QAAQ,EAAE,CAAC,CAAA;IAE/E,IAAI,CAAC,QAAQ,CAAC,QAAQ,EAAE,CAAC;QACvB,MAAM,IAAI,uBAAuB,CAAC,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAA;IAC3D,CAAC;AACH,CAAC"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * 宽松 JSON 解析 —— LLM 评审输出的确定性化(P1.2)
3
+ *
4
+ * reviewer agent 被要求输出严格 JSON,但 LLM 输出天然不可控:
5
+ * 可能带 markdown 代码围栏、前后缀解释文字。此处依次尝试:
6
+ * 原文 -> 剥代码围栏 -> 首尾大括号截取
7
+ * 全部失败返回 undefined,由调用方按「评审失败」处理。
8
+ * (策略改编自 V1 opencode-dynamicworkflows 的 parseJsonLoose。)
9
+ */
10
+ export declare function parseJsonLoose(text: string): unknown;
@@ -0,0 +1,31 @@
1
+ /**
2
+ * 宽松 JSON 解析 —— LLM 评审输出的确定性化(P1.2)
3
+ *
4
+ * reviewer agent 被要求输出严格 JSON,但 LLM 输出天然不可控:
5
+ * 可能带 markdown 代码围栏、前后缀解释文字。此处依次尝试:
6
+ * 原文 -> 剥代码围栏 -> 首尾大括号截取
7
+ * 全部失败返回 undefined,由调用方按「评审失败」处理。
8
+ * (策略改编自 V1 opencode-dynamicworkflows 的 parseJsonLoose。)
9
+ */
10
+ export function parseJsonLoose(text) {
11
+ const trimmed = text.trim();
12
+ const attempts = [trimmed];
13
+ const fence = trimmed.match(/```(?:json)?\s*\n([\s\S]*?)\n```/i);
14
+ if (fence?.[1])
15
+ attempts.push(fence[1].trim());
16
+ const firstBrace = trimmed.search(/[[{]/);
17
+ const lastBrace = Math.max(trimmed.lastIndexOf("}"), trimmed.lastIndexOf("]"));
18
+ if (firstBrace >= 0 && lastBrace > firstBrace) {
19
+ attempts.push(trimmed.slice(firstBrace, lastBrace + 1));
20
+ }
21
+ for (const attempt of attempts) {
22
+ try {
23
+ return JSON.parse(attempt);
24
+ }
25
+ catch {
26
+ // 尝试下一种
27
+ }
28
+ }
29
+ return undefined;
30
+ }
31
+ //# sourceMappingURL=json.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"json.js","sourceRoot":"","sources":["../../src/quality/json.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,MAAM,UAAU,cAAc,CAAC,IAAY;IACzC,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,CAAA;IAC3B,MAAM,QAAQ,GAAa,CAAC,OAAO,CAAC,CAAA;IAEpC,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,mCAAmC,CAAC,CAAA;IAChE,IAAI,KAAK,EAAE,CAAC,CAAC,CAAC;QAAE,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAA;IAE9C,MAAM,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,CAAA;IACzC,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,WAAW,CAAC,GAAG,CAAC,EAAE,OAAO,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAA;IAC9E,IAAI,UAAU,IAAI,CAAC,IAAI,SAAS,GAAG,UAAU,EAAE,CAAC;QAC9C,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,UAAU,EAAE,SAAS,GAAG,CAAC,CAAC,CAAC,CAAA;IACzD,CAAC;IAED,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;QAC/B,IAAI,CAAC;YACH,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAA;QAC5B,CAAC;QAAC,MAAM,CAAC;YACP,QAAQ;QACV,CAAC;IACH,CAAC;IACD,OAAO,SAAS,CAAA;AAClB,CAAC"}
@@ -0,0 +1,17 @@
1
+ /**
2
+ * 内建确定性谓词库 —— check/assert 的常用积木(P1.1)
3
+ *
4
+ * 全部基于可注入的 CommandRunner / Node fs,不触碰 OpenCode API。
5
+ */
6
+ import { type CommandOptions } from "../runtime/command.js";
7
+ /**
8
+ * 运行 shell 命令,退出码为 0 视为成功。
9
+ * 命令一律带超时(默认 120s,可用 options.timeoutMs 覆盖)。
10
+ */
11
+ export declare function commandSuccess(command: string, options?: CommandOptions): Promise<boolean>;
12
+ /** 路径存在(文件或目录均为 true) */
13
+ export declare function fileExists(path: string): Promise<boolean>;
14
+ /** 路径存在且是文件 */
15
+ export declare function isFile(path: string): Promise<boolean>;
16
+ /** 路径存在且是目录 */
17
+ export declare function isDirectory(path: string): Promise<boolean>;
@@ -0,0 +1,44 @@
1
+ /**
2
+ * 内建确定性谓词库 —— check/assert 的常用积木(P1.1)
3
+ *
4
+ * 全部基于可注入的 CommandRunner / Node fs,不触碰 OpenCode API。
5
+ */
6
+ import { stat } from "node:fs/promises";
7
+ import { getCommandRunner } from "../runtime/command.js";
8
+ /**
9
+ * 运行 shell 命令,退出码为 0 视为成功。
10
+ * 命令一律带超时(默认 120s,可用 options.timeoutMs 覆盖)。
11
+ */
12
+ export async function commandSuccess(command, options) {
13
+ const result = await getCommandRunner().run(command, options);
14
+ return result.code === 0 && !result.timedOut;
15
+ }
16
+ /** 路径存在(文件或目录均为 true) */
17
+ export async function fileExists(path) {
18
+ try {
19
+ await stat(path);
20
+ return true;
21
+ }
22
+ catch {
23
+ return false;
24
+ }
25
+ }
26
+ /** 路径存在且是文件 */
27
+ export async function isFile(path) {
28
+ try {
29
+ return (await stat(path)).isFile();
30
+ }
31
+ catch {
32
+ return false;
33
+ }
34
+ }
35
+ /** 路径存在且是目录 */
36
+ export async function isDirectory(path) {
37
+ try {
38
+ return (await stat(path)).isDirectory();
39
+ }
40
+ catch {
41
+ return false;
42
+ }
43
+ }
44
+ //# sourceMappingURL=predicates.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"predicates.js","sourceRoot":"","sources":["../../src/quality/predicates.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAA;AAEvC,OAAO,EAAE,gBAAgB,EAAuB,MAAM,uBAAuB,CAAA;AAE7E;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,OAAe,EACf,OAAwB;IAExB,MAAM,MAAM,GAAG,MAAM,gBAAgB,EAAE,CAAC,GAAG,CAAC,OAAO,EAAE,OAAO,CAAC,CAAA;IAC7D,OAAO,MAAM,CAAC,IAAI,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAA;AAC9C,CAAC;AAED,yBAAyB;AACzB,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,IAAY;IAC3C,IAAI,CAAC;QACH,MAAM,IAAI,CAAC,IAAI,CAAC,CAAA;QAChB,OAAO,IAAI,CAAA;IACb,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAA;IACd,CAAC;AACH,CAAC;AAED,eAAe;AACf,MAAM,CAAC,KAAK,UAAU,MAAM,CAAC,IAAY;IACvC,IAAI,CAAC;QACH,OAAO,CAAC,MAAM,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,EAAE,CAAA;IACpC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAA;IACd,CAAC;AACH,CAAC;AAED,eAAe;AACf,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,IAAY;IAC5C,IAAI,CAAC;QACH,OAAO,CAAC,MAAM,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,WAAW,EAAE,CAAA;IACzC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAA;IACd,CAAC;AACH,CAAC"}