@tea-agent/loop-agent 0.26.5-beta.1 → 0.27.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 +2 -2
- package/CHANGELOG.md +29 -0
- package/README.md +13 -23
- package/dist/application/dag/args.js +7 -7
- package/dist/application/dag/generate-task-dag.js +2 -2
- package/dist/application/dag/run-dag.js +3 -4
- package/dist/application/dag/validate-dag.js +1 -1
- package/dist/application/task-lifecycle/advance.js +845 -0
- package/dist/application/task-lifecycle/gates.js +80 -0
- package/dist/application/task-lifecycle/index.js +7 -0
- package/dist/application/task-lifecycle/observe.js +395 -0
- package/dist/application/task-lifecycle/plan-transitions.js +224 -0
- package/dist/application/task-lifecycle/recommendations.js +180 -0
- package/dist/application/task-lifecycle/record.js +73 -0
- package/dist/application/task-lifecycle/types.js +1 -0
- package/dist/cli/command-definitions.js +14 -111
- package/dist/cli/help.js +6 -7
- package/dist/cli/program.js +26 -90
- package/dist/cli/update/policy.js +0 -1
- package/dist/commands/dag-final-verification.js +1 -1
- package/dist/commands/dag-init-hybrid.js +1 -1
- package/dist/commands/delegate.js +2 -2
- package/dist/commands/init.js +21 -19
- package/dist/commands/run-dag-progress.js +1 -1
- package/dist/commands/status.js +18 -17
- package/dist/commands/study-init.js +2 -2
- package/dist/commands/task-advance.js +335 -0
- package/dist/commands/task-contract.js +3 -5
- package/dist/commands/task-source-prepare.js +9 -4
- package/dist/commands/task-status.js +133 -0
- package/dist/governance/manifest-types.js +2 -2
- package/dist/shared/operator/capabilities.js +1358 -243
- package/dist/task/contract/adopt.js +1 -1
- package/dist/task/contract/apply.js +1 -1
- package/dist/task/contract/import-revision.js +1 -1
- package/dist/task/contract/recover.js +2 -2
- package/dist/task/read-model.js +18 -23
- package/dist/task/runtime.js +1 -1
- package/dist/task/source-prepare/completeness.js +1 -1
- package/dist/task/source-prepare/parse-intent.js +6 -1
- package/dist/task/source-prepare/prepare.js +23 -20
- package/dist/worker/console/operator-actions.js +288 -95
- package/dist/worker/console/recovery-cta.js +4 -4
- package/dist/worker/console/static/assets/{index-CSRIhuzh.js → index-CNO7n6qB.js} +1 -1
- package/dist/worker/console/static/index.html +1 -1
- package/dist/worker/materialize/harness-task-materializer.js +6 -5
- package/dist/worker/run-task/run-task.js +204 -112
- package/dist/worker/runner/run-ready.js +1 -1
- package/dist/workflows/dag/frontend-implementation-contract.js +5 -57
- package/dist/workflows/dag/frontend-prewrite-gate.js +1 -6
- package/dist/workflows/dag/init-hybrid.js +11 -42
- package/docs/templates/evaluation/agents-map-slim-v1.md +1 -1
- package/docs/templates/evaluation/agents-map-verbose-v0.md +3 -3
- package/docs/templates/harness.schema.json +2 -2
- package/docs/templates/init-managed-agents.md +13 -16
- package/docs/templates/production-readiness-checklist.md +3 -3
- package/harness.json +4 -4
- package/package.json +1 -1
- package/scripts/kb-bootstrap-init-skeleton.sh +2 -1
- package/scripts/kb-graph-incremental-prepare.mjs +2 -2
- package/skills/loop-agent/SKILL.md +18 -13
- package/skills/loop-agent/references/README.md +1 -1
- package/skills/loop-agent/references/command-reference.md +55 -84
- package/skills/loop-agent/references/harness-policy.md +19 -23
- package/skills/loop-agent/references/hybrid-dag.md +31 -33
- package/skills/loop-agent/references/long-running-loop.md +2 -2
- package/skills/loop-agent/references/one-shot-runs.md +4 -5
- package/skills/loop-agent/references/orchestrator-and-interventions.md +3 -3
- package/skills/loop-agent/references/post-implementation-and-patterns.md +4 -4
- package/skills/loop-agent/references/source-and-plan-practice.md +45 -51
- package/skills/loop-agent/references/task-workflow.md +14 -15
|
@@ -61,7 +61,7 @@ export async function adoptTaskContract(input) {
|
|
|
61
61
|
}
|
|
62
62
|
const state = await observeTaskContractState(input.repoRoot, input.taskId);
|
|
63
63
|
if (state.effectiveStatus === "transaction-incomplete") {
|
|
64
|
-
throw new ContractMutationError("TRANSACTION_INCOMPLETE", "unfinished transaction; run task
|
|
64
|
+
throw new ContractMutationError("TRANSACTION_INCOMPLETE", "unfinished transaction; re-run task advance (auto recover-contract)");
|
|
65
65
|
}
|
|
66
66
|
const currentRevision = logicalRevisionForState(state);
|
|
67
67
|
if (input.expectedRevision !== currentRevision) {
|
|
@@ -180,7 +180,7 @@ export async function applyTaskContract(input) {
|
|
|
180
180
|
}
|
|
181
181
|
const state = await observeTaskContractState(input.repoRoot, input.taskId);
|
|
182
182
|
if (state.effectiveStatus === "transaction-incomplete") {
|
|
183
|
-
throw new ContractMutationError("TRANSACTION_INCOMPLETE", "unfinished transaction; run task
|
|
183
|
+
throw new ContractMutationError("TRANSACTION_INCOMPLETE", "unfinished transaction; re-run task advance (auto recover-contract)", { errors: state.observationErrors });
|
|
184
184
|
}
|
|
185
185
|
const currentRevision = logicalRevisionForState(state);
|
|
186
186
|
if (input.expectedRevision !== currentRevision) {
|
|
@@ -17,7 +17,7 @@ export async function refreshManagedContractAfterImport(input) {
|
|
|
17
17
|
return null;
|
|
18
18
|
}
|
|
19
19
|
if (state.effectiveStatus === "transaction-incomplete") {
|
|
20
|
-
throw new Error(`TRANSACTION_INCOMPLETE: cannot refresh contract after import-prd for ${input.taskId}; run task
|
|
20
|
+
throw new Error(`TRANSACTION_INCOMPLETE: cannot refresh contract after import-prd for ${input.taskId}; re-run task advance (auto recover-contract)`);
|
|
21
21
|
}
|
|
22
22
|
const requestId = input.requestId ??
|
|
23
23
|
`import-prd-${Date.now()}-${Math.random().toString(16).slice(2, 10)}`;
|
|
@@ -27,10 +27,10 @@ export async function doctorTaskContract(input) {
|
|
|
27
27
|
}
|
|
28
28
|
const recommendations = [];
|
|
29
29
|
if (state.effectiveStatus === "transaction-incomplete") {
|
|
30
|
-
recommendations.push("
|
|
30
|
+
recommendations.push("Re-run: loop-agent task advance <id> --json");
|
|
31
31
|
}
|
|
32
32
|
if (state.effectiveStatus === "legacy-unversioned") {
|
|
33
|
-
recommendations.push("
|
|
33
|
+
recommendations.push("Re-run: loop-agent task advance <id> --from-draft <draft.json> --json");
|
|
34
34
|
}
|
|
35
35
|
if (state.effectiveStatus === "externally-modified") {
|
|
36
36
|
recommendations.push("Review out-of-band edits, then apply a new Draft or adopt current facts");
|
package/dist/task/read-model.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { access } from "node:fs/promises";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { queryCursorRuns } from "../records/one-shot-runs.js";
|
|
4
|
-
import {
|
|
4
|
+
import { listExplicitlyAssociatedRuns } from "../application/task-lifecycle/observe.js";
|
|
5
5
|
import { getTaskDir, getTaskPaths, getTaskStatus } from "./runtime.js";
|
|
6
6
|
async function exists(filePath) {
|
|
7
7
|
try {
|
|
@@ -13,19 +13,14 @@ async function exists(filePath) {
|
|
|
13
13
|
}
|
|
14
14
|
}
|
|
15
15
|
async function listRelatedDagRuns(repoRoot, taskId) {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
if (spec && JSON.stringify(spec).includes(taskId)) {
|
|
25
|
-
related.push(entry);
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
return related;
|
|
16
|
+
// Explicit association only (taskContractBinding / sourceBinding /
|
|
17
|
+
// workerAssociation / lifecycle.json). No taskId substring or JSON scan.
|
|
18
|
+
const related = await listExplicitlyAssociatedRuns(repoRoot, taskId);
|
|
19
|
+
return related.map((entry) => ({
|
|
20
|
+
runId: entry.runId,
|
|
21
|
+
lifecycle: entry.lifecycle,
|
|
22
|
+
runDir: entry.runDir,
|
|
23
|
+
}));
|
|
29
24
|
}
|
|
30
25
|
function closeoutPath(repoRoot, taskId) {
|
|
31
26
|
return path.join(repoRoot, "docs", "progress", `${taskId}.md`);
|
|
@@ -33,42 +28,42 @@ function closeoutPath(repoRoot, taskId) {
|
|
|
33
28
|
function deriveNextAction(input) {
|
|
34
29
|
if (!input.sourceReady) {
|
|
35
30
|
return {
|
|
36
|
-
command: `
|
|
37
|
-
reason: "source materials are not ready",
|
|
31
|
+
command: `loop-agent task advance ${input.taskId} --prd <path> --allowed-path <glob> --json`,
|
|
32
|
+
reason: "source materials are not ready; advance from PRD + boundaries",
|
|
38
33
|
};
|
|
39
34
|
}
|
|
40
35
|
if (!input.dagDraftExists && !input.latestDagRun) {
|
|
41
36
|
return {
|
|
42
|
-
command: `
|
|
37
|
+
command: `loop-agent task advance ${input.taskId} --json`,
|
|
43
38
|
reason: "source ready; DAG draft/run evidence not found",
|
|
44
39
|
};
|
|
45
40
|
}
|
|
46
41
|
if (input.dagDraftExists && !input.latestDagRun) {
|
|
47
42
|
return {
|
|
48
|
-
command: `
|
|
49
|
-
reason: "DAG draft exists
|
|
43
|
+
command: `loop-agent task advance ${input.taskId} --json`,
|
|
44
|
+
reason: "DAG draft exists; advance to writeSet gate or continue lifecycle",
|
|
50
45
|
};
|
|
51
46
|
}
|
|
52
47
|
if (input.latestDagRun && input.latestDagRun.lifecycle !== "completed") {
|
|
53
48
|
return {
|
|
54
|
-
command: `
|
|
49
|
+
command: `loop-agent task status ${input.taskId} --json`,
|
|
55
50
|
reason: "DAG run exists but is not completed",
|
|
56
51
|
};
|
|
57
52
|
}
|
|
58
53
|
if (input.latestDagRun && input.promotionRequired && !input.promotionReady) {
|
|
59
54
|
return {
|
|
60
|
-
command: `
|
|
55
|
+
command: `loop-agent task advance ${input.taskId} --json`,
|
|
61
56
|
reason: "completed run evidence needs promotion into task artifacts",
|
|
62
57
|
};
|
|
63
58
|
}
|
|
64
59
|
if (!input.closeoutExists) {
|
|
65
60
|
return {
|
|
66
|
-
command: `
|
|
61
|
+
command: `loop-agent task advance ${input.taskId} --json`,
|
|
67
62
|
reason: "promoted task evidence needs closeout",
|
|
68
63
|
};
|
|
69
64
|
}
|
|
70
65
|
return {
|
|
71
|
-
command: `
|
|
66
|
+
command: `loop-agent task status ${input.taskId} --json`,
|
|
72
67
|
reason: "task has source, run evidence, promotion, and closeout records",
|
|
73
68
|
};
|
|
74
69
|
}
|
package/dist/task/runtime.js
CHANGED
|
@@ -299,7 +299,7 @@ export function getSupportedNextCommand(step, taskId, status, options) {
|
|
|
299
299
|
return `npm run dev -- loop status ${taskId}`;
|
|
300
300
|
}
|
|
301
301
|
if (step === 'analyze' || step === 'plan' || step === 'spec' || step === 'implement' || step === 'verify' || step === 'retrospective') {
|
|
302
|
-
return `npm run dev --
|
|
302
|
+
return `npm run dev -- task advance ${taskId} --profile auto --json`;
|
|
303
303
|
}
|
|
304
304
|
return `${step} step not yet implemented in this prototype; human intervention required`;
|
|
305
305
|
}
|
|
@@ -31,7 +31,7 @@ export function observationBlocksPrepare(state) {
|
|
|
31
31
|
return {
|
|
32
32
|
code: "TRANSACTION_INCOMPLETE",
|
|
33
33
|
level: "blocking",
|
|
34
|
-
message: "unfinished contract transaction; run task
|
|
34
|
+
message: "unfinished contract transaction; re-run task advance (auto recover-contract) before prepare",
|
|
35
35
|
};
|
|
36
36
|
}
|
|
37
37
|
const disallowed = state.observationErrors.filter((e) => !FRESH_SOURCE_MISSING_CODES.has(e.code));
|
|
@@ -335,7 +335,12 @@ export function fillMissingRequirementFacts(base, supplemental) {
|
|
|
335
335
|
...base.sourceLabels,
|
|
336
336
|
...supplemental.sourceLabels,
|
|
337
337
|
]),
|
|
338
|
-
acceptanceConflicts:
|
|
338
|
+
acceptanceConflicts: [
|
|
339
|
+
...(base.acceptanceConflicts ?? []),
|
|
340
|
+
...(base.acceptanceCriteria.length === 0
|
|
341
|
+
? (supplemental.acceptanceConflicts ?? [])
|
|
342
|
+
: []),
|
|
343
|
+
],
|
|
339
344
|
};
|
|
340
345
|
}
|
|
341
346
|
export function parseVerifyFlag(raw) {
|
|
@@ -65,16 +65,16 @@ function buildNextSteps(input) {
|
|
|
65
65
|
next.push(`loop-agent task source prepare ${input.taskId} --use-imported-prd ... --apply --json`);
|
|
66
66
|
}
|
|
67
67
|
if (input.ok && input.mode === "apply") {
|
|
68
|
-
next.push(`loop-agent task
|
|
69
|
-
next.push(`loop-agent
|
|
70
|
-
next.push("审查 DAG writeSet 后再
|
|
68
|
+
next.push(`loop-agent task status ${input.taskId} --json`);
|
|
69
|
+
next.push(`loop-agent task advance ${input.taskId} --profile auto --json`);
|
|
70
|
+
next.push("审查 DAG writeSet 后再 task advance --approve-gate ...");
|
|
71
71
|
}
|
|
72
72
|
if (input.gaps.some((g) => g.code === "DIRTY_SOURCE")) {
|
|
73
|
-
next.push("loop-agent task
|
|
73
|
+
next.push("loop-agent task status --json | task advance --from-draft ...");
|
|
74
74
|
next.push("或显式 --force-overwrite-source 后重跑 prepare");
|
|
75
75
|
}
|
|
76
76
|
if (input.gaps.some((g) => g.code === "TRANSACTION_INCOMPLETE")) {
|
|
77
|
-
next.push(`loop-agent task
|
|
77
|
+
next.push(`loop-agent task advance ${input.taskId} --json`);
|
|
78
78
|
}
|
|
79
79
|
return next;
|
|
80
80
|
}
|
|
@@ -321,10 +321,18 @@ export async function prepareTaskSource(input) {
|
|
|
321
321
|
ok: !blocking,
|
|
322
322
|
gaps,
|
|
323
323
|
});
|
|
324
|
+
const blockingCodes = gaps
|
|
325
|
+
.filter((gap) => gap.level === "blocking")
|
|
326
|
+
.map((gap) => gap.code);
|
|
327
|
+
const transactionIncomplete = blockingCodes.includes("TRANSACTION_INCOMPLETE");
|
|
324
328
|
if (input.mode !== "apply") {
|
|
325
329
|
return {
|
|
326
330
|
ok: !blocking,
|
|
327
|
-
outcome: blocking
|
|
331
|
+
outcome: blocking
|
|
332
|
+
? transactionIncomplete
|
|
333
|
+
? "needs-reconcile"
|
|
334
|
+
: "invalid"
|
|
335
|
+
: "succeeded",
|
|
328
336
|
mode: "dry-run",
|
|
329
337
|
taskId,
|
|
330
338
|
taskKind: built.draft.taskKind,
|
|
@@ -339,13 +347,11 @@ export async function prepareTaskSource(input) {
|
|
|
339
347
|
...(blocking
|
|
340
348
|
? {
|
|
341
349
|
error: {
|
|
342
|
-
code:
|
|
350
|
+
code: transactionIncomplete
|
|
351
|
+
? "TRANSACTION_INCOMPLETE"
|
|
352
|
+
: "INVALID_INPUT",
|
|
343
353
|
message: "task source draft has blocking gaps",
|
|
344
|
-
details: {
|
|
345
|
-
gaps: gaps
|
|
346
|
-
.filter((g) => g.level === "blocking")
|
|
347
|
-
.map((g) => g.code),
|
|
348
|
-
},
|
|
354
|
+
details: { gaps: blockingCodes },
|
|
349
355
|
},
|
|
350
356
|
}
|
|
351
357
|
: {}),
|
|
@@ -365,19 +371,16 @@ export async function prepareTaskSource(input) {
|
|
|
365
371
|
gaps,
|
|
366
372
|
risks,
|
|
367
373
|
projected,
|
|
368
|
-
next: [`loop-agent task
|
|
374
|
+
next: [`loop-agent task advance ${taskId} --json`],
|
|
369
375
|
contractStatus: state.effectiveStatus,
|
|
370
376
|
warnings,
|
|
371
377
|
error: {
|
|
372
378
|
code: "TRANSACTION_INCOMPLETE",
|
|
373
|
-
message: "unfinished transaction; run task
|
|
379
|
+
message: "unfinished transaction; re-run task advance (auto recover-contract)",
|
|
374
380
|
},
|
|
375
381
|
};
|
|
376
382
|
}
|
|
377
383
|
if (blocking) {
|
|
378
|
-
const blockingCodes = gaps
|
|
379
|
-
.filter((g) => g.level === "blocking")
|
|
380
|
-
.map((g) => g.code);
|
|
381
384
|
const outcome = blockingCodes.includes("DIRTY_SOURCE")
|
|
382
385
|
? "blocked"
|
|
383
386
|
: "invalid";
|
|
@@ -417,7 +420,7 @@ export async function prepareTaskSource(input) {
|
|
|
417
420
|
gaps,
|
|
418
421
|
risks,
|
|
419
422
|
projected,
|
|
420
|
-
next: [`loop-agent task
|
|
423
|
+
next: [`loop-agent task status ${taskId} --details --json`],
|
|
421
424
|
error: {
|
|
422
425
|
code: "OBSERVED_HASH_CONFLICT",
|
|
423
426
|
message: "unable to determine expectedObservedHash",
|
|
@@ -477,8 +480,8 @@ export async function prepareTaskSource(input) {
|
|
|
477
480
|
risks,
|
|
478
481
|
projected,
|
|
479
482
|
next: [
|
|
480
|
-
`loop-agent task
|
|
481
|
-
`loop-agent task
|
|
483
|
+
`loop-agent task status ${taskId} --json`,
|
|
484
|
+
`loop-agent task status ${taskId} --details --json`,
|
|
482
485
|
],
|
|
483
486
|
error: {
|
|
484
487
|
code: error.code,
|