@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,322 @@
1
+ import { mkdir, writeFile } from "node:fs/promises";
2
+ import path from "node:path";
3
+ import { executePiStep, } from "./pi-executor.js";
4
+ import { redactPromptForLog, truncateOutput } from "./cursor-executor.js";
5
+ import { readGitStatusPorcelain, runPostRunWriteGuard, } from "./shell-write-guard.js";
6
+ export const DAG_PI_READONLY_TOOLS = ["read", "grep", "find", "ls"];
7
+ export const DAG_PI_WRITE_TOOLS = [
8
+ "read",
9
+ "bash",
10
+ "edit",
11
+ "write",
12
+ "grep",
13
+ "find",
14
+ "ls",
15
+ ];
16
+ export const DEFAULT_DAG_PI_PROVIDER = "wizard-local";
17
+ export const DAG_PI_MODEL_PROVIDERS = {
18
+ "gpt-5.3-codex-spark": "wizard-local",
19
+ "gpt-5.5": "wizard-local",
20
+ "glm-5.2": "wizard-local",
21
+ };
22
+ const SAFE_PI_STEPS = new Set([
23
+ "analyze",
24
+ "plan",
25
+ "verify",
26
+ "retrospective",
27
+ "spec",
28
+ ]);
29
+ const PI_WRITE_STEPS = new Set([...SAFE_PI_STEPS, "implement"]);
30
+ const ROLE_TO_PERSONA = {
31
+ planner: "planner",
32
+ scout: "scout",
33
+ implementer: "planner",
34
+ reviewer: "reviewer",
35
+ supervisor: "supervisor",
36
+ verifier: "verifier",
37
+ closeout: "closeout",
38
+ };
39
+ const PI_STEP_PERSONA_ALIASES = {
40
+ analyze: "scout",
41
+ plan: "planner",
42
+ verify: "verifier",
43
+ retrospective: "closeout",
44
+ spec: "planner",
45
+ generic: "planner",
46
+ scout: "scout",
47
+ planner: "planner",
48
+ reviewer: "reviewer",
49
+ supervisor: "supervisor",
50
+ verifier: "verifier",
51
+ closeout: "closeout",
52
+ };
53
+ const PERSONA_TO_EXECUTOR_STEP = {
54
+ closeout: "retrospective",
55
+ planner: "plan",
56
+ reviewer: "analyze",
57
+ scout: "analyze",
58
+ supervisor: "analyze",
59
+ verifier: "verify",
60
+ };
61
+ const PI_WRITE_ROLE_TO_STEP = {
62
+ planner: "plan",
63
+ scout: "analyze",
64
+ implementer: "implement",
65
+ reviewer: "analyze",
66
+ supervisor: "analyze",
67
+ verifier: "verify",
68
+ closeout: "retrospective",
69
+ };
70
+ const PI_WRITE_STEP_ALIASES = {
71
+ analyze: "analyze",
72
+ plan: "plan",
73
+ verify: "verify",
74
+ retrospective: "retrospective",
75
+ spec: "spec",
76
+ generic: "plan",
77
+ scout: "analyze",
78
+ planner: "plan",
79
+ reviewer: "analyze",
80
+ supervisor: "analyze",
81
+ verifier: "verify",
82
+ closeout: "retrospective",
83
+ implement: "implement",
84
+ implementer: "implement",
85
+ };
86
+ /** Map DAG `piStep` / `role` to a DAG persona for read-only Pi nodes. */
87
+ export function resolveDagPiPersona(task) {
88
+ if (task.piStep) {
89
+ const mapped = PI_STEP_PERSONA_ALIASES[task.piStep.trim().toLowerCase()];
90
+ if (mapped)
91
+ return mapped;
92
+ }
93
+ if (task.role) {
94
+ return ROLE_TO_PERSONA[task.role];
95
+ }
96
+ return "planner";
97
+ }
98
+ function resolvePiExecutorStep(persona) {
99
+ return PERSONA_TO_EXECUTOR_STEP[persona];
100
+ }
101
+ function isDagPiWriteTask(task) {
102
+ return task.executor === "pi" && task.toolProfile === "write";
103
+ }
104
+ /** Map DAG `piStep` / `role` to a safe workflow step for read-only Pi or bounded write. */
105
+ export function resolveDagPiStepName(task) {
106
+ if (isDagPiWriteTask(task)) {
107
+ if (task.piStep) {
108
+ const mapped = PI_WRITE_STEP_ALIASES[task.piStep.trim().toLowerCase()];
109
+ if (mapped)
110
+ return mapped;
111
+ }
112
+ if (task.role) {
113
+ return PI_WRITE_ROLE_TO_STEP[task.role];
114
+ }
115
+ return "implement";
116
+ }
117
+ return resolvePiExecutorStep(resolveDagPiPersona(task));
118
+ }
119
+ /** Pi DAG nodes are read-only unless they explicitly opt into the bounded write tool profile. */
120
+ export function resolveDagPiToolNames(task) {
121
+ if (isDagPiWriteTask(task)) {
122
+ return [...DAG_PI_WRITE_TOOLS];
123
+ }
124
+ return [...DAG_PI_READONLY_TOOLS];
125
+ }
126
+ export function buildDagPiUserMessage(task, persona, step) {
127
+ const role = task.role ?? "unspecified";
128
+ const writePolicy = task.writePolicy ?? "read-only (default)";
129
+ if (isDagPiWriteTask(task)) {
130
+ return [
131
+ `You are executing hybrid DAG node "${task.id}" (role=${role}, piStep=${step}, writePolicy=${writePolicy}).`,
132
+ "The system prompt contains the full DAG envelope: objective, constraints, upstream context, and task.",
133
+ "Use write tools only for the bounded implementation requested by this node.",
134
+ `Allowed paths: ${(task.allowedPaths ?? []).join(", ") || "(none declared)"}.`,
135
+ `Write set: ${(task.writeSet ?? []).join(", ") || "(none declared)"}.`,
136
+ `Forbidden paths: ${(task.forbiddenPaths ?? []).join(", ") || "(none declared)"}.`,
137
+ "Preserve unrelated files. Do not edit outside allowedPaths/writeSet. Never write forbiddenPaths, .harness/dag-runs/**, or undeclared root artifacts/**.",
138
+ "Return concise Markdown with changed files, verification performed or still required, and residual risks.",
139
+ "Do not wrap the output in code fences and do not add conversational preamble.",
140
+ ].join(" ");
141
+ }
142
+ return [
143
+ `You are executing hybrid DAG node "${task.id}" (role=${role}, piPersona=${persona}, piStep=${step}, writePolicy=${writePolicy}).`,
144
+ "The system prompt contains the full DAG envelope: objective, constraints, upstream context, and task.",
145
+ "Use read-only tools only. Do not edit, write, or commit repository files.",
146
+ "Return your conclusion as plain Markdown text suitable for downstream DAG nodes.",
147
+ "Do not wrap the output in code fences and do not add conversational preamble.",
148
+ ].join(" ");
149
+ }
150
+ function resolveDagPiModelConfig(model) {
151
+ return { provider: DAG_PI_MODEL_PROVIDERS[model] ?? DEFAULT_DAG_PI_PROVIDER, model };
152
+ }
153
+ const SUMMARY_STDOUT_MAX = 4_000;
154
+ const SUMMARY_STDERR_MAX = 2_000;
155
+ export function buildPiPromptRedactedMarkdown(prompt) {
156
+ const redacted = redactPromptForLog(prompt);
157
+ return [
158
+ "# Pi prompt (redacted)",
159
+ "",
160
+ `- ${redacted}`,
161
+ "",
162
+ "Full prompt text is not stored. See node `result.summary.md` for execution metadata.",
163
+ "",
164
+ ].join("\n");
165
+ }
166
+ export function buildPiResultSummaryMarkdown(input) {
167
+ const stdout = truncateOutput(input.result.stdout, SUMMARY_STDOUT_MAX);
168
+ const stderr = truncateOutput(input.result.stderr || "", SUMMARY_STDERR_MAX);
169
+ const lines = [
170
+ "# Pi execution summary",
171
+ "",
172
+ "## Run metadata",
173
+ "",
174
+ "| Field | Value |",
175
+ "|-------|-------|",
176
+ `| channel | ${input.context.channel} |`,
177
+ `| dagRunId | ${input.context.dagRunId} |`,
178
+ `| dagNodeId | ${input.context.dagNodeId} |`,
179
+ `| cwd | ${input.context.cwd} |`,
180
+ `| model | ${input.model} |`,
181
+ `| piPersona | ${input.persona} |`,
182
+ `| piStep | ${input.step} |`,
183
+ `| ok | ${input.result.ok} |`,
184
+ `| failureCategory | ${input.result.failureCategory} |`,
185
+ `| backend | ${input.result.backend ?? "(unknown)"} |`,
186
+ `| sdkAttempted | ${input.result.sdkAttempted ?? false} |`,
187
+ `| durationMs | ${input.result.durationMs} |`,
188
+ `| tokensUsed | ${input.result.tokensUsed} |`,
189
+ `| parsedEvents | ${input.result.parsedEvents} |`,
190
+ "",
191
+ "## Prompt reference",
192
+ "",
193
+ `- ${redactPromptForLog(input.prompt)}`,
194
+ "",
195
+ "## Assistant output",
196
+ "",
197
+ ];
198
+ if (input.result.assistantText.trim()) {
199
+ lines.push("```markdown", input.result.assistantText, "```", "");
200
+ }
201
+ if (stdout.text.trim()) {
202
+ lines.push("### raw stdout", "", "```text", stdout.text, "```", "");
203
+ if (stdout.truncated)
204
+ lines.push("_(stdout truncated)_", "");
205
+ }
206
+ if (stderr.text.trim()) {
207
+ lines.push("### stderr", "", "```text", stderr.text, "```", "");
208
+ if (stderr.truncated)
209
+ lines.push("_(stderr truncated)_", "");
210
+ }
211
+ return lines.join("\n");
212
+ }
213
+ export async function writePiExecutorArtifacts(artifactsDir, input) {
214
+ await mkdir(artifactsDir, { recursive: true });
215
+ const promptPath = path.join(artifactsDir, "prompt.redacted.md");
216
+ const summaryPath = path.join(artifactsDir, "result.summary.md");
217
+ await writeFile(promptPath, buildPiPromptRedactedMarkdown(input.prompt), "utf-8");
218
+ await writeFile(summaryPath, buildPiResultSummaryMarkdown(input), "utf-8");
219
+ return { promptPath, summaryPath };
220
+ }
221
+ export async function executeDagPiNode(input, meta, piStepFn = executePiStep) {
222
+ const started = Date.now();
223
+ const persona = resolveDagPiPersona(input.task);
224
+ const step = resolveDagPiStepName(input.task);
225
+ const isWriteTask = isDagPiWriteTask(input.task);
226
+ const allowedSteps = isWriteTask ? PI_WRITE_STEPS : SAFE_PI_STEPS;
227
+ if (!allowedSteps.has(step)) {
228
+ return {
229
+ ok: false,
230
+ stdout: "",
231
+ stderr: `unsafe pi step mapping: ${step}`,
232
+ failureCategory: "invalid-output",
233
+ durationMs: 0,
234
+ };
235
+ }
236
+ let beforeStatus;
237
+ if (isWriteTask) {
238
+ try {
239
+ beforeStatus = await readGitStatusPorcelain(input.cwd);
240
+ }
241
+ catch {
242
+ beforeStatus = undefined;
243
+ }
244
+ }
245
+ const result = await piStepFn({
246
+ attachedFiles: [],
247
+ modelConfig: resolveDagPiModelConfig(input.model),
248
+ prompt: input.prompt,
249
+ repoRoot: input.cwd,
250
+ step,
251
+ toolNames: resolveDagPiToolNames(input.task),
252
+ userMessage: buildDagPiUserMessage(input.task, persona, step),
253
+ });
254
+ const context = {
255
+ channel: "dag",
256
+ cwd: input.cwd,
257
+ dagRunId: meta.runId,
258
+ dagNodeId: input.task.id,
259
+ };
260
+ await writePiExecutorArtifacts(path.join(meta.runDir, input.task.id), {
261
+ prompt: input.prompt,
262
+ model: input.model,
263
+ context,
264
+ result,
265
+ persona,
266
+ step,
267
+ });
268
+ const mapped = mapPiResultToDagNodeResult(result);
269
+ if (!isWriteTask) {
270
+ return mapped;
271
+ }
272
+ let writeGuardOk = true;
273
+ let writeGuardViolations = [];
274
+ if (beforeStatus !== undefined) {
275
+ try {
276
+ const guard = await runPostRunWriteGuard({
277
+ rootCwd: input.cwd,
278
+ beforeStatus,
279
+ writePolicy: input.task.writePolicy,
280
+ writeSet: input.task.writeSet,
281
+ allowedPaths: input.task.allowedPaths,
282
+ forbiddenPaths: input.task.forbiddenPaths,
283
+ concurrentSiblingWriteSets: meta.concurrentSiblingWriteSets,
284
+ });
285
+ writeGuardOk = guard.ok;
286
+ writeGuardViolations = guard.violations;
287
+ }
288
+ catch {
289
+ writeGuardOk = false;
290
+ writeGuardViolations = ["write guard failed: git status unavailable"];
291
+ }
292
+ }
293
+ if (writeGuardOk) {
294
+ return mapped;
295
+ }
296
+ const stderrParts = [mapped.stderr];
297
+ stderrParts.push(`write guard failed: ${writeGuardViolations.join(", ")}`);
298
+ if (meta.writeGuardAttribution === "best-effort") {
299
+ stderrParts.push("write guard note: concurrent rank writers use best-effort per-node attribution; keep same-rank writeSet entries disjoint");
300
+ }
301
+ return {
302
+ ...mapped,
303
+ ok: false,
304
+ stderr: stderrParts.filter(Boolean).join("\n\n"),
305
+ failureCategory: mapped.ok ? "write-guard" : mapped.failureCategory,
306
+ durationMs: mapped.durationMs || Date.now() - started,
307
+ };
308
+ }
309
+ export function mapPiResultToDagNodeResult(result) {
310
+ return {
311
+ ok: result.ok,
312
+ stdout: result.assistantText || result.stdout,
313
+ stderr: result.stderr,
314
+ failureCategory: result.failureCategory,
315
+ durationMs: result.durationMs,
316
+ assistantText: result.assistantText,
317
+ backend: result.backend,
318
+ sdkAttempted: result.sdkAttempted,
319
+ tokensUsed: result.tokensUsed,
320
+ parsedEvents: result.parsedEvents,
321
+ };
322
+ }
@@ -0,0 +1,45 @@
1
+ import { mkdir, writeFile } from "node:fs/promises";
2
+ import path from "node:path";
3
+ export function buildStaticResultSummaryMarkdown(input) {
4
+ const lines = [
5
+ "# Static execution summary",
6
+ "",
7
+ "## Run metadata",
8
+ "",
9
+ "| Field | Value |",
10
+ "|-------|-------|",
11
+ `| dagRunId | ${input.runId} |`,
12
+ `| dagNodeId | ${input.nodeId} |`,
13
+ `| status | ${input.status} |`,
14
+ "",
15
+ "## Result",
16
+ "",
17
+ input.resultMarkdown.trim(),
18
+ "",
19
+ ];
20
+ return `${lines.join("\n")}\n`;
21
+ }
22
+ export async function executeDagStaticNode(input, meta) {
23
+ const staticConfig = input.task.static;
24
+ if (!staticConfig) {
25
+ throw new Error(`static task ${input.task.id} requires static config`);
26
+ }
27
+ const status = staticConfig.status ?? "success";
28
+ const started = Date.now();
29
+ const nodeDir = path.join(meta.runDir, input.task.id);
30
+ await mkdir(nodeDir, { recursive: true });
31
+ const summary = buildStaticResultSummaryMarkdown({
32
+ nodeId: input.task.id,
33
+ runId: meta.runId,
34
+ resultMarkdown: staticConfig.resultMarkdown,
35
+ status,
36
+ });
37
+ await writeFile(path.join(nodeDir, "result.summary.md"), summary, "utf-8");
38
+ return {
39
+ ok: status === "success",
40
+ stdout: staticConfig.resultMarkdown,
41
+ stderr: status === "error" ? staticConfig.resultMarkdown : "",
42
+ failureCategory: status === "error" ? "static-error" : "success",
43
+ durationMs: Date.now() - started,
44
+ };
45
+ }
@@ -0,0 +1,4 @@
1
+ export * from "./dag-cursor-executor.js";
2
+ export * from "../workflows/dag/executor-registry.js";
3
+ export * from "./dag-pi-executor.js";
4
+ export * from "./dag-static-executor.js";
@@ -0,0 +1,8 @@
1
+ export * from "./pi.js";
2
+ export * from "./pi-sdk.js";
3
+ export * from "./cursor.js";
4
+ export * from "./cursor-worker.js";
5
+ export * from "./shell.js";
6
+ export * from "./static.js";
7
+ export * from "./dag.js";
8
+ export * from "./config.js";
@@ -0,0 +1,60 @@
1
+ export const DEFAULT_DAG_CURSOR_MODEL = "composer-2.5";
2
+ export const DEFAULT_DAG_MODELS = {
3
+ HIGH: "gpt-5.5",
4
+ MED: "composer-2.5",
5
+ LOW: "composer-2.5",
6
+ };
7
+ export function resolveModelSelection(manifest, taskConfig, step, options) {
8
+ const retryAttempt = options?.retryAttempt ?? 0;
9
+ if (Object.keys(manifest.modelProfiles ?? {}).length === 0) {
10
+ return {
11
+ modelConfig: manifest.models?.[step],
12
+ };
13
+ }
14
+ const profileName = resolveProfileName(manifest, taskConfig.complexity, step, retryAttempt);
15
+ const profile = profileName ? manifest.modelProfiles?.[profileName] : undefined;
16
+ if (!profile) {
17
+ return {
18
+ modelConfig: manifest.models?.[step],
19
+ profileName,
20
+ };
21
+ }
22
+ return {
23
+ modelConfig: {
24
+ provider: profile.provider,
25
+ model: profile.model,
26
+ thinking: profile.thinking,
27
+ timeoutMs: profile.timeoutMs ?? manifest.models?.[step]?.timeoutMs,
28
+ fallback: profile.fallback,
29
+ },
30
+ profileName,
31
+ };
32
+ }
33
+ function resolveProfileName(manifest, complexity, step, retryAttempt) {
34
+ if (step === 'implement' && retryAttempt > 0 && manifest.modelRouting?.implementRetry) {
35
+ return manifest.modelRouting.implementRetry;
36
+ }
37
+ const route = manifest.modelRouting?.[step];
38
+ if (!route) {
39
+ return undefined;
40
+ }
41
+ return route[complexity];
42
+ }
43
+ export function formatModelSelectionLabel(modelConfig, profileName) {
44
+ if (!modelConfig) {
45
+ return profileName ? `${profileName}` : 'default';
46
+ }
47
+ const base = modelConfig.provider && modelConfig.model
48
+ ? `${modelConfig.provider}/${modelConfig.model}`
49
+ : modelConfig.model ?? 'default';
50
+ return profileName ? `${profileName}:${base}` : base;
51
+ }
52
+ export function formatFallbackLabel(profile) {
53
+ if (!profile?.fallback) {
54
+ return undefined;
55
+ }
56
+ const fallback = profile.fallback;
57
+ return fallback.provider && fallback.model
58
+ ? `${fallback.provider}/${fallback.model}`
59
+ : fallback.model;
60
+ }
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Normalize Pi SDK session events into CLI-compatible JSONL lines
3
+ * so existing stdout extractors can be reused without modification.
4
+ */
5
+ function isRecord(value) {
6
+ return typeof value === 'object' && value !== null;
7
+ }
8
+ /** Map SDK event shapes to the JSONL format emitted by `pi -p --mode json`. */
9
+ export function normalizePiSessionEvent(event) {
10
+ const type = typeof event.type === 'string' ? event.type : undefined;
11
+ if (type === 'assistant_message' && isRecord(event.message)) {
12
+ return {
13
+ type: 'turn_end',
14
+ message: event.message,
15
+ usage: event.usage,
16
+ };
17
+ }
18
+ if (type === 'tool_start' && typeof event.toolName === 'string') {
19
+ return {
20
+ type: 'tool_execution_start',
21
+ toolName: event.toolName,
22
+ toolCallId: event.toolCallId,
23
+ input: event.input,
24
+ };
25
+ }
26
+ if (type === 'tool_end' && typeof event.toolName === 'string') {
27
+ return {
28
+ type: 'tool_execution_end',
29
+ toolName: event.toolName,
30
+ toolCallId: event.toolCallId,
31
+ isError: event.isError,
32
+ };
33
+ }
34
+ return event;
35
+ }
36
+ /** Serialize one SDK session event as a single JSONL line. */
37
+ export function serializeSessionEvent(event) {
38
+ return JSON.stringify(normalizePiSessionEvent(event));
39
+ }
40
+ /** Serialize multiple events into newline-delimited JSON (no trailing newline). */
41
+ export function serializeSessionEvents(events) {
42
+ return events.map(serializeSessionEvent).join('\n');
43
+ }