@harness-engineering/orchestrator 0.2.6 → 0.2.8
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 +20 -0
- package/dist/index.d.mts +736 -26
- package/dist/index.d.ts +736 -26
- package/dist/index.js +6052 -431
- package/dist/index.mjs +6067 -436
- package/package.json +12 -3
package/dist/index.d.ts
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
|
-
import { Issue, AgentEvent, TokenUsage, WorkflowConfig, Result, WorkflowDefinition,
|
|
1
|
+
import { Issue, AgentEvent, TokenUsage, ConcernSignal, ScopeTier, EscalationConfig, RoutingDecision, WorkflowConfig, IssueTrackerClient, Result, WorkflowDefinition, TrackerConfig, WorkspaceConfig, HooksConfig, AgentBackend, SessionStartParams, AgentSession, AgentError, TurnParams, TurnResult } from '@harness-engineering/types';
|
|
2
|
+
import { EnrichedSpec, ComplexityScore, SimulationResult } from '@harness-engineering/intelligence';
|
|
3
|
+
import { execFile } from 'node:child_process';
|
|
2
4
|
import { EventEmitter } from 'node:events';
|
|
3
5
|
|
|
4
6
|
/**
|
|
5
7
|
* Run attempt lifecycle phases (internal to orchestrator).
|
|
6
8
|
* Tracks the current phase of a single run attempt.
|
|
7
9
|
*/
|
|
8
|
-
type RunAttemptPhase = 'PreparingWorkspace' | 'BuildingPrompt' | 'LaunchingAgent' | 'InitializingSession' | 'StreamingTurn' | 'Finishing' | 'Succeeded' | 'Failed' | 'TimedOut' | 'Stalled' | 'CanceledByReconciliation';
|
|
10
|
+
type RunAttemptPhase = 'PreparingWorkspace' | 'BuildingPrompt' | 'LaunchingAgent' | 'InitializingSession' | 'StreamingTurn' | 'RateLimitSleeping' | 'Finishing' | 'Succeeded' | 'Failed' | 'TimedOut' | 'Stalled' | 'CanceledByReconciliation';
|
|
9
11
|
/**
|
|
10
12
|
* Live session metadata tracked while an agent subprocess is running.
|
|
11
13
|
*/
|
|
@@ -55,12 +57,14 @@ interface TokenTotals {
|
|
|
55
57
|
inputTokens: number;
|
|
56
58
|
outputTokens: number;
|
|
57
59
|
totalTokens: number;
|
|
60
|
+
cacheReadTokens: number;
|
|
61
|
+
cacheCreationTokens: number;
|
|
58
62
|
secondsRunning: number;
|
|
59
63
|
}
|
|
60
64
|
/**
|
|
61
65
|
* Rate limit snapshot (populated by agent events).
|
|
62
66
|
*/
|
|
63
|
-
interface RateLimitSnapshot {
|
|
67
|
+
interface RateLimitSnapshot$1 {
|
|
64
68
|
requestsRemaining: number | null;
|
|
65
69
|
requestsLimit: number | null;
|
|
66
70
|
tokensRemaining: number | null;
|
|
@@ -72,25 +76,50 @@ interface RateLimitSnapshot {
|
|
|
72
76
|
interface OrchestratorState {
|
|
73
77
|
pollIntervalMs: number;
|
|
74
78
|
maxConcurrentAgents: number;
|
|
79
|
+
globalCooldownUntilMs: number | null;
|
|
80
|
+
recentRequestTimestamps: number[];
|
|
81
|
+
recentInputTokens: {
|
|
82
|
+
timestamp: number;
|
|
83
|
+
tokens: number;
|
|
84
|
+
}[];
|
|
85
|
+
recentOutputTokens: {
|
|
86
|
+
timestamp: number;
|
|
87
|
+
tokens: number;
|
|
88
|
+
}[];
|
|
89
|
+
globalCooldownMs: number;
|
|
90
|
+
maxRequestsPerMinute: number;
|
|
91
|
+
maxRequestsPerSecond: number;
|
|
92
|
+
maxInputTokensPerMinute: number;
|
|
93
|
+
maxOutputTokensPerMinute: number;
|
|
75
94
|
running: Map<string, RunningEntry>;
|
|
76
95
|
claimed: Set<string>;
|
|
77
96
|
retryAttempts: Map<string, RetryEntry>;
|
|
78
97
|
completed: Set<string>;
|
|
79
98
|
tokenTotals: TokenTotals;
|
|
80
|
-
rateLimits: RateLimitSnapshot;
|
|
99
|
+
rateLimits: RateLimitSnapshot$1;
|
|
100
|
+
/** Running count of claim rejections (another orchestrator won the race). */
|
|
101
|
+
claimRejections: number;
|
|
81
102
|
}
|
|
82
103
|
|
|
83
104
|
/**
|
|
84
105
|
* Discriminated union of events that drive the orchestrator state machine.
|
|
85
106
|
* All events are data -- the caller constructs them from I/O results.
|
|
86
107
|
*/
|
|
87
|
-
type OrchestratorEvent = TickEvent | WorkerExitEvent | AgentUpdateEvent | RetryFiredEvent | StallDetectedEvent;
|
|
108
|
+
type OrchestratorEvent = TickEvent | WorkerExitEvent | AgentUpdateEvent | RetryFiredEvent | StallDetectedEvent | ClaimRejectedEvent;
|
|
88
109
|
interface TickEvent {
|
|
89
110
|
type: 'tick';
|
|
90
111
|
candidates: Issue[];
|
|
91
112
|
runningStates: Map<string, Issue>;
|
|
92
113
|
/** Caller-supplied wall clock (ms since epoch). Keeps state machine pure. */
|
|
93
114
|
nowMs: number;
|
|
115
|
+
/** Pre-computed concern signals from intelligence pipeline (issueId → signals) */
|
|
116
|
+
concernSignals?: Map<string, ConcernSignal[]>;
|
|
117
|
+
/** Pre-computed enriched specs from intelligence pipeline (issueId → spec) */
|
|
118
|
+
enrichedSpecs?: Map<string, EnrichedSpec>;
|
|
119
|
+
/** Pre-computed complexity scores from intelligence pipeline (issueId → score) */
|
|
120
|
+
complexityScores?: Map<string, ComplexityScore>;
|
|
121
|
+
/** Pre-computed PESL simulation results from intelligence pipeline (issueId -> result) */
|
|
122
|
+
simulationResults?: Map<string, SimulationResult>;
|
|
94
123
|
}
|
|
95
124
|
interface WorkerExitEvent {
|
|
96
125
|
type: 'worker_exit';
|
|
@@ -115,15 +144,21 @@ interface StallDetectedEvent {
|
|
|
115
144
|
type: 'stall_detected';
|
|
116
145
|
issueId: string;
|
|
117
146
|
}
|
|
147
|
+
interface ClaimRejectedEvent {
|
|
148
|
+
type: 'claim_rejected';
|
|
149
|
+
issueId: string;
|
|
150
|
+
}
|
|
118
151
|
/**
|
|
119
152
|
* Discriminated union of side effects returned by the state machine.
|
|
120
153
|
* These are data describing what to do -- the orchestrator loop executes them.
|
|
121
154
|
*/
|
|
122
|
-
type SideEffect = DispatchEffect | StopEffect | ScheduleRetryEffect | ReleaseClaimEffect | CleanWorkspaceEffect | UpdateTokensEffect | EmitLogEffect;
|
|
155
|
+
type SideEffect = DispatchEffect | StopEffect | ScheduleRetryEffect | ReleaseClaimEffect | CleanWorkspaceEffect | UpdateTokensEffect | EmitLogEffect | EscalateEffect | ClaimEffect;
|
|
123
156
|
interface DispatchEffect {
|
|
124
157
|
type: 'dispatch';
|
|
125
158
|
issue: Issue;
|
|
126
159
|
attempt: number | null;
|
|
160
|
+
/** Which backend to dispatch to. Defaults to 'primary' for backward compat. */
|
|
161
|
+
backend?: 'local' | 'primary';
|
|
127
162
|
}
|
|
128
163
|
interface StopEffect {
|
|
129
164
|
type: 'stop';
|
|
@@ -158,6 +193,28 @@ interface EmitLogEffect {
|
|
|
158
193
|
message: string;
|
|
159
194
|
context?: Record<string, unknown>;
|
|
160
195
|
}
|
|
196
|
+
interface EscalateEffect {
|
|
197
|
+
type: 'escalate';
|
|
198
|
+
issueId: string;
|
|
199
|
+
identifier: string;
|
|
200
|
+
reasons: string[];
|
|
201
|
+
/** Issue title for context in the interaction queue */
|
|
202
|
+
issueTitle?: string;
|
|
203
|
+
/** Issue description for context in the interaction queue */
|
|
204
|
+
issueDescription?: string | null;
|
|
205
|
+
/** Enriched spec from intelligence pipeline, if available */
|
|
206
|
+
enrichedSpec?: EnrichedSpec;
|
|
207
|
+
/** Complexity score from intelligence pipeline, if available */
|
|
208
|
+
complexityScore?: ComplexityScore;
|
|
209
|
+
}
|
|
210
|
+
interface ClaimEffect {
|
|
211
|
+
type: 'claim';
|
|
212
|
+
issue: Issue;
|
|
213
|
+
/** Which backend to dispatch to after a successful claim */
|
|
214
|
+
backend?: 'local' | 'primary';
|
|
215
|
+
/** Retry attempt number, if this is a retry dispatch */
|
|
216
|
+
attempt: number | null;
|
|
217
|
+
}
|
|
161
218
|
|
|
162
219
|
/**
|
|
163
220
|
* Calculate retry delay based on attempt number and retry type.
|
|
@@ -211,10 +268,245 @@ declare function canDispatch(state: OrchestratorState, issueState: string, maxCo
|
|
|
211
268
|
*/
|
|
212
269
|
declare function reconcile(state: OrchestratorState, runningStates: ReadonlyMap<string, Issue>, activeStates: string[], terminalStates: string[]): SideEffect[];
|
|
213
270
|
|
|
271
|
+
/**
|
|
272
|
+
* Artifact presence metadata for scope tier detection.
|
|
273
|
+
*/
|
|
274
|
+
interface ArtifactPresence {
|
|
275
|
+
hasSpec: boolean;
|
|
276
|
+
hasPlans: boolean;
|
|
277
|
+
}
|
|
278
|
+
/**
|
|
279
|
+
* Derive artifact presence from an Issue's spec and plans fields.
|
|
280
|
+
*/
|
|
281
|
+
declare function artifactPresenceFromIssue(issue: Issue): ArtifactPresence;
|
|
282
|
+
/**
|
|
283
|
+
* Detect the scope tier for an issue based on label overrides and artifact presence.
|
|
284
|
+
*
|
|
285
|
+
* Label override (e.g., `scope:quick-fix`) takes precedence.
|
|
286
|
+
* Otherwise, infer from spec/plan presence:
|
|
287
|
+
* - No spec, no plan -> full-exploration
|
|
288
|
+
* - Spec or plan exists -> guided-change
|
|
289
|
+
*/
|
|
290
|
+
declare function detectScopeTier(issue: Issue, artifacts: ArtifactPresence): ScopeTier;
|
|
291
|
+
/**
|
|
292
|
+
* Pure routing function. Determines whether an issue should be dispatched
|
|
293
|
+
* to the local backend, the primary backend, or escalated to needs-human.
|
|
294
|
+
*
|
|
295
|
+
* Routing rules (in order):
|
|
296
|
+
* 1. If tier is in alwaysHuman -> needs-human
|
|
297
|
+
* 2. If tier is in primaryExecute -> dispatch-primary
|
|
298
|
+
* 3. If tier is in autoExecute -> dispatch-local
|
|
299
|
+
* 4. If tier is in signalGated -> check concern signals
|
|
300
|
+
* 5. Otherwise -> dispatch-local (safe default)
|
|
301
|
+
*/
|
|
302
|
+
declare function routeIssue(scopeTier: ScopeTier, concernSignals: ConcernSignal[], config: EscalationConfig): RoutingDecision;
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* Candidate skills the orchestrator may dispatch to. Keep this set
|
|
306
|
+
* small on purpose — the triage router is a coarse routing layer, not
|
|
307
|
+
* a skill registry.
|
|
308
|
+
*/
|
|
309
|
+
type TriageSkill = 'code-review' | 'security-review' | 'planning' | 'debugging' | 'refactoring' | 'docs';
|
|
310
|
+
/**
|
|
311
|
+
* Shape-of-work signals the triage router consumes. These are derived
|
|
312
|
+
* from the Issue plus diff metadata by the caller.
|
|
313
|
+
*/
|
|
314
|
+
interface TriageSignals {
|
|
315
|
+
/** Raw title/body prefix, e.g. "feat:", "fix:", "docs:", "security:" */
|
|
316
|
+
titlePrefix?: string;
|
|
317
|
+
/** True if the diff touches auth, crypto, session, or similar paths */
|
|
318
|
+
touchesSecuritySensitivePaths?: boolean;
|
|
319
|
+
/** True if the diff touches a migrations or schema directory */
|
|
320
|
+
touchesMigrationPaths?: boolean;
|
|
321
|
+
/** Count of changed files in the diff */
|
|
322
|
+
changedFileCount?: number;
|
|
323
|
+
/** Whether tests are failing on the issue branch */
|
|
324
|
+
hasFailingTests?: boolean;
|
|
325
|
+
/** Whether the issue is marked or labeled as a rollback */
|
|
326
|
+
isRollback?: boolean;
|
|
327
|
+
/** True if every changed file is `.md` */
|
|
328
|
+
isDocsOnly?: boolean;
|
|
329
|
+
}
|
|
330
|
+
/**
|
|
331
|
+
* Structured decision the orchestrator persists and consumes.
|
|
332
|
+
*/
|
|
333
|
+
interface TriageDecision {
|
|
334
|
+
/** Target skill for dispatch. */
|
|
335
|
+
skill: TriageSkill;
|
|
336
|
+
/** Specific agent within the skill, optional. */
|
|
337
|
+
agent?: string;
|
|
338
|
+
/** Router confidence in the decision. */
|
|
339
|
+
confidence: 'high' | 'medium' | 'low';
|
|
340
|
+
/** Human-readable justifications (one entry per rule that matched). */
|
|
341
|
+
reasons: string[];
|
|
342
|
+
}
|
|
343
|
+
/**
|
|
344
|
+
* Optional user/project overrides for path heuristics and rule thresholds.
|
|
345
|
+
*/
|
|
346
|
+
interface TriageConfig {
|
|
347
|
+
/** Max changed files for a fix to still route to code-review (default 3). */
|
|
348
|
+
smallFixChangedFileMax?: number;
|
|
349
|
+
}
|
|
350
|
+
/**
|
|
351
|
+
* Extract the conventional-commit prefix (lowercased) from a title.
|
|
352
|
+
* Returns undefined if no prefix is present.
|
|
353
|
+
*/
|
|
354
|
+
declare function extractTitlePrefix(title: string | null | undefined): string | undefined;
|
|
355
|
+
/**
|
|
356
|
+
* Decide which skill/agent the orchestrator should dispatch this issue to.
|
|
357
|
+
*
|
|
358
|
+
* Rule order (first match wins):
|
|
359
|
+
* 1. isRollback → debugging (high)
|
|
360
|
+
* 2. security prefix or security paths → security-review (high)
|
|
361
|
+
* 3. docs prefix or docs-only diff → docs (high)
|
|
362
|
+
* 4. hasFailingTests → debugging (medium)
|
|
363
|
+
* 5. touchesMigrationPaths → planning (high)
|
|
364
|
+
* 6. fix: with small change → code-review (high)
|
|
365
|
+
* 7. feat: → planning (medium)
|
|
366
|
+
* 8. refactor: → refactoring (medium)
|
|
367
|
+
* 9. default → code-review (low)
|
|
368
|
+
*
|
|
369
|
+
* Triage is meant to run BEFORE `routeIssue()`; the selected skill is
|
|
370
|
+
* persisted on the live session so downstream dispatch does not
|
|
371
|
+
* re-derive it.
|
|
372
|
+
*/
|
|
373
|
+
declare function triageIssue(issue: Issue, signals: TriageSignals, config?: TriageConfig): TriageDecision;
|
|
374
|
+
|
|
375
|
+
/**
|
|
376
|
+
* A pending human interaction, typically from an escalation.
|
|
377
|
+
*/
|
|
378
|
+
interface PendingInteraction {
|
|
379
|
+
/** Unique interaction ID */
|
|
380
|
+
id: string;
|
|
381
|
+
/** ID of the related issue */
|
|
382
|
+
issueId: string;
|
|
383
|
+
/** Interaction type */
|
|
384
|
+
type: 'needs-human';
|
|
385
|
+
/** Reasons for escalation */
|
|
386
|
+
reasons: string[];
|
|
387
|
+
/** Context for the human */
|
|
388
|
+
context: {
|
|
389
|
+
issueTitle: string;
|
|
390
|
+
issueDescription: string | null;
|
|
391
|
+
specPath: string | null;
|
|
392
|
+
planPath: string | null;
|
|
393
|
+
relatedFiles: string[];
|
|
394
|
+
/** Enriched spec from intelligence pipeline, if available */
|
|
395
|
+
enrichedSpec?: {
|
|
396
|
+
intent: string;
|
|
397
|
+
summary: string;
|
|
398
|
+
affectedSystems: unknown[];
|
|
399
|
+
unknowns: string[];
|
|
400
|
+
ambiguities: string[];
|
|
401
|
+
riskSignals: string[];
|
|
402
|
+
};
|
|
403
|
+
/** Complexity score from intelligence pipeline, if available */
|
|
404
|
+
complexityScore?: {
|
|
405
|
+
overall: number;
|
|
406
|
+
confidence: number;
|
|
407
|
+
riskLevel: string;
|
|
408
|
+
blastRadius: {
|
|
409
|
+
services: number;
|
|
410
|
+
modules: number;
|
|
411
|
+
filesEstimated: number;
|
|
412
|
+
testFilesAffected: number;
|
|
413
|
+
};
|
|
414
|
+
dimensions: {
|
|
415
|
+
structural: number;
|
|
416
|
+
semantic: number;
|
|
417
|
+
historical: number;
|
|
418
|
+
};
|
|
419
|
+
reasoning: string[];
|
|
420
|
+
recommendedRoute: string;
|
|
421
|
+
};
|
|
422
|
+
};
|
|
423
|
+
/** ISO timestamp of creation */
|
|
424
|
+
createdAt: string;
|
|
425
|
+
/** Current status */
|
|
426
|
+
status: 'pending' | 'claimed' | 'resolved';
|
|
427
|
+
}
|
|
428
|
+
/**
|
|
429
|
+
* Persistent queue of pending human interactions.
|
|
430
|
+
* Each interaction is stored as a separate JSON file in the configured directory.
|
|
431
|
+
*/
|
|
432
|
+
declare class InteractionQueue {
|
|
433
|
+
private dir;
|
|
434
|
+
private pushListeners;
|
|
435
|
+
/**
|
|
436
|
+
* @param dir - Directory path for storing interaction JSON files
|
|
437
|
+
*/
|
|
438
|
+
constructor(dir: string);
|
|
439
|
+
/**
|
|
440
|
+
* Register a listener that fires after each push.
|
|
441
|
+
*/
|
|
442
|
+
onPush(listener: (interaction: PendingInteraction) => void): void;
|
|
443
|
+
/**
|
|
444
|
+
* Push a new interaction to the queue.
|
|
445
|
+
* If a pending interaction for the same issueId already exists, it is
|
|
446
|
+
* replaced (upsert) so the directory never accumulates duplicates.
|
|
447
|
+
*/
|
|
448
|
+
push(interaction: PendingInteraction): Promise<void>;
|
|
449
|
+
/**
|
|
450
|
+
* List all interactions (regardless of status).
|
|
451
|
+
*/
|
|
452
|
+
list(): Promise<PendingInteraction[]>;
|
|
453
|
+
/**
|
|
454
|
+
* List only pending interactions.
|
|
455
|
+
*/
|
|
456
|
+
listPending(): Promise<PendingInteraction[]>;
|
|
457
|
+
/**
|
|
458
|
+
* Update the status of an interaction.
|
|
459
|
+
*/
|
|
460
|
+
updateStatus(id: string, status: PendingInteraction['status']): Promise<void>;
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
/**
|
|
464
|
+
* A persisted record of a single intelligence pipeline analysis run.
|
|
465
|
+
*/
|
|
466
|
+
interface AnalysisRecord {
|
|
467
|
+
/** Issue ID this analysis belongs to */
|
|
468
|
+
issueId: string;
|
|
469
|
+
/** Issue identifier (human-readable) */
|
|
470
|
+
identifier: string;
|
|
471
|
+
/** Enriched spec from SEL, or null if skipped */
|
|
472
|
+
spec: EnrichedSpec | null;
|
|
473
|
+
/** Complexity score from CML, or null if skipped */
|
|
474
|
+
score: ComplexityScore | null;
|
|
475
|
+
/** PESL simulation result, or null if not run */
|
|
476
|
+
simulation: SimulationResult | null;
|
|
477
|
+
/** ISO timestamp when this analysis was recorded */
|
|
478
|
+
analyzedAt: string;
|
|
479
|
+
/** External tracker ID (e.g., "github:owner/repo#42"), populated at analysis time. Null if no tracker configured. */
|
|
480
|
+
externalId: string | null;
|
|
481
|
+
}
|
|
482
|
+
/**
|
|
483
|
+
* Persistent archive of intelligence pipeline analysis results.
|
|
484
|
+
* Each analysis is stored as a separate JSON file keyed by issue ID.
|
|
485
|
+
* Overwrites previous analysis for the same issue (latest result wins).
|
|
486
|
+
*/
|
|
487
|
+
declare class AnalysisArchive {
|
|
488
|
+
private dir;
|
|
489
|
+
constructor(dir: string);
|
|
490
|
+
private safePath;
|
|
491
|
+
/**
|
|
492
|
+
* Write an analysis record to disk, overwriting any previous record for the same issue.
|
|
493
|
+
*/
|
|
494
|
+
save(record: AnalysisRecord): Promise<void>;
|
|
495
|
+
/**
|
|
496
|
+
* Read the analysis record for a specific issue.
|
|
497
|
+
*/
|
|
498
|
+
get(issueId: string): Promise<AnalysisRecord | null>;
|
|
499
|
+
/**
|
|
500
|
+
* List all archived analysis records.
|
|
501
|
+
*/
|
|
502
|
+
list(): Promise<AnalysisRecord[]>;
|
|
503
|
+
}
|
|
504
|
+
|
|
214
505
|
interface ApplyEventResult {
|
|
215
506
|
nextState: OrchestratorState;
|
|
216
507
|
effects: SideEffect[];
|
|
217
508
|
}
|
|
509
|
+
declare function resolveEscalationConfig(config: WorkflowConfig): EscalationConfig;
|
|
218
510
|
/**
|
|
219
511
|
* Pure state machine transition function.
|
|
220
512
|
*
|
|
@@ -229,6 +521,189 @@ declare function applyEvent(state: OrchestratorState, event: OrchestratorEvent,
|
|
|
229
521
|
*/
|
|
230
522
|
declare function createEmptyState(config: WorkflowConfig): OrchestratorState;
|
|
231
523
|
|
|
524
|
+
/**
|
|
525
|
+
* Computes the delay (in ms) needed before the next API request can be made,
|
|
526
|
+
* based on a snapshot of recent request/token activity and rate-limit config.
|
|
527
|
+
*
|
|
528
|
+
* Returns 0 when no throttling is needed.
|
|
529
|
+
*/
|
|
530
|
+
interface RateLimitSnapshot {
|
|
531
|
+
globalCooldownUntilMs: number | null;
|
|
532
|
+
recentRequestTimestamps: number[];
|
|
533
|
+
recentInputTokens: {
|
|
534
|
+
timestamp: number;
|
|
535
|
+
tokens: number;
|
|
536
|
+
}[];
|
|
537
|
+
recentOutputTokens: {
|
|
538
|
+
timestamp: number;
|
|
539
|
+
tokens: number;
|
|
540
|
+
}[];
|
|
541
|
+
}
|
|
542
|
+
interface RateLimitConfig {
|
|
543
|
+
maxRequestsPerMinute: number;
|
|
544
|
+
maxRequestsPerSecond: number;
|
|
545
|
+
maxInputTokensPerMinute: number;
|
|
546
|
+
maxOutputTokensPerMinute: number;
|
|
547
|
+
}
|
|
548
|
+
declare function computeRateLimitDelay(snapshot: RateLimitSnapshot, config: RateLimitConfig): number;
|
|
549
|
+
|
|
550
|
+
/**
|
|
551
|
+
* Renders an AnalysisRecord as a structured markdown comment.
|
|
552
|
+
* Format: summary header + reasoning bullets + collapsible JSON with discriminator.
|
|
553
|
+
*
|
|
554
|
+
* Used by both the orchestrator auto-publish and the CLI publish-analyses command.
|
|
555
|
+
*/
|
|
556
|
+
declare function renderAnalysisComment(record: AnalysisRecord): string;
|
|
557
|
+
|
|
558
|
+
interface PublishedIndex {
|
|
559
|
+
[issueId: string]: string;
|
|
560
|
+
}
|
|
561
|
+
/**
|
|
562
|
+
* Load the published analyses index from disk.
|
|
563
|
+
* Returns an empty object if the file does not exist or is unparseable.
|
|
564
|
+
*/
|
|
565
|
+
declare function loadPublishedIndex(projectRoot: string): PublishedIndex;
|
|
566
|
+
/**
|
|
567
|
+
* Persist the published analyses index to disk.
|
|
568
|
+
* Creates parent directories if they do not exist.
|
|
569
|
+
*/
|
|
570
|
+
declare function savePublishedIndex(projectRoot: string, index: PublishedIndex): void;
|
|
571
|
+
|
|
572
|
+
/**
|
|
573
|
+
* Resolves the orchestrator identity. Uses the explicit configId if
|
|
574
|
+
* provided; otherwise reads or creates a persisted machine UUID at
|
|
575
|
+
* ~/.harness/orchestrator-id and derives a stable hash.
|
|
576
|
+
*
|
|
577
|
+
* Format: `orchestrator-{first8charsOfSha256(uuid)}`
|
|
578
|
+
* Example: `orchestrator-a7f3b2c1`
|
|
579
|
+
*/
|
|
580
|
+
declare function resolveOrchestratorId(configId?: string): Promise<string>;
|
|
581
|
+
/** Exposed for testing only -- returns the path to the identity file. */
|
|
582
|
+
declare const ORCHESTRATOR_IDENTITY_FILE: string;
|
|
583
|
+
|
|
584
|
+
interface ClaimManagerConfig {
|
|
585
|
+
/** Delay in ms between claim write and verification read. Default: 2000 */
|
|
586
|
+
verifyDelayMs?: number;
|
|
587
|
+
/** State name used for claimed/in-progress issues. Default: 'in-progress' */
|
|
588
|
+
claimedState?: string;
|
|
589
|
+
}
|
|
590
|
+
/**
|
|
591
|
+
* Manages claim coordination for multi-orchestrator dispatch.
|
|
592
|
+
*
|
|
593
|
+
* Uses the tracker as the shared state layer. Claims are optimistic:
|
|
594
|
+
* write then verify after a delay to detect races.
|
|
595
|
+
*/
|
|
596
|
+
declare class ClaimManager {
|
|
597
|
+
private tracker;
|
|
598
|
+
private orchestratorId;
|
|
599
|
+
private verifyDelayMs;
|
|
600
|
+
private claimedState;
|
|
601
|
+
constructor(tracker: IssueTrackerClient, orchestratorId: string, config?: ClaimManagerConfig);
|
|
602
|
+
/**
|
|
603
|
+
* Optimistically claims an issue then verifies ownership after a delay.
|
|
604
|
+
*
|
|
605
|
+
* Returns 'claimed' if the assignee matches this orchestrator after
|
|
606
|
+
* the verify delay, 'rejected' if another orchestrator won the race.
|
|
607
|
+
*/
|
|
608
|
+
claimAndVerify(issueId: string): Promise<Result<'claimed' | 'rejected', Error>>;
|
|
609
|
+
/**
|
|
610
|
+
* Releases a claimed issue back to the available pool.
|
|
611
|
+
*/
|
|
612
|
+
release(issueId: string): Promise<Result<void, Error>>;
|
|
613
|
+
/**
|
|
614
|
+
* Refreshes claim timestamps for all running issues in parallel.
|
|
615
|
+
* Failures are non-fatal — individual heartbeat failures are swallowed
|
|
616
|
+
* so one failing claim does not block others.
|
|
617
|
+
*/
|
|
618
|
+
heartbeat(issueIds: string[]): Promise<void>;
|
|
619
|
+
/**
|
|
620
|
+
* Checks whether an issue's claim is stale based on its updatedAt
|
|
621
|
+
* timestamp and the configured TTL.
|
|
622
|
+
*
|
|
623
|
+
* Returns true if the claim should be considered expired (the
|
|
624
|
+
* owning orchestrator may have crashed).
|
|
625
|
+
*/
|
|
626
|
+
isStale(issue: Issue, ttlMs: number): boolean;
|
|
627
|
+
/**
|
|
628
|
+
* Scans the tracker for "in-progress" issues assigned to this orchestrator
|
|
629
|
+
* and releases any that are not currently running in memory.
|
|
630
|
+
*
|
|
631
|
+
* Called once during orchestrator startup to clean up orphaned claims
|
|
632
|
+
* from a previous crash or unclean shutdown.
|
|
633
|
+
*
|
|
634
|
+
* @param runningIssueIds - Set of issue IDs currently in the running map
|
|
635
|
+
* @returns List of issue IDs that were released
|
|
636
|
+
*/
|
|
637
|
+
reconcileOnStartup(runningIssueIds: ReadonlySet<string>): Promise<Result<string[], Error>>;
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
/**
|
|
641
|
+
* Minimal logger interface for PR detection.
|
|
642
|
+
* Accepts any structured logger that provides debug/info/warn.
|
|
643
|
+
*/
|
|
644
|
+
interface PRDetectorLogger {
|
|
645
|
+
debug(message: string, meta?: Record<string, unknown>): void;
|
|
646
|
+
info(message: string, meta?: Record<string, unknown>): void;
|
|
647
|
+
warn(message: string, meta?: Record<string, unknown>): void;
|
|
648
|
+
}
|
|
649
|
+
/**
|
|
650
|
+
* Function signature compatible with Node's `child_process.execFile`.
|
|
651
|
+
* Allows injection for testing.
|
|
652
|
+
*/
|
|
653
|
+
type ExecFileFn = typeof execFile;
|
|
654
|
+
/**
|
|
655
|
+
* Detects whether GitHub issues or branches already have open pull requests.
|
|
656
|
+
*
|
|
657
|
+
* Uses the `gh` CLI under the hood. All checks are fail-open: if `gh` is not
|
|
658
|
+
* installed, auth is missing, or the network is down, candidates pass through
|
|
659
|
+
* rather than being incorrectly blocked.
|
|
660
|
+
*/
|
|
661
|
+
declare class PRDetector {
|
|
662
|
+
private logger;
|
|
663
|
+
private execFileFn;
|
|
664
|
+
private projectRoot;
|
|
665
|
+
constructor(opts: {
|
|
666
|
+
logger: PRDetectorLogger;
|
|
667
|
+
execFileFn?: ExecFileFn;
|
|
668
|
+
projectRoot: string;
|
|
669
|
+
});
|
|
670
|
+
/**
|
|
671
|
+
* Parse a `github:owner/repo#N` externalId into its parts.
|
|
672
|
+
* Returns null for invalid or non-GitHub formats.
|
|
673
|
+
*/
|
|
674
|
+
parseExternalId(externalId: string): {
|
|
675
|
+
owner: string;
|
|
676
|
+
repo: string;
|
|
677
|
+
number: number;
|
|
678
|
+
} | null;
|
|
679
|
+
/**
|
|
680
|
+
* Checks whether a remote branch has an open pull request via `gh`.
|
|
681
|
+
* Returns true if a PR exists, false otherwise. Failures are treated
|
|
682
|
+
* as "no PR" to err on the side of preserving work.
|
|
683
|
+
*/
|
|
684
|
+
branchHasPullRequest(branch: string): Promise<boolean>;
|
|
685
|
+
/**
|
|
686
|
+
* Checks whether a GitHub issue (identified by externalId) has an open PR
|
|
687
|
+
* linked to it via `closes #N` or similar keywords. Fail-open on API errors
|
|
688
|
+
* or non-GitHub externalId formats.
|
|
689
|
+
*/
|
|
690
|
+
hasOpenPRForExternalId(externalId: string): Promise<boolean>;
|
|
691
|
+
/**
|
|
692
|
+
* Checks whether an issue identifier has an open GitHub PR by searching
|
|
693
|
+
* for a branch matching the `feat/<identifier>` naming convention used
|
|
694
|
+
* by dispatched agents. Fail-open on API errors.
|
|
695
|
+
*/
|
|
696
|
+
hasOpenPRForIdentifier(identifier: string): Promise<boolean>;
|
|
697
|
+
/**
|
|
698
|
+
* Filters out candidates that already have an open GitHub PR, running
|
|
699
|
+
* checks with limited concurrency to avoid overwhelming the GitHub API.
|
|
700
|
+
* For candidates with an externalId, searches for PRs linked to the
|
|
701
|
+
* GitHub issue. Falls back to `feat/<identifier>` branch lookup otherwise.
|
|
702
|
+
* Fail-open on API errors.
|
|
703
|
+
*/
|
|
704
|
+
filterCandidatesWithOpenPRs(candidates: Issue[]): Promise<Issue[]>;
|
|
705
|
+
}
|
|
706
|
+
|
|
232
707
|
declare class WorkflowLoader {
|
|
233
708
|
loadWorkflow(filePath: string): Promise<Result<WorkflowDefinition, Error>>;
|
|
234
709
|
}
|
|
@@ -261,6 +736,28 @@ declare class RoadmapTrackerAdapter implements IssueTrackerClient {
|
|
|
261
736
|
* @param stateNames - List of statuses to filter by
|
|
262
737
|
*/
|
|
263
738
|
fetchIssuesByStates(stateNames: string[]): Promise<Result<Issue[], Error>>;
|
|
739
|
+
/**
|
|
740
|
+
* Transitions a roadmap feature into the first configured terminal state
|
|
741
|
+
* and rewrites the markdown file. Idempotent: if the feature is already
|
|
742
|
+
* in a terminal state, this is a no-op.
|
|
743
|
+
*
|
|
744
|
+
* The orchestrator calls this after a successful agent exit so the feature
|
|
745
|
+
* is no longer returned by `fetchCandidateIssues` on the next tick — and,
|
|
746
|
+
* critically, after an orchestrator restart.
|
|
747
|
+
*/
|
|
748
|
+
markIssueComplete(issueId: string): Promise<Result<void, Error>>;
|
|
749
|
+
/**
|
|
750
|
+
* Claims an issue by transitioning its status to "in-progress" and
|
|
751
|
+
* writing the orchestratorId into the assignee field. Idempotent if
|
|
752
|
+
* already claimed by the same orchestratorId.
|
|
753
|
+
*/
|
|
754
|
+
claimIssue(issueId: string, orchestratorId: string): Promise<Result<void, Error>>;
|
|
755
|
+
/**
|
|
756
|
+
* Releases a claimed issue by transitioning back to the first active
|
|
757
|
+
* state and clearing the assignee field.
|
|
758
|
+
*/
|
|
759
|
+
releaseIssue(issueId: string): Promise<Result<void, Error>>;
|
|
760
|
+
private findFeatureById;
|
|
264
761
|
/**
|
|
265
762
|
* Fetches full issue details for a list of identifiers.
|
|
266
763
|
*
|
|
@@ -290,7 +787,11 @@ declare class LinearGraphQLStub implements LinearGraphQLExtension {
|
|
|
290
787
|
|
|
291
788
|
declare class WorkspaceManager {
|
|
292
789
|
private config;
|
|
790
|
+
/** Absolute path to the git repository root (resolved lazily). */
|
|
791
|
+
private repoRoot;
|
|
293
792
|
constructor(config: WorkspaceConfig);
|
|
793
|
+
/** Runs a git command and returns stdout. Extracted for testability. */
|
|
794
|
+
protected git(args: string[], cwd: string): Promise<string>;
|
|
294
795
|
/**
|
|
295
796
|
* Sanitizes an issue identifier to be safe for use as a directory name.
|
|
296
797
|
*/
|
|
@@ -300,15 +801,45 @@ declare class WorkspaceManager {
|
|
|
300
801
|
*/
|
|
301
802
|
resolvePath(identifier: string): string;
|
|
302
803
|
/**
|
|
303
|
-
*
|
|
804
|
+
* Discovers the git repository root from the workspace root directory.
|
|
805
|
+
*/
|
|
806
|
+
private getRepoRoot;
|
|
807
|
+
/**
|
|
808
|
+
* Ensures the workspace exists as a git worktree so the agent has
|
|
809
|
+
* access to the full project source.
|
|
304
810
|
*/
|
|
305
811
|
ensureWorkspace(identifier: string): Promise<Result<string, Error>>;
|
|
812
|
+
/**
|
|
813
|
+
* Best-effort `git fetch origin` so subsequent ref resolution sees the
|
|
814
|
+
* latest remote state. Failures (offline, no remote, auth errors) are
|
|
815
|
+
* swallowed — dispatch should not be blocked by transient network issues.
|
|
816
|
+
*/
|
|
817
|
+
private tryFetch;
|
|
818
|
+
/**
|
|
819
|
+
* Resolves the ref that new worktrees should be based on.
|
|
820
|
+
*
|
|
821
|
+
* Priority order:
|
|
822
|
+
* 1. `config.baseRef` (explicit override). Throws if it doesn't resolve.
|
|
823
|
+
* 2. Default branch via `git symbolic-ref --short refs/remotes/origin/HEAD`.
|
|
824
|
+
* 3. Common fallbacks: `origin/main`, `origin/master`, `main`, `master`.
|
|
825
|
+
* 4. `HEAD` as an ultimate fallback (preserves old behavior for unusual
|
|
826
|
+
* repos without any of the above).
|
|
827
|
+
*/
|
|
828
|
+
private resolveBaseRef;
|
|
829
|
+
/** Returns true iff `git rev-parse --verify` accepts the ref. */
|
|
830
|
+
private refExists;
|
|
306
831
|
/**
|
|
307
832
|
* Checks if a workspace exists.
|
|
308
833
|
*/
|
|
309
834
|
exists(identifier: string): Promise<boolean>;
|
|
310
835
|
/**
|
|
311
|
-
*
|
|
836
|
+
* Checks whether a worktree has commits ahead of the base branch that have
|
|
837
|
+
* been pushed to a remote branch. Returns the remote branch name if found,
|
|
838
|
+
* or null if the worktree is on a detached HEAD with no pushed branch.
|
|
839
|
+
*/
|
|
840
|
+
findPushedBranch(identifier: string): Promise<string | null>;
|
|
841
|
+
/**
|
|
842
|
+
* Removes a workspace directory and its git worktree registration.
|
|
312
843
|
*/
|
|
313
844
|
removeWorkspace(identifier: string): Promise<Result<void, Error>>;
|
|
314
845
|
}
|
|
@@ -334,22 +865,65 @@ declare class MockBackend implements AgentBackend {
|
|
|
334
865
|
healthCheck(): Promise<Result<void, AgentError>>;
|
|
335
866
|
}
|
|
336
867
|
|
|
337
|
-
declare class ClaudeBackend implements AgentBackend {
|
|
338
|
-
readonly name = "claude";
|
|
339
|
-
private command;
|
|
340
|
-
constructor(command?: string);
|
|
341
|
-
startSession(params: SessionStartParams): Promise<Result<AgentSession, AgentError>>;
|
|
342
|
-
runTurn(session: AgentSession, params: TurnParams): AsyncGenerator<AgentEvent, TurnResult, void>;
|
|
343
|
-
stopSession(_session: AgentSession): Promise<Result<void, AgentError>>;
|
|
344
|
-
healthCheck(): Promise<Result<void, AgentError>>;
|
|
345
|
-
}
|
|
346
|
-
|
|
347
868
|
declare class PromptRenderer {
|
|
348
869
|
private engine;
|
|
349
870
|
constructor();
|
|
350
871
|
render(template: string, context: Record<string, unknown>): Promise<string>;
|
|
351
872
|
}
|
|
352
873
|
|
|
874
|
+
/**
|
|
875
|
+
* Result of a single maintenance task run.
|
|
876
|
+
*/
|
|
877
|
+
interface RunResult {
|
|
878
|
+
/** ID of the task that was run */
|
|
879
|
+
taskId: string;
|
|
880
|
+
/** ISO timestamp when the run started */
|
|
881
|
+
startedAt: string;
|
|
882
|
+
/** ISO timestamp when the run completed */
|
|
883
|
+
completedAt: string;
|
|
884
|
+
/** Outcome of the run */
|
|
885
|
+
status: 'success' | 'failure' | 'skipped' | 'no-issues';
|
|
886
|
+
/** Number of issues/findings detected */
|
|
887
|
+
findings: number;
|
|
888
|
+
/** Number of issues fixed */
|
|
889
|
+
fixed: number;
|
|
890
|
+
/** URL of the created/updated PR, or null if no PR was created */
|
|
891
|
+
prUrl: string | null;
|
|
892
|
+
/** Whether an existing PR was updated (vs newly created) */
|
|
893
|
+
prUpdated: boolean;
|
|
894
|
+
/** Error message if status is 'failure' */
|
|
895
|
+
error?: string;
|
|
896
|
+
}
|
|
897
|
+
/**
|
|
898
|
+
* Schedule entry for a single task, used in MaintenanceStatus.
|
|
899
|
+
*/
|
|
900
|
+
interface ScheduleEntry {
|
|
901
|
+
/** Task identifier */
|
|
902
|
+
taskId: string;
|
|
903
|
+
/** ISO timestamp of the next scheduled run */
|
|
904
|
+
nextRun: string;
|
|
905
|
+
/** Result of the most recent run, or null if never run */
|
|
906
|
+
lastRun: RunResult | null;
|
|
907
|
+
}
|
|
908
|
+
/**
|
|
909
|
+
* Overall maintenance module status, exposed via dashboard API.
|
|
910
|
+
*/
|
|
911
|
+
interface MaintenanceStatus {
|
|
912
|
+
/** Whether this orchestrator instance is the maintenance leader */
|
|
913
|
+
isLeader: boolean;
|
|
914
|
+
/** ISO timestamp of the last successful leader claim, or null */
|
|
915
|
+
lastLeaderClaim: string | null;
|
|
916
|
+
/** Schedule state for all enabled tasks */
|
|
917
|
+
schedule: ScheduleEntry[];
|
|
918
|
+
/** Currently executing task, or null if idle */
|
|
919
|
+
activeRun: {
|
|
920
|
+
taskId: string;
|
|
921
|
+
startedAt: string;
|
|
922
|
+
} | null;
|
|
923
|
+
/** History of completed runs (most recent first) */
|
|
924
|
+
history: RunResult[];
|
|
925
|
+
}
|
|
926
|
+
|
|
353
927
|
/**
|
|
354
928
|
* The central orchestrator that manages the lifecycle of coding agents.
|
|
355
929
|
*
|
|
@@ -370,7 +944,28 @@ declare class Orchestrator extends EventEmitter {
|
|
|
370
944
|
private promptTemplate;
|
|
371
945
|
private server?;
|
|
372
946
|
private interval?;
|
|
947
|
+
private heartbeatInterval?;
|
|
373
948
|
private logger;
|
|
949
|
+
private interactionQueue;
|
|
950
|
+
private localRunner;
|
|
951
|
+
private pipeline;
|
|
952
|
+
private analysisArchive;
|
|
953
|
+
private graphStore;
|
|
954
|
+
private claimManager;
|
|
955
|
+
private prDetector;
|
|
956
|
+
private maintenanceScheduler;
|
|
957
|
+
private maintenanceReporter;
|
|
958
|
+
private orchestratorIdPromise;
|
|
959
|
+
/** Project root directory, derived from workspace root. */
|
|
960
|
+
private get projectRoot();
|
|
961
|
+
private graphLoaded;
|
|
962
|
+
private enrichedSpecsByIssue;
|
|
963
|
+
/** Tracks recently-failed intelligence analysis to avoid re-requesting every tick */
|
|
964
|
+
private analysisFailureCache;
|
|
965
|
+
/** Guards against overlapping ticks when a tick takes longer than the polling interval */
|
|
966
|
+
private tickInProgress;
|
|
967
|
+
/** Current tick-phase activity visible to the dashboard */
|
|
968
|
+
private tickActivity;
|
|
374
969
|
/**
|
|
375
970
|
* Creates a new Orchestrator instance.
|
|
376
971
|
*
|
|
@@ -381,15 +976,71 @@ declare class Orchestrator extends EventEmitter {
|
|
|
381
976
|
constructor(config: WorkflowConfig, promptTemplate: string, overrides?: {
|
|
382
977
|
tracker?: IssueTrackerClient;
|
|
383
978
|
backend?: AgentBackend;
|
|
979
|
+
execFileFn?: ExecFileFn;
|
|
384
980
|
});
|
|
385
981
|
private createTracker;
|
|
386
982
|
private createBackend;
|
|
387
983
|
/**
|
|
388
|
-
*
|
|
984
|
+
* Creates a TaskRunner for the maintenance scheduler.
|
|
985
|
+
* Provides stub implementations for check/agent/command execution.
|
|
986
|
+
* Phase 4 (PRManager) and Phase 5 (Reporter) will enhance these.
|
|
987
|
+
*/
|
|
988
|
+
private createMaintenanceTaskRunner;
|
|
989
|
+
/**
|
|
990
|
+
* Initializes the maintenance subsystem: reporter, scheduler, and server route wiring.
|
|
991
|
+
* Extracted from start() to keep function length under threshold.
|
|
992
|
+
*/
|
|
993
|
+
private initMaintenance;
|
|
994
|
+
private createLocalBackend;
|
|
995
|
+
private createIntelligencePipeline;
|
|
996
|
+
/**
|
|
997
|
+
* Create the AnalysisProvider for the intelligence pipeline.
|
|
389
998
|
*
|
|
390
|
-
*
|
|
391
|
-
*
|
|
999
|
+
* Resolution order:
|
|
1000
|
+
* 1. Explicit `intelligence.provider` config (separate key/endpoint)
|
|
1001
|
+
* 2. Local backend config (agent.localBackend + localEndpoint/localModel)
|
|
1002
|
+
* 3. Primary agent backend config (agent.apiKey + agent.backend)
|
|
392
1003
|
*/
|
|
1004
|
+
private createAnalysisProvider;
|
|
1005
|
+
private createProviderFromExplicitConfig;
|
|
1006
|
+
private runPeslSimulations;
|
|
1007
|
+
private archiveAnalysisResults;
|
|
1008
|
+
/**
|
|
1009
|
+
* Auto-publish analysis results to the external tracker as structured comments.
|
|
1010
|
+
* Fires only when:
|
|
1011
|
+
* - A tracker is configured in harness.config.json (roadmap.tracker)
|
|
1012
|
+
* - GITHUB_TOKEN env var is available
|
|
1013
|
+
* - The record has a non-null externalId
|
|
1014
|
+
* - The record has not already been published (per the published index)
|
|
1015
|
+
*
|
|
1016
|
+
* Errors are non-fatal: a failed publish logs a warning but does not block
|
|
1017
|
+
* the orchestrator tick.
|
|
1018
|
+
*/
|
|
1019
|
+
private autoPublishAnalyses;
|
|
1020
|
+
private publishAnalysisForIssue;
|
|
1021
|
+
private runIntelligencePipeline;
|
|
1022
|
+
/**
|
|
1023
|
+
* Analyzes a single candidate issue through the intelligence pipeline.
|
|
1024
|
+
* Returns connection error state and whether the circuit breaker tripped.
|
|
1025
|
+
*/
|
|
1026
|
+
private analyzeCandidate;
|
|
1027
|
+
private evictExpiredFailures;
|
|
1028
|
+
private filterEligibleForAnalysis;
|
|
1029
|
+
private analyzeCandidates;
|
|
1030
|
+
/**
|
|
1031
|
+
* Lazily initializes the ClaimManager if it hasn't been created yet.
|
|
1032
|
+
* Called from both start() and asyncTick() to avoid duplicating the init block.
|
|
1033
|
+
*/
|
|
1034
|
+
private ensureClaimManager;
|
|
1035
|
+
/**
|
|
1036
|
+
* Loads the graph store and hydrates the enriched spec cache from the analysis
|
|
1037
|
+
* archive on the first tick. Subsequent calls are no-ops. All failures are
|
|
1038
|
+
* non-fatal — empty graph / empty cache are valid fallbacks.
|
|
1039
|
+
*/
|
|
1040
|
+
private loadPersistedData;
|
|
1041
|
+
private loadGraphStore;
|
|
1042
|
+
private hydrateSpecCache;
|
|
1043
|
+
asyncTick(): Promise<void>;
|
|
393
1044
|
tick(): Promise<void>;
|
|
394
1045
|
/**
|
|
395
1046
|
* Processes a side effect generated by the state machine.
|
|
@@ -397,6 +1048,53 @@ declare class Orchestrator extends EventEmitter {
|
|
|
397
1048
|
* @param effect - The effect to handle
|
|
398
1049
|
*/
|
|
399
1050
|
private handleEffect;
|
|
1051
|
+
/**
|
|
1052
|
+
* Guards workspace cleanup by checking whether the agent pushed a branch
|
|
1053
|
+
* that does not yet have a pull request. If so, the worktree is preserved
|
|
1054
|
+
* and an interaction is queued so a human can create the PR manually.
|
|
1055
|
+
*/
|
|
1056
|
+
private cleanWorkspaceWithGuard;
|
|
1057
|
+
/**
|
|
1058
|
+
* Delegates to PRDetector.branchHasPullRequest.
|
|
1059
|
+
* @see PRDetector#branchHasPullRequest
|
|
1060
|
+
*/
|
|
1061
|
+
private branchHasPullRequest;
|
|
1062
|
+
/**
|
|
1063
|
+
* Delegates to PRDetector.filterCandidatesWithOpenPRs.
|
|
1064
|
+
* @see PRDetector#filterCandidatesWithOpenPRs
|
|
1065
|
+
*/
|
|
1066
|
+
private filterCandidatesWithOpenPRs;
|
|
1067
|
+
/**
|
|
1068
|
+
* Scans candidate issues for stale claims from other orchestrators.
|
|
1069
|
+
* An issue is considered stale if:
|
|
1070
|
+
* - It is in an "in-progress" state
|
|
1071
|
+
* - It has an assignee that is NOT this orchestrator
|
|
1072
|
+
* - Its updatedAt timestamp exceeds the heartbeat TTL
|
|
1073
|
+
*
|
|
1074
|
+
* Stale claims are released so the issue becomes available on subsequent ticks.
|
|
1075
|
+
*/
|
|
1076
|
+
private releaseStaleClaims;
|
|
1077
|
+
/**
|
|
1078
|
+
* Handles an escalation effect by writing to the interaction queue and logging.
|
|
1079
|
+
*/
|
|
1080
|
+
private handleEscalation;
|
|
1081
|
+
/**
|
|
1082
|
+
* Handles a claim effect by calling claimAndVerify on the ClaimManager.
|
|
1083
|
+
* If claimed, proceeds to dispatch. If rejected, emits a claim_rejected
|
|
1084
|
+
* event to clean up the state machine.
|
|
1085
|
+
*/
|
|
1086
|
+
private handleClaimEffect;
|
|
1087
|
+
/**
|
|
1088
|
+
* Posts a structured comment on the GitHub issue when the orchestrator claims it.
|
|
1089
|
+
* Fire-and-forget: failures are logged but never block dispatch.
|
|
1090
|
+
*/
|
|
1091
|
+
private postClaimComment;
|
|
1092
|
+
/**
|
|
1093
|
+
* Posts a lifecycle event comment to the GitHub issue.
|
|
1094
|
+
* Supports: claimed, completed, released.
|
|
1095
|
+
* Fire-and-forget: failures are logged but never block the caller.
|
|
1096
|
+
*/
|
|
1097
|
+
private postLifecycleComment;
|
|
400
1098
|
/**
|
|
401
1099
|
* Dispatches a new agent to work on an issue.
|
|
402
1100
|
*
|
|
@@ -404,10 +1102,9 @@ declare class Orchestrator extends EventEmitter {
|
|
|
404
1102
|
* @param attempt - The retry attempt number
|
|
405
1103
|
*/
|
|
406
1104
|
private dispatchIssue;
|
|
407
|
-
/**
|
|
408
|
-
* Runs an agent session in a background task to avoid blocking the main loop.
|
|
409
|
-
*/
|
|
410
1105
|
private runAgentInBackgroundTask;
|
|
1106
|
+
private recordOutcomeIfPipelineEnabled;
|
|
1107
|
+
private handleCompletionSideEffects;
|
|
411
1108
|
/**
|
|
412
1109
|
* Informs the state machine that an agent worker has exited.
|
|
413
1110
|
*/
|
|
@@ -418,26 +1115,39 @@ declare class Orchestrator extends EventEmitter {
|
|
|
418
1115
|
* @param issueId - The ID of the issue to stop
|
|
419
1116
|
*/
|
|
420
1117
|
private stopIssue;
|
|
1118
|
+
/**
|
|
1119
|
+
* Dispatch a work item immediately, bypassing the normal tick → roadmap cycle.
|
|
1120
|
+
* Used by the dashboard's "Dispatch Now" action.
|
|
1121
|
+
*/
|
|
1122
|
+
dispatchAdHoc(issue: Issue): Promise<void>;
|
|
421
1123
|
/**
|
|
422
1124
|
* Starts the polling loop and the internal HTTP server.
|
|
1125
|
+
* Runs startup reconciliation to release orphaned claims before the first tick.
|
|
423
1126
|
*/
|
|
424
|
-
start(): void
|
|
1127
|
+
start(): Promise<void>;
|
|
425
1128
|
/**
|
|
426
1129
|
* Stops the orchestrator, clearing the polling interval and stopping the server.
|
|
427
1130
|
*/
|
|
428
1131
|
stop(): Promise<void>;
|
|
1132
|
+
/** Update tick activity and broadcast the change to connected clients. */
|
|
1133
|
+
private setTickActivity;
|
|
429
1134
|
/**
|
|
430
1135
|
* Returns a point-in-time snapshot of the orchestrator's internal state.
|
|
431
1136
|
*/
|
|
432
1137
|
getSnapshot(): Record<string, unknown>;
|
|
1138
|
+
/** Returns the maintenance scheduler status, or null if maintenance is not enabled. */
|
|
1139
|
+
getMaintenanceStatus(): MaintenanceStatus | null;
|
|
433
1140
|
}
|
|
434
1141
|
|
|
435
1142
|
/**
|
|
436
1143
|
* Launches the Ink TUI for the given Orchestrator instance.
|
|
437
1144
|
* Returns a function to wait for the TUI to exit.
|
|
1145
|
+
*
|
|
1146
|
+
* @deprecated The TUI is maintained as a fallback for headless/SSH environments.
|
|
1147
|
+
* The web dashboard (served at port 8080) is the primary monitoring interface.
|
|
438
1148
|
*/
|
|
439
1149
|
declare function launchTUI(orchestrator: Orchestrator): {
|
|
440
1150
|
waitUntilExit: () => Promise<void>;
|
|
441
1151
|
};
|
|
442
1152
|
|
|
443
|
-
export { type AgentUpdateEvent, type ApplyEventResult,
|
|
1153
|
+
export { type AgentUpdateEvent, AnalysisArchive, type AnalysisRecord, type ApplyEventResult, type ArtifactPresence, ClaimManager, type ClaimManagerConfig, type CleanWorkspaceEffect, type DispatchEffect, type EmitLogEffect, type EscalateEffect, type ExecFileFn, InteractionQueue, type LinearGraphQLExtension, LinearGraphQLStub, type LiveSession, MockBackend, ORCHESTRATOR_IDENTITY_FILE, Orchestrator, type OrchestratorEvent, type OrchestratorState, PRDetector, type PRDetectorLogger, type PendingInteraction, PromptRenderer, type PublishedIndex, type RateLimitSnapshot as RateLimitComputeSnapshot, type RateLimitConfig, type RateLimitSnapshot$1 as RateLimitSnapshot, type ReleaseClaimEffect, type RetryEntry, type RetryFiredEvent, RoadmapTrackerAdapter, type RunAttemptPhase, type RunningEntry, type ScheduleRetryEffect, type SideEffect, type StallDetectedEvent, type StopEffect, type TickEvent, type TokenTotals, type TriageConfig, type TriageDecision, type TriageSignals, type TriageSkill, type UpdateTokensEffect, type WorkerExitEvent, WorkflowLoader, WorkspaceHooks, WorkspaceManager, applyEvent, artifactPresenceFromIssue, calculateRetryDelay, canDispatch, computeRateLimitDelay, createEmptyState, detectScopeTier, extractTitlePrefix, getAvailableSlots, getDefaultConfig, getPerStateCount, isEligible, launchTUI, loadPublishedIndex, reconcile, renderAnalysisComment, resolveEscalationConfig, resolveOrchestratorId, routeIssue, savePublishedIndex, selectCandidates, sortCandidates, triageIssue, validateWorkflowConfig };
|