@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,226 @@
1
+ export const DAG_RECOVERY_ACTIONS = [
2
+ "none",
3
+ "monitor",
4
+ "retry-node",
5
+ "rerun-after-fix",
6
+ "resume-or-reject",
7
+ "manual-review",
8
+ "inspect-upstream",
9
+ "unknown",
10
+ ];
11
+ const REPORT_DERIVED_ONLY = "Derived planning hint only; dag report does not execute retry or resume.";
12
+ const TRANSIENT_EXECUTOR_RAW = new Set([
13
+ "network",
14
+ "quota",
15
+ "rate-limit",
16
+ "unavailable",
17
+ ]);
18
+ function isInProgressStatus(status) {
19
+ return (status === "PENDING" ||
20
+ status === "RUNNING" ||
21
+ status === "pending" ||
22
+ status === "running");
23
+ }
24
+ function isPausedRunStatus(status) {
25
+ return status === "paused";
26
+ }
27
+ function executorAction(raw) {
28
+ if (raw !== undefined && TRANSIENT_EXECUTOR_RAW.has(raw)) {
29
+ return "retry-node";
30
+ }
31
+ return "rerun-after-fix";
32
+ }
33
+ function writeGuardAction(raw) {
34
+ if (raw === "write-guard") {
35
+ return "manual-review";
36
+ }
37
+ return "manual-review";
38
+ }
39
+ function isPausedContext(status, lifecycle, runStatus) {
40
+ return (isPausedRunStatus(status) ||
41
+ runStatus === "paused" ||
42
+ lifecycle === "paused");
43
+ }
44
+ function decisionEnvelopeAction(status, lifecycle, runStatus) {
45
+ if (isPausedContext(status, lifecycle, runStatus)) {
46
+ return "resume-or-reject";
47
+ }
48
+ return "manual-review";
49
+ }
50
+ function planByNormalizedCategory(input) {
51
+ const { status, normalizedFailureCategory: category, failureCategory: raw, nodeId, runId, lifecycle, runStatus, } = input;
52
+ const target = nodeId ?? runId ?? "run";
53
+ switch (category) {
54
+ case "success":
55
+ return {
56
+ action: "none",
57
+ summary: "No recovery needed.",
58
+ reason: `${target} finished successfully.`,
59
+ humanRequired: false,
60
+ autoRetryEligible: false,
61
+ };
62
+ case "skipped":
63
+ return {
64
+ action: "inspect-upstream",
65
+ summary: "Inspect upstream failures before retrying downstream nodes.",
66
+ reason: `${target} was skipped because an upstream dependency failed or was rejected.`,
67
+ humanRequired: false,
68
+ autoRetryEligible: false,
69
+ commandHint: "Resolve upstream ERROR/SKIPPED nodes first; do not auto-retry skipped nodes until upstream is fixed.",
70
+ };
71
+ case "timeout":
72
+ return {
73
+ action: "retry-node",
74
+ summary: `Retry ${target} after confirming the timeout was transient.`,
75
+ reason: `${target} exceeded its execution time limit${raw ? ` (${raw})` : ""}.`,
76
+ humanRequired: false,
77
+ autoRetryEligible: true,
78
+ commandHint: `Investigate logs/artifacts, then re-run ${target}. ${REPORT_DERIVED_ONLY}`,
79
+ };
80
+ case "executor":
81
+ return {
82
+ action: executorAction(raw),
83
+ summary: executorAction(raw) === "retry-node"
84
+ ? `Retry ${target} if the executor/backend error looks transient.`
85
+ : `Fix executor/backend conditions, then re-run ${target}.`,
86
+ reason: `${target} failed in the executor layer${raw ? ` (${raw})` : ""}.`,
87
+ humanRequired: false,
88
+ autoRetryEligible: executorAction(raw) === "retry-node",
89
+ commandHint: executorAction(raw) === "retry-node"
90
+ ? `Retry ${target} once transient network/quota/rate-limit issues clear. ${REPORT_DERIVED_ONLY}`
91
+ : `Inspect backend logs and node artifacts before re-running ${target}. ${REPORT_DERIVED_ONLY}`,
92
+ };
93
+ case "auth":
94
+ return {
95
+ action: "rerun-after-fix",
96
+ summary: "Fix credentials/API keys, then re-run the affected node or DAG.",
97
+ reason: `${target} failed due to missing or invalid auth${raw ? ` (${raw})` : ""}.`,
98
+ humanRequired: true,
99
+ autoRetryEligible: false,
100
+ commandHint: "Update API keys/credentials in the environment or repo config, then re-run.",
101
+ };
102
+ case "write-guard":
103
+ return {
104
+ action: writeGuardAction(raw),
105
+ summary: "Review writeSet/writePolicy and approve or adjust writes before re-run.",
106
+ reason: `${target} was blocked by the write guard${raw ? ` (${raw})` : ""}.`,
107
+ humanRequired: true,
108
+ autoRetryEligible: false,
109
+ commandHint: "Inspect writeSet/writePolicy, node prompt, and result.summary.md; revise policy or scope before re-run.",
110
+ };
111
+ case "validation":
112
+ return {
113
+ action: "rerun-after-fix",
114
+ summary: "Fix schema/output/test validation issues, then re-run the node.",
115
+ reason: `${target} produced invalid or unverifiable output${raw ? ` (${raw})` : ""}.`,
116
+ humanRequired: false,
117
+ autoRetryEligible: false,
118
+ commandHint: "Inspect schema/output/tests and node artifacts; correct the underlying issue before re-run.",
119
+ };
120
+ case "shell-command":
121
+ return {
122
+ action: "rerun-after-fix",
123
+ summary: "Inspect shell output/result.summary.md, fix the command, then re-run.",
124
+ reason: `${target} shell command failed${raw ? ` (${raw})` : ""}.`,
125
+ humanRequired: false,
126
+ autoRetryEligible: false,
127
+ commandHint: "Review shell stdout/stderr and result.summary.md; adjust shell config or repo state before re-run.",
128
+ };
129
+ case "static-error":
130
+ return {
131
+ action: "rerun-after-fix",
132
+ summary: "Inspect static executor config/output, then re-run the node.",
133
+ reason: `${target} static executor reported an error${raw ? ` (${raw})` : ""}.`,
134
+ humanRequired: false,
135
+ autoRetryEligible: false,
136
+ commandHint: "Review static executor configuration and emitted markdown before re-run.",
137
+ };
138
+ case "decision-envelope":
139
+ return {
140
+ action: decisionEnvelopeAction(status, lifecycle, runStatus),
141
+ summary: decisionEnvelopeAction(status, lifecycle, runStatus) ===
142
+ "resume-or-reject"
143
+ ? "Resume after human approval or reject the paused run."
144
+ : "Manually review the decision envelope before continuing.",
145
+ reason: `${target} requires decision-gate review${raw ? ` (${raw})` : ""}.`,
146
+ humanRequired: true,
147
+ autoRetryEligible: false,
148
+ commandHint: decisionEnvelopeAction(status, lifecycle, runStatus) ===
149
+ "resume-or-reject"
150
+ ? "Use dag approve / dag reject, then dag resume when approved."
151
+ : "Inspect decision.envelope.json and human-escalation artifacts.",
152
+ };
153
+ case "human-required": {
154
+ const action = isPausedContext(status, lifecycle, runStatus)
155
+ ? "resume-or-reject"
156
+ : "manual-review";
157
+ return {
158
+ action,
159
+ summary: action === "resume-or-reject"
160
+ ? "Provide human input, then resume or reject the paused run."
161
+ : "Provide human input before choosing the next recovery action.",
162
+ reason: `${target} is waiting for human escalation${raw ? ` (${raw})` : ""}.`,
163
+ humanRequired: true,
164
+ autoRetryEligible: false,
165
+ commandHint: action === "resume-or-reject"
166
+ ? "Use dag approve / dag reject, then dag resume when approved."
167
+ : "Inspect human-escalation artifacts and update the run/task contract before continuing.",
168
+ };
169
+ }
170
+ case "human-rejected":
171
+ return {
172
+ action: "manual-review",
173
+ summary: "Revise contract/source or escalation options before any retry.",
174
+ reason: `${target} was human-rejected${raw ? ` (${raw})` : ""}; automatic retry is not recommended.`,
175
+ humanRequired: true,
176
+ autoRetryEligible: false,
177
+ commandHint: "Review human-rejection.json and update the DAG/task contract before starting a new run.",
178
+ };
179
+ case "unknown":
180
+ return {
181
+ action: "manual-review",
182
+ summary: "Manually inspect failure artifacts; no automatic retry recommended.",
183
+ reason: `${target} failed with an unclassified category${raw ? ` (${raw})` : ""}.`,
184
+ humanRequired: true,
185
+ autoRetryEligible: false,
186
+ commandHint: "Inspect node artifacts, executor.jsonl, and state.json before choosing a recovery path.",
187
+ };
188
+ default: {
189
+ const exhaustive = category;
190
+ return {
191
+ action: "unknown",
192
+ summary: "Unknown recovery path.",
193
+ reason: `Unhandled normalized category: ${exhaustive}.`,
194
+ humanRequired: true,
195
+ autoRetryEligible: false,
196
+ };
197
+ }
198
+ }
199
+ }
200
+ export function planDagRecovery(input) {
201
+ if (isInProgressStatus(input.status)) {
202
+ return {
203
+ action: "monitor",
204
+ summary: "Monitor the in-progress run; no recovery action yet.",
205
+ reason: `${input.nodeId ?? input.runId ?? "Run"} is still ${input.status.toLowerCase()}.`,
206
+ humanRequired: false,
207
+ autoRetryEligible: false,
208
+ commandHint: "Wait for the node/run to finish or pause before planning recovery.",
209
+ };
210
+ }
211
+ if (isPausedRunStatus(input.status)) {
212
+ const pausedRecommendation = planByNormalizedCategory(input);
213
+ if (pausedRecommendation.action === "none") {
214
+ return {
215
+ action: "resume-or-reject",
216
+ summary: "Resume after review or reject the paused run.",
217
+ reason: "Run is paused and awaiting human decision.",
218
+ humanRequired: true,
219
+ autoRetryEligible: false,
220
+ commandHint: "Use dag approve / dag reject, then dag resume when approved.",
221
+ };
222
+ }
223
+ return pausedRecommendation;
224
+ }
225
+ return planByNormalizedCategory(input);
226
+ }
@@ -0,0 +1,136 @@
1
+ import { z } from "zod";
2
+ import { pathMatchesPattern } from "../../shared/git-progress.js";
3
+ export const repairFailureClassSchema = z.enum([
4
+ "syntax",
5
+ "runtime",
6
+ "logic",
7
+ "boundary",
8
+ "environment",
9
+ "governance",
10
+ "unknown",
11
+ ]);
12
+ export const repairArtifactSchema = z.object({
13
+ schemaVersion: z.literal(1),
14
+ verdict: z.enum(["pass", "request-revision"]),
15
+ failureClass: repairFailureClassSchema,
16
+ rootCause: z.string().min(1),
17
+ fixScope: z.array(z.string().min(1)).default([]),
18
+ invariant: z.string().min(1),
19
+ evidenceRefs: z.array(z.string().min(1)).default([]),
20
+ rawLogFallbackAllowed: z.boolean().default(false),
21
+ });
22
+ const REPAIR_ARTIFACT_FENCE = /```(?:REPAIR_ARTIFACT_JSON|json)\s*([\s\S]*?)```/gi;
23
+ function extractCandidateJsonBlocks(text) {
24
+ const blocks = [];
25
+ for (const match of text.matchAll(REPAIR_ARTIFACT_FENCE)) {
26
+ const candidate = match[1]?.trim();
27
+ if (candidate)
28
+ blocks.push(candidate);
29
+ }
30
+ const trimmed = text.trim();
31
+ if (trimmed.startsWith("{") && trimmed.endsWith("}")) {
32
+ blocks.push(trimmed);
33
+ }
34
+ return blocks;
35
+ }
36
+ export function parseRepairArtifactFromText(text) {
37
+ const candidates = extractCandidateJsonBlocks(text);
38
+ if (candidates.length === 0) {
39
+ return { ok: false, reason: "missing REPAIR_ARTIFACT_JSON fenced block" };
40
+ }
41
+ const errors = [];
42
+ for (const candidate of candidates) {
43
+ try {
44
+ const parsed = JSON.parse(candidate);
45
+ const result = repairArtifactSchema.safeParse(parsed);
46
+ if (result.success) {
47
+ return { ok: true, artifact: result.data };
48
+ }
49
+ errors.push(result.error.issues.map((issue) => issue.message).join(", "));
50
+ }
51
+ catch (error) {
52
+ errors.push(error instanceof Error ? error.message : String(error));
53
+ }
54
+ }
55
+ return {
56
+ ok: false,
57
+ reason: `invalid repair artifact JSON: ${errors.join("; ")}`,
58
+ };
59
+ }
60
+ function normalizePath(value) {
61
+ return value.replace(/\\/g, "/").replace(/^\.\//, "");
62
+ }
63
+ function isPathLikeScope(scope) {
64
+ const normalized = normalizePath(scope);
65
+ if (normalized === "none")
66
+ return false;
67
+ if (normalized === "." || normalized === "**" || normalized === "/*")
68
+ return false;
69
+ return (normalized.includes("/") ||
70
+ /\.(ts|tsx|js|jsx|mjs|cjs|json|md|sh|yml|yaml)$/.test(normalized));
71
+ }
72
+ function scopeWithinPatterns(scope, patterns) {
73
+ if (patterns.length === 0)
74
+ return true;
75
+ const normalized = normalizePath(scope);
76
+ return patterns.some((pattern) => pathMatchesPattern(normalized, pattern));
77
+ }
78
+ export function validateRepairArtifactScope(input) {
79
+ const { artifact, repairTask } = input;
80
+ if (artifact.verdict === "pass") {
81
+ if (artifact.fixScope.length > 0) {
82
+ return {
83
+ ok: false,
84
+ reason: "pass repair artifact requires empty fixScope",
85
+ };
86
+ }
87
+ return { ok: true, artifact };
88
+ }
89
+ if (artifact.fixScope.length === 0) {
90
+ return {
91
+ ok: false,
92
+ reason: "request-revision repair artifact requires non-empty fixScope",
93
+ };
94
+ }
95
+ if (!repairTask) {
96
+ return {
97
+ ok: false,
98
+ reason: "repair artifact gate cannot find downstream repair task",
99
+ };
100
+ }
101
+ const allowed = repairTask.allowedPaths ?? [];
102
+ const writeSet = repairTask.writeSet ?? [];
103
+ const forbidden = repairTask.forbiddenPaths ?? [];
104
+ for (const scope of artifact.fixScope) {
105
+ if (!isPathLikeScope(scope)) {
106
+ return {
107
+ ok: false,
108
+ reason: `fixScope "${scope}" must be a concrete path or glob inside repair task allowedPaths/writeSet`,
109
+ };
110
+ }
111
+ if (!scopeWithinPatterns(scope, allowed) || !scopeWithinPatterns(scope, writeSet)) {
112
+ return {
113
+ ok: false,
114
+ reason: `fixScope "${scope}" is outside repair task allowedPaths/writeSet`,
115
+ };
116
+ }
117
+ if (forbidden.some((pattern) => pathMatchesPattern(normalizePath(scope), pattern))) {
118
+ return {
119
+ ok: false,
120
+ reason: `fixScope "${scope}" matches repair task forbiddenPaths`,
121
+ };
122
+ }
123
+ }
124
+ return { ok: true, artifact };
125
+ }
126
+ export function formatRepairArtifactForPrompt(artifact) {
127
+ return [
128
+ "Repair artifact:",
129
+ `- verdict: ${artifact.verdict}`,
130
+ `- failureClass: ${artifact.failureClass}`,
131
+ `- rootCause: ${artifact.rootCause}`,
132
+ `- fixScope: ${artifact.fixScope.length > 0 ? artifact.fixScope.join(", ") : "(none)"}`,
133
+ `- invariant: ${artifact.invariant}`,
134
+ `- evidenceRefs: ${artifact.evidenceRefs.length > 0 ? artifact.evidenceRefs.join(", ") : "(none)"}`,
135
+ ].join("\n");
136
+ }