Skip to content

Commit d689e5d

Browse files
masonwyatt23claude
andcommitted
feat: wire genome into autopilot and coordinator
Autopilot integration: - Loads genome manifest at start - Retrieves task-relevant genome sections for sub-agent context - Proposes genome updates after completing work items - Evaluates genome fitness every 10 ticks - Auto-advances generation when milestone completes - Extracts next milestone from backlog Coordinator integration: - Retrieves genome sections for batch task descriptions - Injects genome context into all sub-agent system prompts - Proposes knowledge updates after coordinator completes All integration is gracefully optional — genome not required. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent adc285c commit d689e5d

2 files changed

Lines changed: 171 additions & 2 deletions

File tree

src/agent/autopilot-loop.ts

Lines changed: 126 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import { sleep } from "./error-handler.ts";
2727
import type { ProviderRouter } from "../providers/router.ts";
2828
import type { ToolRegistry } from "../tools/registry.ts";
2929
import type { ToolContext } from "../tools/types.ts";
30+
import type { GenomeManifest } from "../genome/manifest.ts";
3031

3132
/* ── Vision interface (inline until vision.ts exists) ──────────── */
3233

@@ -176,6 +177,7 @@ export class AutopilotLoop {
176177
private config!: AutopilotConfig;
177178
private lastScanHash = "";
178179
private focusState: FocusState = "unknown";
180+
private genome: GenomeManifest | null = null;
179181

180182
/* ── Public API ────────────────────────────────────────────── */
181183

@@ -193,6 +195,14 @@ export class AutopilotLoop {
193195
this.queue = new WorkQueue(config.toolContext.cwd);
194196
await this.queue.load();
195197

198+
// Load genome if available — enhances agent context and enables auto-evolution
199+
try {
200+
const { loadManifest } = await import("../genome/manifest.ts");
201+
this.genome = await loadManifest(config.toolContext.cwd);
202+
} catch {
203+
this.genome = null;
204+
}
205+
196206
config.onProgress?.({ type: "started", goal: vision.goal });
197207

198208
await this.runLoop();
@@ -263,13 +273,22 @@ export class AutopilotLoop {
263273
break;
264274
}
265275

266-
// Step 3: Re-assess vision (every 5 ticks)
276+
// Step 3: Re-assess vision (every 5 ticks) + genome evolution
267277
if (this.tickNumber % 5 === 0) {
268278
const complete = await this.assessVision();
269279
if (complete) {
280+
// If genome exists, end generation and advance before wrapping up
281+
if (this.genome) {
282+
await this.advanceGeneration();
283+
}
270284
this.wrapUpRequested = true;
271285
continue; // Next iteration will handle wrap-up
272286
}
287+
288+
// Evaluate genome fitness periodically (every 10 ticks)
289+
if (this.genome && this.tickNumber % 10 === 0) {
290+
await this.evaluateGenomeFitness();
291+
}
273292
}
274293

275294
// Step 4: Scan for new work (every 3 ticks)
@@ -639,10 +658,27 @@ Reply with JSON only: {"focusAreas": ["..."], "assessment": "...", "isComplete":
639658
itemDescription: item.title,
640659
});
641660

661+
// Build context with genome sections if available
662+
let genomeContext = "";
663+
if (this.genome) {
664+
try {
665+
const { retrieveSections, formatGenomeForPrompt } = await import("../genome/retriever.ts");
666+
const sections = await retrieveSections(
667+
this.config.toolContext.cwd,
668+
`${item.title} ${item.description}`,
669+
4000,
670+
);
671+
genomeContext = formatGenomeForPrompt(sections);
672+
} catch {
673+
// Genome retrieval failed — continue without
674+
}
675+
}
676+
642677
const contextPrompt = [
643678
`## Vision`,
644679
`Goal: ${this.vision.goal}`,
645680
`Focus areas: ${this.vision.focusAreas.join(", ") || "none"}`,
681+
genomeContext ? `\n${genomeContext}` : "",
646682
``,
647683
`## Task`,
648684
`${item.title}: ${item.description}`,
@@ -791,6 +827,23 @@ Reply with JSON only: {"focusAreas": ["..."], "assessment": "...", "isComplete":
791827
if (this.vision.progress.length > 50) {
792828
this.vision.progress = this.vision.progress.slice(-50);
793829
}
830+
831+
// Propose genome progress update if genome exists
832+
if (this.genome) {
833+
try {
834+
const { proposeUpdate } = await import("../genome/scribe.ts");
835+
await proposeUpdate(this.config.toolContext.cwd, {
836+
agentId: "autopilot",
837+
section: "knowledge/discoveries.md",
838+
operation: "append",
839+
content: `- [${new Date().toISOString().split("T")[0]}] Tick ${this.tickNumber}: ${this.itemsCompleted} completed, ${this.itemsFailed} failed`,
840+
rationale: "Autopilot progress update",
841+
generation: this.genome.generation.number,
842+
});
843+
} catch {
844+
// Genome proposal failed — not critical
845+
}
846+
}
794847
}
795848

796849
// Notify user if terminal unfocused
@@ -805,6 +858,64 @@ Reply with JSON only: {"focusAreas": ["..."], "assessment": "...", "isComplete":
805858
}
806859
}
807860

861+
/* ── Genome integration ──────────────────────────────────────── */
862+
863+
/**
864+
* Evaluate genome fitness and consolidate pending proposals.
865+
*/
866+
private async evaluateGenomeFitness(): Promise<void> {
867+
try {
868+
const { evaluateGeneration, formatGenerationReport } = await import("../genome/generations.ts");
869+
const { consolidateProposals } = await import("../genome/scribe.ts");
870+
const cwd = this.config.toolContext.cwd;
871+
872+
// First consolidate any pending proposals
873+
await consolidateProposals(cwd, this.config.router);
874+
875+
// Then evaluate fitness
876+
const report = await evaluateGeneration(cwd, this.config.router);
877+
878+
this.config.onProgress?.({
879+
type: "notification",
880+
title: "Genome Fitness",
881+
body: `Gen ${report.generation}: ${(report.fitness.milestoneProgress * 100).toFixed(0)}% milestone, ${report.mutations} mutations`,
882+
});
883+
} catch {
884+
// Genome evaluation failed — not critical
885+
}
886+
}
887+
888+
/**
889+
* End the current generation and start the next one.
890+
*/
891+
private async advanceGeneration(): Promise<void> {
892+
try {
893+
const { endGeneration, startGeneration } = await import("../genome/generations.ts");
894+
const { loadManifest } = await import("../genome/manifest.ts");
895+
const { readSection } = await import("../genome/manifest.ts");
896+
const cwd = this.config.toolContext.cwd;
897+
898+
await endGeneration(cwd);
899+
900+
// Check backlog for next milestone
901+
const backlog = await readSection(cwd, "milestones/backlog.md");
902+
const nextMilestone = backlog
903+
? extractFirstMilestone(backlog)
904+
: "Continue development";
905+
906+
const genNum = await startGeneration(cwd, nextMilestone);
907+
this.genome = await loadManifest(cwd);
908+
909+
this.config.onProgress?.({
910+
type: "notification",
911+
title: "Generation Advanced",
912+
body: `Generation ${genNum}: ${nextMilestone}`,
913+
});
914+
} catch {
915+
// Generation advance failed — not critical
916+
}
917+
}
918+
808919
/* ── Step 10: Abortable sleep ──────────────────────────────── */
809920

810921
private async abortableSleep(ms: number): Promise<void> {
@@ -833,3 +944,17 @@ export function createAutopilotLoop(): AutopilotLoop {
833944
_instance = new AutopilotLoop();
834945
return _instance;
835946
}
947+
948+
/**
949+
* Extract the first milestone from a backlog markdown file.
950+
* Looks for the first ## heading or first bullet point.
951+
*/
952+
function extractFirstMilestone(backlog: string): string {
953+
for (const line of backlog.split("\n")) {
954+
const heading = line.match(/^##\s+(.+)/);
955+
if (heading) return heading[1]!.trim();
956+
const bullet = line.match(/^[-*]\s+(.+)/);
957+
if (bullet) return bullet[1]!.trim();
958+
}
959+
return "Continue development";
960+
}

src/agent/coordinator.ts

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -373,6 +373,20 @@ async function dispatchTasks(
373373
for (let batch = 0; batch < wave.length; batch += maxParallel) {
374374
const batchTasks = wave.slice(batch, batch + maxParallel);
375375

376+
// Retrieve genome context for task-relevant section injection
377+
let genomeContext = "";
378+
try {
379+
const { genomeExists } = await import("../genome/manifest.ts");
380+
if (genomeExists(config.toolContext.cwd)) {
381+
const { retrieveSections, formatGenomeForPrompt } = await import("../genome/retriever.ts");
382+
const taskDescriptions = batchTasks.map((t) => t.description).join(" ");
383+
const sections = await retrieveSections(config.toolContext.cwd, taskDescriptions, 3000);
384+
genomeContext = formatGenomeForPrompt(sections);
385+
}
386+
} catch {
387+
// Genome not available — continue without
388+
}
389+
376390
const agentConfigs: SubAgentConfig[] = batchTasks.map((task) => {
377391
const teammate = team ? pickTeammateForTask(team, task.role) : null;
378392
const agentName = teammate?.name ?? `${task.role}-${task.id}`;
@@ -386,10 +400,13 @@ async function dispatchTasks(
386400
agentName,
387401
});
388402

403+
const promptParts = [config.systemPrompt, agentPrompt];
404+
if (genomeContext) promptParts.push(genomeContext);
405+
389406
return {
390407
name: agentName,
391408
prompt: task.description,
392-
systemPrompt: config.systemPrompt + "\n\n" + agentPrompt,
409+
systemPrompt: promptParts.join("\n\n"),
393410
router: config.router,
394411
toolRegistry: config.toolRegistry,
395412
toolContext: config.toolContext,
@@ -501,6 +518,33 @@ export async function coordinate(
501518

502519
config.onProgress?.({ type: "complete", summary });
503520

521+
// Propose genome update with coordinator findings
522+
try {
523+
const { genomeExists, loadManifest } = await import("../genome/manifest.ts");
524+
if (genomeExists(config.toolContext.cwd)) {
525+
const { proposeUpdate } = await import("../genome/scribe.ts");
526+
const manifest = await loadManifest(config.toolContext.cwd);
527+
if (manifest) {
528+
const findings = taskResults
529+
.filter((t) => t.success)
530+
.map((t) => `- ${t.summary?.slice(0, 100) ?? "Task completed"}`)
531+
.join("\n");
532+
if (findings) {
533+
await proposeUpdate(config.toolContext.cwd, {
534+
agentId: "coordinator",
535+
section: "knowledge/discoveries.md",
536+
operation: "append",
537+
content: `## Coordinator run: ${goal.slice(0, 80)}\n${findings}`,
538+
rationale: `Coordinator completed ${successCount}/${taskResults.length} tasks for: ${goal.slice(0, 100)}`,
539+
generation: manifest.generation.number,
540+
});
541+
}
542+
}
543+
}
544+
} catch {
545+
// Genome proposal failed — not critical
546+
}
547+
504548
return { tasks: taskResults, verificationPassed, summary };
505549
}
506550

0 commit comments

Comments
 (0)