@bluecopa/harness 0.1.0-snapshot.24 → 0.1.0-snapshot.26
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/package.json +1 -1
- package/src/arc/arc-loop.ts +32 -4
package/package.json
CHANGED
package/src/arc/arc-loop.ts
CHANGED
|
@@ -77,6 +77,7 @@ export class ArcLoop {
|
|
|
77
77
|
private readonly resilience: ResiliencePolicy | undefined;
|
|
78
78
|
private readonly traceWriter: ((event: TraceEvent) => void) | undefined;
|
|
79
79
|
private readonly tracedRunning = new Set<string>();
|
|
80
|
+
private readonly reportedCompletions = new Set<string>();
|
|
80
81
|
private readonly processListeners: Promise<void>[] = [];
|
|
81
82
|
private readonly skillRouter: SkillRouter | undefined;
|
|
82
83
|
private skillSummaries: SkillSummary[] | null = null;
|
|
@@ -421,9 +422,10 @@ export class ArcLoop {
|
|
|
421
422
|
this.trace({ type: 'turn_end', turn });
|
|
422
423
|
yield { type: 'turn_end', turn };
|
|
423
424
|
|
|
424
|
-
// Wait for
|
|
425
|
-
//
|
|
426
|
-
//
|
|
425
|
+
// Wait for all processes dispatched this turn to reach a terminal state.
|
|
426
|
+
// When the orchestrator dispatches N parallel threads, there's no useful
|
|
427
|
+
// work it can do with partial results — giving it a turn early just wastes
|
|
428
|
+
// an LLM call on empty Check loops.
|
|
427
429
|
const active = [...this.processes.values()].filter(
|
|
428
430
|
p => p.status === 'running' || p.status === 'pending',
|
|
429
431
|
);
|
|
@@ -431,7 +433,7 @@ export class ArcLoop {
|
|
|
431
433
|
const deadline = Date.now() + (this.config.processTimeout ?? 120_000);
|
|
432
434
|
await new Promise<void>(resolve => {
|
|
433
435
|
const check = () => {
|
|
434
|
-
if (active.
|
|
436
|
+
if (active.every(p => p.status !== 'running' && p.status !== 'pending') || Date.now() > deadline) {
|
|
435
437
|
resolve();
|
|
436
438
|
} else {
|
|
437
439
|
setTimeout(check, 10);
|
|
@@ -440,6 +442,32 @@ export class ArcLoop {
|
|
|
440
442
|
check();
|
|
441
443
|
});
|
|
442
444
|
}
|
|
445
|
+
|
|
446
|
+
// Inject process completion messages into the orchestrator's conversation
|
|
447
|
+
// so it sees episodeIds and can pass them as contextEpisodeIds.
|
|
448
|
+
const completionMessages: AgentMessage[] = [];
|
|
449
|
+
for (const [id, proc] of this.processes) {
|
|
450
|
+
if (proc.result && !this.reportedCompletions.has(id)) {
|
|
451
|
+
this.reportedCompletions.add(id);
|
|
452
|
+
const ep = proc.result.episode;
|
|
453
|
+
const status = proc.result.success ? 'success' : `failed: ${proc.result.error ?? 'unknown'}`;
|
|
454
|
+
completionMessages.push({
|
|
455
|
+
role: 'user',
|
|
456
|
+
content: `[Thread ${id} completed] (episodeId: ${ep.id}) — ${status}\n${ep.summary}`,
|
|
457
|
+
});
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
if (completionMessages.length > 0) {
|
|
461
|
+
this.ctx.recordTurn({
|
|
462
|
+
role: 'process_result',
|
|
463
|
+
messages: completionMessages,
|
|
464
|
+
tokenEstimate: completionMessages.reduce((s, m) => {
|
|
465
|
+
const text = typeof m.content === 'string' ? m.content : getTextContent(m.content);
|
|
466
|
+
return s + estimateTokens(text);
|
|
467
|
+
}, 0),
|
|
468
|
+
timestamp: Date.now(),
|
|
469
|
+
});
|
|
470
|
+
}
|
|
443
471
|
}
|
|
444
472
|
|
|
445
473
|
await this.waitForProcessListeners();
|