heyio 3.2.3 → 3.3.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/dist/api/routes/squads.js +6 -6
- package/dist/api/routes/squads.js.map +1 -1
- package/dist/copilot/tools.d.ts.map +1 -1
- package/dist/copilot/tools.js +19 -6
- package/dist/copilot/tools.js.map +1 -1
- package/dist/squad/agent.d.ts +5 -0
- package/dist/squad/agent.d.ts.map +1 -1
- package/dist/squad/agent.js +10 -1
- package/dist/squad/agent.js.map +1 -1
- package/dist/squad/execution/index.d.ts +7 -5
- package/dist/squad/execution/index.d.ts.map +1 -1
- package/dist/squad/execution/index.js +4 -3
- package/dist/squad/execution/index.js.map +1 -1
- package/dist/squad/execution/instance.js +1 -1
- package/dist/squad/execution/instance.js.map +1 -1
- package/dist/squad/execution/planning.d.ts +20 -0
- package/dist/squad/execution/planning.d.ts.map +1 -0
- package/dist/squad/execution/planning.js +62 -0
- package/dist/squad/execution/planning.js.map +1 -0
- package/dist/squad/execution/review.d.ts +21 -0
- package/dist/squad/execution/review.d.ts.map +1 -0
- package/dist/squad/execution/review.js +151 -0
- package/dist/squad/execution/review.js.map +1 -0
- package/dist/squad/execution/runner.d.ts +32 -7
- package/dist/squad/execution/runner.d.ts.map +1 -1
- package/dist/squad/execution/runner.js +40 -25
- package/dist/squad/execution/runner.js.map +1 -1
- package/dist/squad/execution/tasks.d.ts +13 -2
- package/dist/squad/execution/tasks.d.ts.map +1 -1
- package/dist/squad/execution/tasks.js +58 -59
- package/dist/squad/execution/tasks.js.map +1 -1
- package/node_modules/@io/shared/dist/types/events.d.ts +1 -1
- package/node_modules/@io/shared/dist/types/events.d.ts.map +1 -1
- package/node_modules/@io/shared/package.json +1 -1
- package/node_modules/@io/shared/src/types/events.ts +2 -0
- package/node_modules/@io/shared/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/dist/squad/execution/meeting.d.ts +0 -30
- package/dist/squad/execution/meeting.d.ts.map +0 -1
- package/dist/squad/execution/meeting.js +0 -147
- package/dist/squad/execution/meeting.js.map +0 -1
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { createChildLogger } from '../../logging/logger.js';
|
|
2
|
+
import { getEventBus } from '../event-bus.js';
|
|
3
|
+
import { parseTierHint } from '../model-selector.js';
|
|
4
|
+
import { transitionInstance } from './instance.js';
|
|
5
|
+
const logger = () => createChildLogger('planning');
|
|
6
|
+
/**
|
|
7
|
+
* Team Lead plans the instance alone — one LLM call to produce a task list.
|
|
8
|
+
* No round-table, no consensus, no veto at planning stage.
|
|
9
|
+
*/
|
|
10
|
+
export async function planInstance(params) {
|
|
11
|
+
const log = logger();
|
|
12
|
+
const { instance, runtime, objective, attachments } = params;
|
|
13
|
+
// Transition to planning (already in 'planning' from createInstance, transition to working skipped — we use a direct status)
|
|
14
|
+
const teamLead = runtime.members.get('technical-pm');
|
|
15
|
+
if (!teamLead)
|
|
16
|
+
throw new Error('No team lead available for planning');
|
|
17
|
+
// Track activity under this instance
|
|
18
|
+
teamLead.setInstanceId(instance.id);
|
|
19
|
+
// Build roster description so the lead knows who's available
|
|
20
|
+
const roster = [...runtime.members.entries()]
|
|
21
|
+
.filter(([role]) => role !== 'technical-pm')
|
|
22
|
+
.map(([role]) => `- ${role}`)
|
|
23
|
+
.join('\n');
|
|
24
|
+
const attachmentNote = attachments?.length
|
|
25
|
+
? `\n\nThe user has provided ${attachments.length} file(s) as reference material. They are attached to this message — review them as part of your planning.`
|
|
26
|
+
: '';
|
|
27
|
+
const taskPlan = await teamLead.send(`You are the team lead. Plan the following objective and produce a task list for your team.\n\nObjective: ${objective}${attachmentNote}\n\nYour team members (by role):\n${roster}\n\nFor each task, specify:\n1. A brief description of the work\n2. Which team member role should do it (must match a role above)\n3. The model tier needed (fast for simple edits/docs, standard for typical coding, reasoning for complex architecture/analysis)\n\nFormat each task as: "TASK: <description> | ASSIGN: <role> | MODEL_TIER: <fast|standard|reasoning>"\n\nChoose the lowest tier that can handle each task to minimize cost. Tasks will execute in parallel, so ensure they are independent and don't conflict on the same files.`, attachments);
|
|
28
|
+
const tasks = parseTaskList(taskPlan, instance.id);
|
|
29
|
+
instance.tasks = tasks;
|
|
30
|
+
instance.meetingLog = [`[technical-pm] Plan:\n${taskPlan}`];
|
|
31
|
+
await getEventBus().emit({
|
|
32
|
+
id: crypto.randomUUID(),
|
|
33
|
+
timestamp: new Date(),
|
|
34
|
+
type: 'instance:planning_complete',
|
|
35
|
+
squadId: instance.squadId,
|
|
36
|
+
instanceId: instance.id,
|
|
37
|
+
data: { taskCount: tasks.length, objective },
|
|
38
|
+
});
|
|
39
|
+
log.info({ instanceId: instance.id, tasks: tasks.length }, 'Planning complete');
|
|
40
|
+
return { tasks };
|
|
41
|
+
}
|
|
42
|
+
/** Parse the team lead's task list into structured tasks */
|
|
43
|
+
function parseTaskList(taskPlan, instanceId) {
|
|
44
|
+
const tasks = [];
|
|
45
|
+
const lines = taskPlan.split('\n');
|
|
46
|
+
for (const line of lines) {
|
|
47
|
+
const match = line.match(/TASK:\s*(.+?)\s*\|\s*ASSIGN:\s*([^|]+)(?:\s*\|\s*MODEL_TIER:\s*(\w+))?/i);
|
|
48
|
+
if (match) {
|
|
49
|
+
const tierHint = parseTierHint(match[3]);
|
|
50
|
+
tasks.push({
|
|
51
|
+
id: crypto.randomUUID(),
|
|
52
|
+
description: match[1].trim(),
|
|
53
|
+
assignedTo: match[2].trim().toLowerCase().replace(/\s+/g, '-'),
|
|
54
|
+
status: 'pending',
|
|
55
|
+
modelTier: tierHint,
|
|
56
|
+
retryCount: 0,
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return tasks;
|
|
61
|
+
}
|
|
62
|
+
//# sourceMappingURL=planning.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"planning.js","sourceRoot":"","sources":["../../../src/squad/execution/planning.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAE5D,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAE9C,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAErD,OAAO,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AAEnD,MAAM,MAAM,GAAG,GAAG,EAAE,CAAC,iBAAiB,CAAC,UAAU,CAAC,CAAC;AAMnD;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,MAKlC;IACA,MAAM,GAAG,GAAG,MAAM,EAAE,CAAC;IACrB,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,GAAG,MAAM,CAAC;IAE7D,6HAA6H;IAC7H,MAAM,QAAQ,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;IACrD,IAAI,CAAC,QAAQ;QAAE,MAAM,IAAI,KAAK,CAAC,qCAAqC,CAAC,CAAC;IAEtE,qCAAqC;IACrC,QAAQ,CAAC,aAAa,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;IAEpC,6DAA6D;IAC7D,MAAM,MAAM,GAAG,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC;SAC3C,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC,IAAI,KAAK,cAAc,CAAC;SAC3C,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC,KAAK,IAAI,EAAE,CAAC;SAC5B,IAAI,CAAC,IAAI,CAAC,CAAC;IAEb,MAAM,cAAc,GAAG,WAAW,EAAE,MAAM;QACzC,CAAC,CAAC,6BAA6B,WAAW,CAAC,MAAM,2GAA2G;QAC5J,CAAC,CAAC,EAAE,CAAC;IAEN,MAAM,QAAQ,GAAG,MAAM,QAAQ,CAAC,IAAI,CACnC,4GAA4G,SAAS,GAAG,cAAc,qCAAqC,MAAM,shBAAshB,EACvsB,WAAW,CACX,CAAC;IAEF,MAAM,KAAK,GAAG,aAAa,CAAC,QAAQ,EAAE,QAAQ,CAAC,EAAE,CAAC,CAAC;IAEnD,QAAQ,CAAC,KAAK,GAAG,KAAK,CAAC;IACvB,QAAQ,CAAC,UAAU,GAAG,CAAC,yBAAyB,QAAQ,EAAE,CAAC,CAAC;IAE5D,MAAM,WAAW,EAAE,CAAC,IAAI,CAAC;QACxB,EAAE,EAAE,MAAM,CAAC,UAAU,EAAE;QACvB,SAAS,EAAE,IAAI,IAAI,EAAE;QACrB,IAAI,EAAE,4BAA4B;QAClC,OAAO,EAAE,QAAQ,CAAC,OAAO;QACzB,UAAU,EAAE,QAAQ,CAAC,EAAE;QACvB,IAAI,EAAE,EAAE,SAAS,EAAE,KAAK,CAAC,MAAM,EAAE,SAAS,EAAE;KAC5C,CAAC,CAAC;IAEH,GAAG,CAAC,IAAI,CAAC,EAAE,UAAU,EAAE,QAAQ,CAAC,EAAE,EAAE,KAAK,EAAE,KAAK,CAAC,MAAM,EAAE,EAAE,mBAAmB,CAAC,CAAC;IAEhF,OAAO,EAAE,KAAK,EAAE,CAAC;AAClB,CAAC;AAED,4DAA4D;AAC5D,SAAS,aAAa,CAAC,QAAgB,EAAE,UAAkB;IAC1D,MAAM,KAAK,GAAmB,EAAE,CAAC;IACjC,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAEnC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QAC1B,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,yEAAyE,CAAC,CAAC;QACpG,IAAI,KAAK,EAAE,CAAC;YACX,MAAM,QAAQ,GAAG,aAAa,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;YACzC,KAAK,CAAC,IAAI,CAAC;gBACV,EAAE,EAAE,MAAM,CAAC,UAAU,EAAE;gBACvB,WAAW,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE;gBAC5B,UAAU,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC;gBAC9D,MAAM,EAAE,SAAS;gBACjB,SAAS,EAAE,QAAQ;gBACnB,UAAU,EAAE,CAAC;aACb,CAAC,CAAC;QACJ,CAAC;IACF,CAAC;IAED,OAAO,KAAK,CAAC;AACd,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { SquadRuntime } from '../manager.js';
|
|
2
|
+
import type { Instance } from './instance.js';
|
|
3
|
+
export interface ReviewResult {
|
|
4
|
+
approved: boolean;
|
|
5
|
+
cycles: number;
|
|
6
|
+
failureReason?: string;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Gated review cycle:
|
|
10
|
+
* 1. Lead reviews all task results
|
|
11
|
+
* 2. If lead rejects → identifies tasks to rework, specialists rework in parallel
|
|
12
|
+
* 3. If lead approves → QA/veto reviews
|
|
13
|
+
* 4. If QA rejects → same rework loop
|
|
14
|
+
* 5. Max 5 cycles total, then fail to inbox
|
|
15
|
+
*/
|
|
16
|
+
export declare function reviewWork(params: {
|
|
17
|
+
instance: Instance;
|
|
18
|
+
runtime: SquadRuntime;
|
|
19
|
+
objective: string;
|
|
20
|
+
}): Promise<ReviewResult>;
|
|
21
|
+
//# sourceMappingURL=review.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"review.d.ts","sourceRoot":"","sources":["../../../src/squad/execution/review.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAGlD,OAAO,KAAK,EAAE,QAAQ,EAAgB,MAAM,eAAe,CAAC;AAQ5D,MAAM,WAAW,YAAY;IAC5B,QAAQ,EAAE,OAAO,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,CAAC,EAAE,MAAM,CAAC;CACvB;AAED;;;;;;;GAOG;AACH,wBAAsB,UAAU,CAAC,MAAM,EAAE;IACxC,QAAQ,EAAE,QAAQ,CAAC;IACnB,OAAO,EAAE,YAAY,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;CAClB,GAAG,OAAO,CAAC,YAAY,CAAC,CAyHxB"}
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import { createChildLogger } from '../../logging/logger.js';
|
|
2
|
+
import { getEventBus } from '../event-bus.js';
|
|
3
|
+
import { selectModelForRole } from '../model-selector.js';
|
|
4
|
+
import { addInboxEntry } from '../../store/inbox.js';
|
|
5
|
+
import { transitionInstance } from './instance.js';
|
|
6
|
+
import { executeRework } from './tasks.js';
|
|
7
|
+
const logger = () => createChildLogger('review');
|
|
8
|
+
const MAX_REVIEW_CYCLES = 5;
|
|
9
|
+
/**
|
|
10
|
+
* Gated review cycle:
|
|
11
|
+
* 1. Lead reviews all task results
|
|
12
|
+
* 2. If lead rejects → identifies tasks to rework, specialists rework in parallel
|
|
13
|
+
* 3. If lead approves → QA/veto reviews
|
|
14
|
+
* 4. If QA rejects → same rework loop
|
|
15
|
+
* 5. Max 5 cycles total, then fail to inbox
|
|
16
|
+
*/
|
|
17
|
+
export async function reviewWork(params) {
|
|
18
|
+
const log = logger();
|
|
19
|
+
const { instance, runtime, objective } = params;
|
|
20
|
+
await transitionInstance(instance.id, 'reviewing');
|
|
21
|
+
const teamLead = runtime.members.get('technical-pm');
|
|
22
|
+
if (!teamLead)
|
|
23
|
+
throw new Error('No team lead for review');
|
|
24
|
+
// Track review activity under this instance
|
|
25
|
+
teamLead.setInstanceId(instance.id);
|
|
26
|
+
// Find veto/QA members
|
|
27
|
+
const vetoMembers = [...runtime.members.entries()].filter(([role]) => {
|
|
28
|
+
const skill = runtime.skills.get(role);
|
|
29
|
+
return skill?.veto && role !== 'technical-pm';
|
|
30
|
+
});
|
|
31
|
+
let cycles = 0;
|
|
32
|
+
while (cycles < MAX_REVIEW_CYCLES) {
|
|
33
|
+
cycles++;
|
|
34
|
+
log.info({ instanceId: instance.id, cycle: cycles }, 'Review cycle');
|
|
35
|
+
await getEventBus().emit({
|
|
36
|
+
id: crypto.randomUUID(),
|
|
37
|
+
timestamp: new Date(),
|
|
38
|
+
type: 'instance:review_cycle',
|
|
39
|
+
squadId: instance.squadId,
|
|
40
|
+
instanceId: instance.id,
|
|
41
|
+
data: { cycle: cycles, maxCycles: MAX_REVIEW_CYCLES },
|
|
42
|
+
});
|
|
43
|
+
// Build task summary for reviewer
|
|
44
|
+
const taskSummary = instance.tasks
|
|
45
|
+
.map((t) => `- [${t.status}] ${t.description} (${t.assignedTo})\n Result: ${(t.result ?? 'No result').slice(0, 1000)}`)
|
|
46
|
+
.join('\n\n');
|
|
47
|
+
// Step 1: Team Lead reviews
|
|
48
|
+
const reviewModel = selectModelForRole('technical-pm', 'review');
|
|
49
|
+
if (teamLead.getModel() !== reviewModel) {
|
|
50
|
+
await teamLead.switchModel(reviewModel);
|
|
51
|
+
}
|
|
52
|
+
const leadReview = await teamLead.send(`Review all completed work for this objective:\n\nObjective: ${objective}\n\nTask results:\n${taskSummary}\n\nDoes the work meet the objective? Reply with one of:\n- "APPROVED" if all work is satisfactory\n- "REWORK:" followed by a JSON array of objects with "taskId" and "feedback" for each task that needs revision. Example:\nREWORK: [{"taskId": "abc-123", "feedback": "Missing error handling"}]`);
|
|
53
|
+
if (leadReview.toUpperCase().includes('APPROVED')) {
|
|
54
|
+
// Step 2: QA/veto review (gated behind lead approval)
|
|
55
|
+
if (vetoMembers.length === 0) {
|
|
56
|
+
log.info({ instanceId: instance.id }, 'No veto members, lead approval is final');
|
|
57
|
+
return { approved: true, cycles };
|
|
58
|
+
}
|
|
59
|
+
for (const [role, agent] of vetoMembers) {
|
|
60
|
+
agent.setInstanceId(instance.id);
|
|
61
|
+
const qaModel = selectModelForRole(role, 'review');
|
|
62
|
+
if (agent.getModel() !== qaModel) {
|
|
63
|
+
await agent.switchModel(qaModel);
|
|
64
|
+
}
|
|
65
|
+
const qaReview = await agent.send(`As QA, review the following completed work:\n\nObjective: ${objective}\n\nTask results:\n${taskSummary}\n\nDoes this meet quality standards? Reply with:\n- "APPROVED" if satisfactory\n- "REWORK:" followed by a JSON array of objects with "taskId" and "feedback" for tasks that need revision.`);
|
|
66
|
+
if (qaReview.toUpperCase().includes('REWORK:')) {
|
|
67
|
+
// QA rejected — parse rework instructions and loop
|
|
68
|
+
const reworkTasks = parseReworkInstructions(qaReview, instance.tasks);
|
|
69
|
+
if (reworkTasks.length > 0) {
|
|
70
|
+
log.info({ instanceId: instance.id, cycle: cycles, rejectedBy: role, reworkCount: reworkTasks.length }, 'QA rejected, reworking');
|
|
71
|
+
await transitionInstance(instance.id, 'working');
|
|
72
|
+
await executeRework({ instance, runtime, reworkTasks });
|
|
73
|
+
await transitionInstance(instance.id, 'reviewing');
|
|
74
|
+
}
|
|
75
|
+
break; // Back to top of review loop
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
// If we didn't break (all QA approved), we're done
|
|
79
|
+
if (!leadReview.toUpperCase().includes('REWORK:')) {
|
|
80
|
+
// Check that we actually reached this point without a QA rejection
|
|
81
|
+
const allQaApproved = true; // If we didn't break, all approved
|
|
82
|
+
if (allQaApproved) {
|
|
83
|
+
log.info({ instanceId: instance.id, cycles }, 'All reviewers approved');
|
|
84
|
+
return { approved: true, cycles };
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
else if (leadReview.toUpperCase().includes('REWORK:')) {
|
|
89
|
+
// Lead rejected — parse rework and loop
|
|
90
|
+
const reworkTasks = parseReworkInstructions(leadReview, instance.tasks);
|
|
91
|
+
if (reworkTasks.length > 0) {
|
|
92
|
+
log.info({ instanceId: instance.id, cycle: cycles, reworkCount: reworkTasks.length }, 'Lead rejected, reworking');
|
|
93
|
+
await transitionInstance(instance.id, 'working');
|
|
94
|
+
await executeRework({ instance, runtime, reworkTasks });
|
|
95
|
+
await transitionInstance(instance.id, 'reviewing');
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
// Couldn't parse rework instructions — treat as approved to avoid infinite loop
|
|
99
|
+
log.warn({ instanceId: instance.id }, 'Could not parse rework instructions, treating as approved');
|
|
100
|
+
return { approved: true, cycles };
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
// Ambiguous response — treat as approved
|
|
105
|
+
log.warn({ instanceId: instance.id }, 'Ambiguous review response, treating as approved');
|
|
106
|
+
return { approved: true, cycles };
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
// Exhausted review cycles — send to inbox
|
|
110
|
+
const failureReason = `Failed to pass review after ${MAX_REVIEW_CYCLES} cycles for objective: ${objective}`;
|
|
111
|
+
log.error({ instanceId: instance.id }, failureReason);
|
|
112
|
+
await addInboxEntry({
|
|
113
|
+
squadId: instance.squadId,
|
|
114
|
+
instanceId: instance.id,
|
|
115
|
+
kind: 'note',
|
|
116
|
+
title: `Review failed: ${objective.slice(0, 60)}`,
|
|
117
|
+
content: `Instance ${instance.id} exhausted ${MAX_REVIEW_CYCLES} review/rework cycles without reaching approval.\n\nObjective: ${objective}\n\nFinal task states:\n${instance.tasks.map((t) => `- [${t.status}] ${t.description}: ${(t.result ?? 'no result').slice(0, 200)}`).join('\n')}`,
|
|
118
|
+
});
|
|
119
|
+
return { approved: false, cycles, failureReason };
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Parse rework instructions from a review response.
|
|
123
|
+
* Expects JSON array after "REWORK:" with {taskId, feedback} objects.
|
|
124
|
+
* Falls back to assigning feedback to all non-done tasks if parsing fails.
|
|
125
|
+
*/
|
|
126
|
+
function parseReworkInstructions(response, tasks) {
|
|
127
|
+
const reworkMatch = response.match(/REWORK:\s*(\[[\s\S]*?\])/i);
|
|
128
|
+
if (reworkMatch) {
|
|
129
|
+
try {
|
|
130
|
+
const parsed = JSON.parse(reworkMatch[1]);
|
|
131
|
+
if (Array.isArray(parsed)) {
|
|
132
|
+
return parsed
|
|
133
|
+
.filter((item) => item.taskId && item.feedback)
|
|
134
|
+
.map((item) => ({
|
|
135
|
+
taskId: item.taskId,
|
|
136
|
+
feedback: item.feedback,
|
|
137
|
+
}));
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
catch {
|
|
141
|
+
// Fall through to fallback
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
// Fallback: extract plain-text feedback and apply to all completed tasks
|
|
145
|
+
const feedbackMatch = response.match(/REWORK:\s*([\s\S]+)/i);
|
|
146
|
+
const feedback = feedbackMatch?.[1]?.trim() ?? 'Please review and fix issues.';
|
|
147
|
+
return tasks
|
|
148
|
+
.filter((t) => t.status === 'done' || t.status === 'failed')
|
|
149
|
+
.map((t) => ({ taskId: t.id, feedback }));
|
|
150
|
+
}
|
|
151
|
+
//# sourceMappingURL=review.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"review.js","sourceRoot":"","sources":["../../../src/squad/execution/review.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,18 +1,43 @@
|
|
|
1
1
|
import type { Squad } from '@io/shared';
|
|
2
|
-
import { type
|
|
2
|
+
import { type SquadRuntime } from '../manager.js';
|
|
3
|
+
import { type Instance, type PrResult } from './index.js';
|
|
3
4
|
export interface RunResult {
|
|
4
5
|
instanceId: string;
|
|
5
6
|
success: boolean;
|
|
6
7
|
pr?: PrResult | null;
|
|
7
8
|
error?: string;
|
|
8
9
|
}
|
|
10
|
+
export interface InitResult {
|
|
11
|
+
instance: Instance;
|
|
12
|
+
runtime: SquadRuntime;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Initialize an instance: boot the squad runtime and persist the instance to DB.
|
|
16
|
+
* This is the critical part that MUST succeed before we tell the user "started."
|
|
17
|
+
*/
|
|
18
|
+
export declare function initInstance(params: {
|
|
19
|
+
squad: Squad;
|
|
20
|
+
objective: string;
|
|
21
|
+
issueRef?: string;
|
|
22
|
+
}): Promise<InitResult>;
|
|
23
|
+
/**
|
|
24
|
+
* Execute an already-initialized instance through its full lifecycle:
|
|
25
|
+
* planning → tasks → review → PR → cleanup
|
|
26
|
+
*/
|
|
27
|
+
export declare function executeInstance(params: {
|
|
28
|
+
instance: Instance;
|
|
29
|
+
runtime: SquadRuntime;
|
|
30
|
+
squad: Squad;
|
|
31
|
+
objective: string;
|
|
32
|
+
attachments?: Array<{
|
|
33
|
+
type: 'file';
|
|
34
|
+
path: string;
|
|
35
|
+
displayName?: string;
|
|
36
|
+
}>;
|
|
37
|
+
}): Promise<RunResult>;
|
|
9
38
|
/**
|
|
10
|
-
* Run a full instance lifecycle
|
|
11
|
-
*
|
|
12
|
-
* 2. Hold round-table meeting
|
|
13
|
-
* 3. Execute tasks
|
|
14
|
-
* 4. Create PR
|
|
15
|
-
* 5. Clean up
|
|
39
|
+
* Run a full instance lifecycle (init + execute).
|
|
40
|
+
* Convenience wrapper for callers that don't need to split the phases.
|
|
16
41
|
*/
|
|
17
42
|
export declare function runInstance(params: {
|
|
18
43
|
squad: Squad;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../../../src/squad/execution/runner.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../../../src/squad/execution/runner.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAExC,OAAO,EAAE,KAAK,YAAY,EAA8B,MAAM,eAAe,CAAC;AAC9E,OAAO,EACN,KAAK,QAAQ,EACb,KAAK,QAAQ,EAOb,MAAM,YAAY,CAAC;AAIpB,MAAM,WAAW,SAAS;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,OAAO,CAAC;IACjB,EAAE,CAAC,EAAE,QAAQ,GAAG,IAAI,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,UAAU;IAC1B,QAAQ,EAAE,QAAQ,CAAC;IACnB,OAAO,EAAE,YAAY,CAAC;CACtB;AAED;;;GAGG;AACH,wBAAsB,YAAY,CAAC,MAAM,EAAE;IAC1C,KAAK,EAAE,KAAK,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;CAClB,GAAG,OAAO,CAAC,UAAU,CAAC,CAatB;AAED;;;GAGG;AACH,wBAAsB,eAAe,CAAC,MAAM,EAAE;IAC7C,QAAQ,EAAE,QAAQ,CAAC;IACnB,OAAO,EAAE,YAAY,CAAC;IACtB,KAAK,EAAE,KAAK,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAC1E,GAAG,OAAO,CAAC,SAAS,CAAC,CA0DrB;AAED;;;GAGG;AACH,wBAAsB,WAAW,CAAC,MAAM,EAAE;IACzC,KAAK,EAAE,KAAK,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAC1E,GAAG,OAAO,CAAC,SAAS,CAAC,CAMrB"}
|
|
@@ -1,51 +1,57 @@
|
|
|
1
1
|
import { createChildLogger } from '../../logging/logger.js';
|
|
2
2
|
import { bootSquad, getSquadRuntime } from '../manager.js';
|
|
3
|
-
import { cleanupInstance, createInstance, createPullRequest, executeTasks,
|
|
3
|
+
import { cleanupInstance, createInstance, createPullRequest, executeTasks, planInstance, reviewWork, } from './index.js';
|
|
4
4
|
const logger = () => createChildLogger('runner');
|
|
5
5
|
/**
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* 2. Hold round-table meeting
|
|
9
|
-
* 3. Execute tasks
|
|
10
|
-
* 4. Create PR
|
|
11
|
-
* 5. Clean up
|
|
6
|
+
* Initialize an instance: boot the squad runtime and persist the instance to DB.
|
|
7
|
+
* This is the critical part that MUST succeed before we tell the user "started."
|
|
12
8
|
*/
|
|
13
|
-
export async function
|
|
14
|
-
const
|
|
15
|
-
const { squad, objective, issueRef, attachments } = params;
|
|
9
|
+
export async function initInstance(params) {
|
|
10
|
+
const { squad, objective, issueRef } = params;
|
|
16
11
|
// Ensure squad is booted
|
|
17
12
|
let runtime = getSquadRuntime(squad.id);
|
|
18
13
|
if (!runtime) {
|
|
19
14
|
runtime = await bootSquad(squad);
|
|
20
15
|
}
|
|
21
|
-
//
|
|
16
|
+
// Create instance (persists to DB + creates worktree)
|
|
22
17
|
const instance = await createInstance({ squad, issueRef, objective });
|
|
23
|
-
|
|
18
|
+
return { instance, runtime };
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Execute an already-initialized instance through its full lifecycle:
|
|
22
|
+
* planning → tasks → review → PR → cleanup
|
|
23
|
+
*/
|
|
24
|
+
export async function executeInstance(params) {
|
|
25
|
+
const log = logger();
|
|
26
|
+
const { instance, runtime, squad, objective, attachments } = params;
|
|
27
|
+
log.info({ instanceId: instance.id }, 'Starting instance execution');
|
|
24
28
|
try {
|
|
25
|
-
//
|
|
26
|
-
|
|
27
|
-
if (
|
|
28
|
-
log.warn({ instanceId: instance.id }, '
|
|
29
|
+
// Team Lead plans
|
|
30
|
+
await planInstance({ instance, runtime, objective, attachments });
|
|
31
|
+
if (instance.tasks.length === 0) {
|
|
32
|
+
log.warn({ instanceId: instance.id }, 'No tasks generated from planning');
|
|
29
33
|
return {
|
|
30
34
|
instanceId: instance.id,
|
|
31
35
|
success: false,
|
|
32
|
-
error:
|
|
36
|
+
error: 'Planning produced no actionable tasks',
|
|
33
37
|
};
|
|
34
38
|
}
|
|
35
|
-
|
|
36
|
-
|
|
39
|
+
// Execute tasks in parallel
|
|
40
|
+
await executeTasks({ instance, runtime });
|
|
41
|
+
// Gated review/rework cycles
|
|
42
|
+
const reviewResult = await reviewWork({ instance, runtime, objective });
|
|
43
|
+
if (!reviewResult.approved) {
|
|
44
|
+
await cleanupInstance(instance.id, squad);
|
|
37
45
|
return {
|
|
38
46
|
instanceId: instance.id,
|
|
39
47
|
success: false,
|
|
40
|
-
error:
|
|
48
|
+
error: reviewResult.failureReason ?? 'Review failed',
|
|
41
49
|
};
|
|
42
50
|
}
|
|
43
|
-
//
|
|
44
|
-
await executeTasks({ instance, runtime });
|
|
45
|
-
// 4. Create PR
|
|
51
|
+
// Create PR
|
|
46
52
|
const prTitle = objective.slice(0, 72);
|
|
47
53
|
const pr = await createPullRequest({ instance, title: prTitle, squadName: squad.name });
|
|
48
|
-
//
|
|
54
|
+
// Cleanup (if no PR was created — otherwise keep the branch for review)
|
|
49
55
|
if (!pr) {
|
|
50
56
|
await cleanupInstance(instance.id, squad);
|
|
51
57
|
}
|
|
@@ -57,7 +63,7 @@ export async function runInstance(params) {
|
|
|
57
63
|
}
|
|
58
64
|
catch (err) {
|
|
59
65
|
const errMsg = err instanceof Error ? err.message : String(err);
|
|
60
|
-
log.error({ instanceId: instance.id, error: errMsg.slice(0, 300) }, 'Instance
|
|
66
|
+
log.error({ instanceId: instance.id, error: errMsg.slice(0, 300) }, 'Instance execution failed');
|
|
61
67
|
await cleanupInstance(instance.id, squad);
|
|
62
68
|
return {
|
|
63
69
|
instanceId: instance.id,
|
|
@@ -66,4 +72,13 @@ export async function runInstance(params) {
|
|
|
66
72
|
};
|
|
67
73
|
}
|
|
68
74
|
}
|
|
75
|
+
/**
|
|
76
|
+
* Run a full instance lifecycle (init + execute).
|
|
77
|
+
* Convenience wrapper for callers that don't need to split the phases.
|
|
78
|
+
*/
|
|
79
|
+
export async function runInstance(params) {
|
|
80
|
+
const { squad, objective, issueRef, attachments } = params;
|
|
81
|
+
const { instance, runtime } = await initInstance({ squad, objective, issueRef });
|
|
82
|
+
return executeInstance({ instance, runtime, squad, objective, attachments });
|
|
83
|
+
}
|
|
69
84
|
//# sourceMappingURL=runner.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runner.js","sourceRoot":"","sources":["../../../src/squad/execution/runner.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAC5D,OAAO,EAAqB,SAAS,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAC9E,OAAO,EAGN,eAAe,EACf,cAAc,EACd,iBAAiB,EACjB,YAAY,EACZ,UAAU,GACV,MAAM,YAAY,CAAC;AAEpB,MAAM,MAAM,GAAG,GAAG,EAAE,CAAC,iBAAiB,CAAC,QAAQ,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"runner.js","sourceRoot":"","sources":["../../../src/squad/execution/runner.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,11 +1,22 @@
|
|
|
1
1
|
import type { SquadRuntime } from '../manager.js';
|
|
2
2
|
import { type Instance } from './instance.js';
|
|
3
3
|
/**
|
|
4
|
-
* Execute all tasks in
|
|
5
|
-
*
|
|
4
|
+
* Execute all tasks in parallel.
|
|
5
|
+
* No per-task review — that's handled by the review phase.
|
|
6
6
|
*/
|
|
7
7
|
export declare function executeTasks(params: {
|
|
8
8
|
instance: Instance;
|
|
9
9
|
runtime: SquadRuntime;
|
|
10
10
|
}): Promise<void>;
|
|
11
|
+
/**
|
|
12
|
+
* Execute tasks that need rework (called during review cycles).
|
|
13
|
+
*/
|
|
14
|
+
export declare function executeRework(params: {
|
|
15
|
+
instance: Instance;
|
|
16
|
+
runtime: SquadRuntime;
|
|
17
|
+
reworkTasks: Array<{
|
|
18
|
+
taskId: string;
|
|
19
|
+
feedback: string;
|
|
20
|
+
}>;
|
|
21
|
+
}): Promise<void>;
|
|
11
22
|
//# sourceMappingURL=tasks.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tasks.d.ts","sourceRoot":"","sources":["../../../src/squad/execution/tasks.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"tasks.d.ts","sourceRoot":"","sources":["../../../src/squad/execution/tasks.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAElD,OAAO,EAAE,KAAK,QAAQ,EAAyC,MAAM,eAAe,CAAC;AAIrF;;;GAGG;AACH,wBAAsB,YAAY,CAAC,MAAM,EAAE;IAC1C,QAAQ,EAAE,QAAQ,CAAC;IACnB,OAAO,EAAE,YAAY,CAAC;CACtB,GAAG,OAAO,CAAC,IAAI,CAAC,CAehB;AAED;;GAEG;AACH,wBAAsB,aAAa,CAAC,MAAM,EAAE;IAC3C,QAAQ,EAAE,QAAQ,CAAC;IACnB,OAAO,EAAE,YAAY,CAAC;IACtB,WAAW,EAAE,KAAK,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACzD,GAAG,OAAO,CAAC,IAAI,CAAC,CA0ChB"}
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import { createChildLogger } from '../../logging/logger.js';
|
|
2
|
-
import {
|
|
3
|
-
import { selectModelForRole, selectModelForTask } from '../model-selector.js';
|
|
2
|
+
import { selectModelForTask } from '../model-selector.js';
|
|
4
3
|
import { transitionInstance } from './instance.js';
|
|
5
4
|
const logger = () => createChildLogger('task-exec');
|
|
6
5
|
/**
|
|
7
|
-
* Execute all tasks in
|
|
8
|
-
*
|
|
6
|
+
* Execute all tasks in parallel.
|
|
7
|
+
* No per-task review — that's handled by the review phase.
|
|
9
8
|
*/
|
|
10
9
|
export async function executeTasks(params) {
|
|
11
10
|
const log = logger();
|
|
@@ -14,77 +13,77 @@ export async function executeTasks(params) {
|
|
|
14
13
|
const teamLead = runtime.members.get('technical-pm');
|
|
15
14
|
if (!teamLead)
|
|
16
15
|
throw new Error('No team lead for task execution');
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
16
|
+
// Execute all tasks concurrently
|
|
17
|
+
await Promise.all(instance.tasks
|
|
18
|
+
.filter((task) => task.status !== 'done')
|
|
19
|
+
.map((task) => executeTask(task, instance, runtime, log)));
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Execute tasks that need rework (called during review cycles).
|
|
23
|
+
*/
|
|
24
|
+
export async function executeRework(params) {
|
|
25
|
+
const log = logger();
|
|
26
|
+
const { instance, runtime, reworkTasks } = params;
|
|
27
|
+
await Promise.all(reworkTasks.map(async ({ taskId, feedback }) => {
|
|
28
|
+
const task = instance.tasks.find((t) => t.id === taskId);
|
|
29
|
+
if (!task)
|
|
30
|
+
return;
|
|
31
|
+
task.retryCount++;
|
|
21
32
|
task.status = 'in_progress';
|
|
22
33
|
const agent = runtime.members.get(task.assignedTo);
|
|
23
34
|
if (!agent) {
|
|
24
|
-
log.warn({ role: task.assignedTo }, 'No agent for assigned role, team lead will handle');
|
|
25
|
-
// Reassign to team lead for delegation decision
|
|
26
|
-
task.result = `No agent with role '${task.assignedTo}' available.`;
|
|
27
35
|
task.status = 'failed';
|
|
28
|
-
|
|
36
|
+
task.result = `No agent with role '${task.assignedTo}' available.`;
|
|
37
|
+
return;
|
|
29
38
|
}
|
|
30
|
-
//
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
39
|
+
// Track activity under this instance
|
|
40
|
+
agent.setInstanceId(instance.id);
|
|
41
|
+
const model = selectModelForTask(task.modelTier, task.retryCount);
|
|
42
|
+
if (agent.getModel() !== model) {
|
|
43
|
+
await agent.switchModel(model);
|
|
34
44
|
}
|
|
35
45
|
try {
|
|
36
|
-
// Agent executes the task
|
|
37
46
|
const workingDir = instance.worktree?.path ?? '';
|
|
38
|
-
const
|
|
39
|
-
const result = await agent.send(taskPrompt);
|
|
47
|
+
const result = await agent.send(`Your previous work on "${task.description}" needs revision.\n\nFeedback: ${feedback}\n\n${workingDir ? `Working directory: ${workingDir}\n\n` : ''}Please address this feedback and report your updated results.`);
|
|
40
48
|
task.result = result;
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
if (teamLead.getModel() !== reviewModel) {
|
|
44
|
-
await teamLead.switchModel(reviewModel);
|
|
45
|
-
}
|
|
46
|
-
const review = await teamLead.send(`An agent (${task.assignedTo}) completed a task. Review their work:\n\nTask: ${task.description}\n\nAgent's report:\n${result.slice(0, 2000)}\n\nIs this satisfactory? Reply with:\n- "APPROVED" if the work meets requirements\n- "REDO: <feedback>" if it needs changes`);
|
|
47
|
-
if (review.toUpperCase().includes('APPROVED')) {
|
|
48
|
-
task.status = 'done';
|
|
49
|
-
log.info({ taskId: task.id }, 'Task approved');
|
|
50
|
-
}
|
|
51
|
-
else if (review.toUpperCase().includes('REDO:')) {
|
|
52
|
-
// Retry with escalated model
|
|
53
|
-
task.retryCount++;
|
|
54
|
-
const escalatedModel = selectModelForTask(task.modelTier, task.retryCount);
|
|
55
|
-
log.info({ taskId: task.id, retryCount: task.retryCount, model: escalatedModel }, 'Task needs revision, escalating model');
|
|
56
|
-
if (agent.getModel() !== escalatedModel) {
|
|
57
|
-
await agent.switchModel(escalatedModel);
|
|
58
|
-
}
|
|
59
|
-
const feedback = review.replace(/^.*REDO:\s*/i, '');
|
|
60
|
-
const retry = await agent.send(`Your work on "${task.description}" needs revision. Feedback from team lead:\n\n${feedback}\n\nPlease address this feedback and report your updated results.`);
|
|
61
|
-
task.result = retry;
|
|
62
|
-
task.status = 'done'; // Accept after one retry
|
|
63
|
-
log.info({ taskId: task.id }, 'Task completed after revision with escalated model');
|
|
64
|
-
}
|
|
65
|
-
else {
|
|
66
|
-
task.status = 'done';
|
|
67
|
-
}
|
|
49
|
+
task.status = 'done';
|
|
50
|
+
log.info({ taskId: task.id, retryCount: task.retryCount }, 'Rework task completed');
|
|
68
51
|
}
|
|
69
52
|
catch (err) {
|
|
70
|
-
log.error({ err, taskId: task.id }, '
|
|
53
|
+
log.error({ err, taskId: task.id }, 'Rework task failed');
|
|
71
54
|
task.status = 'failed';
|
|
72
55
|
task.result = `Error: ${err instanceof Error ? err.message : String(err)}`;
|
|
73
56
|
}
|
|
57
|
+
}));
|
|
58
|
+
}
|
|
59
|
+
async function executeTask(task, instance, runtime, log) {
|
|
60
|
+
log.info({ taskId: task.id, assignedTo: task.assignedTo, modelTier: task.modelTier }, 'Executing task');
|
|
61
|
+
task.status = 'in_progress';
|
|
62
|
+
const agent = runtime.members.get(task.assignedTo);
|
|
63
|
+
if (!agent) {
|
|
64
|
+
log.warn({ role: task.assignedTo }, 'No agent for assigned role');
|
|
65
|
+
task.result = `No agent with role '${task.assignedTo}' available.`;
|
|
66
|
+
task.status = 'failed';
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
// Track activity under this instance
|
|
70
|
+
agent.setInstanceId(instance.id);
|
|
71
|
+
const model = selectModelForTask(task.modelTier, task.retryCount);
|
|
72
|
+
if (agent.getModel() !== model) {
|
|
73
|
+
await agent.switchModel(model);
|
|
74
74
|
}
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
.
|
|
80
|
-
.
|
|
81
|
-
|
|
82
|
-
if (finalReview.toUpperCase().includes('READY_FOR_PR:')) {
|
|
83
|
-
instance.meetingLog.push(`[technical-pm] Final review: ${finalReview}`);
|
|
75
|
+
try {
|
|
76
|
+
const workingDir = instance.worktree?.path ?? '';
|
|
77
|
+
const taskPrompt = buildTaskPrompt(task, workingDir, instance);
|
|
78
|
+
const result = await agent.send(taskPrompt);
|
|
79
|
+
task.result = result;
|
|
80
|
+
task.status = 'done';
|
|
81
|
+
log.info({ taskId: task.id }, 'Task completed');
|
|
84
82
|
}
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
83
|
+
catch (err) {
|
|
84
|
+
log.error({ err, taskId: task.id }, 'Task execution failed');
|
|
85
|
+
task.status = 'failed';
|
|
86
|
+
task.result = `Error: ${err instanceof Error ? err.message : String(err)}`;
|
|
88
87
|
}
|
|
89
88
|
}
|
|
90
89
|
function buildTaskPrompt(task, workingDir, instance) {
|