@tea-agent/loop-agent 0.1.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 (264) hide show
  1. package/AGENTS.md +121 -0
  2. package/CHANGELOG.md +37 -0
  3. package/README.md +144 -0
  4. package/bin/loop-agent.js +21 -0
  5. package/dist/adapters/aimax.js +91 -0
  6. package/dist/adapters/context.js +32 -0
  7. package/dist/adapters/index.js +28 -0
  8. package/dist/adapters/loop-agent.js +98 -0
  9. package/dist/adapters/types.js +1 -0
  10. package/dist/cli/catalog.js +259 -0
  11. package/dist/cli/help.js +55 -0
  12. package/dist/cli/index.js +3 -0
  13. package/dist/cli/program.js +505 -0
  14. package/dist/cli.js +12 -0
  15. package/dist/commands/closeout.js +13 -0
  16. package/dist/commands/coverage-audit.js +14 -0
  17. package/dist/commands/cursor-prompt.js +222 -0
  18. package/dist/commands/cursor-worker.js +43 -0
  19. package/dist/commands/dag-approve.js +102 -0
  20. package/dist/commands/dag-final-verification.js +76 -0
  21. package/dist/commands/dag-init-hybrid.js +56 -0
  22. package/dist/commands/dag-reconcile-tasks.js +51 -0
  23. package/dist/commands/dag-reject.js +91 -0
  24. package/dist/commands/dag-report.js +177 -0
  25. package/dist/commands/dag-resume.js +34 -0
  26. package/dist/commands/dag-run-task.js +470 -0
  27. package/dist/commands/dag-validate.js +186 -0
  28. package/dist/commands/dag-workflow-compile.js +91 -0
  29. package/dist/commands/dag-workflow-plan.js +130 -0
  30. package/dist/commands/dag-workflow-validate.js +66 -0
  31. package/dist/commands/delegate.js +132 -0
  32. package/dist/commands/docs-archive.js +5 -0
  33. package/dist/commands/docs-audit.js +5 -0
  34. package/dist/commands/doctor.js +50 -0
  35. package/dist/commands/goal.js +92 -0
  36. package/dist/commands/handoff-check.js +5 -0
  37. package/dist/commands/harvest.js +44 -0
  38. package/dist/commands/inspect.js +11 -0
  39. package/dist/commands/instructions.js +195 -0
  40. package/dist/commands/knowledge.js +64 -0
  41. package/dist/commands/loop-benchmark.js +72 -0
  42. package/dist/commands/loop.js +241 -0
  43. package/dist/commands/new-task.js +5 -0
  44. package/dist/commands/pi-prompt.js +181 -0
  45. package/dist/commands/pi-reuse-benchmark.js +153 -0
  46. package/dist/commands/plan-list.js +5 -0
  47. package/dist/commands/promote-run.js +29 -0
  48. package/dist/commands/reference-index.js +16 -0
  49. package/dist/commands/run-dag.js +184 -0
  50. package/dist/commands/spine.js +38 -0
  51. package/dist/commands/stats.js +84 -0
  52. package/dist/commands/status.js +56 -0
  53. package/dist/commands/study-init.js +192 -0
  54. package/dist/commands/workflow.js +259 -0
  55. package/dist/commands/worktree-create.js +31 -0
  56. package/dist/commands/worktree-list.js +5 -0
  57. package/dist/commands/worktree-remove.js +26 -0
  58. package/dist/cursor-worker-entry.js +8 -0
  59. package/dist/executors/config-core.js +55 -0
  60. package/dist/executors/config.js +2 -0
  61. package/dist/executors/cursor-artifacts.js +33 -0
  62. package/dist/executors/cursor-execution-log.js +81 -0
  63. package/dist/executors/cursor-executor-artifacts.js +135 -0
  64. package/dist/executors/cursor-executor.js +468 -0
  65. package/dist/executors/cursor-run.js +115 -0
  66. package/dist/executors/cursor-tool.js +94 -0
  67. package/dist/executors/cursor-worker-client.js +213 -0
  68. package/dist/executors/cursor-worker-protocol.js +18 -0
  69. package/dist/executors/cursor-worker-server.js +54 -0
  70. package/dist/executors/cursor-worker.js +3 -0
  71. package/dist/executors/cursor.js +6 -0
  72. package/dist/executors/dag-cursor-executor.js +88 -0
  73. package/dist/executors/dag-pi-executor.js +322 -0
  74. package/dist/executors/dag-static-executor.js +45 -0
  75. package/dist/executors/dag.js +4 -0
  76. package/dist/executors/index.js +8 -0
  77. package/dist/executors/model-routing.js +60 -0
  78. package/dist/executors/pi-event-serializer.js +43 -0
  79. package/dist/executors/pi-executor.js +606 -0
  80. package/dist/executors/pi-reuse-benchmark.js +316 -0
  81. package/dist/executors/pi-runtime-reuse.js +29 -0
  82. package/dist/executors/pi-sdk-executor.js +255 -0
  83. package/dist/executors/pi-sdk.js +1 -0
  84. package/dist/executors/pi.js +3 -0
  85. package/dist/executors/shell-executor.js +300 -0
  86. package/dist/executors/shell-presets.js +47 -0
  87. package/dist/executors/shell-verification.js +251 -0
  88. package/dist/executors/shell-write-guard.js +126 -0
  89. package/dist/executors/shell.js +3 -0
  90. package/dist/executors/static.js +1 -0
  91. package/dist/governance/checks.js +434 -0
  92. package/dist/governance/harness.js +9 -0
  93. package/dist/governance/index.js +3 -0
  94. package/dist/governance/manifest-types.js +128 -0
  95. package/dist/governance/manifest.js +2 -0
  96. package/dist/governance/path-guard.js +69 -0
  97. package/dist/governance/path-guards.js +2 -0
  98. package/dist/governance/profiles.js +3 -0
  99. package/dist/governance/requirement-coverage.js +425 -0
  100. package/dist/governance/skill-safety.js +135 -0
  101. package/dist/governance/spine-audit.js +152 -0
  102. package/dist/records/closeout.js +2 -0
  103. package/dist/records/harvest.js +236 -0
  104. package/dist/records/index.js +3 -0
  105. package/dist/records/one-shot-runs.js +421 -0
  106. package/dist/records/promotion.js +199 -0
  107. package/dist/shared/artifacts-core.js +88 -0
  108. package/dist/shared/artifacts.js +2 -0
  109. package/dist/shared/context-files.js +32 -0
  110. package/dist/shared/context.js +2 -0
  111. package/dist/shared/copy-dir.js +17 -0
  112. package/dist/shared/git-progress.js +165 -0
  113. package/dist/shared/index.js +5 -0
  114. package/dist/shared/logger.js +23 -0
  115. package/dist/shared/one-shot-prompt-args.js +98 -0
  116. package/dist/shared/path-refs.js +31 -0
  117. package/dist/shared/prompts.js +26 -0
  118. package/dist/shared/reference-context.js +238 -0
  119. package/dist/shared/timeout-policy.js +19 -0
  120. package/dist/shared/timeout.js +1 -0
  121. package/dist/shared/types.js +5 -0
  122. package/dist/task/config-types.js +97 -0
  123. package/dist/task/config.js +2 -0
  124. package/dist/task/delegate.js +220 -0
  125. package/dist/task/goal-audit.js +51 -0
  126. package/dist/task/goal-policy.js +8 -0
  127. package/dist/task/goal.js +3 -0
  128. package/dist/task/ids.js +1 -0
  129. package/dist/task/index.js +9 -0
  130. package/dist/task/lifecycle.js +1 -0
  131. package/dist/task/paths.js +1 -0
  132. package/dist/task/read-model.js +149 -0
  133. package/dist/task/runtime.js +699 -0
  134. package/dist/task/source-state.js +1 -0
  135. package/dist/task/state.js +55 -0
  136. package/dist/task/subagent-guidance.js +1 -0
  137. package/dist/task/workflow-state-types.js +92 -0
  138. package/dist/task/worktree-cleanup.js +140 -0
  139. package/dist/task/worktree.js +171 -0
  140. package/dist/workflows/dag/authoring.js +8 -0
  141. package/dist/workflows/dag/authority-surface.js +138 -0
  142. package/dist/workflows/dag/canvas-observer.js +474 -0
  143. package/dist/workflows/dag/decision-envelope.js +502 -0
  144. package/dist/workflows/dag/decision-evidence.js +153 -0
  145. package/dist/workflows/dag/decision-gates.js +1 -0
  146. package/dist/workflows/dag/executor-registry.js +25 -0
  147. package/dist/workflows/dag/facts.js +4 -0
  148. package/dist/workflows/dag/failure-category.js +111 -0
  149. package/dist/workflows/dag/final-verification.js +180 -0
  150. package/dist/workflows/dag/governance-constants.js +5 -0
  151. package/dist/workflows/dag/governance-profile.js +405 -0
  152. package/dist/workflows/dag/index.js +6 -0
  153. package/dist/workflows/dag/init-hybrid.js +855 -0
  154. package/dist/workflows/dag/knowledge-curator.js +162 -0
  155. package/dist/workflows/dag/lifecycle.js +484 -0
  156. package/dist/workflows/dag/prompt-source.js +88 -0
  157. package/dist/workflows/dag/prompt.js +130 -0
  158. package/dist/workflows/dag/reconcile-tasks.js +404 -0
  159. package/dist/workflows/dag/recovery-recommendation.js +226 -0
  160. package/dist/workflows/dag/repair-artifact.js +136 -0
  161. package/dist/workflows/dag/report.js +1019 -0
  162. package/dist/workflows/dag/runner.js +1677 -0
  163. package/dist/workflows/dag/runtime.js +5 -0
  164. package/dist/workflows/dag/skill-instructions.js +471 -0
  165. package/dist/workflows/dag/skills.js +41 -0
  166. package/dist/workflows/dag/spec.js +3 -0
  167. package/dist/workflows/dag/topo.js +30 -0
  168. package/dist/workflows/dag/types.js +275 -0
  169. package/dist/workflows/dag/upstream-artifacts.js +95 -0
  170. package/dist/workflows/dag/validate.js +527 -0
  171. package/dist/workflows/dynamic/artifacts.js +65 -0
  172. package/dist/workflows/dynamic/compile.js +360 -0
  173. package/dist/workflows/dynamic/compileTypes.js +1 -0
  174. package/dist/workflows/dynamic/errors.js +5 -0
  175. package/dist/workflows/dynamic/index.js +7 -0
  176. package/dist/workflows/dynamic/profiles.js +156 -0
  177. package/dist/workflows/dynamic/spec.js +114 -0
  178. package/dist/workflows/dynamic/validate.js +275 -0
  179. package/dist/workflows/loop/actions.js +1334 -0
  180. package/dist/workflows/loop/benchmark.js +510 -0
  181. package/dist/workflows/loop/closeout.js +134 -0
  182. package/dist/workflows/loop/context.js +48 -0
  183. package/dist/workflows/loop/events.js +25 -0
  184. package/dist/workflows/loop/hash.js +32 -0
  185. package/dist/workflows/loop/index.js +8 -0
  186. package/dist/workflows/loop/paths.js +17 -0
  187. package/dist/workflows/loop/rounds.js +81 -0
  188. package/dist/workflows/loop/signals.js +55 -0
  189. package/dist/workflows/loop/state.js +116 -0
  190. package/dist/workflows/loop/templates.js +54 -0
  191. package/dist/workflows/loop/types.js +28 -0
  192. package/docs/README.md +62 -0
  193. package/docs/agent-dag-recovery-playbook.md +158 -0
  194. package/docs/agent-dag-runner.md +40 -0
  195. package/docs/cursor-executor-usage.md +25 -0
  196. package/docs/decisions/README.md +3 -0
  197. package/docs/design/README.md +36 -0
  198. package/docs/development-principles.md +71 -0
  199. package/docs/dynamic-workflow-dag-engine-roadmap.md +1749 -0
  200. package/docs/exec-plans/README.md +6 -0
  201. package/docs/exec-plans/active/README.md +5 -0
  202. package/docs/exec-plans/completed/README.md +5 -0
  203. package/docs/feature-workflow.md +184 -0
  204. package/docs/harness-methodology-debugging.md +153 -0
  205. package/docs/harness-methodology-tdd.md +130 -0
  206. package/docs/harness-methodology-verification.md +27 -0
  207. package/docs/loop-agent-harness.md +42 -0
  208. package/docs/progress/README.md +3 -0
  209. package/docs/reports/README.md +3 -0
  210. package/docs/templates/adr.md +60 -0
  211. package/docs/templates/agent-dag-authority-surface-audit.prompt.md +94 -0
  212. package/docs/templates/agent-dag-decision-envelope.schema.json +213 -0
  213. package/docs/templates/agent-dag-decision-gate-dogfood-report.md +117 -0
  214. package/docs/templates/agent-dag-decision-gate.prompt.md +246 -0
  215. package/docs/templates/agent-dag-process-supervisor.prompt.md +98 -0
  216. package/docs/templates/agent-dag-report.schema.json +423 -0
  217. package/docs/templates/agent-dag-review-verdict.prompt.md +68 -0
  218. package/docs/templates/agent-dag.base.json +195 -0
  219. package/docs/templates/agent-dag.final-verification.json +190 -0
  220. package/docs/templates/agent-dag.schema.json +316 -0
  221. package/docs/templates/agent-dag.supervised-implementation.json +500 -0
  222. package/docs/templates/exec-plan.md +64 -0
  223. package/docs/templates/feature-spec.md +53 -0
  224. package/docs/templates/hybrid-dag.json +193 -0
  225. package/docs/templates/progress-log.md +17 -0
  226. package/docs/templates/project-start-checklist.md +9 -0
  227. package/docs/templates/qa-report.md +42 -0
  228. package/docs/templates/sprint-contract.md +29 -0
  229. package/docs/verification-matrix.md +30 -0
  230. package/examples/decision-gate-agent-dag.json +123 -0
  231. package/examples/example-dag.json +51 -0
  232. package/examples/hybrid-loop-agent-dag.json +194 -0
  233. package/harness.json +92 -0
  234. package/package.json +61 -0
  235. package/skills/ai-engineering-context/SKILL.md +48 -0
  236. package/skills/loop-agent/SKILL.md +260 -0
  237. package/skills/loop-agent/references/README.md +63 -0
  238. package/skills/loop-agent/references/command-reference.md +315 -0
  239. package/skills/loop-agent/references/harness-policy.md +258 -0
  240. package/skills/loop-agent/references/hybrid-dag.md +216 -0
  241. package/skills/loop-agent/references/learned/README.md +21 -0
  242. package/skills/loop-agent/references/model-routing.md +36 -0
  243. package/skills/loop-agent/references/multi-worktree.md +54 -0
  244. package/skills/loop-agent/references/one-shot-runs.md +85 -0
  245. package/skills/loop-agent/references/orchestrator-and-interventions.md +169 -0
  246. package/skills/loop-agent/references/pi-prompt.md +23 -0
  247. package/skills/loop-agent/references/pi-subagent-assisted-mode.md +83 -0
  248. package/skills/loop-agent/references/post-implementation-and-patterns.md +44 -0
  249. package/skills/loop-agent/references/task-workflow.md +84 -0
  250. package/skills/loop-agent/references/verification-and-failure-handling.md +74 -0
  251. package/skills/requesting-code-review/SKILL.md +101 -0
  252. package/skills/requesting-code-review/code-reviewer.md +168 -0
  253. package/skills/systematic-debugging/CREATION-LOG.md +119 -0
  254. package/skills/systematic-debugging/SKILL.md +296 -0
  255. package/skills/systematic-debugging/condition-based-waiting-example.ts +158 -0
  256. package/skills/systematic-debugging/condition-based-waiting.md +115 -0
  257. package/skills/systematic-debugging/defense-in-depth.md +122 -0
  258. package/skills/systematic-debugging/find-polluter.sh +63 -0
  259. package/skills/systematic-debugging/root-cause-tracing.md +169 -0
  260. package/skills/systematic-debugging/test-academic.md +14 -0
  261. package/skills/systematic-debugging/test-pressure-1.md +58 -0
  262. package/skills/systematic-debugging/test-pressure-2.md +68 -0
  263. package/skills/systematic-debugging/test-pressure-3.md +69 -0
  264. package/skills/verification-before-completion/SKILL.md +154 -0
@@ -0,0 +1,360 @@
1
+ import path from "node:path";
2
+ import { parseDagSpec } from "../dag/types.js";
3
+ import { assertValidWorkflowSpec } from "./validate.js";
4
+ import { workflowNodeSchema, } from "./spec.js";
5
+ const READ_ONLY_FORBIDDEN_PATHS = ["artifacts/**", ".harness/**"];
6
+ export function defaultCompileManifestPath(outputPath) {
7
+ const parsed = path.parse(outputPath);
8
+ const base = parsed.base.endsWith(".dag.json")
9
+ ? parsed.base.slice(0, -".dag.json".length)
10
+ : parsed.name;
11
+ return path.join(parsed.dir, `${base}.compile-manifest.json`);
12
+ }
13
+ function stringifyOutputSchema(outputSchema) {
14
+ if (outputSchema === undefined)
15
+ return undefined;
16
+ return `Workflow outputSchema:\n${JSON.stringify(outputSchema, null, 2)}`;
17
+ }
18
+ function compileTemplate(value, inputs) {
19
+ return value.replace(/\{\{\s*inputs\.([A-Za-z0-9_-]+)\s*\}\}/g, (_match, key) => {
20
+ const replacement = inputs[key];
21
+ if (typeof replacement === "string" ||
22
+ typeof replacement === "number" ||
23
+ typeof replacement === "boolean") {
24
+ return String(replacement);
25
+ }
26
+ return "";
27
+ });
28
+ }
29
+ function dagRoleForNode(node) {
30
+ if (node.type === "reduce_agent")
31
+ return "closeout";
32
+ if (node.type === "verify_agent")
33
+ return "verifier";
34
+ if (node.type === "human_gate")
35
+ return "supervisor";
36
+ if (node.type === "agent" || node.type === "map_agent")
37
+ return "reviewer";
38
+ return undefined;
39
+ }
40
+ function executorForAgentLikeNode(node) {
41
+ return node.executor ?? "pi";
42
+ }
43
+ function dagWriteFields(spec, node) {
44
+ if (node?.writePolicy === "none") {
45
+ return {
46
+ writePolicy: "none",
47
+ allowedPaths: [],
48
+ forbiddenPaths: [],
49
+ };
50
+ }
51
+ if (node?.writePolicy === "read-only") {
52
+ return {
53
+ writePolicy: "read-only",
54
+ allowedPaths: [],
55
+ forbiddenPaths: READ_ONLY_FORBIDDEN_PATHS,
56
+ };
57
+ }
58
+ if (spec.permissions.edits === "deny") {
59
+ return {
60
+ writePolicy: "read-only",
61
+ allowedPaths: [],
62
+ forbiddenPaths: READ_ONLY_FORBIDDEN_PATHS,
63
+ };
64
+ }
65
+ return {
66
+ writePolicy: "exclusive",
67
+ allowedPaths: spec.permissions.allowedPaths ?? [],
68
+ forbiddenPaths: spec.permissions.forbiddenPaths ?? [],
69
+ writeSet: spec.permissions.writeSet ?? [],
70
+ };
71
+ }
72
+ function buildAgentPrompt(node) {
73
+ const role = node.role ? `Workflow role: ${node.role}\n` : "";
74
+ const prompt = node.prompt ?? `Execute workflow node ${node.id}.`;
75
+ return [
76
+ `Workflow node: ${node.id}`,
77
+ `Workflow primitive: ${node.type}`,
78
+ role.trim(),
79
+ prompt,
80
+ ]
81
+ .filter(Boolean)
82
+ .join("\n\n");
83
+ }
84
+ function compileCommandNode(spec, node) {
85
+ const command = node.command ?? "";
86
+ return {
87
+ id: node.id,
88
+ depends_on: node.dependsOn,
89
+ complexity: "LOW",
90
+ subtask_prompt: `Run workflow command node ${node.id}.`,
91
+ executor: "shell",
92
+ writePolicy: "none",
93
+ allowedPaths: [],
94
+ forbiddenPaths: [],
95
+ shell: {
96
+ commands: [compileTemplate(command, spec.inputs)],
97
+ },
98
+ outputContract: stringifyOutputSchema(node.outputSchema),
99
+ };
100
+ }
101
+ function compileHumanGateNode(spec, node) {
102
+ return {
103
+ id: node.id,
104
+ depends_on: node.dependsOn,
105
+ complexity: "LOW",
106
+ subtask_prompt: [
107
+ `Workflow human gate: ${node.id}`,
108
+ node.reason ?? "Review upstream workflow outputs and decide whether execution may continue.",
109
+ "Return a decision envelope. Set requiresHuman=true when explicit approval is needed.",
110
+ ].join("\n\n"),
111
+ executor: "pi",
112
+ role: "supervisor",
113
+ ...dagWriteFields(spec, node),
114
+ decisionGate: { enabled: true, mode: "pause-on-human" },
115
+ outputContract: node.approvalSchema
116
+ ? `Workflow approvalSchema:\n${JSON.stringify(node.approvalSchema, null, 2)}`
117
+ : undefined,
118
+ };
119
+ }
120
+ function compileArtifactTransformNode(node) {
121
+ return {
122
+ id: node.id,
123
+ depends_on: node.dependsOn,
124
+ complexity: "LOW",
125
+ subtask_prompt: `Apply deterministic artifact transform ${node.id}.`,
126
+ executor: "static",
127
+ writePolicy: "none",
128
+ allowedPaths: [],
129
+ forbiddenPaths: [],
130
+ static: {
131
+ resultMarkdown: node.transform?.template ?? "artifact transform completed",
132
+ },
133
+ outputContract: stringifyOutputSchema(node.outputSchema),
134
+ };
135
+ }
136
+ function compileConditionNode(node) {
137
+ return {
138
+ id: node.id,
139
+ depends_on: node.dependsOn,
140
+ complexity: "LOW",
141
+ subtask_prompt: `Evaluate workflow condition ${node.id}.`,
142
+ executor: "static",
143
+ writePolicy: "none",
144
+ allowedPaths: [],
145
+ forbiddenPaths: [],
146
+ static: {
147
+ resultMarkdown: `Dynamic condition barrier for ${node.id}.`,
148
+ },
149
+ outputContract: stringifyOutputSchema(node.outputSchema),
150
+ dynamicCondition: {
151
+ workflowNodeId: node.id,
152
+ cases: node.cases ?? [],
153
+ default: node.default,
154
+ },
155
+ };
156
+ }
157
+ function stopWhenAnyExpressions(stopWhen) {
158
+ if (typeof stopWhen === "string")
159
+ return [stopWhen];
160
+ if (stopWhen && typeof stopWhen === "object") {
161
+ const any = stopWhen.any;
162
+ if (Array.isArray(any)) {
163
+ return any.filter((entry) => typeof entry === "string");
164
+ }
165
+ }
166
+ return [];
167
+ }
168
+ function compileLoopBodyTask(spec, node) {
169
+ const executor = executorForAgentLikeNode(node);
170
+ return {
171
+ id: node.id,
172
+ dependsOn: node.dependsOn,
173
+ executor,
174
+ role: dagRoleForNode(node),
175
+ complexity: node.type === "reduce_agent" ? "MED" : "LOW",
176
+ subtaskPromptTemplate: buildAgentPrompt(node),
177
+ staticResultTemplate: node.staticResultTemplate ?? node.transform?.template ?? undefined,
178
+ outputContract: stringifyOutputSchema(node.outputSchema),
179
+ ...dagWriteFields(spec, node),
180
+ };
181
+ }
182
+ function compileLoopUntilNode(spec, node) {
183
+ const bodyTasks = (node.body ?? []).map((entry) => compileLoopBodyTask(spec, workflowNodeSchema.parse(entry)));
184
+ return {
185
+ id: node.id,
186
+ depends_on: node.dependsOn,
187
+ complexity: "MED",
188
+ subtask_prompt: buildAgentPrompt(node),
189
+ executor: "static",
190
+ role: dagRoleForNode(node),
191
+ ...dagWriteFields(spec, node),
192
+ static: {
193
+ resultMarkdown: `Dynamic loop_until barrier for ${node.id}.`,
194
+ },
195
+ outputContract: stringifyOutputSchema(node.outputSchema),
196
+ dynamicLoopUntil: {
197
+ workflowNodeId: node.id,
198
+ maxIterations: node.maxIterations ?? spec.limits.maxIterations,
199
+ stopWhenAny: stopWhenAnyExpressions(node.stopWhen),
200
+ bodyTasks,
201
+ },
202
+ };
203
+ }
204
+ function compileAgentLikeNode(spec, node) {
205
+ const executor = executorForAgentLikeNode(node);
206
+ if (executor === "static") {
207
+ return {
208
+ id: node.id,
209
+ depends_on: node.dependsOn,
210
+ complexity: node.type === "reduce_agent" ? "MED" : "LOW",
211
+ subtask_prompt: buildAgentPrompt(node),
212
+ executor: "static",
213
+ role: dagRoleForNode(node),
214
+ ...dagWriteFields(spec, node),
215
+ static: {
216
+ resultMarkdown: node.staticResultTemplate ?? `Workflow node ${node.id} completed.`,
217
+ },
218
+ outputContract: stringifyOutputSchema(node.outputSchema),
219
+ };
220
+ }
221
+ return {
222
+ id: node.id,
223
+ depends_on: node.dependsOn,
224
+ complexity: node.type === "reduce_agent" ? "MED" : "LOW",
225
+ subtask_prompt: buildAgentPrompt(node),
226
+ executor: "pi",
227
+ role: dagRoleForNode(node),
228
+ ...dagWriteFields(spec, node),
229
+ outputContract: stringifyOutputSchema(node.outputSchema),
230
+ };
231
+ }
232
+ function compileNode(spec, node) {
233
+ if (node.type === "command")
234
+ return compileCommandNode(spec, node);
235
+ if (node.type === "human_gate")
236
+ return compileHumanGateNode(spec, node);
237
+ if (node.type === "artifact_transform")
238
+ return compileArtifactTransformNode(node);
239
+ if (node.type === "condition")
240
+ return compileConditionNode(node);
241
+ if (node.type === "loop_until")
242
+ return compileLoopUntilNode(spec, node);
243
+ if (node.type === "map_agent" || node.type === "verify_agent") {
244
+ return compileDynamicExpansionNode(spec, node);
245
+ }
246
+ if (node.type === "reduce_agent" && node.reducer) {
247
+ return compileDynamicReductionNode(spec, node);
248
+ }
249
+ return compileAgentLikeNode(spec, node);
250
+ }
251
+ function compileDynamicExpansionNode(spec, node) {
252
+ if (node.type !== "map_agent" && node.type !== "verify_agent") {
253
+ throw new Error(`unsupported dynamic expansion node type: ${node.type}`);
254
+ }
255
+ const expansionType = node.type;
256
+ const childExecutor = executorForAgentLikeNode(node);
257
+ const childPrompt = buildAgentPrompt(node);
258
+ const childTask = {
259
+ executor: childExecutor,
260
+ role: dagRoleForNode(node),
261
+ complexity: "LOW",
262
+ subtaskPromptTemplate: childPrompt,
263
+ staticResultTemplate: node.staticResultTemplate,
264
+ outputContract: stringifyOutputSchema(node.outputSchema),
265
+ ...dagWriteFields(spec, node),
266
+ };
267
+ return {
268
+ id: node.id,
269
+ depends_on: node.dependsOn,
270
+ complexity: "LOW",
271
+ subtask_prompt: `Expand workflow map_agent node ${node.id}.`,
272
+ executor: "static",
273
+ writePolicy: "none",
274
+ allowedPaths: [],
275
+ forbiddenPaths: [],
276
+ static: {
277
+ resultMarkdown: `Dynamic ${node.type} expansion barrier for ${node.id}.`,
278
+ },
279
+ outputContract: stringifyOutputSchema(node.outputSchema),
280
+ dynamicExpansion: {
281
+ type: expansionType,
282
+ workflowNodeId: node.id,
283
+ itemsFrom: node.itemsFrom ?? "",
284
+ itemName: node.itemName ?? "item",
285
+ maxItems: node.maxItems ?? spec.limits.maxExpandedNodes,
286
+ maxExpandedNodes: spec.limits.maxExpandedNodes,
287
+ workspaceTemplate: node.workspaceTemplate,
288
+ childIdPrefix: node.id,
289
+ childTask,
290
+ },
291
+ };
292
+ }
293
+ function compileDynamicReductionNode(spec, node) {
294
+ return {
295
+ id: node.id,
296
+ depends_on: node.dependsOn,
297
+ complexity: "MED",
298
+ subtask_prompt: buildAgentPrompt(node),
299
+ executor: "static",
300
+ role: dagRoleForNode(node),
301
+ ...dagWriteFields(spec, node),
302
+ static: {
303
+ resultMarkdown: `Dynamic reducer ${node.reducer?.type ?? "unknown"} for ${node.id}.`,
304
+ },
305
+ outputContract: stringifyOutputSchema(node.outputSchema),
306
+ dynamicReduction: node.reducer
307
+ ? {
308
+ type: node.reducer.type,
309
+ workflowNodeId: node.id,
310
+ findingsFrom: node.reducer.findingsFrom,
311
+ verificationsFrom: node.reducer.verificationsFrom,
312
+ refutedFindingsArtifactName: "refuted-findings.json",
313
+ }
314
+ : undefined,
315
+ };
316
+ }
317
+ export function compileWorkflowToDag(spec, options = {}) {
318
+ assertValidWorkflowSpec(spec);
319
+ const tasks = spec.nodes.map((node) => compileNode(spec, node));
320
+ const dag = parseDagSpec({
321
+ version: 2,
322
+ title: `Workflow: ${spec.meta.name}`,
323
+ objective: spec.meta.description,
324
+ successCriteria: [`Workflow final node completed: ${spec.final.from}`],
325
+ globalConstraints: [
326
+ "Compiled from Dynamic Workflow DAG Engine WorkflowSpec.",
327
+ "Workflow origin is recorded in the sidecar compile manifest.",
328
+ ],
329
+ tasks,
330
+ });
331
+ const manifest = {
332
+ version: 1,
333
+ workflow: {
334
+ name: spec.meta.name,
335
+ version: spec.version,
336
+ kind: spec.kind,
337
+ sourcePath: options.sourcePath,
338
+ final: spec.final.from,
339
+ },
340
+ limits: spec.limits,
341
+ permissions: spec.permissions,
342
+ nodes: Object.fromEntries(spec.nodes.map((node) => [
343
+ node.id,
344
+ {
345
+ workflowNodeId: node.id,
346
+ workflowNodeType: node.type,
347
+ dagTaskIds: [node.id],
348
+ commandAllowlist: node.type === "command" ? spec.permissions.shell.commands : undefined,
349
+ outputSchema: node.outputSchema,
350
+ dynamic: node.type === "map_agent" ||
351
+ node.type === "verify_agent" ||
352
+ node.type === "condition" ||
353
+ node.type === "loop_until"
354
+ ? true
355
+ : undefined,
356
+ },
357
+ ])),
358
+ };
359
+ return { dag, manifest };
360
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,5 @@
1
+ export function formatWorkflowValidationIssues(issues) {
2
+ return issues
3
+ .map((issue) => `${issue.type} at ${issue.path}: ${issue.message}`)
4
+ .join("; ");
5
+ }
@@ -0,0 +1,7 @@
1
+ export * from "./spec.js";
2
+ export * from "./errors.js";
3
+ export * from "./validate.js";
4
+ export * from "./compileTypes.js";
5
+ export * from "./compile.js";
6
+ export * from "./artifacts.js";
7
+ export * from "./profiles.js";
@@ -0,0 +1,156 @@
1
+ export const WORKFLOW_PLAN_PROFILES = [
2
+ "pr-review",
3
+ ];
4
+ function buildDiscoverFilesOutput(files) {
5
+ return JSON.stringify({
6
+ files: files.map((file, index) => ({
7
+ path: file,
8
+ findingId: `finding-${index + 1}`,
9
+ expectedStatus: "verified",
10
+ expectedReason: `review finding for ${file} is present in the mapped artifact`,
11
+ })),
12
+ });
13
+ }
14
+ export function buildPrReviewWorkflowSpec(options) {
15
+ const base = options.base ?? "main";
16
+ const head = options.head ?? "HEAD";
17
+ const files = options.changedFiles && options.changedFiles.length > 0
18
+ ? options.changedFiles
19
+ : ["README.md"];
20
+ return {
21
+ version: "0.1",
22
+ kind: "dynamic-agent-workflow",
23
+ meta: {
24
+ name: "pr-review",
25
+ description: `Read-only PR review workflow for task ${options.taskId}.`,
26
+ riskLevel: "low",
27
+ },
28
+ inputs: {
29
+ taskId: options.taskId,
30
+ base,
31
+ head,
32
+ },
33
+ limits: {
34
+ maxConcurrency: 4,
35
+ maxAgentRuns: 50,
36
+ maxExpandedNodes: 50,
37
+ maxRetriesPerNode: 0,
38
+ maxIterations: 3,
39
+ maxWallClockMinutes: 15,
40
+ },
41
+ permissions: {
42
+ filesystem: "read_only",
43
+ shell: {
44
+ mode: "deny",
45
+ commands: [],
46
+ },
47
+ network: "deny",
48
+ edits: "deny",
49
+ },
50
+ nodes: [
51
+ {
52
+ id: "discover-changed-files",
53
+ type: "artifact_transform",
54
+ dependsOn: [],
55
+ transform: {
56
+ type: "markdown_template",
57
+ template: buildDiscoverFilesOutput(files),
58
+ },
59
+ outputSchema: {
60
+ type: "object",
61
+ required: ["files"],
62
+ properties: {
63
+ files: { type: "array" },
64
+ },
65
+ },
66
+ },
67
+ {
68
+ id: "review-each-file",
69
+ type: "map_agent",
70
+ dependsOn: ["discover-changed-files"],
71
+ itemsFrom: "$.nodes['discover-changed-files'].output.files",
72
+ itemName: "file",
73
+ executor: "static",
74
+ role: "reviewer",
75
+ prompt: "Review {{file.path}} and emit one structured finding.",
76
+ staticResultTemplate: "{\"id\":\"{{file.findingId}}\",\"sourceNodeId\":\"review-each-file\",\"targetRef\":\"{{file.path}}\",\"title\":\"Review finding for {{file.path}}\",\"severity\":\"medium\",\"evidence\":[{\"path\":\"{{file.path}}\",\"summary\":\"deterministic profile finding\"}],\"recommendation\":\"Inspect {{file.path}} before merging\",\"expectedStatus\":\"{{file.expectedStatus}}\",\"expectedReason\":\"{{file.expectedReason}}\"}",
77
+ outputSchema: {
78
+ type: "object",
79
+ required: [
80
+ "id",
81
+ "sourceNodeId",
82
+ "targetRef",
83
+ "title",
84
+ "severity",
85
+ "evidence",
86
+ "recommendation",
87
+ ],
88
+ properties: {
89
+ id: { type: "string" },
90
+ sourceNodeId: { type: "string" },
91
+ targetRef: { type: "string" },
92
+ title: { type: "string" },
93
+ severity: { type: "string" },
94
+ evidence: { type: "array" },
95
+ recommendation: { type: "string" },
96
+ },
97
+ },
98
+ },
99
+ {
100
+ id: "verify-findings",
101
+ type: "verify_agent",
102
+ dependsOn: ["review-each-file"],
103
+ producerNodeId: "review-each-file",
104
+ itemsFrom: "$.nodes['review-each-file'].output.children",
105
+ itemName: "finding",
106
+ executor: "static",
107
+ role: "adversarial-verifier",
108
+ prompt: "Independently verify {{finding.output.id}} using only the finding artifact.",
109
+ staticResultTemplate: "{\"findingId\":\"{{finding.output.id}}\",\"verifierNodeId\":\"verify-findings\",\"status\":\"{{finding.output.expectedStatus}}\",\"reason\":\"{{finding.output.expectedReason}}\",\"evidence\":[{\"path\":\"{{finding.output.targetRef}}\",\"summary\":\"independent verifier checked mapped finding\"}]}",
110
+ outputSchema: {
111
+ type: "object",
112
+ required: ["findingId", "verifierNodeId", "status", "reason", "evidence"],
113
+ properties: {
114
+ findingId: { type: "string" },
115
+ verifierNodeId: { type: "string" },
116
+ status: { type: "string" },
117
+ reason: { type: "string" },
118
+ evidence: { type: "array" },
119
+ },
120
+ },
121
+ },
122
+ {
123
+ id: "final-ranked-summary",
124
+ type: "reduce_agent",
125
+ dependsOn: ["verify-findings"],
126
+ role: "closeout",
127
+ prompt: "Create a verified-first PR review report.",
128
+ reducer: {
129
+ type: "verified_findings_report",
130
+ findingsFrom: "$.nodes['review-each-file'].output.children",
131
+ verificationsFrom: "$.nodes['verify-findings'].output.children",
132
+ },
133
+ outputSchema: {
134
+ type: "object",
135
+ required: ["summary", "verifiedFindings", "uncertainFindings", "evidenceRefs"],
136
+ properties: {
137
+ summary: { type: "string" },
138
+ verifiedFindings: { type: "array" },
139
+ uncertainFindings: { type: "array" },
140
+ refutedFindingsRef: { type: "string" },
141
+ evidenceRefs: { type: "array" },
142
+ },
143
+ },
144
+ },
145
+ ],
146
+ final: {
147
+ from: "final-ranked-summary",
148
+ },
149
+ };
150
+ }
151
+ export function buildWorkflowSpecForProfile(input) {
152
+ if (input.profile === "pr-review") {
153
+ return buildPrReviewWorkflowSpec(input);
154
+ }
155
+ throw new Error(`unsupported workflow profile: ${input.profile}`);
156
+ }
@@ -0,0 +1,114 @@
1
+ import { z } from "zod";
2
+ export const WORKFLOW_NODE_ID_PATTERN = /^[a-z][a-z0-9-]*$/;
3
+ export const workflowVersionSchema = z.literal("0.1");
4
+ export const workflowKindSchema = z.literal("dynamic-agent-workflow");
5
+ export const workflowRiskLevelSchema = z.enum(["low", "medium", "high", "critical"]);
6
+ export const workflowNodeTypeSchema = z.enum([
7
+ "agent",
8
+ "map_agent",
9
+ "verify_agent",
10
+ "reduce_agent",
11
+ "command",
12
+ "condition",
13
+ "loop_until",
14
+ "human_gate",
15
+ "artifact_transform",
16
+ ]);
17
+ export const workflowLimitsSchema = z
18
+ .object({
19
+ maxConcurrency: z.number().int().positive().max(32).default(8),
20
+ maxAgentRuns: z.number().int().positive().max(500).default(100),
21
+ maxExpandedNodes: z.number().int().positive().max(1000).default(200),
22
+ maxRetriesPerNode: z.number().int().nonnegative().max(5).default(1),
23
+ maxIterations: z.number().int().positive().max(20).default(3),
24
+ maxWallClockMinutes: z.number().int().positive().max(24 * 60).default(30),
25
+ })
26
+ .default({});
27
+ export const workflowPermissionsSchema = z
28
+ .object({
29
+ filesystem: z.enum(["read_only", "workspace"]).default("read_only"),
30
+ shell: z
31
+ .object({
32
+ mode: z.enum(["deny", "allowlist"]).default("deny"),
33
+ commands: z.array(z.string().min(1)).default([]),
34
+ })
35
+ .default({}),
36
+ network: z.enum(["deny", "allow"]).default("deny"),
37
+ edits: z.enum(["deny", "allow"]).default("deny"),
38
+ allowedPaths: z.array(z.string().min(1)).optional(),
39
+ forbiddenPaths: z.array(z.string().min(1)).optional(),
40
+ writeSet: z.array(z.string().min(1)).optional(),
41
+ })
42
+ .default({});
43
+ export const workflowNodeSchema = z.object({
44
+ id: z.string().regex(WORKFLOW_NODE_ID_PATTERN, "node id must be kebab-case"),
45
+ type: workflowNodeTypeSchema,
46
+ dependsOn: z.array(z.string()).default([]),
47
+ executor: z.enum(["pi", "static"]).optional(),
48
+ role: z.string().optional(),
49
+ prompt: z.string().optional(),
50
+ staticResultTemplate: z.string().optional(),
51
+ writePolicy: z.enum(["none", "read-only", "exclusive"]).optional(),
52
+ outputSchema: z.unknown().optional(),
53
+ command: z.string().optional(),
54
+ itemsFrom: z.string().optional(),
55
+ items: z.array(z.unknown()).optional(),
56
+ itemName: z.string().optional(),
57
+ producerNodeId: z.string().optional(),
58
+ maxItems: z.number().int().positive().optional(),
59
+ concurrency: z.number().int().positive().optional(),
60
+ workspaceTemplate: z.string().min(1).optional(),
61
+ reason: z.string().optional(),
62
+ approvalSchema: z.unknown().optional(),
63
+ cases: z
64
+ .array(z.object({
65
+ when: z.string().min(1),
66
+ then: z.string().min(1),
67
+ }))
68
+ .optional(),
69
+ default: z.string().optional(),
70
+ maxIterations: z.number().int().positive().optional(),
71
+ stopWhen: z.unknown().optional(),
72
+ body: z.array(z.unknown()).optional(),
73
+ transform: z
74
+ .object({
75
+ type: z.literal("markdown_template"),
76
+ template: z.string().min(1),
77
+ })
78
+ .optional(),
79
+ reducer: z
80
+ .object({
81
+ type: z.literal("verified_findings_report"),
82
+ findingsFrom: z.string().min(1),
83
+ verificationsFrom: z.string().min(1),
84
+ })
85
+ .optional(),
86
+ });
87
+ export const workflowSpecSchema = z.object({
88
+ version: workflowVersionSchema,
89
+ kind: workflowKindSchema,
90
+ meta: z
91
+ .object({
92
+ name: z.string().min(1),
93
+ description: z.string().optional(),
94
+ riskLevel: workflowRiskLevelSchema.optional().default("low"),
95
+ })
96
+ .default({ name: "unnamed-workflow", riskLevel: "low" }),
97
+ inputs: z.record(z.string(), z.unknown()).default({}),
98
+ limits: workflowLimitsSchema,
99
+ permissions: workflowPermissionsSchema,
100
+ nodes: z.array(workflowNodeSchema).min(1),
101
+ final: z.object({
102
+ from: z.string().min(1),
103
+ }),
104
+ });
105
+ export const AGENT_LIKE_WORKFLOW_NODE_TYPES = new Set([
106
+ "agent",
107
+ "map_agent",
108
+ "verify_agent",
109
+ "reduce_agent",
110
+ ]);
111
+ export const ITEM_SELECTOR_WORKFLOW_NODE_TYPES = new Set([
112
+ "map_agent",
113
+ "verify_agent",
114
+ ]);