@sebastianandreasson/pi-autonomous-agents 0.5.2 → 0.7.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/README.md +28 -9
- package/SETUP.md +6 -0
- package/docs/PI_SUPERVISOR.md +16 -65
- package/package.json +6 -3
- package/pi.config.json +1 -2
- package/src/cli.mjs +1 -1
- package/src/index.mjs +3 -0
- package/src/pi-client.mjs +68 -119
- package/src/pi-config.mjs +3 -3
- package/src/pi-sdk-turn.mjs +654 -0
- package/src/pi-supervisor.mjs +73 -0
- package/src/pi-telemetry.mjs +4 -0
- package/src/pi-visualizer-server.mjs +522 -0
- package/src/pi-visualizer-shared.mjs +219 -0
- package/src/pi-visualizer.mjs +16 -0
- package/templates/pi.config.example.json +1 -2
- package/src/pi-rpc-adapter.mjs +0 -668
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
const FLOW_STEPS = [
|
|
2
|
+
{ key: 'developer', label: 'Developer' },
|
|
3
|
+
{ key: 'verification', label: 'Verification' },
|
|
4
|
+
{ key: 'tester', label: 'Tester' },
|
|
5
|
+
{ key: 'fix', label: 'Fix' },
|
|
6
|
+
{ key: 'git_finalize', label: 'Git Finalize' },
|
|
7
|
+
{ key: 'visual_capture', label: 'Visual Capture' },
|
|
8
|
+
{ key: 'visual_review', label: 'Visual Review' },
|
|
9
|
+
{ key: 'summary', label: 'Summary' },
|
|
10
|
+
]
|
|
11
|
+
|
|
12
|
+
const KIND_LABELS = {
|
|
13
|
+
main_agent: 'Developer',
|
|
14
|
+
developer_verification: 'Verification',
|
|
15
|
+
developer_reverification: 'Reverification',
|
|
16
|
+
tester_reverification: 'Tester Reverify',
|
|
17
|
+
tester_agent: 'Tester',
|
|
18
|
+
tester_commit: 'Tester Commit',
|
|
19
|
+
fix_agent: 'Fix',
|
|
20
|
+
git_finalize: 'Git Finalize',
|
|
21
|
+
visual_capture: 'Visual Capture',
|
|
22
|
+
visual_review: 'Visual Review',
|
|
23
|
+
iteration_summary: 'Summary',
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const SUCCESS_STATUSES = new Set(['success', 'passed', 'complete'])
|
|
27
|
+
const ERROR_STATUSES = new Set(['failed', 'timed_out', 'stalled', 'blocked', 'canceled'])
|
|
28
|
+
const SKIP_STATUSES = new Set(['skipped', 'not_run', 'not_needed'])
|
|
29
|
+
|
|
30
|
+
export function getFlowSteps() {
|
|
31
|
+
return FLOW_STEPS.map((step) => ({ ...step }))
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function getLabelForKind(kind) {
|
|
35
|
+
const key = String(kind ?? '').trim()
|
|
36
|
+
if (key === '') {
|
|
37
|
+
return 'Unknown'
|
|
38
|
+
}
|
|
39
|
+
return KIND_LABELS[key] || key
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function getStepKeyForKind(kind) {
|
|
43
|
+
switch (String(kind ?? '')) {
|
|
44
|
+
case 'main_agent':
|
|
45
|
+
return 'developer'
|
|
46
|
+
case 'developer_verification':
|
|
47
|
+
case 'developer_reverification':
|
|
48
|
+
case 'tester_reverification':
|
|
49
|
+
return 'verification'
|
|
50
|
+
case 'tester_agent':
|
|
51
|
+
case 'tester_commit':
|
|
52
|
+
return 'tester'
|
|
53
|
+
case 'fix_agent':
|
|
54
|
+
return 'fix'
|
|
55
|
+
case 'git_finalize':
|
|
56
|
+
return 'git_finalize'
|
|
57
|
+
case 'visual_capture':
|
|
58
|
+
return 'visual_capture'
|
|
59
|
+
case 'visual_review':
|
|
60
|
+
return 'visual_review'
|
|
61
|
+
case 'iteration_summary':
|
|
62
|
+
return 'summary'
|
|
63
|
+
default:
|
|
64
|
+
return ''
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export function getStepKeyForActiveRun(activeRun) {
|
|
69
|
+
const activeKind = String(activeRun?.activeKind ?? '').trim()
|
|
70
|
+
if (activeKind !== '') {
|
|
71
|
+
return getStepKeyForKind(activeKind)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const status = String(activeRun?.status ?? '').trim()
|
|
75
|
+
if (status === 'starting_iteration' || status === 'iteration_in_progress' || status === 'agent_running') {
|
|
76
|
+
return 'developer'
|
|
77
|
+
}
|
|
78
|
+
if (status === 'verification_running') {
|
|
79
|
+
return 'verification'
|
|
80
|
+
}
|
|
81
|
+
if (status === 'git_finalize_running') {
|
|
82
|
+
return 'git_finalize'
|
|
83
|
+
}
|
|
84
|
+
if (status === 'visual_capture_running') {
|
|
85
|
+
return 'visual_capture'
|
|
86
|
+
}
|
|
87
|
+
if (status === 'visual_review_running') {
|
|
88
|
+
return 'visual_review'
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return ''
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function normalizeEventStatus(status) {
|
|
95
|
+
const value = String(status ?? '').trim().toLowerCase()
|
|
96
|
+
if (SUCCESS_STATUSES.has(value)) {
|
|
97
|
+
return 'done'
|
|
98
|
+
}
|
|
99
|
+
if (ERROR_STATUSES.has(value)) {
|
|
100
|
+
return 'error'
|
|
101
|
+
}
|
|
102
|
+
if (SKIP_STATUSES.has(value)) {
|
|
103
|
+
return 'skipped'
|
|
104
|
+
}
|
|
105
|
+
return 'pending'
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export function deriveCurrentIteration({ activeRun, summary, telemetry }) {
|
|
109
|
+
const activeIteration = Number(activeRun?.iteration)
|
|
110
|
+
if (Number.isFinite(activeIteration) && activeIteration > 0) {
|
|
111
|
+
return activeIteration
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const summaryIteration = Number(summary?.iteration)
|
|
115
|
+
if (Number.isFinite(summaryIteration) && summaryIteration > 0) {
|
|
116
|
+
return summaryIteration
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const lastTelemetryIteration = Number(telemetry?.at?.(-1)?.iteration)
|
|
120
|
+
if (Number.isFinite(lastTelemetryIteration) && lastTelemetryIteration > 0) {
|
|
121
|
+
return lastTelemetryIteration
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
return 0
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export function deriveFlowSnapshot({ activeRun, summary, telemetry }) {
|
|
128
|
+
const currentIteration = deriveCurrentIteration({ activeRun, summary, telemetry })
|
|
129
|
+
const iterationTelemetry = Array.isArray(telemetry)
|
|
130
|
+
? telemetry.filter((event) => Number(event?.iteration) === currentIteration)
|
|
131
|
+
: []
|
|
132
|
+
const activeStepKey = getStepKeyForActiveRun(activeRun)
|
|
133
|
+
const steps = FLOW_STEPS.map((step) => {
|
|
134
|
+
const matchingEvents = iterationTelemetry.filter((event) => getStepKeyForKind(event?.kind) === step.key)
|
|
135
|
+
const latestEvent = matchingEvents.at(-1) ?? null
|
|
136
|
+
const status = activeStepKey === step.key
|
|
137
|
+
? 'active'
|
|
138
|
+
: latestEvent
|
|
139
|
+
? normalizeEventStatus(latestEvent.status)
|
|
140
|
+
: 'pending'
|
|
141
|
+
|
|
142
|
+
return {
|
|
143
|
+
...step,
|
|
144
|
+
status,
|
|
145
|
+
latestEvent,
|
|
146
|
+
}
|
|
147
|
+
})
|
|
148
|
+
|
|
149
|
+
return {
|
|
150
|
+
iteration: currentIteration,
|
|
151
|
+
activeStepKey,
|
|
152
|
+
steps,
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
export function deriveStageGraph({ activeRun, summary, telemetry }) {
|
|
157
|
+
const flow = deriveFlowSnapshot({ activeRun, summary, telemetry })
|
|
158
|
+
const currentIteration = flow.iteration
|
|
159
|
+
const iterationTelemetry = Array.isArray(telemetry)
|
|
160
|
+
? telemetry.filter((event) => Number(event?.iteration) === currentIteration)
|
|
161
|
+
: []
|
|
162
|
+
|
|
163
|
+
const nodes = iterationTelemetry.map((event, index) => {
|
|
164
|
+
const stepKey = getStepKeyForKind(event?.kind)
|
|
165
|
+
const status = flow.activeStepKey !== '' && flow.activeStepKey === stepKey && index === iterationTelemetry.length - 1
|
|
166
|
+
? 'active'
|
|
167
|
+
: normalizeEventStatus(event?.status)
|
|
168
|
+
|
|
169
|
+
return {
|
|
170
|
+
id: `${String(event?.kind ?? 'event')}-${index}`,
|
|
171
|
+
index,
|
|
172
|
+
kind: String(event?.kind ?? ''),
|
|
173
|
+
label: getLabelForKind(event?.kind),
|
|
174
|
+
stepKey,
|
|
175
|
+
status,
|
|
176
|
+
role: String(event?.role ?? ''),
|
|
177
|
+
terminalReason: String(event?.terminalReason ?? ''),
|
|
178
|
+
notes: String(event?.notes ?? ''),
|
|
179
|
+
iteration: Number(event?.iteration) || currentIteration,
|
|
180
|
+
phase: String(event?.phase ?? ''),
|
|
181
|
+
timestamp: String(event?.timestamp ?? ''),
|
|
182
|
+
retryCount: Number.isFinite(Number(event?.retryCount)) ? Number(event.retryCount) : 0,
|
|
183
|
+
event,
|
|
184
|
+
}
|
|
185
|
+
})
|
|
186
|
+
|
|
187
|
+
const edges = nodes.slice(1).map((node, index) => ({
|
|
188
|
+
from: nodes[index].id,
|
|
189
|
+
to: node.id,
|
|
190
|
+
}))
|
|
191
|
+
|
|
192
|
+
return {
|
|
193
|
+
iteration: currentIteration,
|
|
194
|
+
nodes,
|
|
195
|
+
edges,
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
export function formatActiveLabel(activeRun, flow) {
|
|
200
|
+
const activeStepKey = flow?.activeStepKey || getStepKeyForActiveRun(activeRun)
|
|
201
|
+
if (activeStepKey !== '') {
|
|
202
|
+
const step = FLOW_STEPS.find((entry) => entry.key === activeStepKey)
|
|
203
|
+
if (step) {
|
|
204
|
+
return step.label
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
const status = String(activeRun?.status ?? '').trim()
|
|
209
|
+
if (status === 'idle') {
|
|
210
|
+
return 'Idle'
|
|
211
|
+
}
|
|
212
|
+
if (status === 'starting') {
|
|
213
|
+
return 'Starting'
|
|
214
|
+
}
|
|
215
|
+
if (status === 'starting_iteration') {
|
|
216
|
+
return 'Starting Iteration'
|
|
217
|
+
}
|
|
218
|
+
return status === '' ? 'Unknown' : status
|
|
219
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import process from 'node:process'
|
|
4
|
+
import { loadConfig } from './pi-config.mjs'
|
|
5
|
+
import { startVisualizerServer } from './pi-visualizer-server.mjs'
|
|
6
|
+
|
|
7
|
+
async function main() {
|
|
8
|
+
const config = loadConfig('once')
|
|
9
|
+
const visualizer = await startVisualizerServer(config)
|
|
10
|
+
process.stdout.write(`PI Harness visualizer listening on ${visualizer.url}\n`)
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
main().catch((error) => {
|
|
14
|
+
console.error(error instanceof Error ? error.stack ?? error.message : String(error))
|
|
15
|
+
process.exitCode = 1
|
|
16
|
+
})
|