@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.
@@ -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
+ })
@@ -1,6 +1,5 @@
1
1
  {
2
- "transport": "adapter",
3
- "adapterCommand": "pi-harness adapter",
2
+ "transport": "sdk",
4
3
  "taskFile": "TODOS.md",
5
4
  "developerInstructionsFile": "pi/DEVELOPER.md",
6
5
  "testerInstructionsFile": "pi/TESTER.md",