@tea-agent/loop-agent 0.44.0-next.10 → 0.44.0-next.12
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/CHANGELOG.md +11 -0
- package/dist/build-stamp.json +3 -3
- package/dist/executors/dag-pi/sessions/index.js +6 -0
- package/dist/executors/dag-pi/sessions/plan-batches.js +280 -0
- package/dist/executors/dag-pi/sessions/plan-prompts.js +367 -0
- package/dist/executors/dag-pi/sessions/scout-parallel.js +197 -0
- package/dist/executors/dag-pi/sessions/segmented-plan.js +1184 -0
- package/dist/executors/dag-pi/sessions/writer-evidence.js +60 -0
- package/dist/executors/dag-pi-executor.js +12 -2075
- package/dist/executors/shell-executor.js +39 -11
- package/dist/worker/console/routes.js +5 -0
- package/dist/worker/console/static/app-icon.svg +39 -0
- package/dist/worker/console/static/index.html +3 -0
- package/dist/worker/console/static/manifest.webmanifest +19 -0
- package/dist/worker/observe/static/console-theme.js +38 -0
- package/dist/workflows/dag/checkpoint.js +686 -0
- package/dist/workflows/dag/frontend-repair.js +24 -0
- package/dist/workflows/dag/frontend-review-scopes.js +10 -2
- package/dist/workflows/dag/frontend-test-execution-evidence.js +163 -67
- package/dist/workflows/dag/frontend-test-framework-adapters.js +309 -0
- package/dist/workflows/dag/hybrid/sources.js +1812 -0
- package/dist/workflows/dag/hybrid/templates/backend-test.js +1807 -0
- package/dist/workflows/dag/hybrid/templates/frontend-test.js +702 -0
- package/dist/workflows/dag/hybrid/templates/frontend.js +1529 -0
- package/dist/workflows/dag/hybrid/templates/index.js +8 -0
- package/dist/workflows/dag/hybrid/templates/kg-bootstrap.js +449 -0
- package/dist/workflows/dag/hybrid/templates/knowledge-sync.js +495 -0
- package/dist/workflows/dag/hybrid/templates/shared.js +101 -0
- package/dist/workflows/dag/hybrid/templates/standard.js +265 -0
- package/dist/workflows/dag/hybrid/types.js +32 -0
- package/dist/workflows/dag/init-hybrid.js +42 -7162
- package/dist/workflows/dag/runner.js +11 -1178
- package/dist/workflows/dag/terminal-status.js +502 -0
- package/docs/architecture/dag-execution.md +4 -4
- package/package.json +1 -1
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
/** DAG hybrid standard 模板族(含 reviewed/supervised 对 standard 的装饰关系入口)。 */
|
|
2
|
+
import { assertValidDagSpec } from "../../validate.js";
|
|
3
|
+
import { DEFAULT_DAG_EXECUTOR_MODELS, DEFAULT_DAG_OUTPUT_LANGUAGE, parseDagSpec } from "../../types.js";
|
|
4
|
+
import { TARGET_TEMPLATE_TRANSIENT_RETRY_PROFILE, isCanonicalFinalVerifyShellRetryCandidate, isTargetTemplateImplementPi } from "../../retry-policy.js";
|
|
5
|
+
import { applySddEmbeddedEnhancements } from "../../sdd-embedded.js";
|
|
6
|
+
import { DEFAULT_VERIFY_TIMEOUT_MS } from "../../../../executors/shell-verification.js";
|
|
7
|
+
import { resolveWriterComplexity, writerDeliveryContract } from "../types.js";
|
|
8
|
+
import { GENERATED_DAG_RUNTIME_CONTRACT, HYBRID_DEFAULTS, HYBRID_SKILLS_BY_ROLE, STANDARD_GLOBAL_CONSTRAINTS, applyMavenVerificationPlanning, buildSourceContextBlock, buildVerifyEvidence, buildVerifyShellCommands, canonicalizeVerificationCommands, extractObjective, extractSuccessCriteria, mergeForbiddenPaths, resolveImplementPaths } from "../sources.js";
|
|
9
|
+
import { applyDefaultReadOnlyRetryPolicy, isFrontendTestPathPattern, mapTaskComplexity, replaceSourceSegmentWithTestSegment, stampGeneratedArtifactBindings, } from "./shared.js";
|
|
10
|
+
export function buildStandardHybridDagFromTask(sources) {
|
|
11
|
+
const { taskConfig } = sources;
|
|
12
|
+
const forbiddenPaths = mergeForbiddenPaths(taskConfig);
|
|
13
|
+
const implementPaths = resolveImplementPaths(taskConfig, {
|
|
14
|
+
repoRoot: sources.repoRoot,
|
|
15
|
+
});
|
|
16
|
+
const scoutPaths = deriveParallelScoutPaths(taskConfig);
|
|
17
|
+
const scoutComplexity = mapTaskComplexity(taskConfig.complexity);
|
|
18
|
+
const implementComplexity = resolveWriterComplexity(taskConfig);
|
|
19
|
+
const implementId = implementationNodeId();
|
|
20
|
+
const finalVerifyCommands = sources.verifyCommands?.final ?? [];
|
|
21
|
+
const finalVerifyPlan = canonicalizeVerificationCommands(finalVerifyCommands);
|
|
22
|
+
const plannedFinal = applyMavenVerificationPlanning({
|
|
23
|
+
repoRoot: sources.repoRoot,
|
|
24
|
+
commands: finalVerifyPlan.commands,
|
|
25
|
+
taskConfig,
|
|
26
|
+
});
|
|
27
|
+
const verifyShellCommands = buildVerifyShellCommands({
|
|
28
|
+
repoRoot: sources.repoRoot,
|
|
29
|
+
commands: plannedFinal.commands,
|
|
30
|
+
fallbackCommands: [],
|
|
31
|
+
});
|
|
32
|
+
const verifyShellTask = verifyShellCommands.length > 0
|
|
33
|
+
? [
|
|
34
|
+
{
|
|
35
|
+
id: "verify-shell",
|
|
36
|
+
depends_on: [implementId],
|
|
37
|
+
role: "verifier",
|
|
38
|
+
executor: "shell",
|
|
39
|
+
complexity: "LOW",
|
|
40
|
+
writePolicy: "read-only",
|
|
41
|
+
allowedPaths: taskConfig.allowedPaths.length > 0
|
|
42
|
+
? taskConfig.allowedPaths
|
|
43
|
+
: ["**"],
|
|
44
|
+
forbiddenPaths,
|
|
45
|
+
outputContract: "Archived final shell verification stdout/stderr with exit codes; no worktree writes.",
|
|
46
|
+
subtask_prompt: "Run the adapter-resolved final verification commands before read-only verification review.",
|
|
47
|
+
transientRetryProfile: TARGET_TEMPLATE_TRANSIENT_RETRY_PROFILE,
|
|
48
|
+
shell: {
|
|
49
|
+
commands: verifyShellCommands,
|
|
50
|
+
verifyEvidence: buildVerifyEvidence({
|
|
51
|
+
phase: "final",
|
|
52
|
+
quota: "full",
|
|
53
|
+
commandSource: "adapter",
|
|
54
|
+
commands: plannedFinal.commands,
|
|
55
|
+
fallbackCommands: [],
|
|
56
|
+
finalFullRequired: true,
|
|
57
|
+
commandTimeoutMs: DEFAULT_VERIFY_TIMEOUT_MS,
|
|
58
|
+
plan: finalVerifyPlan,
|
|
59
|
+
preflight: sources.verificationPreflight,
|
|
60
|
+
}),
|
|
61
|
+
cwd: ".",
|
|
62
|
+
timeoutMs: DEFAULT_VERIFY_TIMEOUT_MS,
|
|
63
|
+
...(plannedFinal.mavenVerificationPlan
|
|
64
|
+
? { mavenVerificationPlan: plannedFinal.mavenVerificationPlan }
|
|
65
|
+
: {}),
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
]
|
|
69
|
+
: [];
|
|
70
|
+
const sourceContext = buildSourceContextBlock(sources);
|
|
71
|
+
const globalConstraints = [
|
|
72
|
+
...taskConfig.hardConstraints,
|
|
73
|
+
...(sources.constraintMarkdown
|
|
74
|
+
? [`See 执行约束.md in task source (${sources.taskId})`]
|
|
75
|
+
: []),
|
|
76
|
+
...STANDARD_GLOBAL_CONSTRAINTS,
|
|
77
|
+
];
|
|
78
|
+
const spec = {
|
|
79
|
+
version: 3,
|
|
80
|
+
title: `Hybrid DAG: ${taskConfig.title}`,
|
|
81
|
+
runtimeContract: GENERATED_DAG_RUNTIME_CONTRACT,
|
|
82
|
+
outputLanguage: sources.outputLanguage ?? DEFAULT_DAG_OUTPUT_LANGUAGE,
|
|
83
|
+
objective: extractObjective(sources.requirementMarkdown, taskConfig.title),
|
|
84
|
+
successCriteria: extractSuccessCriteria(sources.requirementMarkdown, sources.taskId),
|
|
85
|
+
globalConstraints,
|
|
86
|
+
defaults: {
|
|
87
|
+
...HYBRID_DEFAULTS,
|
|
88
|
+
contextProfile: taskConfig.contextProfile,
|
|
89
|
+
},
|
|
90
|
+
skillsByRole: HYBRID_SKILLS_BY_ROLE,
|
|
91
|
+
executorModels: sources.executorModelMatrix ?? DEFAULT_DAG_EXECUTOR_MODELS,
|
|
92
|
+
tasks: [
|
|
93
|
+
{
|
|
94
|
+
id: "contract-pi",
|
|
95
|
+
depends_on: [],
|
|
96
|
+
role: "planner",
|
|
97
|
+
executor: "pi",
|
|
98
|
+
complexity: "MED",
|
|
99
|
+
writePolicy: "read-only",
|
|
100
|
+
allowedPaths: taskConfig.allowedPaths.length > 0 ? taskConfig.allowedPaths : ["**"],
|
|
101
|
+
forbiddenPaths,
|
|
102
|
+
outputContract: "Plain Markdown implementation contract (10 lines or fewer); no file writes.",
|
|
103
|
+
subtask_prompt: [
|
|
104
|
+
"Read the task source materials and return a concise implementation contract (10 lines or fewer).",
|
|
105
|
+
"Cover scope, risks, and verification expectations. Read-only: do not modify code, docs, artifacts, or repository files.",
|
|
106
|
+
sourceContext,
|
|
107
|
+
].join("\n\n"),
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
id: "scout-src",
|
|
111
|
+
depends_on: ["contract-pi"],
|
|
112
|
+
role: "scout",
|
|
113
|
+
executor: "pi",
|
|
114
|
+
complexity: scoutComplexity,
|
|
115
|
+
writePolicy: "read-only",
|
|
116
|
+
allowedPaths: scoutPaths.srcPaths,
|
|
117
|
+
forbiddenPaths,
|
|
118
|
+
outputContract: "Plain Markdown source reconnaissance summary; no file writes.",
|
|
119
|
+
subtask_prompt: [
|
|
120
|
+
"Perform read-only source reconnaissance for the task scope.",
|
|
121
|
+
"List key source files, existing patterns, and integration points. Do not modify files.",
|
|
122
|
+
sourceContext,
|
|
123
|
+
].join("\n\n"),
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
id: "scout-tests",
|
|
127
|
+
depends_on: ["contract-pi"],
|
|
128
|
+
role: "scout",
|
|
129
|
+
executor: "pi",
|
|
130
|
+
complexity: scoutComplexity,
|
|
131
|
+
writePolicy: "read-only",
|
|
132
|
+
allowedPaths: scoutPaths.testPaths,
|
|
133
|
+
forbiddenPaths,
|
|
134
|
+
outputContract: "Plain Markdown test coverage reconnaissance summary; no file writes.",
|
|
135
|
+
subtask_prompt: [
|
|
136
|
+
"Perform read-only test reconnaissance for the task scope.",
|
|
137
|
+
"List relevant tests, coverage gaps, and verification commands. Do not modify files.",
|
|
138
|
+
sourceContext,
|
|
139
|
+
].join("\n\n"),
|
|
140
|
+
},
|
|
141
|
+
{
|
|
142
|
+
id: "plan-pi",
|
|
143
|
+
depends_on: ["scout-src", "scout-tests"],
|
|
144
|
+
role: "planner",
|
|
145
|
+
executor: "pi",
|
|
146
|
+
complexity: "MED",
|
|
147
|
+
writePolicy: "read-only",
|
|
148
|
+
allowedPaths: taskConfig.allowedPaths.length > 0 ? taskConfig.allowedPaths : ["**"],
|
|
149
|
+
forbiddenPaths,
|
|
150
|
+
outputContract: "Plain Markdown implementation plan; no file writes.",
|
|
151
|
+
subtask_prompt: [
|
|
152
|
+
"Based on contract-pi, scout-src, and scout-tests upstream output, return a minimal implementation plan.",
|
|
153
|
+
"Identify ordered steps, narrow writeSet boundaries, and verification commands. Read-only: do not modify code, docs, artifacts, or repository files.",
|
|
154
|
+
sourceContext,
|
|
155
|
+
].join("\n\n"),
|
|
156
|
+
},
|
|
157
|
+
{
|
|
158
|
+
id: implementId,
|
|
159
|
+
depends_on: ["plan-pi"],
|
|
160
|
+
role: "implementer",
|
|
161
|
+
executor: "pi",
|
|
162
|
+
toolProfile: "write",
|
|
163
|
+
complexity: implementComplexity,
|
|
164
|
+
writePolicy: "exclusive",
|
|
165
|
+
writeSet: implementPaths.writeSet,
|
|
166
|
+
allowedPaths: implementPaths.allowedPaths,
|
|
167
|
+
forbiddenPaths,
|
|
168
|
+
writerOutcomePolicy: { type: "implementation-outcome-v1" },
|
|
169
|
+
transientRetryProfile: TARGET_TEMPLATE_TRANSIENT_RETRY_PROFILE,
|
|
170
|
+
subtask_prompt: [
|
|
171
|
+
"Implement the approved plan with minimal focused changes.",
|
|
172
|
+
"Stay within writeSet. Do not write root artifacts/** unless artifacts paths are explicitly declared in writeSet.",
|
|
173
|
+
"Apply changes by calling the write/edit tools so files change on disk. Do not paste the new code in your response instead of writing it: the runner verifies the working-tree diff mechanically, and a response-only change is an empty diff that fails this node.",
|
|
174
|
+
"Edit existing files with the structured edit/write tools. NEVER rewrite Markdown (or any file with quoting/backticks/indentation-sensitive content) via bash sed/awk/echo redirection: escaping mistakes silently corrupt the file and self-repair loops burn the run. If a bash text-substitution already damaged a file, restore the damaged region with the edit tool against the original content instead of stacking more substitutions.",
|
|
175
|
+
"The plan node already designed the changes — that design is your input, not your output. Your job is to write those changes into the files in writeSet. The task is not done until the files on disk contain the changes.",
|
|
176
|
+
"If the task only adds tests/docs that use existing code as-is, forbidden implementation paths (e.g. src/**) do not block you: you never need to write them. Write the tests/docs and report changed.",
|
|
177
|
+
...(implementPaths.docIndexCompanions.length > 0
|
|
178
|
+
? [
|
|
179
|
+
`Doc index sync is MANDATORY: ${implementPaths.docIndexCompanions.join(", ")} are catalog index files for this writeSet. When you add, rename, or remove any indexed file, you MUST also update ${implementPaths.docIndexCompanions.join(" and ")} in the same run (append or fix the matching index entry following the existing line style). Verification runs check-doc-index and fails the run when a new file is missing from the index. Skipping this sync is not "minimal change" — it is an incomplete delivery.`,
|
|
180
|
+
]
|
|
181
|
+
: []),
|
|
182
|
+
writerDeliveryContract(taskConfig),
|
|
183
|
+
sourceContext,
|
|
184
|
+
]
|
|
185
|
+
.filter((value) => Boolean(value))
|
|
186
|
+
.join("\n\n"),
|
|
187
|
+
},
|
|
188
|
+
...verifyShellTask,
|
|
189
|
+
{
|
|
190
|
+
id: "verify-pi",
|
|
191
|
+
depends_on: verifyShellTask.length > 0 ? ["verify-shell"] : [implementId],
|
|
192
|
+
role: "verifier",
|
|
193
|
+
executor: "pi",
|
|
194
|
+
complexity: "MED",
|
|
195
|
+
writePolicy: "read-only",
|
|
196
|
+
allowedPaths: taskConfig.allowedPaths.length > 0 ? taskConfig.allowedPaths : ["**"],
|
|
197
|
+
forbiddenPaths,
|
|
198
|
+
outputContract: "Plain Markdown verification matrix and residual risks; no file writes.",
|
|
199
|
+
subtask_prompt: [
|
|
200
|
+
"Review upstream implementation and return the verification matrix.",
|
|
201
|
+
`Task verifyMode=${taskConfig.verifyMode}, verifyPreset=${taskConfig.verifyPreset}.`,
|
|
202
|
+
"List commands to run, expected pass/fail evidence, and residual risks. Read-only: do not modify code, docs, artifacts, or repository files.",
|
|
203
|
+
sourceContext,
|
|
204
|
+
].join("\n\n"),
|
|
205
|
+
},
|
|
206
|
+
{
|
|
207
|
+
id: "closeout-pi",
|
|
208
|
+
depends_on: ["verify-pi"],
|
|
209
|
+
failureAwareDependsOn: ["verify-pi"],
|
|
210
|
+
role: "closeout",
|
|
211
|
+
executor: "pi",
|
|
212
|
+
complexity: "MED",
|
|
213
|
+
writePolicy: "read-only",
|
|
214
|
+
allowedPaths: taskConfig.allowedPaths.length > 0
|
|
215
|
+
? [...taskConfig.allowedPaths, "docs/**"]
|
|
216
|
+
: ["**", "docs/**"],
|
|
217
|
+
forbiddenPaths,
|
|
218
|
+
outputContract: "Plain Markdown closeout summary; no file writes.",
|
|
219
|
+
subtask_prompt: [
|
|
220
|
+
"Return closeout summary: what changed, verification status, risks, and handoff notes.",
|
|
221
|
+
"If upstream failure evidence or a failure-derived skip is present, report the run as failed/partial_failed and never infer a passing result.",
|
|
222
|
+
"Read-only: do not modify code, docs, artifacts, or .harness/dag-runs/.",
|
|
223
|
+
sourceContext,
|
|
224
|
+
].join("\n\n"),
|
|
225
|
+
},
|
|
226
|
+
],
|
|
227
|
+
};
|
|
228
|
+
applySddEmbeddedEnhancements(spec, sources.sddEmbeddedSkills ?? new Set());
|
|
229
|
+
stampTargetTemplateTransientRetryProfile(spec);
|
|
230
|
+
applyDefaultReadOnlyRetryPolicy(spec);
|
|
231
|
+
stampGeneratedArtifactBindings(spec);
|
|
232
|
+
parseDagSpec(spec);
|
|
233
|
+
assertValidDagSpec(spec);
|
|
234
|
+
return spec;
|
|
235
|
+
}
|
|
236
|
+
export function deriveParallelScoutPaths(taskConfig) {
|
|
237
|
+
const allowed = taskConfig.allowedPaths;
|
|
238
|
+
if (allowed.length === 0) {
|
|
239
|
+
return { srcPaths: ["**"], testPaths: ["**"] };
|
|
240
|
+
}
|
|
241
|
+
const srcPaths = allowed.filter((entry) => !isFrontendTestPathPattern(entry));
|
|
242
|
+
const explicitTestPaths = allowed.filter(isFrontendTestPathPattern);
|
|
243
|
+
const derivedTestPaths = allowed
|
|
244
|
+
.map(replaceSourceSegmentWithTestSegment)
|
|
245
|
+
.filter((entry) => Boolean(entry));
|
|
246
|
+
return {
|
|
247
|
+
srcPaths: srcPaths.length > 0 ? srcPaths : allowed,
|
|
248
|
+
testPaths: explicitTestPaths.length > 0
|
|
249
|
+
? explicitTestPaths
|
|
250
|
+
: derivedTestPaths.length > 0
|
|
251
|
+
? derivedTestPaths
|
|
252
|
+
: allowed,
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
export function implementationNodeId() {
|
|
256
|
+
return "implement-pi";
|
|
257
|
+
}
|
|
258
|
+
export function stampTargetTemplateTransientRetryProfile(spec) {
|
|
259
|
+
for (const task of spec.tasks) {
|
|
260
|
+
if (isTargetTemplateImplementPi(task) ||
|
|
261
|
+
isCanonicalFinalVerifyShellRetryCandidate(task)) {
|
|
262
|
+
task.transientRetryProfile = TARGET_TEMPLATE_TRANSIENT_RETRY_PROFILE;
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export const INTERACTIVE_UI_DELIVERY_CONTRACT = [
|
|
2
|
+
"[INTERACTIVE_UI_DELIVERY_CONTRACT]",
|
|
3
|
+
"This is an interactive UI delivery task.",
|
|
4
|
+
"A helper-only, data-only, validation-only, or serializable-view implementation does not satisfy the task.",
|
|
5
|
+
"Before editing: identify the existing UI framework and component conventions, the real render/integration point, and the user-interaction test seam.",
|
|
6
|
+
"Required delivery: a framework-native visible component; integration into the declared route or parent; user interaction and visible error rendering; component-level tests using the repository's UI test stack.",
|
|
7
|
+
"The completion summary must list the component path, integration path, interaction test path, and DOM assertions mapped to acceptance criteria. If any item is missing, do not claim completion.",
|
|
8
|
+
"[/INTERACTIVE_UI_DELIVERY_CONTRACT]",
|
|
9
|
+
].join("\n");
|
|
10
|
+
export function isInteractiveUiTask(taskConfig) {
|
|
11
|
+
return taskConfig.capabilities?.includes("interactive-ui") ?? false;
|
|
12
|
+
}
|
|
13
|
+
export function writerDeliveryContract(taskConfig) {
|
|
14
|
+
return isInteractiveUiTask(taskConfig)
|
|
15
|
+
? INTERACTIVE_UI_DELIVERY_CONTRACT
|
|
16
|
+
: null;
|
|
17
|
+
}
|
|
18
|
+
export function resolveWriterComplexity(taskConfig) {
|
|
19
|
+
if (isInteractiveUiTask(taskConfig))
|
|
20
|
+
return "HIGH";
|
|
21
|
+
if (taskConfig.complexity === "small")
|
|
22
|
+
return "MED";
|
|
23
|
+
if (taskConfig.complexity === "large")
|
|
24
|
+
return "HIGH";
|
|
25
|
+
return "MED";
|
|
26
|
+
}
|
|
27
|
+
export function resolveEnabledExecutors(manifestExecutors) {
|
|
28
|
+
return Object.entries(manifestExecutors)
|
|
29
|
+
.filter(([, config]) => config.enabled !== false)
|
|
30
|
+
.map(([name]) => name)
|
|
31
|
+
.filter((name) => name === "pi");
|
|
32
|
+
}
|