@tea-agent/loop-agent 0.44.0-next.10 → 0.44.0-next.11
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/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/workflows/dag/checkpoint.js +686 -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 +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
/** DAG Pi scout 并行分片会话:结果聚合、串行结果合并与 completedShards 缓存。 */
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { observeFrontendSession } from "../../../workflows/dag/frontend-session-budget.js";
|
|
4
|
+
import { createFrontendScoutEvidenceTools } from "../tools/scout-evidence-tools.js";
|
|
5
|
+
export function aggregateParallelPiResults(results) {
|
|
6
|
+
const first = results[0];
|
|
7
|
+
const failed = results.find((result) => !result.ok);
|
|
8
|
+
const representative = failed ?? first;
|
|
9
|
+
return {
|
|
10
|
+
...representative,
|
|
11
|
+
ok: failed === undefined,
|
|
12
|
+
failureCategory: failed?.failureCategory ?? "success",
|
|
13
|
+
durationMs: Math.max(...results.map((result) => result.durationMs), 0),
|
|
14
|
+
exitCode: failed ? failed.exitCode : 0,
|
|
15
|
+
stderr: results.map((result) => result.stderr).filter(Boolean).join("\n"),
|
|
16
|
+
tokensUsed: results.reduce((total, result) => total + result.tokensUsed, 0),
|
|
17
|
+
parsedEvents: results.reduce((total, result) => total + result.parsedEvents, 0),
|
|
18
|
+
attemptedModels: [
|
|
19
|
+
...new Set(results.flatMap((result) => result.attemptedModels)),
|
|
20
|
+
],
|
|
21
|
+
fallbackUsed: results.some((result) => result.fallbackUsed),
|
|
22
|
+
timedOut: results.some((result) => result.timedOut),
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
export function combineSequentialPiResults(first, second) {
|
|
26
|
+
return {
|
|
27
|
+
...second,
|
|
28
|
+
durationMs: first.durationMs + second.durationMs,
|
|
29
|
+
stderr: [first.stderr, second.stderr].filter(Boolean).join("\n"),
|
|
30
|
+
tokensUsed: first.tokensUsed + second.tokensUsed,
|
|
31
|
+
parsedEvents: first.parsedEvents + second.parsedEvents,
|
|
32
|
+
attemptedModels: [
|
|
33
|
+
...new Set([...first.attemptedModels, ...second.attemptedModels]),
|
|
34
|
+
],
|
|
35
|
+
fallbackUsed: first.fallbackUsed || second.fallbackUsed,
|
|
36
|
+
timedOut: first.timedOut || second.timedOut,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
export async function runFrontendScoutParallelSessions(input) {
|
|
40
|
+
const [{ createTypedEventStore }] = await Promise.all([
|
|
41
|
+
import("../../../workflows/dag/frontend-typed-event-store.js"),
|
|
42
|
+
]);
|
|
43
|
+
const shards = [
|
|
44
|
+
{
|
|
45
|
+
id: "surface",
|
|
46
|
+
toolName: "record_target_surface",
|
|
47
|
+
instruction: [
|
|
48
|
+
"PARALLEL SCOUT SHARD — target surface only.",
|
|
49
|
+
"Inspect routes, entrypoints, implementation ownership, data source, and existing test entrypoints. Inspect project source/config first; do not recursively explore dependency internals merely to reconfirm standard test-runner behavior. If a concrete required capability cannot be established from project evidence, report the precise gap.",
|
|
50
|
+
"Call record_target_surface exactly once with the complete runtime-evidenced surface. Do not call record_design_evidence.",
|
|
51
|
+
].join(" "),
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
id: "design",
|
|
55
|
+
toolName: "record_design_evidence",
|
|
56
|
+
instruction: [
|
|
57
|
+
"PARALLEL SCOUT SHARD — design evidence only.",
|
|
58
|
+
"Inspect project-owned frontend framework, styling/theme conventions, reusable components, and relevant design/spec files. The surface shard owns test-runner/environment discovery; do not duplicate its Vitest/happy-dom investigation. Stop discovery once the design evidence is sufficient and commit it.",
|
|
59
|
+
"Call record_design_evidence for the evidence you actually read. Do not call record_target_surface.",
|
|
60
|
+
].join(" "),
|
|
61
|
+
},
|
|
62
|
+
];
|
|
63
|
+
const outcomes = await Promise.all(shards.map(async (shard) => {
|
|
64
|
+
const cacheKey = JSON.stringify([input.runDir, input.nodeId, shard.id, input.sessionOptions.modelConfig, input.observation?.model, input.observation?.sourceDigest, input.sourceDeclaredPaths]);
|
|
65
|
+
const cached = input.completedShards?.get(cacheKey);
|
|
66
|
+
if (cached)
|
|
67
|
+
return { shard, facts: cached.facts, result: { ...cached.result, durationMs: 0, tokensUsed: 0, parsedEvents: 0, attemptedModels: [], fallbackUsed: false } };
|
|
68
|
+
let shardTools;
|
|
69
|
+
let shardResult;
|
|
70
|
+
try {
|
|
71
|
+
const store = createTypedEventStore();
|
|
72
|
+
const shardNodeId = `${input.nodeId}/parallel/${shard.id}`;
|
|
73
|
+
shardTools = await createFrontendScoutEvidenceTools({
|
|
74
|
+
attemptId: `${input.attemptId}:parallel:${shard.id}`,
|
|
75
|
+
store,
|
|
76
|
+
runDir: input.runDir,
|
|
77
|
+
nodeId: shardNodeId,
|
|
78
|
+
workspaceRoot: input.workspaceRoot,
|
|
79
|
+
sourceDeclaredPaths: input.sourceDeclaredPaths,
|
|
80
|
+
});
|
|
81
|
+
const customTools = shardTools.customTools.filter((tool) => typeof tool === "object" &&
|
|
82
|
+
tool !== null &&
|
|
83
|
+
tool.name === shard.toolName);
|
|
84
|
+
const sessionOptions = {
|
|
85
|
+
...input.sessionOptions,
|
|
86
|
+
sessionEventsPath: path.join(input.runDir, shardNodeId, "session-events.jsonl"),
|
|
87
|
+
};
|
|
88
|
+
const prompt = `${input.basePrompt}\n\n${shard.instruction}`;
|
|
89
|
+
const tools = [...customTools, ...(input.readBudgetTools ?? [])];
|
|
90
|
+
shardResult = await observeFrontendSession({
|
|
91
|
+
...input.observation,
|
|
92
|
+
phase: `scout/parallel/${shard.id}`,
|
|
93
|
+
scopeIds: [shard.id],
|
|
94
|
+
prompt,
|
|
95
|
+
userMessage: sessionOptions.userMessage,
|
|
96
|
+
customTools: tools,
|
|
97
|
+
artifactPath: input.observation?.artifactPath ? `${input.observation.artifactPath}-${shard.id}.json` : undefined,
|
|
98
|
+
committedCount: () => shardTools?.committedFacts().length ?? 0,
|
|
99
|
+
durableCommittedCount: () => shardTools?.committedFacts().length ?? 0,
|
|
100
|
+
}, observer => input.piStepFn({
|
|
101
|
+
...sessionOptions,
|
|
102
|
+
onAttemptObservation: observer,
|
|
103
|
+
prompt,
|
|
104
|
+
writerToolPolicy: {
|
|
105
|
+
requireSdk: true,
|
|
106
|
+
customTools: tools,
|
|
107
|
+
},
|
|
108
|
+
}));
|
|
109
|
+
await shardTools.flush();
|
|
110
|
+
const facts = shardTools.committedFacts();
|
|
111
|
+
const expectedKind = shard.id === "design" ? "design-evidence" : "target-surface";
|
|
112
|
+
if (shardResult.ok && facts.some(record => { const fact = record.fact; return fact.kind === expectedKind && (shard.id !== "surface" || fact.completeness === "complete"); })) {
|
|
113
|
+
input.completedShards?.set(cacheKey, { result: structuredClone(shardResult), facts: structuredClone(facts) });
|
|
114
|
+
}
|
|
115
|
+
return { shard, result: shardResult, tools: shardTools, facts };
|
|
116
|
+
}
|
|
117
|
+
catch (error) {
|
|
118
|
+
const crashMessage = `frontend scout parallel shard ${shard.id} crashed: ${error instanceof Error ? error.message : String(error)}`;
|
|
119
|
+
return {
|
|
120
|
+
shard,
|
|
121
|
+
tools: shardTools,
|
|
122
|
+
result: shardResult
|
|
123
|
+
? {
|
|
124
|
+
...shardResult,
|
|
125
|
+
ok: false,
|
|
126
|
+
failureCategory: shardResult.ok
|
|
127
|
+
? "invalid-output"
|
|
128
|
+
: shardResult.failureCategory,
|
|
129
|
+
stderr: [shardResult.stderr, crashMessage]
|
|
130
|
+
.filter(Boolean)
|
|
131
|
+
.join("\n"),
|
|
132
|
+
}
|
|
133
|
+
: {
|
|
134
|
+
ok: false,
|
|
135
|
+
assistantText: "",
|
|
136
|
+
command: [],
|
|
137
|
+
durationMs: 0,
|
|
138
|
+
exitCode: null,
|
|
139
|
+
failureCategory: "tool-policy",
|
|
140
|
+
modelDisplay: "unknown",
|
|
141
|
+
parsedEvents: 0,
|
|
142
|
+
stderr: crashMessage,
|
|
143
|
+
stdout: "",
|
|
144
|
+
timedOut: false,
|
|
145
|
+
attemptedModels: [],
|
|
146
|
+
fallbackUsed: false,
|
|
147
|
+
tokensUsed: 0,
|
|
148
|
+
},
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
}));
|
|
152
|
+
const ordered = [...outcomes].sort((left, right) => left.shard.id.localeCompare(right.shard.id));
|
|
153
|
+
try {
|
|
154
|
+
for (const outcome of ordered) {
|
|
155
|
+
if (outcome.result.ok) {
|
|
156
|
+
await input.mainTools.adoptCommittedFacts(outcome.facts ?? outcome.tools?.committedFacts() ?? []);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
catch (error) {
|
|
161
|
+
const aggregate = aggregateParallelPiResults(ordered.map((outcome) => outcome.result));
|
|
162
|
+
return {
|
|
163
|
+
...aggregate,
|
|
164
|
+
ok: false,
|
|
165
|
+
failureCategory: "invalid-output",
|
|
166
|
+
stderr: [
|
|
167
|
+
aggregate.stderr,
|
|
168
|
+
`frontend scout parallel fact merge failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
169
|
+
]
|
|
170
|
+
.filter(Boolean)
|
|
171
|
+
.join("\n"),
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
const failed = ordered.find((outcome) => !outcome.result.ok);
|
|
175
|
+
if (failed) {
|
|
176
|
+
const aggregate = aggregateParallelPiResults(ordered.map((outcome) => outcome.result));
|
|
177
|
+
return {
|
|
178
|
+
...aggregate,
|
|
179
|
+
ok: false,
|
|
180
|
+
stderr: `${aggregate.stderr}\nfrontend scout parallel shard failed: ${failed.shard.id}`.trim(),
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
const committedKinds = new Set(ordered.flatMap((outcome) => (outcome.facts ?? outcome.tools?.committedFacts() ?? [])
|
|
184
|
+
.map((record) => record.fact?.kind)
|
|
185
|
+
.filter((kind) => typeof kind === "string")));
|
|
186
|
+
const missing = ["target-surface", "design-evidence"].filter((kind) => !committedKinds.has(kind));
|
|
187
|
+
if (missing.length > 0) {
|
|
188
|
+
const fallback = aggregateParallelPiResults(ordered.map((outcome) => outcome.result));
|
|
189
|
+
return {
|
|
190
|
+
...fallback,
|
|
191
|
+
ok: false,
|
|
192
|
+
failureCategory: "invalid-output",
|
|
193
|
+
stderr: `${fallback.stderr}\nfrontend scout parallel shards committed no ${missing.join(" or ")} fact`.trim(),
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
return aggregateParallelPiResults(ordered.map((outcome) => outcome.result));
|
|
197
|
+
}
|