- Dark Provenance whitepaper: complete sections 2-6 (What Is Dark Provenance, The Speckl Approach, Semantic Preservation & SA-11, Implications, Call to Action) - README: public launch-ready with architecture, roadmap, getting started - SpeckDL SPEC.md: v0.1 language specification with EBNF grammar, type system, compilation pipeline, 5 example Specks - Reference agent: TypeScript scaffold with Orchestrator, TeamLead, SMEs (VerificationSME, CodegenSME), TillDone condition tracking, ProvenanceGraph, and CLI with example Specks
122 lines
No EOL
3.2 KiB
TypeScript
122 lines
No EOL
3.2 KiB
TypeScript
/**
|
|
* TeamLead — Manages a group of SMEs for a specific domain.
|
|
*
|
|
* Receives tasks from the Orchestrator, decomposes them,
|
|
* assigns to SMEs, and aggregates results.
|
|
*/
|
|
|
|
import type {
|
|
AgentId,
|
|
Task,
|
|
TaskMessage,
|
|
TaskResult,
|
|
TaskType,
|
|
SpeckDefinition,
|
|
ProvenanceEdge,
|
|
} from "./types.js";
|
|
import { SME } from "./sme.js";
|
|
import { TillDoneTracker } from "./till-done.js";
|
|
import { ProvenanceGraphImpl } from "./provenance.js";
|
|
|
|
export class TeamLead {
|
|
readonly id: AgentId;
|
|
private smes: SME[] = [];
|
|
protected provenance: ProvenanceGraphImpl;
|
|
protected tillDone: TillDoneTracker;
|
|
|
|
constructor(name: string, specialization: string) {
|
|
this.id = { type: "team_lead", name, specialization };
|
|
this.provenance = new ProvenanceGraphImpl();
|
|
this.tillDone = new TillDoneTracker();
|
|
}
|
|
|
|
/** Register an SME under this team lead. */
|
|
registerSME(sme: SME): void {
|
|
this.smes.push(sme);
|
|
}
|
|
|
|
/** Get the task types this team can handle via its SMEs. */
|
|
get capabilities(): TaskType[] {
|
|
return [...new Set(this.smes.flatMap((sme) => sme.capabilities))];
|
|
}
|
|
|
|
/** Can this team handle the given task? */
|
|
canHandle(task: Task): boolean {
|
|
return this.smes.some((sme) => sme.canHandle(task));
|
|
}
|
|
|
|
/** Handle a task assignment from the Orchestrator. */
|
|
async handleAssignment(
|
|
message: TaskMessage,
|
|
speck?: SpeckDefinition
|
|
): Promise<TaskResult> {
|
|
const task = message.payload.task;
|
|
|
|
// Find the right SME
|
|
const sme = this.smes.find((s) => s.canHandle(task));
|
|
if (!sme) {
|
|
return {
|
|
taskId: task.id,
|
|
status: "failure",
|
|
output: { error: `No SME available for task type ${task.taskType}` },
|
|
provenance: [],
|
|
completedAt: new Date().toISOString(),
|
|
};
|
|
}
|
|
|
|
// Register TillDone condition
|
|
this.tillDone.register(
|
|
task.id,
|
|
`${sme.id.name} completes ${task.taskType}`,
|
|
(result) => result.status === "success"
|
|
);
|
|
|
|
// Execute
|
|
const result = await sme.execute(task, speck);
|
|
|
|
// Evaluate completion
|
|
this.tillDone.evaluate(task.id, result);
|
|
|
|
// Merge provenance
|
|
for (const edge of result.provenance) {
|
|
this.provenance.addEdge(edge);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/** Get all SMEs in this team. */
|
|
getTeam(): SME[] {
|
|
return [...this.smes];
|
|
}
|
|
|
|
/** Check if this team is at capacity (queue depth > threshold). */
|
|
isAtCapacity(threshold: number = 5): boolean {
|
|
return this.pendingCount() > threshold;
|
|
}
|
|
|
|
/** Number of pending tasks (not yet done). */
|
|
private pendingCount(): number {
|
|
return this.tillDone.pending().length;
|
|
}
|
|
}
|
|
|
|
// ─── Pre-built Team Lead configurations ──────────────────────
|
|
|
|
import { VerificationSME } from "./smes/verification-sme.js";
|
|
import { CodegenSME } from "./smes/codegen-sme.js";
|
|
|
|
export class VerificationTeamLead extends TeamLead {
|
|
constructor() {
|
|
super("verification-lead", "verification");
|
|
this.registerSME(new VerificationSME());
|
|
}
|
|
}
|
|
|
|
export class CodegenTeamLead extends TeamLead {
|
|
constructor() {
|
|
super("codegen-lead", "codegen");
|
|
this.registerSME(new CodegenSME("typescript"));
|
|
this.registerSME(new CodegenSME("rust"));
|
|
}
|
|
} |