@tea-agent/loop-agent 0.5.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +142 -142
- package/CHANGELOG.md +116 -98
- package/README.md +195 -195
- package/bin/agent-worker.js +22 -22
- package/bin/loop-agent.js +21 -21
- package/dist/application/dag/args.js +9 -1
- package/dist/application/dag/run-dag.js +16 -2
- package/dist/cli/command-definitions.js +22 -4
- package/dist/cli/help.js +3 -2
- package/dist/cli/program.js +7 -5
- package/dist/commands/import-prd.js +76 -0
- package/dist/commands/init.js +467 -457
- package/dist/commands/instructions.js +90 -58
- package/dist/commands/loop-benchmark.js +11 -11
- package/dist/commands/pi-reuse-benchmark.js +16 -16
- package/dist/executors/cursor-executor.js +1 -1
- package/dist/executors/dag-pi-executor.js +1 -0
- package/dist/executors/pi-sdk-executor.js +63 -1
- package/dist/shared/preview.js +39 -0
- package/dist/task/runtime.js +27 -27
- package/dist/task/source-references.js +221 -0
- package/dist/worker/cli.js +62 -1
- package/dist/worker/loop-agent/loop-agent-client.js +97 -5
- package/dist/worker/materialize/harness-task-materializer.js +162 -5
- package/dist/worker/observability/event-store.js +82 -0
- package/dist/worker/observability/events.js +79 -0
- package/dist/worker/observability/progress-composite.js +33 -0
- package/dist/worker/observability/read-model.js +1013 -0
- package/dist/worker/observability/snapshot-store.js +43 -0
- package/dist/worker/observability/types.js +1 -0
- package/dist/worker/observe/paths.js +64 -0
- package/dist/worker/observe/routes.js +423 -0
- package/dist/worker/observe/server.js +61 -0
- package/dist/worker/observe/static/app.js +1419 -0
- package/dist/worker/observe/static/index.html +63 -0
- package/dist/worker/observe/static/styles.css +613 -0
- package/dist/worker/pool/failure-routing.js +41 -6
- package/dist/worker/pool/run-store.js +50 -0
- package/dist/worker/progress-reporter.js +0 -18
- package/dist/worker/run-task/run-task.js +327 -92
- package/dist/worker/runner/run-ready.js +112 -4
- package/dist/workflows/dag/canvas-observer.js +275 -275
- package/dist/workflows/dag/event-observer.js +132 -0
- package/dist/workflows/dag/init-hybrid.js +146 -13
- package/dist/workflows/dag/observer-compose.js +52 -0
- package/docs/README.md +74 -72
- package/docs/agent-dag-recovery-playbook.md +184 -184
- package/docs/agent-dag-runner.md +42 -42
- package/docs/architecture/runtime-boundaries.md +162 -147
- package/docs/cursor-executor-usage.md +25 -25
- package/docs/decisions/README.md +3 -3
- package/docs/design/README.md +49 -36
- package/docs/development-principles.md +73 -73
- package/docs/dynamic-workflow-dag-engine-roadmap.md +1749 -1749
- package/docs/exec-plans/README.md +6 -6
- package/docs/exec-plans/active/README.md +12 -7
- package/docs/exec-plans/completed/README.md +31 -19
- package/docs/feature-workflow.md +186 -186
- package/docs/harness-methodology-debugging.md +153 -153
- package/docs/harness-methodology-tdd.md +130 -130
- package/docs/harness-methodology-verification.md +27 -27
- package/docs/init-surface.manifest.json +205 -199
- package/docs/loop-agent-harness.md +55 -42
- package/docs/production-readiness.md +96 -96
- package/docs/progress/README.md +3 -3
- package/docs/reports/README.md +9 -5
- package/docs/skills/README.md +6 -6
- package/docs/skills/vetted-skill-registry.md +26 -26
- package/docs/templates/adr.md +60 -60
- package/docs/templates/agent-dag-authority-surface-audit.prompt.md +94 -94
- package/docs/templates/agent-dag-decision-envelope.schema.json +213 -213
- package/docs/templates/agent-dag-decision-gate-dogfood-report.md +117 -117
- package/docs/templates/agent-dag-decision-gate.prompt.md +246 -246
- package/docs/templates/agent-dag-process-supervisor.prompt.md +98 -98
- package/docs/templates/agent-dag-report.schema.json +454 -454
- package/docs/templates/agent-dag-review-verdict.prompt.md +68 -68
- package/docs/templates/agent-dag.base.json +195 -195
- package/docs/templates/agent-dag.final-verification.json +190 -190
- package/docs/templates/agent-dag.schema.json +316 -316
- package/docs/templates/agent-dag.supervised-implementation.json +500 -500
- package/docs/templates/exec-plan.md +64 -64
- package/docs/templates/feature-spec.md +53 -53
- package/docs/templates/hybrid-dag.json +193 -193
- package/docs/templates/init-evolution-review.md +33 -33
- package/docs/templates/production-readiness-checklist.md +57 -57
- package/docs/templates/progress-log.md +17 -17
- package/docs/templates/project-start-checklist.md +9 -9
- package/docs/templates/qa-report.md +48 -48
- package/docs/templates/sprint-contract.md +29 -29
- package/docs/templates/worker-dogfood-evidence.md +52 -0
- package/docs/templates/worker-dogfood-setup.md +48 -0
- package/docs/verification-matrix.md +41 -41
- package/examples/decision-gate-agent-dag.json +123 -123
- package/examples/example-dag.json +51 -51
- package/examples/hybrid-loop-agent-dag.json +194 -194
- package/harness.json +70 -69
- package/package.json +66 -66
- package/skills/ai-engineering-context/SKILL.md +48 -48
- package/skills/code-review-core/SKILL.md +20 -20
- package/skills/codebase-scout/SKILL.md +19 -19
- package/skills/init-capability-evolution/SKILL.md +69 -69
- package/skills/loop-agent/SKILL.md +149 -147
- package/skills/loop-agent/references/README.md +67 -67
- package/skills/loop-agent/references/command-reference.md +412 -403
- package/skills/loop-agent/references/harness-policy.md +263 -259
- package/skills/loop-agent/references/hybrid-dag.md +216 -216
- package/skills/loop-agent/references/learned/README.md +21 -21
- package/skills/loop-agent/references/long-running-loop.md +59 -59
- package/skills/loop-agent/references/model-routing.md +36 -36
- package/skills/loop-agent/references/multi-worktree.md +54 -54
- package/skills/loop-agent/references/one-shot-runs.md +85 -85
- package/skills/loop-agent/references/orchestrator-and-interventions.md +169 -169
- package/skills/loop-agent/references/pi-prompt.md +23 -23
- package/skills/loop-agent/references/pi-subagent-assisted-mode.md +81 -81
- package/skills/loop-agent/references/post-implementation-and-patterns.md +44 -44
- package/skills/loop-agent/references/task-workflow.md +89 -84
- package/skills/loop-agent/references/verification-and-failure-handling.md +128 -128
- package/skills/requesting-code-review/SKILL.md +101 -101
- package/skills/requesting-code-review/code-reviewer.md +168 -168
- package/skills/systematic-debugging/CREATION-LOG.md +119 -119
- package/skills/systematic-debugging/SKILL.md +296 -296
- package/skills/systematic-debugging/condition-based-waiting-example.ts +158 -158
- package/skills/systematic-debugging/condition-based-waiting.md +115 -115
- package/skills/systematic-debugging/defense-in-depth.md +122 -122
- package/skills/systematic-debugging/find-polluter.sh +63 -63
- package/skills/systematic-debugging/root-cause-tracing.md +169 -169
- package/skills/systematic-debugging/test-academic.md +14 -14
- package/skills/systematic-debugging/test-pressure-1.md +58 -58
- package/skills/systematic-debugging/test-pressure-2.md +68 -68
- package/skills/systematic-debugging/test-pressure-3.md +69 -69
- package/skills/test-driven-development/SKILL.md +20 -20
- package/skills/verification-before-completion/SKILL.md +154 -154
- package/skills/webapp-testing/SKILL.md +19 -19
|
@@ -2,10 +2,10 @@ import { createHash } from "node:crypto";
|
|
|
2
2
|
import { readFile, writeFile, mkdir } from "node:fs/promises";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import YAML from "yaml";
|
|
5
|
-
import { deriveFailureRoute } from "../pool/failure-routing.js";
|
|
5
|
+
import { deriveFailureRoute, deriveFailureRouteFromError, } from "../pool/failure-routing.js";
|
|
6
6
|
import { findRunByWorkerRunId, getTaskPoolRoot, readAllTaskPoolStates, recordTaskPoolRun, writeTaskPoolState, } from "../pool/run-store.js";
|
|
7
7
|
import { runTaskSpec, } from "../run-task/run-task.js";
|
|
8
|
-
import { formatDuration, noopProgressReporter } from "../progress-reporter.js";
|
|
8
|
+
import { formatDuration, noopProgressReporter, } from "../progress-reporter.js";
|
|
9
9
|
import { computeReadyQueue } from "../task-graph/ready-queue.js";
|
|
10
10
|
import { taskGraphSpecSchema } from "../task-graph/task-graph-schema.js";
|
|
11
11
|
import { taskSpecSchema } from "../task-spec/schema.js";
|
|
@@ -23,18 +23,43 @@ export async function runReadyTasks(options) {
|
|
|
23
23
|
const total = limitedTaskIds.length;
|
|
24
24
|
let index = 0;
|
|
25
25
|
const batchStartedAt = Date.now();
|
|
26
|
+
emit(progress, {
|
|
27
|
+
type: "readyQueue.computed",
|
|
28
|
+
source: "worker",
|
|
29
|
+
label: `ready queue: ${total} task${total === 1 ? "" : "s"} ready`,
|
|
30
|
+
batchRunId,
|
|
31
|
+
});
|
|
26
32
|
progress.batch(`batch ${batchRunId}: feature=${graph.feature_id}, ${total} task${total === 1 ? "" : "s"} ready → starting`);
|
|
33
|
+
emit(progress, {
|
|
34
|
+
type: "batch.started",
|
|
35
|
+
source: "worker",
|
|
36
|
+
label: `batch ${batchRunId}: feature=${graph.feature_id}, ${total} task${total === 1 ? "" : "s"} ready`,
|
|
37
|
+
batchRunId,
|
|
38
|
+
status: "running",
|
|
39
|
+
});
|
|
27
40
|
for (const taskId of limitedTaskIds) {
|
|
28
41
|
index += 1;
|
|
29
42
|
const node = graph.nodes.find((candidate) => candidate.id === taskId);
|
|
30
43
|
const taskSpecPath = path.join(options.featureDir, "tasks", node?.task ?? `${taskId}.yaml`);
|
|
31
44
|
const taskSpec = await loadTaskSpec(taskSpecPath);
|
|
32
|
-
const
|
|
33
|
-
|
|
45
|
+
const retryOfWorkerRunId = states[taskId]?.retryOfWorkerRunId;
|
|
46
|
+
const workerRunId = retryOfWorkerRunId
|
|
47
|
+
? buildRetryWorkerRunId(taskId, retryOfWorkerRunId, now)
|
|
48
|
+
: options.workerRunIdForTask?.(taskId, taskSpec) ??
|
|
49
|
+
buildStableWorkerRunId(taskId, taskSpec, now);
|
|
34
50
|
if (workerRunId) {
|
|
35
51
|
const existing = await findRunByWorkerRunId(options.repoRoot, workerRunId);
|
|
36
52
|
if (existing) {
|
|
37
53
|
progress.task(`task ${index}/${total} ${taskId}: reuse existing run ${workerRunId}`);
|
|
54
|
+
emit(progress, {
|
|
55
|
+
type: "task.reused",
|
|
56
|
+
source: "worker",
|
|
57
|
+
label: `task ${taskId}: reuse existing run ${workerRunId}`,
|
|
58
|
+
batchRunId,
|
|
59
|
+
taskId,
|
|
60
|
+
workerRunId,
|
|
61
|
+
status: "reused",
|
|
62
|
+
});
|
|
38
63
|
tasks.push({
|
|
39
64
|
taskId,
|
|
40
65
|
workerRunId,
|
|
@@ -46,6 +71,15 @@ export async function runReadyTasks(options) {
|
|
|
46
71
|
}
|
|
47
72
|
const taskStartedAt = Date.now();
|
|
48
73
|
progress.task(`task ${index}/${total} ${taskId} "${taskSpec.title}" (${workerRunId})`);
|
|
74
|
+
emit(progress, {
|
|
75
|
+
type: "task.started",
|
|
76
|
+
source: "worker",
|
|
77
|
+
label: `task ${index}/${total} ${taskId}`,
|
|
78
|
+
batchRunId,
|
|
79
|
+
taskId,
|
|
80
|
+
workerRunId,
|
|
81
|
+
status: "running",
|
|
82
|
+
});
|
|
49
83
|
progress.step(`preflight → materialize → dag-run-task → validate → run-dag → report`);
|
|
50
84
|
let result;
|
|
51
85
|
try {
|
|
@@ -54,6 +88,16 @@ export async function runReadyTasks(options) {
|
|
|
54
88
|
status: "Running",
|
|
55
89
|
updatedAt: new Date().toISOString(),
|
|
56
90
|
workerRunId,
|
|
91
|
+
...(retryOfWorkerRunId ? { retryOfWorkerRunId } : {}),
|
|
92
|
+
});
|
|
93
|
+
emit(progress, {
|
|
94
|
+
type: "state.updated",
|
|
95
|
+
source: "worker",
|
|
96
|
+
label: `task pool state: ${taskId} → Running`,
|
|
97
|
+
batchRunId,
|
|
98
|
+
taskId,
|
|
99
|
+
workerRunId,
|
|
100
|
+
status: "running",
|
|
57
101
|
});
|
|
58
102
|
result = await runner({
|
|
59
103
|
repoRoot: options.repoRoot,
|
|
@@ -72,7 +116,18 @@ export async function runReadyTasks(options) {
|
|
|
72
116
|
}
|
|
73
117
|
catch (error) {
|
|
74
118
|
const message = errorMessage(error);
|
|
119
|
+
const failure = deriveFailureRouteFromError(message, taskId);
|
|
75
120
|
progress.note(`task ${taskId}: ERROR ${message}`);
|
|
121
|
+
emit(progress, {
|
|
122
|
+
type: "task.finished",
|
|
123
|
+
source: "worker",
|
|
124
|
+
label: `task ${taskId} run-error`,
|
|
125
|
+
batchRunId,
|
|
126
|
+
taskId,
|
|
127
|
+
workerRunId,
|
|
128
|
+
status: "failed",
|
|
129
|
+
message,
|
|
130
|
+
});
|
|
76
131
|
try {
|
|
77
132
|
await recordTaskPoolRun({
|
|
78
133
|
repoRoot: options.repoRoot,
|
|
@@ -85,6 +140,8 @@ export async function runReadyTasks(options) {
|
|
|
85
140
|
status: "run-error",
|
|
86
141
|
recordedAt: new Date().toISOString(),
|
|
87
142
|
error: message,
|
|
143
|
+
failure,
|
|
144
|
+
...(retryOfWorkerRunId ? { retryOfWorkerRunId } : {}),
|
|
88
145
|
},
|
|
89
146
|
});
|
|
90
147
|
}
|
|
@@ -106,6 +163,29 @@ export async function runReadyTasks(options) {
|
|
|
106
163
|
continue;
|
|
107
164
|
}
|
|
108
165
|
const failure = deriveFailureRoute(result);
|
|
166
|
+
emit(progress, {
|
|
167
|
+
type: "task.finished",
|
|
168
|
+
source: "worker",
|
|
169
|
+
label: `task ${taskId} ${result.status}`,
|
|
170
|
+
batchRunId,
|
|
171
|
+
taskId,
|
|
172
|
+
workerRunId: result.workerRunId,
|
|
173
|
+
status: result.status,
|
|
174
|
+
durationMs: Date.now() - taskStartedAt,
|
|
175
|
+
});
|
|
176
|
+
if (failure) {
|
|
177
|
+
emit(progress, {
|
|
178
|
+
type: "failure.routed",
|
|
179
|
+
source: "worker",
|
|
180
|
+
label: `failure routed: ${failure.category}`,
|
|
181
|
+
batchRunId,
|
|
182
|
+
taskId,
|
|
183
|
+
workerRunId: result.workerRunId,
|
|
184
|
+
failureCategory: failure.category,
|
|
185
|
+
recommendedFollowUp: failure.recommendedFollowUpKind,
|
|
186
|
+
status: "failed",
|
|
187
|
+
});
|
|
188
|
+
}
|
|
109
189
|
if (result.status === "succeeded") {
|
|
110
190
|
progress.step(`✓ ${taskId} succeeded in ${formatDuration(Date.now() - taskStartedAt)} (promoted + closed out)`);
|
|
111
191
|
}
|
|
@@ -124,6 +204,7 @@ export async function runReadyTasks(options) {
|
|
|
124
204
|
dagPath: result.dagPath,
|
|
125
205
|
recordedAt: new Date().toISOString(),
|
|
126
206
|
...(failure ? { failure } : {}),
|
|
207
|
+
...(retryOfWorkerRunId ? { retryOfWorkerRunId } : {}),
|
|
127
208
|
...(result.failureArtifacts ? { failureArtifacts: result.failureArtifacts } : {}),
|
|
128
209
|
};
|
|
129
210
|
try {
|
|
@@ -160,8 +241,22 @@ export async function runReadyTasks(options) {
|
|
|
160
241
|
await mkdir(path.dirname(batchRunPath), { recursive: true });
|
|
161
242
|
await writeFile(batchRunPath, `${JSON.stringify(output, null, 2)}\n`, "utf-8");
|
|
162
243
|
progress.batch(`batch ${batchRunId} ${output.status}: ${summary.succeeded} succeeded, ${summary.failed} failed, ${summary.reused} reused in ${formatDuration(Date.now() - batchStartedAt)} → ${batchRunPath}`);
|
|
244
|
+
emit(progress, {
|
|
245
|
+
type: "batch.finished",
|
|
246
|
+
source: "worker",
|
|
247
|
+
label: `batch ${batchRunId} ${output.status}`,
|
|
248
|
+
batchRunId,
|
|
249
|
+
status: output.status === "completed" ? "succeeded" : "failed",
|
|
250
|
+
durationMs: Date.now() - batchStartedAt,
|
|
251
|
+
});
|
|
163
252
|
return output;
|
|
164
253
|
}
|
|
254
|
+
function emit(progress, input) {
|
|
255
|
+
const structured = progress;
|
|
256
|
+
if (typeof structured.event !== "function")
|
|
257
|
+
return;
|
|
258
|
+
void structured.event(input).catch(() => { });
|
|
259
|
+
}
|
|
165
260
|
export function buildBatchRunId(now) {
|
|
166
261
|
return `batch-${now.toISOString().replace(/[-:.]/g, "").slice(0, 15)}`;
|
|
167
262
|
}
|
|
@@ -177,6 +272,19 @@ export function buildStableWorkerRunId(taskId, taskSpec, now) {
|
|
|
177
272
|
.slice(0, 10);
|
|
178
273
|
return `wr-${date}-${slug}-${hash}`;
|
|
179
274
|
}
|
|
275
|
+
/** A retry is a distinct execution attempt and must not collide with failed evidence. */
|
|
276
|
+
export function buildRetryWorkerRunId(taskId, previousWorkerRunId, now) {
|
|
277
|
+
const date = now.toISOString().slice(0, 10).replace(/-/g, "");
|
|
278
|
+
const slug = taskId
|
|
279
|
+
.toLowerCase()
|
|
280
|
+
.replace(/[^a-z0-9]+/g, "-")
|
|
281
|
+
.replace(/^-+|-+$/g, "");
|
|
282
|
+
const hash = createHash("sha256")
|
|
283
|
+
.update(`${previousWorkerRunId}:${now.toISOString()}`)
|
|
284
|
+
.digest("hex")
|
|
285
|
+
.slice(0, 10);
|
|
286
|
+
return `wr-${date}-${slug}-retry-${hash}`;
|
|
287
|
+
}
|
|
180
288
|
async function loadTaskGraph(featureDir) {
|
|
181
289
|
const graphPath = path.join(featureDir, "tasks", "task-graph.yaml");
|
|
182
290
|
return taskGraphSpecSchema.parse(YAML.parse(await readFile(graphPath, "utf-8")));
|