opencastle 0.14.0 → 0.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/convoy/store.d.ts +1 -0
- package/dist/cli/convoy/store.d.ts.map +1 -1
- package/dist/cli/convoy/store.js +5 -0
- package/dist/cli/convoy/store.js.map +1 -1
- package/dist/cli/run/schema.d.ts +5 -0
- package/dist/cli/run/schema.d.ts.map +1 -1
- package/dist/cli/run/schema.js +98 -143
- package/dist/cli/run/schema.js.map +1 -1
- package/dist/cli/run/schema.test.js +53 -215
- package/dist/cli/run/schema.test.js.map +1 -1
- package/dist/cli/run.d.ts.map +1 -1
- package/dist/cli/run.js +202 -104
- package/dist/cli/run.js.map +1 -1
- package/dist/cli/types.d.ts +2 -58
- package/dist/cli/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/cli/convoy/store.ts +7 -0
- package/src/cli/run/schema.test.ts +61 -241
- package/src/cli/run/schema.ts +105 -153
- package/src/cli/run.ts +216 -105
- package/src/cli/types.ts +2 -66
- package/src/dashboard/node_modules/.vite/deps/_metadata.json +6 -6
- package/src/orchestrator/agents/team-lead.agent.md +2 -2
- package/src/orchestrator/prompts/generate-task-spec.prompt.md +26 -5
- package/dist/cli/run/loop-executor.d.ts +0 -3
- package/dist/cli/run/loop-executor.d.ts.map +0 -1
- package/dist/cli/run/loop-executor.js +0 -155
- package/dist/cli/run/loop-executor.js.map +0 -1
- package/dist/cli/run/loop-reporter.d.ts +0 -6
- package/dist/cli/run/loop-reporter.d.ts.map +0 -1
- package/dist/cli/run/loop-reporter.js +0 -112
- package/dist/cli/run/loop-reporter.js.map +0 -1
- package/src/cli/run/loop-executor.ts +0 -199
- package/src/cli/run/loop-reporter.ts +0 -125
|
@@ -1,155 +0,0 @@
|
|
|
1
|
-
import { readFile } from 'node:fs/promises';
|
|
2
|
-
import { spawn } from 'node:child_process';
|
|
3
|
-
import { resolve } from 'node:path';
|
|
4
|
-
import { formatDuration } from './executor.js';
|
|
5
|
-
import { parseTimeout } from './schema.js';
|
|
6
|
-
async function runBackpressureCommand(command, active, timeoutMs) {
|
|
7
|
-
return new Promise((res) => {
|
|
8
|
-
const child = spawn('sh', ['-c', command]);
|
|
9
|
-
active.bpChild = child;
|
|
10
|
-
let output = '';
|
|
11
|
-
let killed = false;
|
|
12
|
-
const timer = setTimeout(() => {
|
|
13
|
-
killed = true;
|
|
14
|
-
child.kill('SIGTERM');
|
|
15
|
-
}, timeoutMs);
|
|
16
|
-
child.stdout.on('data', (data) => { output += data.toString(); });
|
|
17
|
-
child.stderr.on('data', (data) => { output += data.toString(); });
|
|
18
|
-
child.on('close', (code) => {
|
|
19
|
-
clearTimeout(timer);
|
|
20
|
-
active.bpChild = null;
|
|
21
|
-
const exitCode = code ?? 1;
|
|
22
|
-
if (output.length > 5000)
|
|
23
|
-
output = output.slice(0, 5000);
|
|
24
|
-
const timedOut = killed;
|
|
25
|
-
res({
|
|
26
|
-
command,
|
|
27
|
-
exitCode: timedOut ? -1 : exitCode,
|
|
28
|
-
output: timedOut ? `Command timed out after ${timeoutMs}ms` : output,
|
|
29
|
-
passed: !timedOut && exitCode === 0,
|
|
30
|
-
});
|
|
31
|
-
});
|
|
32
|
-
});
|
|
33
|
-
}
|
|
34
|
-
async function runBackpressure(commands, reporter, active, timeoutMs) {
|
|
35
|
-
const results = [];
|
|
36
|
-
for (const command of commands) {
|
|
37
|
-
reporter.onBackpressureStart(command);
|
|
38
|
-
const result = await runBackpressureCommand(command, active, timeoutMs);
|
|
39
|
-
reporter.onBackpressureResult(result);
|
|
40
|
-
results.push(result);
|
|
41
|
-
if (!result.passed)
|
|
42
|
-
return { passed: false, results };
|
|
43
|
-
}
|
|
44
|
-
return { passed: true, results };
|
|
45
|
-
}
|
|
46
|
-
export function createLoopExecutor(spec, adapter, reporter) {
|
|
47
|
-
return {
|
|
48
|
-
async run() {
|
|
49
|
-
const loop = spec.loop;
|
|
50
|
-
const startedAt = new Date();
|
|
51
|
-
const iterations = [];
|
|
52
|
-
let aborted = false;
|
|
53
|
-
const active = { task: null, bpChild: null };
|
|
54
|
-
const timeoutMs = parseTimeout(loop.timeout);
|
|
55
|
-
const sigintHandler = () => {
|
|
56
|
-
aborted = true;
|
|
57
|
-
if (active.task && typeof adapter.kill === 'function') {
|
|
58
|
-
adapter.kill(active.task);
|
|
59
|
-
}
|
|
60
|
-
if (active.bpChild && !active.bpChild.killed) {
|
|
61
|
-
active.bpChild.kill('SIGTERM');
|
|
62
|
-
}
|
|
63
|
-
};
|
|
64
|
-
process.on('SIGINT', sigintHandler);
|
|
65
|
-
let stoppedReason = 'max-iterations';
|
|
66
|
-
try {
|
|
67
|
-
for (let i = 1; i <= loop.max_iterations; i++) {
|
|
68
|
-
if (aborted) {
|
|
69
|
-
stoppedReason = 'user-abort';
|
|
70
|
-
break;
|
|
71
|
-
}
|
|
72
|
-
reporter.onIterationStart(i, loop.max_iterations);
|
|
73
|
-
// Re-read prompt from disk each iteration for latest content
|
|
74
|
-
const promptContent = await readFile(resolve(process.cwd(), loop.prompt), 'utf8');
|
|
75
|
-
const syntheticTask = {
|
|
76
|
-
id: `loop-${i}`,
|
|
77
|
-
prompt: promptContent,
|
|
78
|
-
agent: 'autonomous',
|
|
79
|
-
timeout: loop.timeout,
|
|
80
|
-
depends_on: [],
|
|
81
|
-
files: [],
|
|
82
|
-
description: `Loop iteration ${i}`,
|
|
83
|
-
max_retries: 1,
|
|
84
|
-
};
|
|
85
|
-
const iterStart = Date.now();
|
|
86
|
-
active.task = syntheticTask;
|
|
87
|
-
const adapterResult = await adapter.execute(syntheticTask, { verbose: spec._verbose });
|
|
88
|
-
active.task = null;
|
|
89
|
-
if (!adapterResult.success) {
|
|
90
|
-
const duration = Date.now() - iterStart;
|
|
91
|
-
const iterResult = {
|
|
92
|
-
iteration: i,
|
|
93
|
-
status: 'failed',
|
|
94
|
-
duration,
|
|
95
|
-
output: adapterResult.output,
|
|
96
|
-
backpressureResults: [],
|
|
97
|
-
};
|
|
98
|
-
iterations.push(iterResult);
|
|
99
|
-
reporter.onIterationDone(i, iterResult);
|
|
100
|
-
stoppedReason = 'error';
|
|
101
|
-
break;
|
|
102
|
-
}
|
|
103
|
-
let backpressureResults = [];
|
|
104
|
-
if (loop.backpressure && loop.backpressure.length > 0) {
|
|
105
|
-
const bp = await runBackpressure(loop.backpressure, reporter, active, timeoutMs);
|
|
106
|
-
backpressureResults = bp.results;
|
|
107
|
-
if (!bp.passed) {
|
|
108
|
-
const duration = Date.now() - iterStart;
|
|
109
|
-
const iterResult = {
|
|
110
|
-
iteration: i,
|
|
111
|
-
status: 'backpressure-fail',
|
|
112
|
-
duration,
|
|
113
|
-
output: adapterResult.output,
|
|
114
|
-
backpressureResults,
|
|
115
|
-
};
|
|
116
|
-
iterations.push(iterResult);
|
|
117
|
-
reporter.onIterationDone(i, iterResult);
|
|
118
|
-
stoppedReason = 'backpressure-fail';
|
|
119
|
-
break;
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
const duration = Date.now() - iterStart;
|
|
123
|
-
const iterResult = {
|
|
124
|
-
iteration: i,
|
|
125
|
-
status: 'done',
|
|
126
|
-
duration,
|
|
127
|
-
output: adapterResult.output,
|
|
128
|
-
backpressureResults,
|
|
129
|
-
};
|
|
130
|
-
iterations.push(iterResult);
|
|
131
|
-
reporter.onIterationDone(i, iterResult);
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
finally {
|
|
135
|
-
process.off('SIGINT', sigintHandler);
|
|
136
|
-
}
|
|
137
|
-
const completedAt = new Date();
|
|
138
|
-
const completedIterations = iterations.filter((it) => it.status === 'done').length;
|
|
139
|
-
const report = {
|
|
140
|
-
name: spec.name,
|
|
141
|
-
mode: 'loop',
|
|
142
|
-
startedAt: startedAt.toISOString(),
|
|
143
|
-
completedAt: completedAt.toISOString(),
|
|
144
|
-
duration: formatDuration(completedAt.getTime() - startedAt.getTime()),
|
|
145
|
-
totalIterations: iterations.length,
|
|
146
|
-
completedIterations,
|
|
147
|
-
stoppedReason,
|
|
148
|
-
iterations,
|
|
149
|
-
};
|
|
150
|
-
await reporter.onComplete(report);
|
|
151
|
-
return report;
|
|
152
|
-
},
|
|
153
|
-
};
|
|
154
|
-
}
|
|
155
|
-
//# sourceMappingURL=loop-executor.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"loop-executor.js","sourceRoot":"","sources":["../../../src/cli/run/loop-executor.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"loop-reporter.d.ts","sourceRoot":"","sources":["../../../src/cli/run/loop-reporter.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAIV,YAAY,EACb,MAAM,aAAa,CAAA;AAUpB,wBAAgB,kBAAkB,CAChC,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,OAAO,CAAA;CAAO,GACtD,YAAY,CAoGd"}
|
|
@@ -1,112 +0,0 @@
|
|
|
1
|
-
import { mkdir, writeFile } from 'node:fs/promises';
|
|
2
|
-
import { resolve } from 'node:path';
|
|
3
|
-
import { formatDuration } from './executor.js';
|
|
4
|
-
import { c } from '../prompt.js';
|
|
5
|
-
import { appendEvent } from '../log.js';
|
|
6
|
-
import { appendLesson } from '../lesson.js';
|
|
7
|
-
const STOPPED_LABELS = {
|
|
8
|
-
'max-iterations': 'reached max iterations',
|
|
9
|
-
'plan-empty': 'plan exhausted',
|
|
10
|
-
'backpressure-fail': 'backpressure check failed',
|
|
11
|
-
'user-abort': 'aborted by user',
|
|
12
|
-
error: 'agent error',
|
|
13
|
-
};
|
|
14
|
-
export function createLoopReporter(specName, options = {}) {
|
|
15
|
-
const reportDir = options.reportDir ?? resolve(process.cwd(), '.opencastle', 'runs');
|
|
16
|
-
const verbose = options.verbose ?? false;
|
|
17
|
-
return {
|
|
18
|
-
onIterationStart(i, max) {
|
|
19
|
-
console.log(`\n \u21bb Iteration ${i}/${max}`);
|
|
20
|
-
},
|
|
21
|
-
onIterationDone(_i, result) {
|
|
22
|
-
const dur = formatDuration(result.duration);
|
|
23
|
-
if (result.status === 'done') {
|
|
24
|
-
console.log(` ${c.green('\u2713')} Completed (${dur})`);
|
|
25
|
-
}
|
|
26
|
-
else if (result.status === 'backpressure-fail') {
|
|
27
|
-
console.log(` ${c.yellow('\u26a0')} Backpressure failed (${dur})`);
|
|
28
|
-
}
|
|
29
|
-
else {
|
|
30
|
-
console.log(` ${c.red('\u2717')} Failed (${dur})`);
|
|
31
|
-
if (result.output) {
|
|
32
|
-
const lines = result.output.split('\n').slice(0, 5);
|
|
33
|
-
for (const line of lines)
|
|
34
|
-
console.log(` ${line}`);
|
|
35
|
-
if (result.output.split('\n').length > 5)
|
|
36
|
-
console.log(` ... (truncated)`);
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
if (verbose && result.output && result.status === 'done') {
|
|
40
|
-
console.log(` Output: ${result.output.slice(0, 500)}`);
|
|
41
|
-
}
|
|
42
|
-
appendEvent({
|
|
43
|
-
type: 'delegation',
|
|
44
|
-
timestamp: new Date().toISOString(),
|
|
45
|
-
session_id: specName,
|
|
46
|
-
agent: 'autonomous',
|
|
47
|
-
task: `Loop iteration ${_i}`,
|
|
48
|
-
mechanism: 'run-loop',
|
|
49
|
-
outcome: result.status === 'done' ? 'success' : result.status,
|
|
50
|
-
duration_sec: Math.round(result.duration / 1000),
|
|
51
|
-
}).catch(() => { });
|
|
52
|
-
},
|
|
53
|
-
onBackpressureStart(cmd) {
|
|
54
|
-
console.log(` \u23ce Running: ${cmd}`);
|
|
55
|
-
},
|
|
56
|
-
onBackpressureResult(result) {
|
|
57
|
-
if (result.passed) {
|
|
58
|
-
console.log(` ${c.green('\u2713')} Exit ${result.exitCode}`);
|
|
59
|
-
}
|
|
60
|
-
else {
|
|
61
|
-
console.log(` ${c.red('\u2717')} Exit ${result.exitCode}`);
|
|
62
|
-
if (result.output) {
|
|
63
|
-
const lines = result.output.split('\n').slice(0, 5);
|
|
64
|
-
for (const line of lines)
|
|
65
|
-
console.log(` ${line}`);
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
},
|
|
69
|
-
async onComplete(report) {
|
|
70
|
-
console.log(`\n ${c.dim('\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500')}`);
|
|
71
|
-
console.log(` ${c.bold('Loop complete:')} ${report.name}`);
|
|
72
|
-
console.log(` Duration: ${report.duration}`);
|
|
73
|
-
console.log();
|
|
74
|
-
console.log(` Iterations: ${report.totalIterations} total \u2014 ` +
|
|
75
|
-
`${c.green(String(report.completedIterations))} completed`);
|
|
76
|
-
console.log(` Stopped: ${STOPPED_LABELS[report.stoppedReason]}`);
|
|
77
|
-
try {
|
|
78
|
-
await mkdir(reportDir, { recursive: true });
|
|
79
|
-
const timestamp = new Date().toISOString().replace(/[:.]/g, '-').slice(0, 19);
|
|
80
|
-
const reportPath = resolve(reportDir, `loop-${specName}-${timestamp}.json`);
|
|
81
|
-
await writeFile(reportPath, JSON.stringify(report, null, 2), 'utf8');
|
|
82
|
-
console.log(` Report: ${reportPath}`);
|
|
83
|
-
}
|
|
84
|
-
catch (err) {
|
|
85
|
-
console.log(` \u2717 Could not write report: ${err.message}`);
|
|
86
|
-
}
|
|
87
|
-
await appendEvent({
|
|
88
|
-
type: 'session',
|
|
89
|
-
timestamp: new Date().toISOString(),
|
|
90
|
-
agent: 'opencastle-run',
|
|
91
|
-
task: `Loop: ${report.name}`,
|
|
92
|
-
outcome: report.stoppedReason === 'max-iterations' || report.stoppedReason === 'plan-empty' ? 'success' : 'failure',
|
|
93
|
-
duration_min: Math.round((new Date(report.completedAt).getTime() - new Date(report.startedAt).getTime()) / 60000),
|
|
94
|
-
mode: 'loop',
|
|
95
|
-
total_iterations: report.totalIterations,
|
|
96
|
-
completed_iterations: report.completedIterations,
|
|
97
|
-
stopped_reason: report.stoppedReason,
|
|
98
|
-
}).catch(() => { });
|
|
99
|
-
if (report.stoppedReason === 'error' || report.stoppedReason === 'backpressure-fail') {
|
|
100
|
-
const lastIter = report.iterations[report.iterations.length - 1];
|
|
101
|
-
await appendLesson({
|
|
102
|
-
title: `Run loop "${report.name}" stopped: ${report.stoppedReason}`,
|
|
103
|
-
category: 'general',
|
|
104
|
-
severity: 'medium',
|
|
105
|
-
problem: `Loop stopped after ${report.totalIterations} iterations due to ${report.stoppedReason}.${lastIter?.output ? ` Last output: ${lastIter.output.slice(0, 200)}` : ''}`,
|
|
106
|
-
}).catch(() => { });
|
|
107
|
-
}
|
|
108
|
-
console.log();
|
|
109
|
-
},
|
|
110
|
-
};
|
|
111
|
-
}
|
|
112
|
-
//# sourceMappingURL=loop-reporter.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"loop-reporter.js","sourceRoot":"","sources":["../../../src/cli/run/loop-reporter.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,199 +0,0 @@
|
|
|
1
|
-
import { readFile } from 'node:fs/promises'
|
|
2
|
-
import { spawn } from 'node:child_process'
|
|
3
|
-
import type { ChildProcess } from 'node:child_process'
|
|
4
|
-
import { resolve } from 'node:path'
|
|
5
|
-
import { formatDuration } from './executor.js'
|
|
6
|
-
import { parseTimeout } from './schema.js'
|
|
7
|
-
import type {
|
|
8
|
-
TaskSpec,
|
|
9
|
-
LoopRunReport,
|
|
10
|
-
LoopIterationResult,
|
|
11
|
-
BackpressureResult,
|
|
12
|
-
AgentAdapter,
|
|
13
|
-
LoopReporter,
|
|
14
|
-
LoopExecutor,
|
|
15
|
-
Task,
|
|
16
|
-
} from '../types.js'
|
|
17
|
-
|
|
18
|
-
interface ActiveState {
|
|
19
|
-
task: Task | null
|
|
20
|
-
bpChild: ChildProcess | null
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
async function runBackpressureCommand(
|
|
24
|
-
command: string,
|
|
25
|
-
active: ActiveState,
|
|
26
|
-
timeoutMs: number,
|
|
27
|
-
): Promise<BackpressureResult> {
|
|
28
|
-
return new Promise((res) => {
|
|
29
|
-
const child = spawn('sh', ['-c', command])
|
|
30
|
-
active.bpChild = child
|
|
31
|
-
let output = ''
|
|
32
|
-
let killed = false
|
|
33
|
-
|
|
34
|
-
const timer = setTimeout(() => {
|
|
35
|
-
killed = true
|
|
36
|
-
child.kill('SIGTERM')
|
|
37
|
-
}, timeoutMs)
|
|
38
|
-
|
|
39
|
-
child.stdout.on('data', (data: Buffer) => { output += data.toString() })
|
|
40
|
-
child.stderr.on('data', (data: Buffer) => { output += data.toString() })
|
|
41
|
-
|
|
42
|
-
child.on('close', (code: number | null) => {
|
|
43
|
-
clearTimeout(timer)
|
|
44
|
-
active.bpChild = null
|
|
45
|
-
const exitCode = code ?? 1
|
|
46
|
-
if (output.length > 5000) output = output.slice(0, 5000)
|
|
47
|
-
const timedOut = killed
|
|
48
|
-
res({
|
|
49
|
-
command,
|
|
50
|
-
exitCode: timedOut ? -1 : exitCode,
|
|
51
|
-
output: timedOut ? `Command timed out after ${timeoutMs}ms` : output,
|
|
52
|
-
passed: !timedOut && exitCode === 0,
|
|
53
|
-
})
|
|
54
|
-
})
|
|
55
|
-
})
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
async function runBackpressure(
|
|
59
|
-
commands: string[],
|
|
60
|
-
reporter: LoopReporter,
|
|
61
|
-
active: ActiveState,
|
|
62
|
-
timeoutMs: number,
|
|
63
|
-
): Promise<{ passed: boolean; results: BackpressureResult[] }> {
|
|
64
|
-
const results: BackpressureResult[] = []
|
|
65
|
-
for (const command of commands) {
|
|
66
|
-
reporter.onBackpressureStart(command)
|
|
67
|
-
const result = await runBackpressureCommand(command, active, timeoutMs)
|
|
68
|
-
reporter.onBackpressureResult(result)
|
|
69
|
-
results.push(result)
|
|
70
|
-
if (!result.passed) return { passed: false, results }
|
|
71
|
-
}
|
|
72
|
-
return { passed: true, results }
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
export function createLoopExecutor(
|
|
76
|
-
spec: TaskSpec,
|
|
77
|
-
adapter: AgentAdapter,
|
|
78
|
-
reporter: LoopReporter,
|
|
79
|
-
): LoopExecutor {
|
|
80
|
-
return {
|
|
81
|
-
async run(): Promise<LoopRunReport> {
|
|
82
|
-
const loop = spec.loop!
|
|
83
|
-
const startedAt = new Date()
|
|
84
|
-
const iterations: LoopIterationResult[] = []
|
|
85
|
-
let aborted = false
|
|
86
|
-
const active: ActiveState = { task: null, bpChild: null }
|
|
87
|
-
const timeoutMs = parseTimeout(loop.timeout)
|
|
88
|
-
|
|
89
|
-
const sigintHandler = () => {
|
|
90
|
-
aborted = true
|
|
91
|
-
if (active.task && typeof adapter.kill === 'function') {
|
|
92
|
-
adapter.kill(active.task)
|
|
93
|
-
}
|
|
94
|
-
if (active.bpChild && !active.bpChild.killed) {
|
|
95
|
-
active.bpChild.kill('SIGTERM')
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
process.on('SIGINT', sigintHandler)
|
|
99
|
-
|
|
100
|
-
let stoppedReason: LoopRunReport['stoppedReason'] = 'max-iterations'
|
|
101
|
-
|
|
102
|
-
try {
|
|
103
|
-
for (let i = 1; i <= loop.max_iterations; i++) {
|
|
104
|
-
if (aborted) {
|
|
105
|
-
stoppedReason = 'user-abort'
|
|
106
|
-
break
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
reporter.onIterationStart(i, loop.max_iterations)
|
|
110
|
-
|
|
111
|
-
// Re-read prompt from disk each iteration for latest content
|
|
112
|
-
const promptContent = await readFile(resolve(process.cwd(), loop.prompt), 'utf8')
|
|
113
|
-
|
|
114
|
-
const syntheticTask: Task = {
|
|
115
|
-
id: `loop-${i}`,
|
|
116
|
-
prompt: promptContent,
|
|
117
|
-
agent: 'autonomous',
|
|
118
|
-
timeout: loop.timeout,
|
|
119
|
-
depends_on: [],
|
|
120
|
-
files: [],
|
|
121
|
-
description: `Loop iteration ${i}`,
|
|
122
|
-
max_retries: 1,
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
const iterStart = Date.now()
|
|
126
|
-
active.task = syntheticTask
|
|
127
|
-
const adapterResult = await adapter.execute(syntheticTask, { verbose: spec._verbose })
|
|
128
|
-
active.task = null
|
|
129
|
-
|
|
130
|
-
if (!adapterResult.success) {
|
|
131
|
-
const duration = Date.now() - iterStart
|
|
132
|
-
const iterResult: LoopIterationResult = {
|
|
133
|
-
iteration: i,
|
|
134
|
-
status: 'failed',
|
|
135
|
-
duration,
|
|
136
|
-
output: adapterResult.output,
|
|
137
|
-
backpressureResults: [],
|
|
138
|
-
}
|
|
139
|
-
iterations.push(iterResult)
|
|
140
|
-
reporter.onIterationDone(i, iterResult)
|
|
141
|
-
stoppedReason = 'error'
|
|
142
|
-
break
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
let backpressureResults: BackpressureResult[] = []
|
|
146
|
-
if (loop.backpressure && loop.backpressure.length > 0) {
|
|
147
|
-
const bp = await runBackpressure(loop.backpressure, reporter, active, timeoutMs)
|
|
148
|
-
backpressureResults = bp.results
|
|
149
|
-
if (!bp.passed) {
|
|
150
|
-
const duration = Date.now() - iterStart
|
|
151
|
-
const iterResult: LoopIterationResult = {
|
|
152
|
-
iteration: i,
|
|
153
|
-
status: 'backpressure-fail',
|
|
154
|
-
duration,
|
|
155
|
-
output: adapterResult.output,
|
|
156
|
-
backpressureResults,
|
|
157
|
-
}
|
|
158
|
-
iterations.push(iterResult)
|
|
159
|
-
reporter.onIterationDone(i, iterResult)
|
|
160
|
-
stoppedReason = 'backpressure-fail'
|
|
161
|
-
break
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
const duration = Date.now() - iterStart
|
|
166
|
-
const iterResult: LoopIterationResult = {
|
|
167
|
-
iteration: i,
|
|
168
|
-
status: 'done',
|
|
169
|
-
duration,
|
|
170
|
-
output: adapterResult.output,
|
|
171
|
-
backpressureResults,
|
|
172
|
-
}
|
|
173
|
-
iterations.push(iterResult)
|
|
174
|
-
reporter.onIterationDone(i, iterResult)
|
|
175
|
-
}
|
|
176
|
-
} finally {
|
|
177
|
-
process.off('SIGINT', sigintHandler)
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
const completedAt = new Date()
|
|
181
|
-
const completedIterations = iterations.filter((it) => it.status === 'done').length
|
|
182
|
-
|
|
183
|
-
const report: LoopRunReport = {
|
|
184
|
-
name: spec.name,
|
|
185
|
-
mode: 'loop',
|
|
186
|
-
startedAt: startedAt.toISOString(),
|
|
187
|
-
completedAt: completedAt.toISOString(),
|
|
188
|
-
duration: formatDuration(completedAt.getTime() - startedAt.getTime()),
|
|
189
|
-
totalIterations: iterations.length,
|
|
190
|
-
completedIterations,
|
|
191
|
-
stoppedReason,
|
|
192
|
-
iterations,
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
await reporter.onComplete(report)
|
|
196
|
-
return report
|
|
197
|
-
},
|
|
198
|
-
}
|
|
199
|
-
}
|
|
@@ -1,125 +0,0 @@
|
|
|
1
|
-
import { mkdir, writeFile } from 'node:fs/promises'
|
|
2
|
-
import { resolve } from 'node:path'
|
|
3
|
-
import { formatDuration } from './executor.js'
|
|
4
|
-
import { c } from '../prompt.js'
|
|
5
|
-
import { appendEvent } from '../log.js'
|
|
6
|
-
import { appendLesson } from '../lesson.js'
|
|
7
|
-
import type {
|
|
8
|
-
LoopRunReport,
|
|
9
|
-
LoopIterationResult,
|
|
10
|
-
BackpressureResult,
|
|
11
|
-
LoopReporter,
|
|
12
|
-
} from '../types.js'
|
|
13
|
-
|
|
14
|
-
const STOPPED_LABELS: Record<LoopRunReport['stoppedReason'], string> = {
|
|
15
|
-
'max-iterations': 'reached max iterations',
|
|
16
|
-
'plan-empty': 'plan exhausted',
|
|
17
|
-
'backpressure-fail': 'backpressure check failed',
|
|
18
|
-
'user-abort': 'aborted by user',
|
|
19
|
-
error: 'agent error',
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export function createLoopReporter(
|
|
23
|
-
specName: string,
|
|
24
|
-
options: { reportDir?: string; verbose?: boolean } = {},
|
|
25
|
-
): LoopReporter {
|
|
26
|
-
const reportDir = options.reportDir ?? resolve(process.cwd(), '.opencastle', 'runs')
|
|
27
|
-
const verbose = options.verbose ?? false
|
|
28
|
-
|
|
29
|
-
return {
|
|
30
|
-
onIterationStart(i: number, max: number): void {
|
|
31
|
-
console.log(`\n \u21bb Iteration ${i}/${max}`)
|
|
32
|
-
},
|
|
33
|
-
|
|
34
|
-
onIterationDone(_i: number, result: LoopIterationResult): void {
|
|
35
|
-
const dur = formatDuration(result.duration)
|
|
36
|
-
if (result.status === 'done') {
|
|
37
|
-
console.log(` ${c.green('\u2713')} Completed (${dur})`)
|
|
38
|
-
} else if (result.status === 'backpressure-fail') {
|
|
39
|
-
console.log(` ${c.yellow('\u26a0')} Backpressure failed (${dur})`)
|
|
40
|
-
} else {
|
|
41
|
-
console.log(` ${c.red('\u2717')} Failed (${dur})`)
|
|
42
|
-
if (result.output) {
|
|
43
|
-
const lines = result.output.split('\n').slice(0, 5)
|
|
44
|
-
for (const line of lines) console.log(` ${line}`)
|
|
45
|
-
if (result.output.split('\n').length > 5) console.log(` ... (truncated)`)
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
if (verbose && result.output && result.status === 'done') {
|
|
49
|
-
console.log(` Output: ${result.output.slice(0, 500)}`)
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
appendEvent({
|
|
53
|
-
type: 'delegation',
|
|
54
|
-
timestamp: new Date().toISOString(),
|
|
55
|
-
session_id: specName,
|
|
56
|
-
agent: 'autonomous',
|
|
57
|
-
task: `Loop iteration ${_i}`,
|
|
58
|
-
mechanism: 'run-loop',
|
|
59
|
-
outcome: result.status === 'done' ? 'success' : result.status,
|
|
60
|
-
duration_sec: Math.round(result.duration / 1000),
|
|
61
|
-
}).catch(() => {})
|
|
62
|
-
},
|
|
63
|
-
|
|
64
|
-
onBackpressureStart(cmd: string): void {
|
|
65
|
-
console.log(` \u23ce Running: ${cmd}`)
|
|
66
|
-
},
|
|
67
|
-
|
|
68
|
-
onBackpressureResult(result: BackpressureResult): void {
|
|
69
|
-
if (result.passed) {
|
|
70
|
-
console.log(` ${c.green('\u2713')} Exit ${result.exitCode}`)
|
|
71
|
-
} else {
|
|
72
|
-
console.log(` ${c.red('\u2717')} Exit ${result.exitCode}`)
|
|
73
|
-
if (result.output) {
|
|
74
|
-
const lines = result.output.split('\n').slice(0, 5)
|
|
75
|
-
for (const line of lines) console.log(` ${line}`)
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
},
|
|
79
|
-
|
|
80
|
-
async onComplete(report: LoopRunReport): Promise<void> {
|
|
81
|
-
console.log(`\n ${c.dim('\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500')}`)
|
|
82
|
-
console.log(` ${c.bold('Loop complete:')} ${report.name}`)
|
|
83
|
-
console.log(` Duration: ${report.duration}`)
|
|
84
|
-
console.log()
|
|
85
|
-
console.log(
|
|
86
|
-
` Iterations: ${report.totalIterations} total \u2014 ` +
|
|
87
|
-
`${c.green(String(report.completedIterations))} completed`,
|
|
88
|
-
)
|
|
89
|
-
console.log(` Stopped: ${STOPPED_LABELS[report.stoppedReason]}`)
|
|
90
|
-
|
|
91
|
-
try {
|
|
92
|
-
await mkdir(reportDir, { recursive: true })
|
|
93
|
-
const timestamp = new Date().toISOString().replace(/[:.]/g, '-').slice(0, 19)
|
|
94
|
-
const reportPath = resolve(reportDir, `loop-${specName}-${timestamp}.json`)
|
|
95
|
-
await writeFile(reportPath, JSON.stringify(report, null, 2), 'utf8')
|
|
96
|
-
console.log(` Report: ${reportPath}`)
|
|
97
|
-
} catch (err: unknown) {
|
|
98
|
-
console.log(` \u2717 Could not write report: ${(err as Error).message}`)
|
|
99
|
-
}
|
|
100
|
-
await appendEvent({
|
|
101
|
-
type: 'session',
|
|
102
|
-
timestamp: new Date().toISOString(),
|
|
103
|
-
agent: 'opencastle-run',
|
|
104
|
-
task: `Loop: ${report.name}`,
|
|
105
|
-
outcome: report.stoppedReason === 'max-iterations' || report.stoppedReason === 'plan-empty' ? 'success' : 'failure',
|
|
106
|
-
duration_min: Math.round((new Date(report.completedAt).getTime() - new Date(report.startedAt).getTime()) / 60000),
|
|
107
|
-
mode: 'loop',
|
|
108
|
-
total_iterations: report.totalIterations,
|
|
109
|
-
completed_iterations: report.completedIterations,
|
|
110
|
-
stopped_reason: report.stoppedReason,
|
|
111
|
-
}).catch(() => {})
|
|
112
|
-
|
|
113
|
-
if (report.stoppedReason === 'error' || report.stoppedReason === 'backpressure-fail') {
|
|
114
|
-
const lastIter = report.iterations[report.iterations.length - 1]
|
|
115
|
-
await appendLesson({
|
|
116
|
-
title: `Run loop "${report.name}" stopped: ${report.stoppedReason}`,
|
|
117
|
-
category: 'general',
|
|
118
|
-
severity: 'medium',
|
|
119
|
-
problem: `Loop stopped after ${report.totalIterations} iterations due to ${report.stoppedReason}.${lastIter?.output ? ` Last output: ${lastIter.output.slice(0, 200)}` : ''}`,
|
|
120
|
-
}).catch(() => {})
|
|
121
|
-
}
|
|
122
|
-
console.log()
|
|
123
|
-
},
|
|
124
|
-
}
|
|
125
|
-
}
|