pi-harness-runtime 0.2.0 → 0.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/README.md +133 -158
- package/harness/agent-handoff.ts +189 -0
- package/harness/blackboard.ts +291 -0
- package/harness/context-window-manager.ts +210 -0
- package/harness/e2e/playwright-runner.ts +252 -0
- package/harness/e2e/test-engine.ts +402 -0
- package/harness/job-state-machine.ts +363 -0
- package/harness/loop-runtime.ts +337 -0
- package/harness/master-planner.ts +327 -0
- package/harness/project-detector/detector.ts +328 -0
- package/harness/repair-engine.ts +340 -0
- package/harness/task-graph.ts +336 -0
- package/index.ts +294 -2
- package/package.json +6 -3
- package/packages/checkpoint/README.md +3 -0
- package/packages/checkpoint/src/checkpoint-manager.ts +38 -0
- package/packages/provider-router/src/provider-router.ts +42 -0
- package/packages/providers/README.md +3 -0
- package/packages/providers/adapters.ts +261 -0
- package/packages/quota-manager/README.md +3 -0
- package/packages/quota-manager/quota-manager.ts +328 -0
- package/packages/runtime/README.md +3 -0
- package/packages/scheduler/README.md +3 -0
- package/packages/scheduler/src/scheduler.ts +50 -0
- package/packages/shared-context/README.md +3 -0
- package/packages/shared-context/src/shared-context.ts +42 -0
- package/packages/tui/README.md +3 -0
- package/packages/types/src/runtime-types.ts +439 -0
- package/packages/worktree/README.md +3 -0
- package/packages/worktree/worktree.ts +293 -0
- package/skills/harness-runtime/SKILL.md +186 -72
|
@@ -0,0 +1,439 @@
|
|
|
1
|
+
export type JobStatus =
|
|
2
|
+
| "created"
|
|
3
|
+
| "planning"
|
|
4
|
+
| "queued"
|
|
5
|
+
| "running"
|
|
6
|
+
| "testing"
|
|
7
|
+
| "e2e_testing"
|
|
8
|
+
| "reviewing"
|
|
9
|
+
| "repairing"
|
|
10
|
+
| "paused_quota"
|
|
11
|
+
| "waiting_human"
|
|
12
|
+
| "blocked"
|
|
13
|
+
| "ready_for_client"
|
|
14
|
+
| "archived"
|
|
15
|
+
| "cancelled";
|
|
16
|
+
|
|
17
|
+
export type TaskStatus =
|
|
18
|
+
| "pending"
|
|
19
|
+
| "ready"
|
|
20
|
+
| "running"
|
|
21
|
+
| "testing"
|
|
22
|
+
| "reviewing"
|
|
23
|
+
| "done"
|
|
24
|
+
| "failed"
|
|
25
|
+
| "blocked";
|
|
26
|
+
|
|
27
|
+
export type ProviderState =
|
|
28
|
+
| "available"
|
|
29
|
+
| "limited"
|
|
30
|
+
| "exhausted"
|
|
31
|
+
| "disabled"
|
|
32
|
+
| "unknown";
|
|
33
|
+
|
|
34
|
+
export interface RuntimeTask {
|
|
35
|
+
id: string;
|
|
36
|
+
title: string;
|
|
37
|
+
description: string;
|
|
38
|
+
status: TaskStatus;
|
|
39
|
+
assignedProvider?: string;
|
|
40
|
+
worktreePath?: string;
|
|
41
|
+
acceptanceCriteria?: string[];
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export interface RuntimeCheckpoint {
|
|
45
|
+
version: number;
|
|
46
|
+
jobId: string;
|
|
47
|
+
status: JobStatus;
|
|
48
|
+
requirement: string;
|
|
49
|
+
currentTaskId?: string;
|
|
50
|
+
provider?: string;
|
|
51
|
+
resumeAt?: string;
|
|
52
|
+
lastError?: string;
|
|
53
|
+
createdAt: string;
|
|
54
|
+
updatedAt: string;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export interface RuntimeEvent {
|
|
58
|
+
ts: string;
|
|
59
|
+
jobId: string;
|
|
60
|
+
type: string;
|
|
61
|
+
message: string;
|
|
62
|
+
data?: Record<string, unknown>;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export interface RuntimeContext {
|
|
66
|
+
jobId: string;
|
|
67
|
+
requirement: string;
|
|
68
|
+
tasks: RuntimeTask[];
|
|
69
|
+
providerStates: Record<string, ProviderState>;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export interface ProviderSelection {
|
|
73
|
+
providerId: string;
|
|
74
|
+
reason: string;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// ─── Task Graph (RFC-0016) ───────────────────────────────────────────
|
|
78
|
+
|
|
79
|
+
export interface TaskNode {
|
|
80
|
+
id: string;
|
|
81
|
+
title: string;
|
|
82
|
+
description: string;
|
|
83
|
+
status: TaskStatus;
|
|
84
|
+
dependencies: string[]; // task IDs this depends on
|
|
85
|
+
dependents: string[]; // task IDs that depend on this
|
|
86
|
+
assignedAgent?: string;
|
|
87
|
+
worktreePath?: string;
|
|
88
|
+
acceptanceCriteria?: string[];
|
|
89
|
+
retryCount?: number;
|
|
90
|
+
maxRetries?: number;
|
|
91
|
+
createdAt: string;
|
|
92
|
+
updatedAt: string;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export interface TaskGraph {
|
|
96
|
+
jobId: string;
|
|
97
|
+
nodes: Record<string, TaskNode>;
|
|
98
|
+
topologicalOrder: string[]; // Cached topological sort
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// ─── Blackboard (RFC-0011) ──────────────────────────────────────────
|
|
102
|
+
|
|
103
|
+
export interface BlackboardRecord {
|
|
104
|
+
jobId: string;
|
|
105
|
+
status: JobStatus;
|
|
106
|
+
nextAction?: NextAction;
|
|
107
|
+
tasks: TaskGraph;
|
|
108
|
+
agentRegistry: AgentRegistry;
|
|
109
|
+
reports: Record<string, AgentReport>;
|
|
110
|
+
locks: Record<string, LockInfo>;
|
|
111
|
+
updatedAt: string;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export interface NextAction {
|
|
115
|
+
taskId?: string;
|
|
116
|
+
agentId?: string;
|
|
117
|
+
instruction: string;
|
|
118
|
+
priority: "high" | "normal" | "low";
|
|
119
|
+
createdAt: string;
|
|
120
|
+
expiresAt?: string;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export interface AgentRegistry {
|
|
124
|
+
agents: Record<string, AgentInfo>;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export interface AgentInfo {
|
|
128
|
+
id: string;
|
|
129
|
+
name: string;
|
|
130
|
+
provider: string;
|
|
131
|
+
model?: string;
|
|
132
|
+
status: "idle" | "working" | "waiting" | "failed";
|
|
133
|
+
currentTaskId?: string;
|
|
134
|
+
startedAt?: string;
|
|
135
|
+
lastHeartbeat?: string;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
export interface AgentReport {
|
|
139
|
+
agentId: string;
|
|
140
|
+
taskId: string;
|
|
141
|
+
status: "success" | "failure" | "partial";
|
|
142
|
+
summary: string;
|
|
143
|
+
filesChanged?: string[];
|
|
144
|
+
testsRun?: number;
|
|
145
|
+
testsPassed?: number;
|
|
146
|
+
testsFailed?: number;
|
|
147
|
+
createdAt: string;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
export interface LockInfo {
|
|
151
|
+
taskId: string;
|
|
152
|
+
agentId: string;
|
|
153
|
+
acquiredAt: string;
|
|
154
|
+
expiresAt?: string;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// ─── Context Window Manager (RFC-0010) ──────────────────────────────
|
|
158
|
+
|
|
159
|
+
export interface ContextWindowStats {
|
|
160
|
+
provider: string;
|
|
161
|
+
model: string;
|
|
162
|
+
maxTokens: number;
|
|
163
|
+
usedTokens: number;
|
|
164
|
+
availableTokens: number;
|
|
165
|
+
utilizationPct: number;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
export interface ContextWindowConfig {
|
|
169
|
+
warningThreshold: number; // e.g., 0.8 = warn at 80%
|
|
170
|
+
criticalThreshold: number; // e.g., 0.95 = critical at 95%
|
|
171
|
+
strategy: "truncate" | "summarize" | "split";
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// ─── Quota Manager (RFC-0003) ────────────────────────────────────────
|
|
175
|
+
|
|
176
|
+
export interface QuotaSignal {
|
|
177
|
+
provider: string;
|
|
178
|
+
windowType: "5h" | "daily" | "weekly" | "monthly";
|
|
179
|
+
usedPct: number;
|
|
180
|
+
remainingPct: number;
|
|
181
|
+
resetsAt?: string;
|
|
182
|
+
exhausted: boolean;
|
|
183
|
+
source: "api_response" | "provider_status" | "playwright" | "local_estimate";
|
|
184
|
+
capturedAt: string;
|
|
185
|
+
retryAfterMs?: number;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
export interface QuotaState {
|
|
189
|
+
provider: string;
|
|
190
|
+
available: boolean;
|
|
191
|
+
limited: boolean;
|
|
192
|
+
exhausted: boolean;
|
|
193
|
+
signals: QuotaSignal[];
|
|
194
|
+
nextAvailableAt?: string;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// ─── Provider Adapter (RFC-0002) ─────────────────────────────────────
|
|
198
|
+
|
|
199
|
+
export interface ProviderConfig {
|
|
200
|
+
id: string;
|
|
201
|
+
name: string;
|
|
202
|
+
apiKey?: string;
|
|
203
|
+
baseUrl?: string;
|
|
204
|
+
models: string[];
|
|
205
|
+
capabilities: ProviderCapability[];
|
|
206
|
+
rateLimits: RateLimitConfig;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
export type ProviderCapability =
|
|
210
|
+
| "code"
|
|
211
|
+
| "review"
|
|
212
|
+
| "plan"
|
|
213
|
+
| "test"
|
|
214
|
+
| "e2e"
|
|
215
|
+
| "refactor";
|
|
216
|
+
|
|
217
|
+
export interface RateLimitConfig {
|
|
218
|
+
requestsPerMinute?: number;
|
|
219
|
+
tokensPerMinute?: number;
|
|
220
|
+
tokensPerDay?: number;
|
|
221
|
+
concurrentRequests?: number;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
export interface ProviderResponse {
|
|
225
|
+
content: string;
|
|
226
|
+
usage?: {
|
|
227
|
+
input: number;
|
|
228
|
+
output: number;
|
|
229
|
+
cacheRead?: number;
|
|
230
|
+
cacheWrite?: number;
|
|
231
|
+
cost?: number;
|
|
232
|
+
};
|
|
233
|
+
model?: string;
|
|
234
|
+
finishReason?: "stop" | "length" | "content_filter" | "error";
|
|
235
|
+
error?: string;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
export interface ProviderRequest {
|
|
239
|
+
model: string;
|
|
240
|
+
messages: ProviderMessage[];
|
|
241
|
+
temperature?: number;
|
|
242
|
+
maxTokens?: number;
|
|
243
|
+
stop?: string[];
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
export interface ProviderMessage {
|
|
247
|
+
role: "system" | "user" | "assistant";
|
|
248
|
+
content: string;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// ─── Agent Handoff (RFC-0012) ───────────────────────────────────────
|
|
252
|
+
|
|
253
|
+
export interface HandoffContext {
|
|
254
|
+
jobId: string;
|
|
255
|
+
taskId: string;
|
|
256
|
+
fromAgent: string;
|
|
257
|
+
toAgent: string;
|
|
258
|
+
sharedFiles: string[];
|
|
259
|
+
taskHistory: HandoffEvent[];
|
|
260
|
+
summary: string;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
export interface HandoffEvent {
|
|
264
|
+
ts: string;
|
|
265
|
+
agentId: string;
|
|
266
|
+
action: string;
|
|
267
|
+
result?: string;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// ─── Project Detector (RFC-0014) ─────────────────────────────────────
|
|
271
|
+
|
|
272
|
+
export type ProjectType =
|
|
273
|
+
| "frappe_erpnext"
|
|
274
|
+
| "frappe_spa"
|
|
275
|
+
| "nextjs"
|
|
276
|
+
| "react_vite"
|
|
277
|
+
| "django"
|
|
278
|
+
| "laravel"
|
|
279
|
+
| "generic_web"
|
|
280
|
+
| "unknown";
|
|
281
|
+
|
|
282
|
+
export interface ProjectDetection {
|
|
283
|
+
projectType: ProjectType;
|
|
284
|
+
confidence: number;
|
|
285
|
+
signals: string[];
|
|
286
|
+
recommendedSeedStrategy: SeedStrategy;
|
|
287
|
+
recommendedE2EStrategy: E2EStrategy;
|
|
288
|
+
framework?: string;
|
|
289
|
+
version?: string;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
export type SeedStrategy =
|
|
293
|
+
| "frappe_doc_insert"
|
|
294
|
+
| "frappe_site_seed"
|
|
295
|
+
| "nextjs_factory"
|
|
296
|
+
| "react_factory"
|
|
297
|
+
| "django_fixture"
|
|
298
|
+
| "laravel_factory"
|
|
299
|
+
| "generic_sql";
|
|
300
|
+
|
|
301
|
+
export type E2EStrategy =
|
|
302
|
+
| "bench_site_browser_flow"
|
|
303
|
+
| "next_dev_server_flow"
|
|
304
|
+
| "vite_dev_server_flow"
|
|
305
|
+
| "django_test_client_flow"
|
|
306
|
+
| "laravel_dusk_flow"
|
|
307
|
+
| "generic_playwright_flow";
|
|
308
|
+
|
|
309
|
+
// ─── E2E Test Engine (RFC-0013) ─────────────────────────────────────
|
|
310
|
+
|
|
311
|
+
export interface E2EScenario {
|
|
312
|
+
id: string;
|
|
313
|
+
name: string;
|
|
314
|
+
description: string;
|
|
315
|
+
steps: E2EStep[];
|
|
316
|
+
required: boolean;
|
|
317
|
+
tags?: string[];
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
export interface E2EStep {
|
|
321
|
+
action:
|
|
322
|
+
| "navigate"
|
|
323
|
+
| "click"
|
|
324
|
+
| "type"
|
|
325
|
+
| "wait"
|
|
326
|
+
| "screenshot"
|
|
327
|
+
| "assert"
|
|
328
|
+
| "hover"
|
|
329
|
+
| "select"
|
|
330
|
+
| "upload";
|
|
331
|
+
selector?: string;
|
|
332
|
+
value?: string;
|
|
333
|
+
timeout?: number;
|
|
334
|
+
assertCondition?: string;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
export interface E2EResult {
|
|
338
|
+
scenarioId: string;
|
|
339
|
+
status: "passed" | "failed" | "skipped" | "error";
|
|
340
|
+
duration: number;
|
|
341
|
+
stepsExecuted: number;
|
|
342
|
+
stepsPassed: number;
|
|
343
|
+
stepsFailed: number;
|
|
344
|
+
screenshotPath?: string;
|
|
345
|
+
tracePath?: string;
|
|
346
|
+
videoPath?: string;
|
|
347
|
+
errorMessage?: string;
|
|
348
|
+
failedStep?: number;
|
|
349
|
+
executedAt: string;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
export interface E2EReport {
|
|
353
|
+
jobId: string;
|
|
354
|
+
scenarios: E2EScenario[];
|
|
355
|
+
results: E2EResult[];
|
|
356
|
+
summary: {
|
|
357
|
+
total: number;
|
|
358
|
+
passed: number;
|
|
359
|
+
failed: number;
|
|
360
|
+
skipped: number;
|
|
361
|
+
duration: number;
|
|
362
|
+
};
|
|
363
|
+
createdAt: string;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
// ─── Repair Engine (RFC-0018) ─────────────────────────────────────────
|
|
367
|
+
|
|
368
|
+
export interface RepairTask {
|
|
369
|
+
id: string;
|
|
370
|
+
originalTaskId: string;
|
|
371
|
+
failureType: FailureType;
|
|
372
|
+
description: string;
|
|
373
|
+
attemptedFixes: AttemptedFix[];
|
|
374
|
+
status: "pending" | "in_progress" | "resolved" | "escalated";
|
|
375
|
+
retryPolicy: RetryPolicy;
|
|
376
|
+
createdAt: string;
|
|
377
|
+
resolvedAt?: string;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
export type FailureType =
|
|
381
|
+
| "test_failure"
|
|
382
|
+
| "e2e_failure"
|
|
383
|
+
| "build_error"
|
|
384
|
+
| "runtime_error"
|
|
385
|
+
| "lint_error"
|
|
386
|
+
| "type_error"
|
|
387
|
+
| "quota_exhausted"
|
|
388
|
+
| "provider_error"
|
|
389
|
+
| "unknown";
|
|
390
|
+
|
|
391
|
+
export interface AttemptedFix {
|
|
392
|
+
attempt: number;
|
|
393
|
+
description: string;
|
|
394
|
+
success: boolean;
|
|
395
|
+
output?: string;
|
|
396
|
+
timestamp: string;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
export interface RetryPolicy {
|
|
400
|
+
maxRetries: number;
|
|
401
|
+
backoffMs: number;
|
|
402
|
+
backoffMultiplier: number;
|
|
403
|
+
escalationAfter?: number;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
// ─── Worktree (RFC-0005) ─────────────────────────────────────────────
|
|
407
|
+
|
|
408
|
+
export interface WorktreeInfo {
|
|
409
|
+
name: string;
|
|
410
|
+
path: string;
|
|
411
|
+
branch: string;
|
|
412
|
+
jobId?: string;
|
|
413
|
+
taskId?: string;
|
|
414
|
+
createdAt: string;
|
|
415
|
+
status: "active" | "merged" | "abandoned";
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
// ─── Loop Runtime (RFC-0001) ─────────────────────────────────────────
|
|
419
|
+
|
|
420
|
+
export interface LoopConfig {
|
|
421
|
+
jobId: string;
|
|
422
|
+
requirement: string;
|
|
423
|
+
providerPolicy: {
|
|
424
|
+
plannerProvider: string;
|
|
425
|
+
codeProviders: string[];
|
|
426
|
+
reviewProvider: string;
|
|
427
|
+
fallbackProviders: string[];
|
|
428
|
+
};
|
|
429
|
+
maxIterations?: number;
|
|
430
|
+
checkpointInterval?: number;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
export interface LoopState {
|
|
434
|
+
jobId: string;
|
|
435
|
+
iteration: number;
|
|
436
|
+
currentTaskId?: string;
|
|
437
|
+
status: "running" | "paused" | "completed" | "failed";
|
|
438
|
+
lastActivity?: string;
|
|
439
|
+
}
|