Files
ai_ops/src/agents/domain-events.ts

113 lines
2.9 KiB
TypeScript

import { randomUUID } from "node:crypto";
import type { JsonObject } from "./types.js";
export type PlanningDomainEventType = "requirements_defined" | "tasks_planned";
export type ExecutionDomainEventType = "code_committed" | "task_blocked";
export type ValidationDomainEventType = "validation_passed" | "validation_failed";
export type IntegrationDomainEventType = "branch_merged";
export type DomainEventType =
| PlanningDomainEventType
| ExecutionDomainEventType
| ValidationDomainEventType
| IntegrationDomainEventType;
export type DomainEventSource = "pipeline" | "actor";
export type DomainEventPayload = {
summary?: string;
details?: JsonObject;
errorCode?: string;
artifactPointer?: string;
};
export type DomainEvent<TType extends DomainEventType = DomainEventType> = {
id: string;
type: TType;
source: DomainEventSource;
sessionId: string;
nodeId: string;
attempt: number;
timestamp: string;
payload: DomainEventPayload;
};
export type DomainEventEmission<TType extends DomainEventType = DomainEventType> = {
type: TType;
payload?: DomainEventPayload;
};
export type DomainEventHandler<TType extends DomainEventType = DomainEventType> = (
event: DomainEvent<TType>,
) => Promise<void> | void;
const DOMAIN_EVENT_TYPES = new Set<DomainEventType>([
"requirements_defined",
"tasks_planned",
"code_committed",
"task_blocked",
"validation_passed",
"validation_failed",
"branch_merged",
]);
export function isDomainEventType(value: string): value is DomainEventType {
return DOMAIN_EVENT_TYPES.has(value as DomainEventType);
}
export function createDomainEvent(input: {
type: DomainEventType;
source: DomainEventSource;
sessionId: string;
nodeId: string;
attempt: number;
payload?: DomainEventPayload;
}): DomainEvent {
return {
id: randomUUID(),
type: input.type,
source: input.source,
sessionId: input.sessionId,
nodeId: input.nodeId,
attempt: input.attempt,
timestamp: new Date().toISOString(),
payload: input.payload ?? {},
};
}
export class DomainEventBus {
private readonly handlers = new Map<DomainEventType, Set<DomainEventHandler>>();
subscribe<TType extends DomainEventType>(
type: TType,
handler: DomainEventHandler<TType>,
): () => void {
const typedHandler = handler as DomainEventHandler;
const set = this.handlers.get(type);
if (set) {
set.add(typedHandler);
} else {
this.handlers.set(type, new Set([typedHandler]));
}
return () => {
const current = this.handlers.get(type);
current?.delete(typedHandler);
if (current && current.size === 0) {
this.handlers.delete(type);
}
};
}
async publish<TType extends DomainEventType>(event: DomainEvent<TType>): Promise<void> {
const set = this.handlers.get(event.type);
if (!set || set.size === 0) {
return;
}
for (const handler of set) {
await handler(event);
}
}
}