@bluecopa/harness 0.1.0-snapshot.21 → 0.1.0-snapshot.22
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/agent-runner.ts +12 -1
- package/src/arc/arc-loop.ts +44 -0
package/package.json
CHANGED
package/src/arc/agent-runner.ts
CHANGED
|
@@ -366,6 +366,8 @@ export interface CreateProcessConfig {
|
|
|
366
366
|
parentSignal: AbortSignal;
|
|
367
367
|
/** Custom system prompt for this process (overrides PROCESS_SYSTEM_PROMPT). */
|
|
368
368
|
processSystemPrompt?: string;
|
|
369
|
+
/** Async skill instructions to prepend to system prompt (resolved during process startup). */
|
|
370
|
+
skillPromptPromise?: Promise<string | null>;
|
|
369
371
|
|
|
370
372
|
// Runtime extras
|
|
371
373
|
hookRunner?: HookRunner;
|
|
@@ -421,12 +423,21 @@ export function createProcess(
|
|
|
421
423
|
process.status = 'running';
|
|
422
424
|
const seed = await seedPromise;
|
|
423
425
|
|
|
426
|
+
// Build system prompt: base + optional skill instructions
|
|
427
|
+
let systemPrompt = config.processSystemPrompt ?? PROCESS_SYSTEM_PROMPT;
|
|
428
|
+
if (config.skillPromptPromise) {
|
|
429
|
+
const skillInstructions = await config.skillPromptPromise;
|
|
430
|
+
if (skillInstructions) {
|
|
431
|
+
systemPrompt += '\n\n## Skill Instructions\n' + skillInstructions;
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
|
|
424
435
|
const result = await Promise.race([
|
|
425
436
|
runner.run({
|
|
426
437
|
model,
|
|
427
438
|
prompt: request.action,
|
|
428
439
|
tools: config.processTools,
|
|
429
|
-
systemPrompt
|
|
440
|
+
systemPrompt,
|
|
430
441
|
toolProvider: config.toolProvider,
|
|
431
442
|
maxSteps,
|
|
432
443
|
signal: ac.signal,
|
package/src/arc/arc-loop.ts
CHANGED
|
@@ -27,6 +27,9 @@ import { createProcess, firstEvent } from './agent-runner';
|
|
|
27
27
|
import { EpisodeCompressor } from './episode-compressor';
|
|
28
28
|
import { runConsolidation } from './consolidation';
|
|
29
29
|
import { pickDefined } from './utils';
|
|
30
|
+
import { SkillRouter } from '../skills/skill-router';
|
|
31
|
+
import { loadSkillFromFile } from '../skills/skill-loader';
|
|
32
|
+
import type { SkillSummary } from '../skills/skill-types';
|
|
30
33
|
|
|
31
34
|
// ── Default orchestrator prompt ──
|
|
32
35
|
|
|
@@ -75,6 +78,9 @@ export class ArcLoop {
|
|
|
75
78
|
private readonly traceWriter: ((event: TraceEvent) => void) | undefined;
|
|
76
79
|
private readonly tracedRunning = new Set<string>();
|
|
77
80
|
private readonly processListeners: Promise<void>[] = [];
|
|
81
|
+
private readonly skillRouter: SkillRouter | undefined;
|
|
82
|
+
private skillSummaries: SkillSummary[] | null = null;
|
|
83
|
+
private skillSummariesPromise: Promise<SkillSummary[]> | null = null;
|
|
78
84
|
|
|
79
85
|
constructor(config: ArcLoopConfig) {
|
|
80
86
|
this.config = config;
|
|
@@ -114,6 +120,15 @@ export class ArcLoop {
|
|
|
114
120
|
|
|
115
121
|
this.resilience = config.resilience;
|
|
116
122
|
this.traceWriter = (config as ArcLoopConfig & { traceWriter?: (event: TraceEvent) => void }).traceWriter;
|
|
123
|
+
|
|
124
|
+
if (config.skillIndexPath) {
|
|
125
|
+
this.skillRouter = new SkillRouter();
|
|
126
|
+
// Lazy-load skill summaries on first dispatch
|
|
127
|
+
this.skillSummariesPromise = import('node:fs/promises')
|
|
128
|
+
.then(fs => fs.readFile(config.skillIndexPath!, 'utf-8'))
|
|
129
|
+
.then(raw => JSON.parse(raw) as SkillSummary[])
|
|
130
|
+
.catch(() => []);
|
|
131
|
+
}
|
|
117
132
|
}
|
|
118
133
|
|
|
119
134
|
private trace(kind: TraceEvent['kind']): void {
|
|
@@ -457,6 +472,12 @@ export class ArcLoop {
|
|
|
457
472
|
this.modelMap,
|
|
458
473
|
this.modelMap.medium,
|
|
459
474
|
);
|
|
475
|
+
|
|
476
|
+
// Resolve skill instructions only when skills are configured
|
|
477
|
+
const skillPromptPromise = this.skillRouter
|
|
478
|
+
? this.resolveSkillPrompt(request.action)
|
|
479
|
+
: undefined;
|
|
480
|
+
|
|
460
481
|
const proc = createProcess(request, {
|
|
461
482
|
toolProvider: this.config.toolProvider,
|
|
462
483
|
episodeStore: this.config.episodeStore,
|
|
@@ -469,6 +490,7 @@ export class ArcLoop {
|
|
|
469
490
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
470
491
|
processTools: (profile?.tools ?? this.config.processTools ?? builtinTools) as any,
|
|
471
492
|
processSystemPrompt: profile?.systemPrompt ?? this.config.processSystemPrompt,
|
|
493
|
+
skillPromptPromise,
|
|
472
494
|
parentSignal,
|
|
473
495
|
...pickDefined(this.config, [
|
|
474
496
|
'hookRunner',
|
|
@@ -483,6 +505,28 @@ export class ArcLoop {
|
|
|
483
505
|
return proc;
|
|
484
506
|
}
|
|
485
507
|
|
|
508
|
+
/** Resolve skill instructions for a process action. Returns null if no skill matched. */
|
|
509
|
+
private async resolveSkillPrompt(action: string): Promise<string | null> {
|
|
510
|
+
if (!this.skillRouter || !this.skillSummariesPromise) return null;
|
|
511
|
+
|
|
512
|
+
// Ensure summaries are loaded
|
|
513
|
+
if (!this.skillSummaries) {
|
|
514
|
+
this.skillSummaries = await this.skillSummariesPromise;
|
|
515
|
+
}
|
|
516
|
+
if (this.skillSummaries.length === 0) return null;
|
|
517
|
+
|
|
518
|
+
// Fast match only (keyword + alias, no LLM call)
|
|
519
|
+
const matched = await this.skillRouter.selectSkill(action, this.skillSummaries);
|
|
520
|
+
if (!matched) return null;
|
|
521
|
+
|
|
522
|
+
try {
|
|
523
|
+
const skill = await loadSkillFromFile(matched.path);
|
|
524
|
+
return skill.instructions || null;
|
|
525
|
+
} catch {
|
|
526
|
+
return null;
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
|
|
486
530
|
private traceProcessRunning(procId: string): void {
|
|
487
531
|
if (!this.tracedRunning.has(procId)) {
|
|
488
532
|
this.tracedRunning.add(procId);
|