Refactor pipeline policies, MCP registry, and unified config/runtime
This commit is contained in:
126
src/agents/lifecycle-observer.ts
Normal file
126
src/agents/lifecycle-observer.ts
Normal 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];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user