Implement RPC contract validation baseline

This commit is contained in:
2026-05-14 15:41:51 -04:00
parent 379c07b50c
commit df367756d0
60 changed files with 10704 additions and 47 deletions

View File

@@ -0,0 +1,173 @@
/**
* React hook for subscribing to server events
*/
import { useEffect, useCallback, useRef } from "react";
import type { ServerEvent } from "@mosaiciq/contracts/rpc";
type EventListener = (event: ServerEvent) => void;
type EventType = ServerEvent["type"];
export function useServerEvents(
eventType: EventType,
callback: (data: ServerEvent) => void,
deps: React.DependencyList = []
) {
const callbackRef = useRef(callback);
// Keep callback ref updated
useEffect(() => {
callbackRef.current = callback;
}, [callback]);
useEffect(() => {
// Check if mosaic API is available
if (!window.mosaic) {
console.warn("[useServerEvents] mosaic API not available");
return;
}
// Check if on method exists
if (typeof window.mosaic.on !== "function") {
console.warn("[useServerEvents] mosaic.on method not available");
return;
}
// Subscribe to events
const unsubscribe = window.mosaic.on(eventType, (data: unknown) => {
const event = data as ServerEvent;
callbackRef.current(event);
});
// Cleanup on unmount
return () => {
unsubscribe();
};
}, [eventType, ...deps]);
}
export function useMultiServerEvents(
eventTypes: EventType[],
callback: (event: ServerEvent) => void,
deps: React.DependencyList = []
) {
const callbackRef = useRef(callback);
useEffect(() => {
callbackRef.current = callback;
}, [callback]);
useEffect(() => {
if (!window.mosaic || typeof window.mosaic.on !== "function") {
return;
}
const unsubscribes: Array<() => void> = [];
for (const eventType of eventTypes) {
const unsubscribe = window.mosaic.on(eventType, (data: unknown) => {
const event = data as ServerEvent;
callbackRef.current(event);
});
unsubscribes.push(unsubscribe);
}
return () => {
for (const unsubscribe of unsubscribes) {
unsubscribe();
}
};
}, [eventTypes, ...deps]);
}
/**
* Hook for agent-specific events
*/
export function useAgentEvents(
agentId: string | null,
onProgress?: (progress: number, action: string) => void,
onCompleted?: (output: unknown) => void,
onFailed?: (error: string) => void,
onStreaming?: (chunk: string) => void
) {
const handleEvent = useCallback((event: ServerEvent) => {
if (!agentId) return;
if (event.type === "agent.progress" && event.data.agentId === agentId) {
onProgress?.(event.data.progress, event.data.action);
} else if (event.type === "agent.completed" && event.data.agentId === agentId) {
onCompleted?.(event.data.output);
} else if (event.type === "agent.failed" && event.data.agentId === agentId) {
onFailed?.(event.data.error);
} else if (event.type === "agent.streaming" && event.data.agentId === agentId) {
onStreaming?.(event.data.chunk);
}
}, [agentId, onProgress, onCompleted, onFailed, onStreaming]);
useMultiServerEvents(
["agent.progress", "agent.completed", "agent.failed", "agent.streaming"],
handleEvent,
[agentId]
);
}
/**
* Hook for all agent events (for agent list updates)
*/
export function useAllAgentEvents(
onAgentUpdate?: (agentId: string, update: { status?: string; progress?: number; action?: string }) => void
) {
const handleEvent = useCallback((event: ServerEvent) => {
if (event.type === "agent.progress") {
onAgentUpdate?.(event.data.agentId, {
progress: event.data.progress,
action: event.data.action,
status: "running"
});
} else if (event.type === "agent.completed") {
onAgentUpdate?.(event.data.agentId, {
status: "completed",
progress: 100
});
} else if (event.type === "agent.failed") {
onAgentUpdate?.(event.data.agentId, {
status: "failed"
});
} else if (event.type === "agent.started") {
onAgentUpdate?.(event.data.agentId, {
status: "running",
progress: 0
});
}
}, [onAgentUpdate]);
useMultiServerEvents(
["agent.progress", "agent.completed", "agent.failed", "agent.started"],
handleEvent,
[]
);
}
/**
* Hook for validation events
*/
export function useValidationEvents(
companyId: string | null,
onValidationUpdated?: (data: {
sectionId?: string;
validationState: "verified" | "flagged" | "unverified" | "failed";
agentId: string;
notes?: string;
}) => void
) {
useServerEvents("validation.updated", (event) => {
if (companyId && event.data.companyId === companyId) {
onValidationUpdated?.(event.data as {
sectionId?: string;
validationState: "verified" | "flagged" | "unverified" | "failed";
agentId: string;
notes?: string;
});
}
}, [companyId, onValidationUpdated]);
}