UNPKG

claude-flow

Version:

Ruflo - Enterprise AI agent orchestration for Claude Code. Deploy 60+ specialized agents in coordinated swarms with self-learning, fault-tolerant consensus, vector memory, and MCP integration

148 lines (139 loc) 4.79 kB
/** * Federation Client for PR Artifacts (Phase 8 — beyond-SOTA, ADR-123) * * Sends `pr_artifact_request` to peers, verifies inbound `pr_artifact_response` * envelopes via Phase 7 verifier with the consumer's trust list, falls back * to local recompute on stale/untrusted responses. */ import { verifyArtifact } from '../infrastructure/witness-signer.js'; import { runPageRank } from '../infrastructure/solver-bridge.js'; import { getRegistry } from '../domain/adapter.js'; import type { FederationMessage, FederationTransport, PrArtifactRequest, PrArtifactResponse, PrArtifactStale, } from '../domain/federation-protocol.js'; import type { ArtifactVerificationResult } from '../domain/signed-artifact.js'; import type { PageRankResult } from '../domain/types.js'; export interface FederationClientOptions { installationId: string; transport: FederationTransport; /** Public keys we trust. Empty = trust any valid signature. */ trustedPublicKeys?: string[]; } export interface FetchPrResult { /** How the result was obtained. */ origin: 'peer' | 'local-fallback' | 'stale-fallback' | 'untrusted-fallback'; result: PageRankResult; /** If origin === 'peer', the verification report. */ verification?: ArtifactVerificationResult; /** Optional reason when we fell back. */ fallbackReason?: string; } export class FederationClient { private readonly installationId: string; private readonly transport: FederationTransport; private readonly trustedPublicKeys: string[]; constructor(options: FederationClientOptions) { this.installationId = options.installationId; this.transport = options.transport; this.trustedPublicKeys = options.trustedPublicKeys ?? []; } /** * Fetch a single-entry PR score for `nodeId` over `graphId`. Tries the * peer first; on stale/untrusted/missing, falls back to local recompute. */ async fetchPageRank(input: { peer: string; graphId: string; nodeId: string; alpha?: number; epsilon?: number; seedNodes?: string[]; lastKnownGraphHash?: string; lastKnownResultHash?: string; }): Promise<FetchPrResult> { const request: PrArtifactRequest = { type: 'pr_artifact_request', fromInstallation: this.installationId, graphId: input.graphId, queryNode: input.nodeId, alpha: input.alpha ?? 0.85, epsilon: input.epsilon ?? 1e-3, seedNodes: input.seedNodes ?? [], lastKnownGraphHash: input.lastKnownGraphHash, lastKnownResultHash: input.lastKnownResultHash, }; const response = await this.transport.send(input.peer, request); if (response && response.type === 'pr_artifact_response') { const verification = verifyArtifact(response.envelope, { trustedPublicKeys: this.trustedPublicKeys.length > 0 ? this.trustedPublicKeys : undefined, }); if (verification.valid) { return { origin: 'peer', result: response.envelope.payload.result, verification, }; } return { origin: 'untrusted-fallback', result: await this.localCompute(input), verification, fallbackReason: verification.reason ?? 'verification failed', }; } if (response && response.type === 'pr_artifact_stale') { return { origin: 'stale-fallback', result: await this.localCompute(input), fallbackReason: response.reason, }; } // No usable response return { origin: 'local-fallback', result: await this.localCompute(input), fallbackReason: 'no usable response from peer', }; } private async localCompute(input: { graphId: string; nodeId: string; alpha?: number; epsilon?: number; seedNodes?: string[]; }): Promise<PageRankResult> { const adapter = getRegistry().get(input.graphId); if (!adapter) { throw new Error(`localCompute: no adapter for graphId=${input.graphId}`); } const matrix = await adapter.exportAsSparseMatrix(); return runPageRank(matrix, { graphId: input.graphId, nodeId: input.nodeId, alpha: input.alpha ?? 0.85, epsilon: input.epsilon ?? 1e-3, seedNodes: input.seedNodes ?? [], maxComplexityClass: 'polynomial', coherenceThreshold: 0, }); } } /** * Helper: an in-process transport stitching a client to a server. Useful for * testing the Phase 8 round-trip without spinning up real ADR-104 wiring. */ export function inProcessTransport(server: { handle: (msg: FederationMessage) => Promise<FederationMessage | null> }): FederationTransport { return { async send(_to, msg) { return server.handle(msg); }, onMessage() { // not used in the in-process variant return () => {}; }, }; }