Refactor pipeline policies, MCP registry, and unified config/runtime

This commit is contained in:
2026-02-23 13:56:45 -05:00
parent 889087daa1
commit 9b4216dda9
22 changed files with 1441 additions and 587 deletions

View File

@@ -0,0 +1,126 @@
import {
DomainEventBus,
type DomainEvent,
type DomainEventType,
} from "./domain-events.js";
import type { PipelineNode } from "./manifest.js";
import { type ProjectContextPatch, type FileSystemProjectContextStore } from "./project-context.js";
import { PersonaRegistry } from "./persona-registry.js";
import {
FileSystemStateContextManager,
type SessionHistoryEntry,
} from "./state-context.js";
import type { ActorExecutionResult, ActorResultStatus } from "./pipeline.js";
export type PipelineNodeAttemptObservedEvent = {
sessionId: string;
node: PipelineNode;
attempt: number;
result: ActorExecutionResult;
domainEvents: DomainEvent[];
};
function toBehaviorEvent(status: ActorResultStatus): "onTaskComplete" | "onValidationFail" | undefined {
if (status === "success") {
return "onTaskComplete";
}
if (status === "validation_fail") {
return "onValidationFail";
}
return undefined;
}
export interface PipelineLifecycleObserver {
onNodeAttempt(event: PipelineNodeAttemptObservedEvent): Promise<void>;
}
export class PersistenceLifecycleObserver implements PipelineLifecycleObserver {
constructor(
private readonly input: {
personaRegistry: PersonaRegistry;
stateManager: FileSystemStateContextManager;
projectContextStore: FileSystemProjectContextStore;
domainEventBus?: DomainEventBus;
},
) {}
async onNodeAttempt(event: PipelineNodeAttemptObservedEvent): Promise<void> {
const behaviorEvent = toBehaviorEvent(event.result.status);
const behaviorPatch = behaviorEvent
? await this.input.personaRegistry.emitBehaviorEvent({
personaId: event.node.personaId,
event: behaviorEvent,
sessionId: event.sessionId,
nodeId: event.node.id,
payload: event.result.payload ?? {},
})
: {};
const legacyHistoryEvent: SessionHistoryEntry = {
nodeId: event.node.id,
event: event.result.status,
timestamp: new Date().toISOString(),
...(event.result.payload ? { data: event.result.payload } : {}),
};
const domainHistoryEvents: SessionHistoryEntry[] = event.domainEvents.map((domainEvent) => ({
nodeId: event.node.id,
event: domainEvent.type,
timestamp: domainEvent.timestamp,
data: {
source: domainEvent.source,
attempt: domainEvent.attempt,
...(domainEvent.payload.summary ? { summary: domainEvent.payload.summary } : {}),
...(domainEvent.payload.errorCode ? { errorCode: domainEvent.payload.errorCode } : {}),
...(domainEvent.payload.artifactPointer
? { artifactPointer: domainEvent.payload.artifactPointer }
: {}),
...(domainEvent.payload.details ? { details: domainEvent.payload.details } : {}),
},
}));
await this.input.stateManager.patchState(event.sessionId, {
...(event.result.stateFlags ? { flags: event.result.stateFlags } : {}),
metadata: {
...(event.result.stateMetadata ?? {}),
...behaviorPatch,
},
historyEvent: legacyHistoryEvent,
historyEvents: domainHistoryEvents,
});
const domainEventBus = this.input.domainEventBus;
if (domainEventBus) {
for (const domainEvent of event.domainEvents) {
await domainEventBus.publish(domainEvent);
}
}
const patch: ProjectContextPatch = {
...(event.result.projectContextPatch ?? {}),
artifactPointers: {
[`sessions/${event.sessionId}/last_completed_node`]: event.node.id,
[`sessions/${event.sessionId}/last_attempt`]: String(event.attempt),
...(event.result.projectContextPatch?.artifactPointers ?? {}),
},
};
await this.input.projectContextStore.patchState(patch);
}
}
export class DomainEventCollector {
private readonly events: DomainEvent[] = [];
record(events: DomainEvent[]): void {
this.events.push(...events);
}
toEventTypes(events: DomainEvent[]): DomainEventType[] {
return events.map((event) => event.type);
}
getAll(): DomainEvent[] {
return [...this.events];
}
}