@tea-agent/loop-agent 0.1.0 → 0.2.1
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 +62 -45
- package/CHANGELOG.md +60 -28
- package/README.md +160 -124
- package/bin/loop-agent.js +21 -21
- package/dist/adapters/index.js +3 -2
- package/dist/adapters/loop-agent.js +44 -2
- package/dist/application/dag/args.js +420 -0
- package/dist/application/dag/generate-task-dag.js +280 -0
- package/dist/application/dag/report-dag.js +14 -0
- package/dist/application/dag/run-dag.js +106 -0
- package/dist/application/dag/validate-dag.js +102 -0
- package/dist/application/loop/run-action.js +23 -0
- package/dist/cli/catalog.js +2 -237
- package/dist/cli/command-definitions.js +571 -0
- package/dist/cli/index.js +2 -0
- package/dist/cli/program.js +65 -1
- package/dist/cli/router.js +13 -0
- package/dist/cli-governance/active-residue-check.js +38 -0
- package/dist/commands/dag-report.js +6 -107
- package/dist/commands/dag-run-task.js +8 -466
- package/dist/commands/dag-validate.js +7 -179
- package/dist/commands/examples.js +90 -0
- package/dist/commands/init.js +1518 -0
- package/dist/commands/loop.js +57 -31
- package/dist/commands/pi-prompt.js +2 -9
- package/dist/commands/run-dag.js +7 -180
- package/dist/executors/cursor-executor-artifacts.js +3 -4
- package/dist/executors/cursor-worker-client.js +13 -3
- package/dist/executors/dag-cursor-executor.js +2 -3
- package/dist/executors/dag-pi-executor.js +3 -4
- package/dist/executors/dag-static-executor.js +2 -5
- package/dist/executors/pi-defaults.js +9 -0
- package/dist/executors/shell-executor.js +12 -20
- package/dist/governance/manifest-types.js +1 -0
- package/dist/infrastructure/harness/active-residue-policy.js +73 -0
- package/dist/infrastructure/harness/artifact-store.js +72 -0
- package/dist/infrastructure/harness/atomic-write.js +49 -0
- package/dist/infrastructure/harness/completed-facts-guard.js +40 -0
- package/dist/infrastructure/harness/loop-action-store.js +23 -0
- package/dist/infrastructure/harness/loop-store.js +41 -0
- package/dist/infrastructure/harness/one-shot-run-store.js +94 -0
- package/dist/infrastructure/harness/task-store.js +77 -0
- package/dist/records/one-shot-runs.js +26 -61
- package/dist/records/promotion.js +3 -4
- package/dist/shared/artifacts-core.js +5 -5
- package/dist/shared/logger.js +9 -15
- package/dist/task/delegate.js +4 -4
- package/dist/task/runtime.js +5 -7
- package/dist/task/state.js +6 -20
- package/dist/workflows/dag/convergence/controller.js +277 -0
- package/dist/workflows/dag/dynamic-runtime/condition.js +48 -0
- package/dist/workflows/dag/dynamic-runtime/loop-until.js +156 -0
- package/dist/workflows/dag/dynamic-runtime/map.js +185 -0
- package/dist/workflows/dag/dynamic-runtime/reduction.js +72 -0
- package/dist/workflows/dag/dynamic-runtime/shared.js +133 -0
- package/dist/workflows/dag/failure-routing.js +82 -0
- package/dist/workflows/dag/lifecycle.js +101 -8
- package/dist/workflows/dag/node-execution.js +262 -0
- package/dist/workflows/dag/report.js +73 -1
- package/dist/workflows/dag/run-store.js +36 -0
- package/dist/workflows/dag/runner.js +82 -1341
- package/dist/workflows/dag/scheduler.js +84 -0
- package/dist/workflows/dag/upstream-artifacts.js +20 -18
- package/dist/workflows/loop/actions/cursor-fix.js +191 -0
- package/dist/workflows/loop/actions/dag-action.js +130 -0
- package/dist/workflows/loop/actions/pi-review.js +267 -0
- package/dist/workflows/loop/actions/shared.js +157 -0
- package/dist/workflows/loop/actions/shell-verify.js +82 -0
- package/dist/workflows/loop/actions/types.js +1 -0
- package/dist/workflows/loop/actions/workflow-action.js +255 -0
- package/dist/workflows/loop/actions.js +55 -1212
- package/dist/workflows/loop/closeout.js +5 -4
- package/dist/workflows/loop/context.js +2 -3
- package/dist/workflows/loop/events.js +3 -2
- package/dist/workflows/loop/policy/auto-policy.js +104 -0
- package/dist/workflows/loop/policy/cursor-fix-policy.js +31 -0
- package/dist/workflows/loop/rounds.js +3 -3
- package/dist/workflows/loop/signals.js +4 -7
- package/dist/workflows/loop/state.js +11 -11
- package/docs/README.md +47 -44
- package/docs/agent-dag-recovery-playbook.md +32 -6
- package/docs/agent-dag-runner.md +17 -17
- package/docs/architecture/runtime-boundaries.md +147 -0
- package/docs/cursor-executor-usage.md +5 -5
- package/docs/decisions/README.md +2 -2
- package/docs/design/README.md +24 -24
- package/docs/development-principles.md +50 -50
- package/docs/dynamic-workflow-dag-engine-roadmap.md +6 -6
- package/docs/exec-plans/README.md +4 -4
- package/docs/exec-plans/active/README.md +10 -5
- package/docs/exec-plans/completed/README.md +9 -5
- package/docs/feature-workflow.md +111 -109
- package/docs/harness-methodology-verification.md +18 -18
- package/docs/loop-agent-harness.md +36 -36
- package/docs/production-readiness.md +96 -0
- package/docs/progress/README.md +2 -2
- package/docs/reports/README.md +4 -2
- package/docs/templates/agent-dag-decision-gate-dogfood-report.md +1 -1
- package/docs/templates/agent-dag-process-supervisor.prompt.md +2 -2
- package/docs/templates/agent-dag-report.schema.json +33 -2
- package/docs/templates/agent-dag-review-verdict.prompt.md +1 -1
- 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 +17 -17
- package/docs/templates/agent-dag.supervised-implementation.json +500 -500
- package/docs/templates/hybrid-dag.json +193 -193
- package/docs/templates/production-readiness-checklist.md +57 -0
- package/docs/templates/progress-log.md +7 -7
- package/docs/templates/project-start-checklist.md +8 -8
- package/docs/templates/qa-report.md +17 -11
- package/docs/templates/sprint-contract.md +19 -19
- package/docs/verification-matrix.md +37 -26
- package/examples/example-dag.json +51 -51
- package/examples/hybrid-loop-agent-dag.json +194 -194
- package/harness.json +5 -5
- package/package.json +62 -61
- package/skills/ai-engineering-context/SKILL.md +21 -21
- package/skills/loop-agent/SKILL.md +56 -171
- package/skills/loop-agent/references/README.md +6 -2
- package/skills/loop-agent/references/command-reference.md +107 -65
- package/skills/loop-agent/references/harness-policy.md +115 -115
- package/skills/loop-agent/references/hybrid-dag.md +30 -30
- package/skills/loop-agent/references/learned/README.md +13 -13
- package/skills/loop-agent/references/long-running-loop.md +59 -0
- package/skills/loop-agent/references/model-routing.md +1 -1
- package/skills/loop-agent/references/orchestrator-and-interventions.md +1 -1
- package/skills/loop-agent/references/pi-prompt.md +9 -9
- package/skills/loop-agent/references/pi-subagent-assisted-mode.md +0 -2
- package/skills/loop-agent/references/post-implementation-and-patterns.md +7 -7
- package/skills/loop-agent/references/task-workflow.md +19 -19
- package/skills/loop-agent/references/verification-and-failure-handling.md +54 -0
- package/skills/requesting-code-review/SKILL.md +40 -40
- package/skills/requesting-code-review/code-reviewer.md +4 -4
- package/skills/systematic-debugging/CREATION-LOG.md +43 -43
- package/skills/systematic-debugging/SKILL.md +113 -113
- package/skills/systematic-debugging/condition-based-waiting.md +20 -20
- package/skills/systematic-debugging/defense-in-depth.md +27 -27
- package/skills/systematic-debugging/root-cause-tracing.md +38 -38
- package/skills/systematic-debugging/test-academic.md +6 -6
- package/skills/systematic-debugging/test-pressure-1.md +6 -6
- package/skills/systematic-debugging/test-pressure-2.md +2 -2
- package/skills/systematic-debugging/test-pressure-3.md +6 -6
- package/skills/verification-before-completion/SKILL.md +37 -37
|
@@ -1,186 +1,14 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import
|
|
3
|
-
|
|
4
|
-
import { assertValidDagSpec, collectExecutorModelWarnings, collectForbiddenExecutorIssues, collectGovernanceWarnings, validateDagSpec, } from "../workflows/dag/validate.js";
|
|
5
|
-
import { inferGovernanceProfile } from "../workflows/dag/governance-profile.js";
|
|
6
|
-
import { topoSortToRanks } from "../workflows/dag/topo.js";
|
|
7
|
-
import { runSpineAudit } from "../governance/spine-audit.js";
|
|
8
|
-
export function parseDagValidateArgs(args) {
|
|
9
|
-
if (args.length === 0) {
|
|
10
|
-
throw new Error("usage: dag validate --dag <path> [--strict-models] [--strict-governance]");
|
|
11
|
-
}
|
|
12
|
-
let dagPath;
|
|
13
|
-
let strictModelMatrix = false;
|
|
14
|
-
let strictGovernance = false;
|
|
15
|
-
const forbidExecutors = [];
|
|
16
|
-
let spineTaskId;
|
|
17
|
-
for (let i = 0; i < args.length; i += 1) {
|
|
18
|
-
const arg = args[i];
|
|
19
|
-
if (arg === "--dag") {
|
|
20
|
-
dagPath = args[++i];
|
|
21
|
-
if (!dagPath) {
|
|
22
|
-
throw new Error("dag validate --dag requires a path");
|
|
23
|
-
}
|
|
24
|
-
continue;
|
|
25
|
-
}
|
|
26
|
-
if (arg.startsWith("--dag=")) {
|
|
27
|
-
dagPath = arg.slice("--dag=".length);
|
|
28
|
-
if (!dagPath) {
|
|
29
|
-
throw new Error("dag validate --dag requires a path");
|
|
30
|
-
}
|
|
31
|
-
continue;
|
|
32
|
-
}
|
|
33
|
-
if (arg === "--strict-models") {
|
|
34
|
-
strictModelMatrix = true;
|
|
35
|
-
continue;
|
|
36
|
-
}
|
|
37
|
-
if (arg === "--strict-governance") {
|
|
38
|
-
strictGovernance = true;
|
|
39
|
-
continue;
|
|
40
|
-
}
|
|
41
|
-
if (arg === "--forbid-executor") {
|
|
42
|
-
const executor = args[++i];
|
|
43
|
-
if (!executor) {
|
|
44
|
-
throw new Error("dag validate --forbid-executor requires an executor");
|
|
45
|
-
}
|
|
46
|
-
forbidExecutors.push(executor);
|
|
47
|
-
continue;
|
|
48
|
-
}
|
|
49
|
-
if (arg === "--spine-task") {
|
|
50
|
-
spineTaskId = args[++i];
|
|
51
|
-
if (!spineTaskId) {
|
|
52
|
-
throw new Error("dag validate --spine-task requires a task id");
|
|
53
|
-
}
|
|
54
|
-
continue;
|
|
55
|
-
}
|
|
56
|
-
if (arg.startsWith("--spine-task=")) {
|
|
57
|
-
spineTaskId = arg.slice("--spine-task=".length);
|
|
58
|
-
if (!spineTaskId) {
|
|
59
|
-
throw new Error("dag validate --spine-task requires a task id");
|
|
60
|
-
}
|
|
61
|
-
continue;
|
|
62
|
-
}
|
|
63
|
-
if (arg.startsWith("--forbid-executor=")) {
|
|
64
|
-
const executor = arg.slice("--forbid-executor=".length);
|
|
65
|
-
if (!executor) {
|
|
66
|
-
throw new Error("dag validate --forbid-executor requires an executor");
|
|
67
|
-
}
|
|
68
|
-
forbidExecutors.push(executor);
|
|
69
|
-
continue;
|
|
70
|
-
}
|
|
71
|
-
if (arg.startsWith("-")) {
|
|
72
|
-
throw new Error(`unknown dag validate flag: ${arg}`);
|
|
73
|
-
}
|
|
74
|
-
if (dagPath) {
|
|
75
|
-
throw new Error(`unexpected positional argument: ${arg}`);
|
|
76
|
-
}
|
|
77
|
-
dagPath = arg;
|
|
78
|
-
}
|
|
79
|
-
if (!dagPath) {
|
|
80
|
-
throw new Error("dag validate requires --dag <path>");
|
|
81
|
-
}
|
|
82
|
-
return {
|
|
83
|
-
dagPath: path.resolve(dagPath),
|
|
84
|
-
strictModelMatrix,
|
|
85
|
-
strictGovernance,
|
|
86
|
-
forbidExecutors,
|
|
87
|
-
spineTaskId,
|
|
88
|
-
};
|
|
89
|
-
}
|
|
90
|
-
function formatModelMatrixWarnings(warnings) {
|
|
91
|
-
return warnings.map((warning) => ({
|
|
92
|
-
executor: warning.executor,
|
|
93
|
-
complexity: warning.complexity,
|
|
94
|
-
expected: warning.expected,
|
|
95
|
-
actual: warning.actual,
|
|
96
|
-
}));
|
|
97
|
-
}
|
|
98
|
-
function formatGovernanceWarnings(warnings) {
|
|
99
|
-
return warnings.map((warning) => ({
|
|
100
|
-
type: warning.type,
|
|
101
|
-
message: warning.message,
|
|
102
|
-
}));
|
|
103
|
-
}
|
|
104
|
-
function ensureStrictModelMatrix(warnings) {
|
|
105
|
-
if (warnings.length === 0) {
|
|
106
|
-
return;
|
|
107
|
-
}
|
|
108
|
-
const details = warnings
|
|
109
|
-
.map((entry) => `executor=${entry.executor}, complexity=${entry.complexity}, expected=${entry.expected}, actual=${entry.actual}`)
|
|
110
|
-
.join("; ");
|
|
111
|
-
throw new Error(`non-canonical executorModels overrides are blocked by --strict-models: ${details}`);
|
|
112
|
-
}
|
|
113
|
-
async function loadRawTaskExecutorMap(dagPath) {
|
|
114
|
-
const raw = JSON.parse(await readFile(dagPath, "utf-8"));
|
|
115
|
-
const map = new Map();
|
|
116
|
-
for (const task of raw.tasks ?? []) {
|
|
117
|
-
if (typeof task.id === "string")
|
|
118
|
-
map.set(task.id, task.executor);
|
|
119
|
-
}
|
|
120
|
-
return map;
|
|
121
|
-
}
|
|
122
|
-
function ensureNoForbiddenExecutors(spec, forbidExecutors, rawTaskExecutors) {
|
|
123
|
-
const supported = new Set(["cursor", "pi", "shell", "static"]);
|
|
124
|
-
const normalized = forbidExecutors.map((executor) => {
|
|
125
|
-
if (!supported.has(executor)) {
|
|
126
|
-
throw new Error(`unsupported executor for --forbid-executor: ${executor}`);
|
|
127
|
-
}
|
|
128
|
-
return executor;
|
|
129
|
-
});
|
|
130
|
-
const issues = collectForbiddenExecutorIssues(spec, normalized);
|
|
131
|
-
if (issues.length === 0)
|
|
132
|
-
return;
|
|
133
|
-
const details = issues
|
|
134
|
-
.map((issue) => {
|
|
135
|
-
const implicitDefault = rawTaskExecutors.get(issue.taskId) === undefined;
|
|
136
|
-
return `task=${issue.taskId}, executor=${issue.executor}${implicitDefault ? " (implicit default)" : ""}`;
|
|
137
|
-
})
|
|
138
|
-
.join("; ");
|
|
139
|
-
throw new Error(`forbidden executor present: ${details}`);
|
|
140
|
-
}
|
|
1
|
+
import { parseDagValidateArgs, } from "../application/dag/args.js";
|
|
2
|
+
import { validateDagUseCase } from "../application/dag/validate-dag.js";
|
|
3
|
+
export { parseDagValidateArgs };
|
|
141
4
|
export async function runDagValidate(repoRoot, rawArgs) {
|
|
142
5
|
const parsed = parseDagValidateArgs(rawArgs);
|
|
143
|
-
const
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
const validationIssues = validateDagSpec(spec);
|
|
147
|
-
const warnings = collectExecutorModelWarnings(spec);
|
|
148
|
-
const governanceWarnings = collectGovernanceWarnings(validationIssues);
|
|
149
|
-
if (parsed.strictModelMatrix) {
|
|
150
|
-
ensureStrictModelMatrix(warnings);
|
|
151
|
-
}
|
|
152
|
-
const spineAudit = parsed.spineTaskId
|
|
153
|
-
? await runSpineAudit(repoRoot, parsed.spineTaskId)
|
|
154
|
-
: undefined;
|
|
155
|
-
if (parsed.strictGovernance && spineAudit && !spineAudit.ok) {
|
|
156
|
-
throw new Error(`minimal spec spine audit failed for ${parsed.spineTaskId}: ${spineAudit.findings
|
|
157
|
-
.filter((finding) => finding.severity === "error")
|
|
158
|
-
.map((finding) => `${finding.type}: ${finding.message}`)
|
|
159
|
-
.join("; ")}`);
|
|
160
|
-
}
|
|
161
|
-
assertValidDagSpec(spec, {
|
|
162
|
-
issues: validationIssues,
|
|
163
|
-
strictGovernance: parsed.strictGovernance,
|
|
6
|
+
const result = await validateDagUseCase({
|
|
7
|
+
repoRoot,
|
|
8
|
+
...parsed,
|
|
164
9
|
});
|
|
165
|
-
const { ranks } = topoSortToRanks(spec);
|
|
166
|
-
const governanceProfile = inferGovernanceProfile(spec);
|
|
167
10
|
console.log(JSON.stringify({
|
|
168
11
|
mode: "validate",
|
|
169
|
-
|
|
170
|
-
taskCount: spec.tasks.length,
|
|
171
|
-
ranks,
|
|
172
|
-
governanceProfile,
|
|
173
|
-
spineAudit: spineAudit
|
|
174
|
-
? {
|
|
175
|
-
ok: spineAudit.ok,
|
|
176
|
-
taskId: spineAudit.taskId,
|
|
177
|
-
findings: spineAudit.findings,
|
|
178
|
-
verifyCommandLabels: spineAudit.verifyCommandLabels,
|
|
179
|
-
}
|
|
180
|
-
: undefined,
|
|
181
|
-
warnings: [
|
|
182
|
-
...formatModelMatrixWarnings(warnings),
|
|
183
|
-
...formatGovernanceWarnings(governanceWarnings),
|
|
184
|
-
],
|
|
12
|
+
...result,
|
|
185
13
|
}, null, 2));
|
|
186
14
|
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { copyFile, mkdir, readdir, readFile } from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
async function exists(filePath) {
|
|
5
|
+
try {
|
|
6
|
+
await readFile(filePath, "utf-8");
|
|
7
|
+
return true;
|
|
8
|
+
}
|
|
9
|
+
catch {
|
|
10
|
+
return false;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
async function findPackageRoot() {
|
|
14
|
+
let current = path.dirname(fileURLToPath(import.meta.url));
|
|
15
|
+
while (true) {
|
|
16
|
+
if (await exists(path.join(current, "package.json")))
|
|
17
|
+
return current;
|
|
18
|
+
const parent = path.dirname(current);
|
|
19
|
+
if (parent === current)
|
|
20
|
+
return path.resolve(".");
|
|
21
|
+
current = parent;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
async function examplesRoot() {
|
|
25
|
+
return path.join(await findPackageRoot(), "examples");
|
|
26
|
+
}
|
|
27
|
+
function assertSafeExampleName(name) {
|
|
28
|
+
if (!name || name.includes("/") || name.includes("\\") || name.includes("..")) {
|
|
29
|
+
throw new Error(`invalid example name: ${name}`);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
export async function listBundledExamples() {
|
|
33
|
+
const root = await examplesRoot();
|
|
34
|
+
const entries = await readdir(root, { withFileTypes: true });
|
|
35
|
+
return entries
|
|
36
|
+
.filter((entry) => entry.isFile())
|
|
37
|
+
.map((entry) => entry.name)
|
|
38
|
+
.sort();
|
|
39
|
+
}
|
|
40
|
+
export async function readBundledExample(name) {
|
|
41
|
+
assertSafeExampleName(name);
|
|
42
|
+
return readFile(path.join(await examplesRoot(), name), "utf-8");
|
|
43
|
+
}
|
|
44
|
+
export async function copyBundledExample(input) {
|
|
45
|
+
assertSafeExampleName(input.name);
|
|
46
|
+
const targetRelative = input.output ?? path.join("examples", input.name);
|
|
47
|
+
const target = path.join(input.repoRoot, targetRelative);
|
|
48
|
+
await mkdir(path.dirname(target), { recursive: true });
|
|
49
|
+
await copyFile(path.join(await examplesRoot(), input.name), target);
|
|
50
|
+
return target;
|
|
51
|
+
}
|
|
52
|
+
function parseExamplesArgs(args) {
|
|
53
|
+
const [subcommand, name, ...rest] = args;
|
|
54
|
+
if (!subcommand || !["list", "show", "copy"].includes(subcommand)) {
|
|
55
|
+
throw new Error("usage: examples <list|show|copy> [name] [--output <path>]");
|
|
56
|
+
}
|
|
57
|
+
let output;
|
|
58
|
+
for (let i = 0; i < rest.length; i += 1) {
|
|
59
|
+
const arg = rest[i];
|
|
60
|
+
if (arg === "--output" || arg === "-o") {
|
|
61
|
+
output = rest[++i];
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
if (arg.startsWith("--output=")) {
|
|
65
|
+
output = arg.slice("--output=".length);
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
throw new Error(`unknown examples flag: ${arg}`);
|
|
69
|
+
}
|
|
70
|
+
return { subcommand, name, output };
|
|
71
|
+
}
|
|
72
|
+
export async function runExamples(repoRoot, rawArgs) {
|
|
73
|
+
const parsed = parseExamplesArgs(rawArgs);
|
|
74
|
+
if (parsed.subcommand === "list") {
|
|
75
|
+
console.log((await listBundledExamples()).join("\n"));
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
if (!parsed.name)
|
|
79
|
+
throw new Error(`examples ${parsed.subcommand} requires <name>`);
|
|
80
|
+
if (parsed.subcommand === "show") {
|
|
81
|
+
console.log(await readBundledExample(parsed.name));
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
const target = await copyBundledExample({
|
|
85
|
+
name: parsed.name,
|
|
86
|
+
repoRoot,
|
|
87
|
+
output: parsed.output,
|
|
88
|
+
});
|
|
89
|
+
console.log(target);
|
|
90
|
+
}
|