@rallycry/conveyor-agent 7.2.5 → 7.2.7
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/{chunk-JFWVIDQ2.js → chunk-22S4PJT2.js} +48 -33
- package/dist/chunk-22S4PJT2.js.map +1 -0
- package/dist/cli.js +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -1
- package/dist/task-audit-handler-EWE52OL4.js +876 -0
- package/dist/task-audit-handler-EWE52OL4.js.map +1 -0
- package/package.json +1 -1
- package/dist/chunk-JFWVIDQ2.js.map +0 -1
- package/dist/task-audit-handler-7Q7EJENK.js +0 -584
- package/dist/task-audit-handler-7Q7EJENK.js.map +0 -1
|
@@ -0,0 +1,876 @@
|
|
|
1
|
+
import {
|
|
2
|
+
textResult
|
|
3
|
+
} from "./chunk-C5YAMQJ2.js";
|
|
4
|
+
import {
|
|
5
|
+
createHarness,
|
|
6
|
+
createServiceLogger,
|
|
7
|
+
defineTool
|
|
8
|
+
} from "./chunk-RRXX6DTB.js";
|
|
9
|
+
|
|
10
|
+
// src/runner/task-audit-handler.ts
|
|
11
|
+
import { z } from "zod";
|
|
12
|
+
|
|
13
|
+
// src/runner/task-audit-parsing.ts
|
|
14
|
+
function formatMacroEvaluation(macro) {
|
|
15
|
+
if (!macro) return null;
|
|
16
|
+
const lines = [];
|
|
17
|
+
if (macro.instructionsQuality) lines.push(`- Instructions Quality: ${macro.instructionsQuality}`);
|
|
18
|
+
if (macro.modelSuitability) lines.push(`- Model Suitability: ${macro.modelSuitability}`);
|
|
19
|
+
if (macro.contextAndPlanning) lines.push(`- Context & Planning: ${macro.contextAndPlanning}`);
|
|
20
|
+
if (macro.overallResult) lines.push(`- Overall Result: ${macro.overallResult}`);
|
|
21
|
+
return lines.length > 0 ? lines.join("\n") : null;
|
|
22
|
+
}
|
|
23
|
+
function formatHumanEvaluations(evals) {
|
|
24
|
+
if (evals.length === 0) return null;
|
|
25
|
+
const total = evals.reduce((sum, e) => sum + e.rating, 0);
|
|
26
|
+
const avg = total / evals.length;
|
|
27
|
+
const label = avg > 0.3 ? "Helpful" : avg < -0.3 ? "Unhelpful" : "Neutral";
|
|
28
|
+
const lines = [
|
|
29
|
+
`- Overall: ${label} (avg ${avg.toFixed(2)}, ${evals.length} message${evals.length === 1 ? "" : "s"})`
|
|
30
|
+
];
|
|
31
|
+
for (const e of evals) {
|
|
32
|
+
const ratingLabel = e.rating > 0 ? "helpful" : e.rating < 0 ? "unhelpful" : "neutral";
|
|
33
|
+
lines.push(` - Message ${e.messageIndex}: ${ratingLabel} \u2014 ${e.reasoning}`);
|
|
34
|
+
}
|
|
35
|
+
return lines.join("\n");
|
|
36
|
+
}
|
|
37
|
+
function computeGradeCounts(turnGrades) {
|
|
38
|
+
const counts = {
|
|
39
|
+
planningCorrect: 0,
|
|
40
|
+
planningNeutral: 0,
|
|
41
|
+
planningBlunder: 0,
|
|
42
|
+
buildingCorrect: 0,
|
|
43
|
+
buildingNeutral: 0,
|
|
44
|
+
buildingBlunder: 0,
|
|
45
|
+
humanCorrect: 0,
|
|
46
|
+
humanNeutral: 0,
|
|
47
|
+
humanBlunder: 0
|
|
48
|
+
};
|
|
49
|
+
for (const tg of turnGrades) {
|
|
50
|
+
const key = `${tg.phase}${tg.grade.charAt(0).toUpperCase()}${tg.grade.slice(1)}`;
|
|
51
|
+
if (key in counts) {
|
|
52
|
+
counts[key]++;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return counts;
|
|
56
|
+
}
|
|
57
|
+
function addCosts(base, addition) {
|
|
58
|
+
if (base === null) return addition;
|
|
59
|
+
if (addition === null) return base;
|
|
60
|
+
return base + addition;
|
|
61
|
+
}
|
|
62
|
+
function buildErrorResult(projectId, requestId, taskId, error) {
|
|
63
|
+
return {
|
|
64
|
+
projectId,
|
|
65
|
+
requestId,
|
|
66
|
+
taskId,
|
|
67
|
+
summary: "",
|
|
68
|
+
turnGrades: [],
|
|
69
|
+
planningAccuracy: null,
|
|
70
|
+
buildingAccuracy: null,
|
|
71
|
+
humanAccuracy: null,
|
|
72
|
+
planningCorrect: 0,
|
|
73
|
+
planningNeutral: 0,
|
|
74
|
+
planningBlunder: 0,
|
|
75
|
+
buildingCorrect: 0,
|
|
76
|
+
buildingNeutral: 0,
|
|
77
|
+
buildingBlunder: 0,
|
|
78
|
+
humanCorrect: 0,
|
|
79
|
+
humanNeutral: 0,
|
|
80
|
+
humanBlunder: 0,
|
|
81
|
+
suggestionIds: [],
|
|
82
|
+
auditCostUsd: null,
|
|
83
|
+
model: null,
|
|
84
|
+
error
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
function extractJsonBlock(text) {
|
|
88
|
+
const codeBlockMatch = text.match(/```(?:json)?\s*\n?([\s\S]*?)```/);
|
|
89
|
+
const jsonStr = codeBlockMatch ? codeBlockMatch[1].trim() : text;
|
|
90
|
+
try {
|
|
91
|
+
const parsed = JSON.parse(jsonStr);
|
|
92
|
+
if (parsed && typeof parsed === "object") return parsed;
|
|
93
|
+
} catch {
|
|
94
|
+
}
|
|
95
|
+
const objectMatch = text.match(/\{[\s\S]*\}/);
|
|
96
|
+
if (objectMatch) {
|
|
97
|
+
try {
|
|
98
|
+
const parsed = JSON.parse(objectMatch[0]);
|
|
99
|
+
if (parsed && typeof parsed === "object") return parsed;
|
|
100
|
+
} catch {
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
throw new Error("Could not parse JSON from Claude response");
|
|
104
|
+
}
|
|
105
|
+
function extractGradingResult(text, phase) {
|
|
106
|
+
const parsed = extractJsonBlock(text);
|
|
107
|
+
const turnGrades = Array.isArray(parsed.turnGrades) ? parsed.turnGrades.map((tg) => ({ ...tg, phase })) : [];
|
|
108
|
+
const summary = parsed.summary ?? "";
|
|
109
|
+
const accuracy = typeof parsed.accuracy === "number" ? parsed.accuracy : null;
|
|
110
|
+
return { turnGrades, summary, accuracy };
|
|
111
|
+
}
|
|
112
|
+
function extractHumanResult(text) {
|
|
113
|
+
const parsed = extractJsonBlock(text);
|
|
114
|
+
const humanEvaluations = Array.isArray(parsed.humanEvaluations) ? parsed.humanEvaluations : [];
|
|
115
|
+
const summary = parsed.summary ?? "";
|
|
116
|
+
const humanAccuracy = typeof parsed.humanAccuracy === "number" ? parsed.humanAccuracy : null;
|
|
117
|
+
return { humanEvaluations, summary, humanAccuracy };
|
|
118
|
+
}
|
|
119
|
+
function extractAnalysisResult(text) {
|
|
120
|
+
const parsed = extractJsonBlock(text);
|
|
121
|
+
const summary = parsed.summary ?? "";
|
|
122
|
+
const macro = parsed.macroEvaluation;
|
|
123
|
+
return {
|
|
124
|
+
summary,
|
|
125
|
+
macroEvaluation: macro ? {
|
|
126
|
+
instructionsQuality: macro.instructionsQuality ?? void 0,
|
|
127
|
+
modelSuitability: macro.modelSuitability ?? void 0,
|
|
128
|
+
contextAndPlanning: macro.contextAndPlanning ?? void 0,
|
|
129
|
+
overallResult: macro.overallResult ?? void 0
|
|
130
|
+
} : null
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
function buildMergedSummary(planningResult, buildingResult, humanResult, analysisResult) {
|
|
134
|
+
const summaryParts = [];
|
|
135
|
+
if (analysisResult?.summary) {
|
|
136
|
+
summaryParts.push(analysisResult.summary);
|
|
137
|
+
} else {
|
|
138
|
+
if (planningResult?.summary) summaryParts.push(`Planning: ${planningResult.summary}`);
|
|
139
|
+
if (buildingResult?.summary) summaryParts.push(`Building: ${buildingResult.summary}`);
|
|
140
|
+
if (humanResult?.summary) summaryParts.push(`Human: ${humanResult.summary}`);
|
|
141
|
+
}
|
|
142
|
+
const macroText = analysisResult?.macroEvaluation ? formatMacroEvaluation(analysisResult.macroEvaluation) : null;
|
|
143
|
+
const humanEvalText = humanResult ? formatHumanEvaluations(humanResult.humanEvaluations) : null;
|
|
144
|
+
let summary = summaryParts.join("\n\n");
|
|
145
|
+
if (macroText) summary += `
|
|
146
|
+
|
|
147
|
+
---
|
|
148
|
+
|
|
149
|
+
Macro Evaluation:
|
|
150
|
+
${macroText}`;
|
|
151
|
+
if (humanEvalText) summary += `
|
|
152
|
+
|
|
153
|
+
---
|
|
154
|
+
|
|
155
|
+
Human Contributions:
|
|
156
|
+
${humanEvalText}`;
|
|
157
|
+
return summary;
|
|
158
|
+
}
|
|
159
|
+
function mergeStepResults(planningResult, buildingResult, humanResult, analysisResult, planningTraceLength) {
|
|
160
|
+
const planGrades = planningResult?.turnGrades ?? [];
|
|
161
|
+
const buildGrades = (buildingResult?.turnGrades ?? []).map((tg) => ({
|
|
162
|
+
...tg,
|
|
163
|
+
turnIndex: tg.turnIndex + planningTraceLength
|
|
164
|
+
}));
|
|
165
|
+
return {
|
|
166
|
+
turnGrades: [...planGrades, ...buildGrades],
|
|
167
|
+
summary: buildMergedSummary(planningResult, buildingResult, humanResult, analysisResult),
|
|
168
|
+
planningAccuracy: planningResult?.accuracy ?? null,
|
|
169
|
+
buildingAccuracy: buildingResult?.accuracy ?? null,
|
|
170
|
+
humanAccuracy: humanResult?.humanAccuracy ?? null,
|
|
171
|
+
humanEvaluations: humanResult?.humanEvaluations ?? [],
|
|
172
|
+
macroEvaluation: analysisResult?.macroEvaluation ?? null
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// src/runner/task-audit-trace-slicer.ts
|
|
177
|
+
var PLAN_UPDATE_PATTERN = /mcp__conveyor__update_task$/;
|
|
178
|
+
var PR_CREATION_PATTERN = /mcp__conveyor__create_pull_request$/;
|
|
179
|
+
function eventMatchesTool(event, toolPattern) {
|
|
180
|
+
if (event.type === "tool_use" && typeof event.data.tool === "string") {
|
|
181
|
+
if (toolPattern.test(event.data.tool)) return true;
|
|
182
|
+
}
|
|
183
|
+
if (event.type === "turn_end" && Array.isArray(event.data.toolCalls)) {
|
|
184
|
+
for (const tc of event.data.toolCalls) {
|
|
185
|
+
if (typeof tc.tool === "string" && toolPattern.test(tc.tool)) return true;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
return false;
|
|
189
|
+
}
|
|
190
|
+
function findLastToolCallIndex(trace, toolPattern) {
|
|
191
|
+
for (let i = trace.length - 1; i >= 0; i--) {
|
|
192
|
+
if (eventMatchesTool(trace[i], toolPattern)) return i;
|
|
193
|
+
}
|
|
194
|
+
return -1;
|
|
195
|
+
}
|
|
196
|
+
function sliceTrace(trace) {
|
|
197
|
+
const lastPlanUpdateIndex = findLastToolCallIndex(trace, PLAN_UPDATE_PATTERN);
|
|
198
|
+
const lastPrCreationIndex = findLastToolCallIndex(trace, PR_CREATION_PATTERN);
|
|
199
|
+
const boundaries = { lastPlanUpdateIndex, lastPrCreationIndex };
|
|
200
|
+
if (lastPlanUpdateIndex === -1 && lastPrCreationIndex === -1) {
|
|
201
|
+
return { planningTrace: [], buildingTrace: trace, boundaries };
|
|
202
|
+
}
|
|
203
|
+
if (lastPlanUpdateIndex === -1) {
|
|
204
|
+
return {
|
|
205
|
+
planningTrace: [],
|
|
206
|
+
buildingTrace: lastPrCreationIndex >= 0 ? trace.slice(0, lastPrCreationIndex + 1) : trace,
|
|
207
|
+
boundaries
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
const planEnd = lastPrCreationIndex >= 0 && lastPlanUpdateIndex > lastPrCreationIndex ? lastPrCreationIndex : lastPlanUpdateIndex;
|
|
211
|
+
const planningTrace = trace.slice(0, planEnd + 1);
|
|
212
|
+
const buildEnd = lastPrCreationIndex >= 0 ? lastPrCreationIndex + 1 : trace.length;
|
|
213
|
+
const buildingTrace = trace.slice(planEnd + 1, buildEnd);
|
|
214
|
+
return { planningTrace, buildingTrace, boundaries };
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// src/runner/task-audit-prompts.ts
|
|
218
|
+
var PLANNING_AUDIT_SYSTEM_PROMPT = `You are a chess-style analysis engine grading the PLANNING phase of an AI agent's task execution. You receive only the planning portion of the agent's trace \u2014 from the start through the last plan update. Grade each turn objectively.
|
|
219
|
+
|
|
220
|
+
## Grading Scale
|
|
221
|
+
|
|
222
|
+
- **Correct** (\u265F): Sound moves toward understanding the task \u2014 reading relevant files, analyzing requirements, logical reasoning about approach, following project patterns
|
|
223
|
+
- **Neutral** (\xB7): No significant positive or negative effect \u2014 exploratory reads, minor tangents
|
|
224
|
+
- **Blunder** (??): Wasted effort \u2014 reading irrelevant files repeatedly, ignoring requirements, spending many turns without making progress toward a plan
|
|
225
|
+
|
|
226
|
+
## Specific Blunder Examples
|
|
227
|
+
- Reading the same file 5+ times without advancing understanding
|
|
228
|
+
- Ignoring explicit task requirements or project conventions visible in the code
|
|
229
|
+
- Spending many turns on tangential investigation not related to the task
|
|
230
|
+
- Failing to read key files that are obvious from the task description
|
|
231
|
+
|
|
232
|
+
## Specific Correct Examples
|
|
233
|
+
- Reading context files before implementation
|
|
234
|
+
- Analyzing requirements and dependencies systematically
|
|
235
|
+
- Identifying relevant code patterns to follow
|
|
236
|
+
- Building understanding incrementally
|
|
237
|
+
|
|
238
|
+
## Output Format
|
|
239
|
+
|
|
240
|
+
Output a JSON block:
|
|
241
|
+
|
|
242
|
+
\`\`\`json
|
|
243
|
+
{
|
|
244
|
+
"turnGrades": [
|
|
245
|
+
{
|
|
246
|
+
"turnIndex": 0,
|
|
247
|
+
"grade": "correct",
|
|
248
|
+
"reasoning": "Read relevant config files to understand project structure",
|
|
249
|
+
"eventType": "tool_use",
|
|
250
|
+
"eventSummary": "Read package.json and tsconfig.json"
|
|
251
|
+
}
|
|
252
|
+
],
|
|
253
|
+
"summary": "Brief 2-3 sentence assessment of planning quality",
|
|
254
|
+
"accuracy": 0.85
|
|
255
|
+
}
|
|
256
|
+
\`\`\`
|
|
257
|
+
|
|
258
|
+
**Accuracy Calculation**: accuracy = correct / (correct + blunder). Neutral turns are excluded. Return null if no correct or blunder turns exist.
|
|
259
|
+
|
|
260
|
+
IMPORTANT: Do NOT include a "phase" field in turnGrades \u2014 the phase is set programmatically. Only include turnIndex, grade, reasoning, eventType, and eventSummary.`;
|
|
261
|
+
var BUILDING_AUDIT_SYSTEM_PROMPT = `You are a chess-style analysis engine grading the BUILDING phase of an AI agent's task execution. You receive only the building portion of the agent's trace \u2014 from after the plan was finalized through pull request creation. Grade each turn objectively.
|
|
262
|
+
|
|
263
|
+
## Grading Scale
|
|
264
|
+
|
|
265
|
+
- **Correct** (\u265F): Sound implementation moves \u2014 proper edits, running tests, fixing errors, following plan steps, adapting to feedback
|
|
266
|
+
- **Neutral** (\xB7): No significant positive or negative effect \u2014 exploratory reads during implementation, minor tangents
|
|
267
|
+
- **Blunder** (??): Wasted effort \u2014 incorrect edits immediately reverted, repeated failures without adapting, ignoring the plan, using wrong APIs despite visible patterns
|
|
268
|
+
|
|
269
|
+
## Specific Blunder Examples
|
|
270
|
+
- Making edits that are immediately reverted or overwritten
|
|
271
|
+
- Ignoring explicit plan steps or user corrections
|
|
272
|
+
- Repeated failed tool calls (isError: true) without changing approach
|
|
273
|
+
- Using wrong APIs despite project patterns visible in the trace
|
|
274
|
+
- Spending many turns on tangential work not in the plan
|
|
275
|
+
|
|
276
|
+
## Specific Correct Examples
|
|
277
|
+
- Following plan steps in logical sequence
|
|
278
|
+
- Proper error handling and recovery after tool failures
|
|
279
|
+
- Adapting approach based on test results or errors
|
|
280
|
+
- Efficient tool usage with clear purpose
|
|
281
|
+
- Running tests to verify changes
|
|
282
|
+
|
|
283
|
+
## Output Format
|
|
284
|
+
|
|
285
|
+
Output a JSON block:
|
|
286
|
+
|
|
287
|
+
\`\`\`json
|
|
288
|
+
{
|
|
289
|
+
"turnGrades": [
|
|
290
|
+
{
|
|
291
|
+
"turnIndex": 0,
|
|
292
|
+
"grade": "correct",
|
|
293
|
+
"reasoning": "Implemented the required changes following the plan",
|
|
294
|
+
"eventType": "tool_use",
|
|
295
|
+
"eventSummary": "Edit src/component.tsx"
|
|
296
|
+
}
|
|
297
|
+
],
|
|
298
|
+
"summary": "Brief 2-3 sentence assessment of building quality",
|
|
299
|
+
"accuracy": 0.72
|
|
300
|
+
}
|
|
301
|
+
\`\`\`
|
|
302
|
+
|
|
303
|
+
**Accuracy Calculation**: accuracy = correct / (correct + blunder). Neutral turns are excluded. Return null if no correct or blunder turns exist.
|
|
304
|
+
|
|
305
|
+
IMPORTANT: Turn indices are 0-based relative to the trace slice provided. Do NOT include a "phase" field \u2014 the phase is set programmatically. Only include turnIndex, grade, reasoning, eventType, and eventSummary.`;
|
|
306
|
+
var HUMAN_AUDIT_SYSTEM_PROMPT = `You are evaluating human contributions to an AI agent's task execution. For each human message, assess whether it helped, hindered, or had no effect on the agent's work.
|
|
307
|
+
|
|
308
|
+
## Rating Scale
|
|
309
|
+
|
|
310
|
+
- **Helpful (+1)**: Provided useful context, clear corrections, actionable feedback, or unblocked the agent
|
|
311
|
+
- **Neutral (0)**: Acknowledgements, status checks, or messages with no material impact on execution
|
|
312
|
+
- **Unhelpful (-1)**: Contradictory instructions, incorrect guidance, unnecessary interruptions, or vague requests that caused wasted effort
|
|
313
|
+
|
|
314
|
+
## Output Format
|
|
315
|
+
|
|
316
|
+
Output a JSON block:
|
|
317
|
+
|
|
318
|
+
\`\`\`json
|
|
319
|
+
{
|
|
320
|
+
"humanEvaluations": [
|
|
321
|
+
{
|
|
322
|
+
"messageIndex": 0,
|
|
323
|
+
"rating": 1,
|
|
324
|
+
"reasoning": "Provided clear context about the bug with reproduction steps"
|
|
325
|
+
}
|
|
326
|
+
],
|
|
327
|
+
"summary": "Brief 2-3 sentence assessment of human contributions",
|
|
328
|
+
"humanAccuracy": 0.75
|
|
329
|
+
}
|
|
330
|
+
\`\`\`
|
|
331
|
+
|
|
332
|
+
**humanAccuracy Calculation**: humanAccuracy = helpful / (helpful + unhelpful). Neutral messages are excluded. Return null if no helpful or unhelpful messages exist.
|
|
333
|
+
|
|
334
|
+
Evaluate EVERY human message \u2014 do not skip any. Each entry in humanEvaluations must have a messageIndex (0-based index into the Human Messages list), rating (+1, 0, or -1), and reasoning.`;
|
|
335
|
+
var ANALYSIS_SYSTEM_PROMPT = `You are performing a macro-level analysis of an AI agent's task execution and generating improvement suggestions. You receive grading results from planning, building, and human evaluation phases.
|
|
336
|
+
|
|
337
|
+
## Your Tasks
|
|
338
|
+
|
|
339
|
+
1. **Write an overall summary** (2-4 sentences) that synthesizes the planning, building, and human evaluation findings into a cohesive assessment.
|
|
340
|
+
|
|
341
|
+
2. **Perform a macro evaluation** addressing:
|
|
342
|
+
- **Instructions Quality**: Were the agent instructions appropriate? Too vague, too prescriptive, or missing critical guidance?
|
|
343
|
+
- **Model Suitability**: Was the model choice appropriate for the complexity of this task?
|
|
344
|
+
- **Context & Planning**: Was the plan/context sufficient? Were there information gaps that caused wasted effort?
|
|
345
|
+
- **Overall Result**: Did the final result meet requirements? Was the outcome proportional to effort?
|
|
346
|
+
|
|
347
|
+
3. **Generate suggestions** using the create_suggestion tool. Focus on:
|
|
348
|
+
- Recurring patterns of wasted effort that could be prevented
|
|
349
|
+
- Missing documentation or context that caused confusion
|
|
350
|
+
- Process improvements to prevent common blunders
|
|
351
|
+
- What worked well that should be replicated
|
|
352
|
+
|
|
353
|
+
You MUST call create_suggestion at least once. If the execution was flawless, suggest what made it work well so the pattern can be replicated.
|
|
354
|
+
|
|
355
|
+
## Output Format
|
|
356
|
+
|
|
357
|
+
After calling create_suggestion, output a JSON block:
|
|
358
|
+
|
|
359
|
+
\`\`\`json
|
|
360
|
+
{
|
|
361
|
+
"summary": "Overall 2-4 sentence assessment synthesizing all phases",
|
|
362
|
+
"macroEvaluation": {
|
|
363
|
+
"instructionsQuality": "Brief assessment of instruction quality",
|
|
364
|
+
"modelSuitability": "Brief assessment of model choice",
|
|
365
|
+
"contextAndPlanning": "Brief assessment of context and planning",
|
|
366
|
+
"overallResult": "Brief assessment of the outcome"
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
\`\`\``;
|
|
370
|
+
function buildSessionInfoSection(info) {
|
|
371
|
+
const lines = ["## Session Info"];
|
|
372
|
+
if (info.model) lines.push(`- Model: ${info.model}`);
|
|
373
|
+
if (info.totalCostUsd !== null) lines.push(`- Total Cost: $${info.totalCostUsd.toFixed(4)}`);
|
|
374
|
+
if (info.planningTurns !== null) lines.push(`- Planning Turns: ${info.planningTurns}`);
|
|
375
|
+
if (info.buildingTurns !== null) lines.push(`- Building Turns: ${info.buildingTurns}`);
|
|
376
|
+
if (info.locDiff !== null) lines.push(`- Lines Changed: ${info.locDiff}`);
|
|
377
|
+
lines.push("");
|
|
378
|
+
return lines.join("\n");
|
|
379
|
+
}
|
|
380
|
+
function buildTraceSection(trace) {
|
|
381
|
+
const lines = ["## Agent Trace\n"];
|
|
382
|
+
if (trace.length === 0) {
|
|
383
|
+
lines.push("No agent trace data available.\n");
|
|
384
|
+
} else {
|
|
385
|
+
for (let i = 0; i < trace.length; i++) {
|
|
386
|
+
const event = trace[i];
|
|
387
|
+
const dataStr = JSON.stringify(event.data);
|
|
388
|
+
const truncated = dataStr.length > 2e3 ? dataStr.slice(0, 2e3) + "..." : dataStr;
|
|
389
|
+
lines.push(`### Turn ${i} [${event.type}] @ ${event.timestamp}`);
|
|
390
|
+
lines.push(truncated);
|
|
391
|
+
lines.push("");
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
return lines.join("\n");
|
|
395
|
+
}
|
|
396
|
+
function buildTaskContextSection(task) {
|
|
397
|
+
const parts = [];
|
|
398
|
+
if (task.taskDescription) parts.push(`## Description
|
|
399
|
+
${task.taskDescription}
|
|
400
|
+
`);
|
|
401
|
+
if (task.taskPlan) parts.push(`## Plan
|
|
402
|
+
${task.taskPlan}
|
|
403
|
+
`);
|
|
404
|
+
parts.push(`## Task Status: ${task.taskStatus}`);
|
|
405
|
+
if (task.storyPointValue !== null) parts.push(`## Story Points: ${task.storyPointValue}`);
|
|
406
|
+
parts.push("");
|
|
407
|
+
if (task.sessionInfo) parts.push(buildSessionInfoSection(task.sessionInfo));
|
|
408
|
+
if (task.agentInstructions) parts.push(`## Agent Instructions
|
|
409
|
+
${task.agentInstructions}
|
|
410
|
+
`);
|
|
411
|
+
return parts.join("\n");
|
|
412
|
+
}
|
|
413
|
+
function buildPlanningAuditPrompt(task, planningTrace) {
|
|
414
|
+
const parts = [`# Planning Audit: ${task.taskTitle}
|
|
415
|
+
`];
|
|
416
|
+
parts.push(buildTaskContextSection(task));
|
|
417
|
+
parts.push(buildTraceSection(planningTrace));
|
|
418
|
+
parts.push("Grade each turn above, then output the structured JSON result.");
|
|
419
|
+
return parts.join("\n");
|
|
420
|
+
}
|
|
421
|
+
function buildBuildingAuditPrompt(task, buildingTrace) {
|
|
422
|
+
const parts = [`# Building Audit: ${task.taskTitle}
|
|
423
|
+
`];
|
|
424
|
+
parts.push(buildTaskContextSection(task));
|
|
425
|
+
parts.push(buildTraceSection(buildingTrace));
|
|
426
|
+
parts.push("Grade each turn above, then output the structured JSON result.");
|
|
427
|
+
return parts.join("\n");
|
|
428
|
+
}
|
|
429
|
+
function buildHumanAuditPrompt(task) {
|
|
430
|
+
const parts = [`# Human Contribution Evaluation: ${task.taskTitle}
|
|
431
|
+
`];
|
|
432
|
+
if (task.taskDescription) parts.push(`## Description
|
|
433
|
+
${task.taskDescription}
|
|
434
|
+
`);
|
|
435
|
+
if (task.taskPlan) parts.push(`## Plan
|
|
436
|
+
${task.taskPlan}
|
|
437
|
+
`);
|
|
438
|
+
parts.push(`## Task Status: ${task.taskStatus}
|
|
439
|
+
`);
|
|
440
|
+
parts.push("## Human Messages\n");
|
|
441
|
+
for (let i = 0; i < task.humanMessages.length; i++) {
|
|
442
|
+
const msg = task.humanMessages[i];
|
|
443
|
+
parts.push(`### Message ${i} [${msg.createdAt}]`);
|
|
444
|
+
parts.push(`**${msg.role}**: ${msg.content}
|
|
445
|
+
`);
|
|
446
|
+
}
|
|
447
|
+
parts.push("Evaluate each human message above, then output the structured JSON result.");
|
|
448
|
+
return parts.join("\n");
|
|
449
|
+
}
|
|
450
|
+
function buildGradingResultSection(label, result) {
|
|
451
|
+
const grades = result.turnGrades;
|
|
452
|
+
const correct = grades.filter((g) => g.grade === "correct").length;
|
|
453
|
+
const neutral = grades.filter((g) => g.grade === "neutral").length;
|
|
454
|
+
const blunder = grades.filter((g) => g.grade === "blunder").length;
|
|
455
|
+
const accuracy = result.accuracy === null ? "N/A" : result.accuracy.toFixed(2);
|
|
456
|
+
return [
|
|
457
|
+
`## ${label} Phase Results`,
|
|
458
|
+
`- Summary: ${result.summary}`,
|
|
459
|
+
`- Accuracy: ${accuracy}`,
|
|
460
|
+
`- ${grades.length} turns: ${correct} correct, ${neutral} neutral, ${blunder} blunders`,
|
|
461
|
+
""
|
|
462
|
+
].join("\n");
|
|
463
|
+
}
|
|
464
|
+
function buildAnalysisPrompt(task, planningResult, buildingResult, humanResult) {
|
|
465
|
+
const parts = [`# Task Analysis: ${task.taskTitle}
|
|
466
|
+
`];
|
|
467
|
+
if (task.taskDescription) parts.push(`## Description
|
|
468
|
+
${task.taskDescription}
|
|
469
|
+
`);
|
|
470
|
+
if (task.taskPlan) parts.push(`## Plan
|
|
471
|
+
${task.taskPlan}
|
|
472
|
+
`);
|
|
473
|
+
parts.push(`## Task Status: ${task.taskStatus}`);
|
|
474
|
+
if (task.storyPointValue !== null) parts.push(`## Story Points: ${task.storyPointValue}`);
|
|
475
|
+
parts.push("");
|
|
476
|
+
if (task.sessionInfo) parts.push(buildSessionInfoSection(task.sessionInfo));
|
|
477
|
+
if (planningResult) parts.push(buildGradingResultSection("Planning", planningResult));
|
|
478
|
+
if (buildingResult) parts.push(buildGradingResultSection("Building", buildingResult));
|
|
479
|
+
if (humanResult) {
|
|
480
|
+
const evals = humanResult.humanEvaluations;
|
|
481
|
+
const helpful = evals.filter((e) => e.rating > 0).length;
|
|
482
|
+
const neutralCount = evals.filter((e) => e.rating === 0).length;
|
|
483
|
+
const unhelpful = evals.filter((e) => e.rating < 0).length;
|
|
484
|
+
parts.push("## Human Evaluation Results");
|
|
485
|
+
parts.push(`- Summary: ${humanResult.summary}`);
|
|
486
|
+
parts.push(
|
|
487
|
+
`- ${evals.length} messages: ${helpful} helpful, ${neutralCount} neutral, ${unhelpful} unhelpful`
|
|
488
|
+
);
|
|
489
|
+
parts.push("");
|
|
490
|
+
}
|
|
491
|
+
const allGrades = [...planningResult?.turnGrades ?? [], ...buildingResult?.turnGrades ?? []];
|
|
492
|
+
const blunders = allGrades.filter((g) => g.grade === "blunder");
|
|
493
|
+
if (blunders.length > 0) {
|
|
494
|
+
parts.push("## Notable Blunders");
|
|
495
|
+
for (const b of blunders.slice(0, 10)) {
|
|
496
|
+
parts.push(`- Turn ${b.turnIndex} (${b.phase}): ${b.reasoning}`);
|
|
497
|
+
}
|
|
498
|
+
parts.push("");
|
|
499
|
+
}
|
|
500
|
+
parts.push(
|
|
501
|
+
"Based on the findings above, call create_suggestion for each actionable improvement, then output the JSON result."
|
|
502
|
+
);
|
|
503
|
+
return parts.join("\n");
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
// src/runner/task-audit-progress.ts
|
|
507
|
+
function reportProgress(connection, requestId, taskId, tool, input) {
|
|
508
|
+
void connection.call("reportTaskAuditProgress", {
|
|
509
|
+
projectId: connection.projectId,
|
|
510
|
+
requestId,
|
|
511
|
+
taskId,
|
|
512
|
+
activity: { tool, input, timestamp: (/* @__PURE__ */ new Date()).toISOString() }
|
|
513
|
+
}).catch(() => {
|
|
514
|
+
});
|
|
515
|
+
}
|
|
516
|
+
function processAssistantBlocks(blocks, responseParts, ctx, reportedFirstText) {
|
|
517
|
+
for (const block of blocks) {
|
|
518
|
+
if (block.type === "text" && block.text) {
|
|
519
|
+
responseParts.push(block.text);
|
|
520
|
+
if (reportedFirstText.value) continue;
|
|
521
|
+
reportedFirstText.value = true;
|
|
522
|
+
reportProgress(
|
|
523
|
+
ctx.connection,
|
|
524
|
+
ctx.requestId,
|
|
525
|
+
ctx.taskId,
|
|
526
|
+
"thinking",
|
|
527
|
+
block.text.slice(0, 200)
|
|
528
|
+
);
|
|
529
|
+
} else if (block.type === "tool_use" && block.name) {
|
|
530
|
+
const inputStr = typeof block.input === "string" ? block.input : JSON.stringify(block.input);
|
|
531
|
+
reportProgress(ctx.connection, ctx.requestId, ctx.taskId, block.name, inputStr.slice(0, 500));
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
async function collectResponseFromEvents(events, connection, requestId, taskId) {
|
|
536
|
+
const responseParts = [];
|
|
537
|
+
let costUsd = null;
|
|
538
|
+
let model = null;
|
|
539
|
+
const reportedFirstText = { value: false };
|
|
540
|
+
const ctx = { connection, requestId, taskId };
|
|
541
|
+
for await (const event of events) {
|
|
542
|
+
if (event.type === "system") {
|
|
543
|
+
const sysEvent = event;
|
|
544
|
+
if (sysEvent.subtype === "init" && sysEvent.model) {
|
|
545
|
+
model = sysEvent.model;
|
|
546
|
+
reportProgress(
|
|
547
|
+
connection,
|
|
548
|
+
requestId,
|
|
549
|
+
taskId,
|
|
550
|
+
"system",
|
|
551
|
+
`Initialized model: ${sysEvent.model}`
|
|
552
|
+
);
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
if (event.type === "assistant") {
|
|
556
|
+
const { message } = event;
|
|
557
|
+
processAssistantBlocks(message.content, responseParts, ctx, reportedFirstText);
|
|
558
|
+
}
|
|
559
|
+
if (event.type === "result") {
|
|
560
|
+
const resultEvent = event;
|
|
561
|
+
if (resultEvent.subtype === "success" && resultEvent.total_cost_usd !== void 0) {
|
|
562
|
+
costUsd = resultEvent.total_cost_usd;
|
|
563
|
+
}
|
|
564
|
+
const status = resultEvent.subtype === "success" ? "success" : `error: ${resultEvent.subtype}`;
|
|
565
|
+
reportProgress(connection, requestId, taskId, "complete", status);
|
|
566
|
+
break;
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
return { text: responseParts.join("\n\n").trim(), costUsd, model };
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
// src/runner/task-audit-handler.ts
|
|
573
|
+
var logger = createServiceLogger("TaskAudit");
|
|
574
|
+
var FALLBACK_MODEL = "claude-sonnet-4-6";
|
|
575
|
+
function buildTrackedSuggestionTool(connection, projectId, suggestionIds) {
|
|
576
|
+
return defineTool(
|
|
577
|
+
"create_suggestion",
|
|
578
|
+
"Suggest a cross-cutting improvement for the project based on patterns found during audit. If a similar suggestion exists, your vote is added instead of creating a duplicate.",
|
|
579
|
+
{
|
|
580
|
+
title: z.string().describe("Short title for the suggestion"),
|
|
581
|
+
description: z.string().optional().describe("Details about the suggestion"),
|
|
582
|
+
tag_names: z.array(z.string()).optional().describe("Tag names to categorize the suggestion")
|
|
583
|
+
},
|
|
584
|
+
async ({ title, description, tag_names }) => {
|
|
585
|
+
try {
|
|
586
|
+
const result = await connection.call("createProjectSuggestion", {
|
|
587
|
+
projectId,
|
|
588
|
+
title,
|
|
589
|
+
description,
|
|
590
|
+
tagNames: tag_names
|
|
591
|
+
});
|
|
592
|
+
const id = result.mergedIntoId ?? result.id;
|
|
593
|
+
if (id) {
|
|
594
|
+
suggestionIds.push(id);
|
|
595
|
+
logger.info("Suggestion tracked", { id, merged: result.merged });
|
|
596
|
+
}
|
|
597
|
+
if (result.merged) {
|
|
598
|
+
return textResult(`Merged into existing suggestion (ID: ${id}).`);
|
|
599
|
+
}
|
|
600
|
+
return textResult(`Suggestion created (ID: ${result.id}).`);
|
|
601
|
+
} catch (error) {
|
|
602
|
+
logger.warn("Suggestion creation failed", {
|
|
603
|
+
error: error instanceof Error ? error.message : String(error)
|
|
604
|
+
});
|
|
605
|
+
return textResult(
|
|
606
|
+
`Failed to create suggestion: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
607
|
+
);
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
);
|
|
611
|
+
}
|
|
612
|
+
async function fetchModel(connection) {
|
|
613
|
+
try {
|
|
614
|
+
const ctx = await connection.call("getProjectAgentContextByRole", {
|
|
615
|
+
projectId: connection.projectId,
|
|
616
|
+
role: "reviewer"
|
|
617
|
+
});
|
|
618
|
+
return ctx.model || FALLBACK_MODEL;
|
|
619
|
+
} catch {
|
|
620
|
+
return FALLBACK_MODEL;
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
function buildQueryOptions(systemPrompt, opts, limits, mcpServers = {}) {
|
|
624
|
+
return {
|
|
625
|
+
model: opts.model,
|
|
626
|
+
systemPrompt,
|
|
627
|
+
cwd: opts.projectDir,
|
|
628
|
+
permissionMode: "bypassPermissions",
|
|
629
|
+
allowDangerouslySkipPermissions: true,
|
|
630
|
+
tools: { type: "preset", preset: "claude_code" },
|
|
631
|
+
mcpServers,
|
|
632
|
+
maxTurns: limits.maxTurns,
|
|
633
|
+
maxBudgetUsd: limits.maxBudgetUsd,
|
|
634
|
+
disallowedTools: ["Edit", "Write", "Bash", "NotebookEdit", "MultiEdit"]
|
|
635
|
+
};
|
|
636
|
+
}
|
|
637
|
+
async function runGradingStep(taskId, phase, systemPrompt, userPrompt, limits, opts) {
|
|
638
|
+
const harness = createHarness();
|
|
639
|
+
const events = harness.executeQuery({
|
|
640
|
+
prompt: userPrompt,
|
|
641
|
+
options: buildQueryOptions(systemPrompt, opts, limits)
|
|
642
|
+
});
|
|
643
|
+
const response = await collectResponseFromEvents(events, opts.connection, opts.requestId, taskId);
|
|
644
|
+
if (!response.text) throw new Error(`No response from ${phase} grading step`);
|
|
645
|
+
return {
|
|
646
|
+
result: extractGradingResult(response.text, phase),
|
|
647
|
+
costUsd: response.costUsd,
|
|
648
|
+
model: response.model
|
|
649
|
+
};
|
|
650
|
+
}
|
|
651
|
+
async function runHumanStep(taskId, userPrompt, limits, opts) {
|
|
652
|
+
const harness = createHarness();
|
|
653
|
+
const events = harness.executeQuery({
|
|
654
|
+
prompt: userPrompt,
|
|
655
|
+
options: buildQueryOptions(HUMAN_AUDIT_SYSTEM_PROMPT, opts, limits)
|
|
656
|
+
});
|
|
657
|
+
const response = await collectResponseFromEvents(events, opts.connection, opts.requestId, taskId);
|
|
658
|
+
if (!response.text) throw new Error("No response from human evaluation step");
|
|
659
|
+
return { result: extractHumanResult(response.text), costUsd: response.costUsd };
|
|
660
|
+
}
|
|
661
|
+
async function runAnalysisStep(task, planningResult, buildingResult, humanResult, projectId, suggestionIds, opts) {
|
|
662
|
+
const wrappedTool = buildTrackedSuggestionTool(opts.connection, projectId, suggestionIds);
|
|
663
|
+
const harness = createHarness();
|
|
664
|
+
const mcpServer = harness.createMcpServer({ name: "conveyor", tools: [wrappedTool] });
|
|
665
|
+
const events = harness.executeQuery({
|
|
666
|
+
prompt: buildAnalysisPrompt(task, planningResult, buildingResult, humanResult),
|
|
667
|
+
options: buildQueryOptions(
|
|
668
|
+
ANALYSIS_SYSTEM_PROMPT,
|
|
669
|
+
opts,
|
|
670
|
+
{ maxTurns: 6, maxBudgetUsd: 1 },
|
|
671
|
+
{ conveyor: mcpServer }
|
|
672
|
+
)
|
|
673
|
+
});
|
|
674
|
+
const response = await collectResponseFromEvents(
|
|
675
|
+
events,
|
|
676
|
+
opts.connection,
|
|
677
|
+
opts.requestId,
|
|
678
|
+
task.taskId
|
|
679
|
+
);
|
|
680
|
+
if (!response.text) throw new Error("No response from analysis step");
|
|
681
|
+
return { result: extractAnalysisResult(response.text), costUsd: response.costUsd };
|
|
682
|
+
}
|
|
683
|
+
var EMPTY_STEP = { result: null, costUsd: null, model: null };
|
|
684
|
+
var GRADING_LIMITS = { maxTurns: 8, maxBudgetUsd: 1 };
|
|
685
|
+
async function executeStepSafely(stepLabel, requestId, taskId, fn) {
|
|
686
|
+
try {
|
|
687
|
+
const step = await fn();
|
|
688
|
+
return { result: step.result, costUsd: step.costUsd, model: step.model ?? null };
|
|
689
|
+
} catch (err) {
|
|
690
|
+
logger.warn(`${stepLabel} failed`, {
|
|
691
|
+
requestId,
|
|
692
|
+
taskId,
|
|
693
|
+
error: err instanceof Error ? err.message : String(err)
|
|
694
|
+
});
|
|
695
|
+
return EMPTY_STEP;
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
async function executePlanningStep(taskPayload, planningTrace, opts) {
|
|
699
|
+
if (planningTrace.length === 0) return EMPTY_STEP;
|
|
700
|
+
reportProgress(
|
|
701
|
+
opts.connection,
|
|
702
|
+
opts.requestId,
|
|
703
|
+
taskPayload.taskId,
|
|
704
|
+
"status",
|
|
705
|
+
"Step 1/4: Auditing planning..."
|
|
706
|
+
);
|
|
707
|
+
return await executeStepSafely(
|
|
708
|
+
"Planning audit",
|
|
709
|
+
opts.requestId,
|
|
710
|
+
taskPayload.taskId,
|
|
711
|
+
() => runGradingStep(
|
|
712
|
+
taskPayload.taskId,
|
|
713
|
+
"planning",
|
|
714
|
+
PLANNING_AUDIT_SYSTEM_PROMPT,
|
|
715
|
+
buildPlanningAuditPrompt(taskPayload, planningTrace),
|
|
716
|
+
GRADING_LIMITS,
|
|
717
|
+
opts
|
|
718
|
+
)
|
|
719
|
+
);
|
|
720
|
+
}
|
|
721
|
+
async function executeBuildingStep(taskPayload, buildingTrace, opts) {
|
|
722
|
+
if (buildingTrace.length === 0) return EMPTY_STEP;
|
|
723
|
+
reportProgress(
|
|
724
|
+
opts.connection,
|
|
725
|
+
opts.requestId,
|
|
726
|
+
taskPayload.taskId,
|
|
727
|
+
"status",
|
|
728
|
+
"Step 2/4: Auditing building..."
|
|
729
|
+
);
|
|
730
|
+
return await executeStepSafely(
|
|
731
|
+
"Building audit",
|
|
732
|
+
opts.requestId,
|
|
733
|
+
taskPayload.taskId,
|
|
734
|
+
() => runGradingStep(
|
|
735
|
+
taskPayload.taskId,
|
|
736
|
+
"building",
|
|
737
|
+
BUILDING_AUDIT_SYSTEM_PROMPT,
|
|
738
|
+
buildBuildingAuditPrompt(taskPayload, buildingTrace),
|
|
739
|
+
GRADING_LIMITS,
|
|
740
|
+
opts
|
|
741
|
+
)
|
|
742
|
+
);
|
|
743
|
+
}
|
|
744
|
+
async function executeHumanStep(taskPayload, opts) {
|
|
745
|
+
if (taskPayload.humanMessages.length === 0) return EMPTY_STEP;
|
|
746
|
+
reportProgress(
|
|
747
|
+
opts.connection,
|
|
748
|
+
opts.requestId,
|
|
749
|
+
taskPayload.taskId,
|
|
750
|
+
"status",
|
|
751
|
+
"Step 3/4: Auditing human contributions..."
|
|
752
|
+
);
|
|
753
|
+
return await executeStepSafely(
|
|
754
|
+
"Human audit",
|
|
755
|
+
opts.requestId,
|
|
756
|
+
taskPayload.taskId,
|
|
757
|
+
() => runHumanStep(
|
|
758
|
+
taskPayload.taskId,
|
|
759
|
+
buildHumanAuditPrompt(taskPayload),
|
|
760
|
+
{ maxTurns: 6, maxBudgetUsd: 0.5 },
|
|
761
|
+
opts
|
|
762
|
+
)
|
|
763
|
+
);
|
|
764
|
+
}
|
|
765
|
+
async function executeAnalysisStep(taskPayload, planning, building, human, projectId, suggestionIds, opts) {
|
|
766
|
+
reportProgress(
|
|
767
|
+
opts.connection,
|
|
768
|
+
opts.requestId,
|
|
769
|
+
taskPayload.taskId,
|
|
770
|
+
"status",
|
|
771
|
+
"Step 4/4: Generating analysis & suggestions..."
|
|
772
|
+
);
|
|
773
|
+
return await executeStepSafely(
|
|
774
|
+
"Analysis",
|
|
775
|
+
opts.requestId,
|
|
776
|
+
taskPayload.taskId,
|
|
777
|
+
() => runAnalysisStep(
|
|
778
|
+
taskPayload,
|
|
779
|
+
planning.result,
|
|
780
|
+
building.result,
|
|
781
|
+
human.result,
|
|
782
|
+
projectId,
|
|
783
|
+
suggestionIds,
|
|
784
|
+
opts
|
|
785
|
+
)
|
|
786
|
+
);
|
|
787
|
+
}
|
|
788
|
+
async function reportAuditResult(connection, requestId, taskPayload, steps, planningTraceLength, suggestionIds) {
|
|
789
|
+
let totalCostUsd = null;
|
|
790
|
+
for (const step of [steps.planning, steps.building, steps.human, steps.analysis])
|
|
791
|
+
totalCostUsd = addCosts(totalCostUsd, step.costUsd);
|
|
792
|
+
const merged = mergeStepResults(
|
|
793
|
+
steps.planning.result,
|
|
794
|
+
steps.building.result,
|
|
795
|
+
steps.human.result,
|
|
796
|
+
steps.analysis.result,
|
|
797
|
+
planningTraceLength
|
|
798
|
+
);
|
|
799
|
+
await connection.call("reportTaskAuditResult", {
|
|
800
|
+
projectId: connection.projectId,
|
|
801
|
+
requestId,
|
|
802
|
+
taskId: taskPayload.taskId,
|
|
803
|
+
summary: merged.summary,
|
|
804
|
+
turnGrades: merged.turnGrades,
|
|
805
|
+
planningAccuracy: merged.planningAccuracy,
|
|
806
|
+
buildingAccuracy: merged.buildingAccuracy,
|
|
807
|
+
humanAccuracy: merged.humanAccuracy,
|
|
808
|
+
humanEvaluations: merged.humanEvaluations,
|
|
809
|
+
...computeGradeCounts(merged.turnGrades),
|
|
810
|
+
suggestionIds,
|
|
811
|
+
auditCostUsd: totalCostUsd,
|
|
812
|
+
model: steps.planning.model ?? steps.building.model ?? null
|
|
813
|
+
});
|
|
814
|
+
}
|
|
815
|
+
async function auditSingleTask(taskPayload, request, connection, projectDir, model) {
|
|
816
|
+
const { requestId, projectId } = request;
|
|
817
|
+
const suggestionIds = [];
|
|
818
|
+
const opts = { connection, requestId, projectDir, model };
|
|
819
|
+
const { planningTrace, buildingTrace } = sliceTrace(taskPayload.agentTrace);
|
|
820
|
+
const planning = await executePlanningStep(taskPayload, planningTrace, opts);
|
|
821
|
+
const building = await executeBuildingStep(taskPayload, buildingTrace, opts);
|
|
822
|
+
if (!planning.result && !building.result && (planningTrace.length > 0 || buildingTrace.length > 0)) {
|
|
823
|
+
throw new Error("All grading audit steps failed");
|
|
824
|
+
}
|
|
825
|
+
const human = await executeHumanStep(taskPayload, opts);
|
|
826
|
+
const analysis = await executeAnalysisStep(
|
|
827
|
+
taskPayload,
|
|
828
|
+
planning,
|
|
829
|
+
building,
|
|
830
|
+
human,
|
|
831
|
+
projectId,
|
|
832
|
+
suggestionIds,
|
|
833
|
+
opts
|
|
834
|
+
);
|
|
835
|
+
await reportAuditResult(
|
|
836
|
+
connection,
|
|
837
|
+
requestId,
|
|
838
|
+
taskPayload,
|
|
839
|
+
{ planning, building, human, analysis },
|
|
840
|
+
planningTrace.length,
|
|
841
|
+
suggestionIds
|
|
842
|
+
);
|
|
843
|
+
logger.info("Task audit complete", {
|
|
844
|
+
requestId,
|
|
845
|
+
taskId: taskPayload.taskId,
|
|
846
|
+
suggestions: suggestionIds.length
|
|
847
|
+
});
|
|
848
|
+
}
|
|
849
|
+
async function handleTaskAudit(request, connection, projectDir) {
|
|
850
|
+
const { requestId } = request;
|
|
851
|
+
logger.info("Starting task audit", { requestId, taskCount: request.tasks.length });
|
|
852
|
+
const model = await fetchModel(connection);
|
|
853
|
+
for (const taskPayload of request.tasks) {
|
|
854
|
+
try {
|
|
855
|
+
await auditSingleTask(taskPayload, request, connection, projectDir, model);
|
|
856
|
+
} catch (err) {
|
|
857
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
858
|
+
logger.error("Task audit failed for task", {
|
|
859
|
+
requestId,
|
|
860
|
+
taskId: taskPayload.taskId,
|
|
861
|
+
error: msg
|
|
862
|
+
});
|
|
863
|
+
await connection.call(
|
|
864
|
+
"reportTaskAuditResult",
|
|
865
|
+
buildErrorResult(connection.projectId, requestId, taskPayload.taskId, msg)
|
|
866
|
+
).catch(() => {
|
|
867
|
+
});
|
|
868
|
+
}
|
|
869
|
+
}
|
|
870
|
+
await connection.call("reportTaskAuditBatchComplete", { projectId: connection.projectId, requestId }).catch(() => {
|
|
871
|
+
});
|
|
872
|
+
}
|
|
873
|
+
export {
|
|
874
|
+
handleTaskAudit
|
|
875
|
+
};
|
|
876
|
+
//# sourceMappingURL=task-audit-handler-EWE52OL4.js.map
|