agentic-flow 2.0.14 → 2.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +87 -0
- package/README.md +376 -186
- package/dist/.tsbuildinfo +1 -1
- package/dist/agent-booster/index.d.ts +14 -0
- package/dist/agent-booster/index.d.ts.map +1 -0
- package/dist/agent-booster/index.js +25 -0
- package/dist/agent-booster/index.js.map +1 -0
- package/dist/core/embedding-service.js +1 -1
- package/dist/core/embedding-service.js.map +1 -1
- package/dist/embeddings/optimized-embedder.js +1 -1
- package/dist/embeddings/optimized-embedder.js.map +1 -1
- package/dist/harness/flywheel-governance.d.ts +85 -0
- package/dist/harness/flywheel-governance.d.ts.map +1 -0
- package/dist/harness/flywheel-governance.js +123 -0
- package/dist/harness/flywheel-governance.js.map +1 -0
- package/dist/harness/metaharness.d.ts +6 -0
- package/dist/harness/metaharness.d.ts.map +1 -0
- package/dist/harness/metaharness.js +6 -0
- package/dist/harness/metaharness.js.map +1 -0
- package/dist/harness/provenance.d.ts +72 -0
- package/dist/harness/provenance.d.ts.map +1 -0
- package/dist/harness/provenance.js +92 -0
- package/dist/harness/provenance.js.map +1 -0
- package/dist/mcp/mcp-policy.d.ts +15 -0
- package/dist/mcp/mcp-policy.d.ts.map +1 -0
- package/dist/mcp/mcp-policy.js +36 -0
- package/dist/mcp/mcp-policy.js.map +1 -0
- package/dist/mcp/standalone-stdio.js +11 -0
- package/dist/mcp/standalone-stdio.js.map +1 -1
- package/dist/mcp/tools/harness-tools.d.ts +29 -0
- package/dist/mcp/tools/harness-tools.d.ts.map +1 -0
- package/dist/mcp/tools/harness-tools.js +102 -0
- package/dist/mcp/tools/harness-tools.js.map +1 -0
- package/dist/optimizations/configuration-tuning.d.ts +1 -1
- package/dist/reasoningbank/utils/embeddings.js +2 -2
- package/dist/reasoningbank/utils/embeddings.js.map +1 -1
- package/dist/repair/cli.d.ts +24 -0
- package/dist/repair/cli.d.ts.map +1 -0
- package/dist/repair/cli.js +69 -0
- package/dist/repair/cli.js.map +1 -0
- package/dist/repair/darwin-repair.d.ts +92 -0
- package/dist/repair/darwin-repair.d.ts.map +1 -0
- package/dist/repair/darwin-repair.js +81 -0
- package/dist/repair/darwin-repair.js.map +1 -0
- package/dist/router/cost-optimal-router.d.ts +89 -0
- package/dist/router/cost-optimal-router.d.ts.map +1 -0
- package/dist/router/cost-optimal-router.js +94 -0
- package/dist/router/cost-optimal-router.js.map +1 -0
- package/dist/router/providers/onnx.js +2 -2
- package/dist/router/providers/onnx.js.map +1 -1
- package/dist/router/router.d.ts +29 -0
- package/dist/router/router.d.ts.map +1 -1
- package/dist/router/router.js +83 -0
- package/dist/router/router.js.map +1 -1
- package/dist/router/types.d.ts +1 -1
- package/dist/router/types.d.ts.map +1 -1
- package/dist/router/types.js.map +1 -1
- package/dist/services/embedding-service.js +1 -1
- package/dist/services/embedding-service.js.map +1 -1
- package/dist/transport/quic-loader.d.ts +33 -0
- package/dist/transport/quic-loader.d.ts.map +1 -1
- package/dist/transport/quic-loader.js +95 -0
- package/dist/transport/quic-loader.js.map +1 -1
- package/dist/utils/model-cache.js +1 -1
- package/dist/utils/model-cache.js.map +1 -1
- package/package.json +27 -11
- package/wasm/reasoningbank/reasoningbank_wasm_bg.js +31 -31
- package/wasm/reasoningbank/reasoningbank_wasm_bg.wasm +0 -0
- package/wasm/reasoningbank/reasoningbank_wasm_bg.wasm.d.ts +2 -2
|
@@ -61,6 +61,74 @@ class WebSocketFallbackTransport {
|
|
|
61
61
|
* streamId. Lets callers register both per-stream + catch-all.
|
|
62
62
|
*/
|
|
63
63
|
inboundHandlers = new Set();
|
|
64
|
+
/**
|
|
65
|
+
* Server-accepted (inbound) sockets indexed by peer host (#162). A
|
|
66
|
+
* peer that already dialed us has a live, full-duplex socket here;
|
|
67
|
+
* {@link getOrCreateConnection} reuses it for replies instead of
|
|
68
|
+
* opening a fresh outbound connection — which, on a direct
|
|
69
|
+
* point-to-point link, can hit a per-process stale scoped route
|
|
70
|
+
* (`EHOSTUNREACH`) even while the inbound direction is healthy.
|
|
71
|
+
*/
|
|
72
|
+
inboundByHost = new Map();
|
|
73
|
+
/** Active liveness-probe intervals, cleared on close (#162). */
|
|
74
|
+
heartbeats = new Set();
|
|
75
|
+
/**
|
|
76
|
+
* Liveness ping cadence, derived from `maxIdleTimeoutMs` (#162 — this
|
|
77
|
+
* config was previously accepted but never used). Pings at 1/6 of the
|
|
78
|
+
* idle timeout so a dead socket is caught within ~1/3 of it. Default
|
|
79
|
+
* 30000ms → 5000ms.
|
|
80
|
+
*/
|
|
81
|
+
get pingIntervalMs() {
|
|
82
|
+
return Math.max(1000, Math.floor(this.config.maxIdleTimeoutMs / 6));
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Normalize an address or socket `remoteAddress` to a bare host key
|
|
86
|
+
* for {@link inboundByHost} lookups. Strips `ws(s)://` scheme, an
|
|
87
|
+
* IPv6-mapped IPv4 prefix (`::ffff:`), bracketed IPv6 (`[::1]:port`),
|
|
88
|
+
* and a trailing `:port` on plain `host:port` forms. Bare IPv6
|
|
89
|
+
* (multiple colons, no brackets) is returned unchanged.
|
|
90
|
+
*/
|
|
91
|
+
hostOf(addr) {
|
|
92
|
+
let s = addr.replace(/^wss?:\/\//i, '');
|
|
93
|
+
const bracket = s.match(/^\[([^\]]+)\](?::\d+)?$/);
|
|
94
|
+
if (bracket)
|
|
95
|
+
s = bracket[1];
|
|
96
|
+
else if (/^[^:]+:\d+$/.test(s))
|
|
97
|
+
s = s.slice(0, s.lastIndexOf(':'));
|
|
98
|
+
return s.replace(/^::ffff:/i, '');
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Liveness probe for a socket (#162). Pings on {@link pingIntervalMs};
|
|
102
|
+
* a socket that misses a ping/pong window is terminated so a stale
|
|
103
|
+
* connection can't silently black-hole sends. The interval is unref'd
|
|
104
|
+
* (never keeps the process alive on its own) and cleared on close/error.
|
|
105
|
+
*/
|
|
106
|
+
startHeartbeat(ws) {
|
|
107
|
+
let alive = true;
|
|
108
|
+
ws.on('pong', () => { alive = true; });
|
|
109
|
+
const interval = setInterval(() => {
|
|
110
|
+
if (!alive) {
|
|
111
|
+
try {
|
|
112
|
+
ws.terminate();
|
|
113
|
+
}
|
|
114
|
+
catch { /* already gone */ }
|
|
115
|
+
return; // 'close' fires → stop() clears this interval
|
|
116
|
+
}
|
|
117
|
+
alive = false;
|
|
118
|
+
try {
|
|
119
|
+
ws.ping();
|
|
120
|
+
}
|
|
121
|
+
catch { /* socket is closing */ }
|
|
122
|
+
}, this.pingIntervalMs);
|
|
123
|
+
interval.unref?.();
|
|
124
|
+
this.heartbeats.add(interval);
|
|
125
|
+
const stop = () => {
|
|
126
|
+
clearInterval(interval);
|
|
127
|
+
this.heartbeats.delete(interval);
|
|
128
|
+
};
|
|
129
|
+
ws.on('close', stop);
|
|
130
|
+
ws.on('error', stop);
|
|
131
|
+
}
|
|
64
132
|
/** Compose the per-(address, streamId) queue key. */
|
|
65
133
|
queueKey(address, streamId) {
|
|
66
134
|
return `${address}#${streamId}`;
|
|
@@ -140,6 +208,18 @@ class WebSocketFallbackTransport {
|
|
|
140
208
|
attachServerHandlers(wss) {
|
|
141
209
|
wss.on('connection', (ws, req) => {
|
|
142
210
|
const remoteAddr = `${req.socket.remoteAddress}:${req.socket.remotePort}`;
|
|
211
|
+
// #162: register this accepted socket for outbound reuse, and probe
|
|
212
|
+
// it for liveness. Reusing it for replies avoids a fresh (stale-route
|
|
213
|
+
// prone) outbound dial back to the same peer.
|
|
214
|
+
const host = this.hostOf(req.socket.remoteAddress ?? remoteAddr);
|
|
215
|
+
this.inboundByHost.set(host, ws);
|
|
216
|
+
this.startHeartbeat(ws);
|
|
217
|
+
const dropInbound = () => {
|
|
218
|
+
if (this.inboundByHost.get(host) === ws)
|
|
219
|
+
this.inboundByHost.delete(host);
|
|
220
|
+
};
|
|
221
|
+
ws.on('close', dropInbound);
|
|
222
|
+
ws.on('error', dropInbound);
|
|
143
223
|
ws.on('message', (raw) => {
|
|
144
224
|
try {
|
|
145
225
|
const message = JSON.parse(raw.toString());
|
|
@@ -163,6 +243,15 @@ class WebSocketFallbackTransport {
|
|
|
163
243
|
resolve(existing);
|
|
164
244
|
return;
|
|
165
245
|
}
|
|
246
|
+
// #162: prefer an already-open INBOUND socket from the same peer host
|
|
247
|
+
// before dialing. WebSocket is full-duplex, so the server-accepted
|
|
248
|
+
// socket can carry our replies — and the inbound direction stays
|
|
249
|
+
// healthy when a fresh outbound dial would hit a stale scoped route.
|
|
250
|
+
const inbound = this.inboundByHost.get(this.hostOf(address));
|
|
251
|
+
if (inbound && inbound.readyState === WebSocket.OPEN) {
|
|
252
|
+
resolve(inbound);
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
166
255
|
const url = address.startsWith('ws://') || address.startsWith('wss://')
|
|
167
256
|
? address
|
|
168
257
|
: `ws://${address}`;
|
|
@@ -216,6 +305,7 @@ class WebSocketFallbackTransport {
|
|
|
216
305
|
ws.on('open', () => {
|
|
217
306
|
this.connections.set(address, ws);
|
|
218
307
|
this.connectionsCreated++;
|
|
308
|
+
this.startHeartbeat(ws); // #162: keep the outbound link probed
|
|
219
309
|
resolve(ws);
|
|
220
310
|
});
|
|
221
311
|
ws.on('error', (error) => {
|
|
@@ -325,6 +415,11 @@ class WebSocketFallbackTransport {
|
|
|
325
415
|
}
|
|
326
416
|
this.connections.clear();
|
|
327
417
|
this.messageQueue.clear();
|
|
418
|
+
// Stop all liveness probes and drop the inbound index (#162).
|
|
419
|
+
for (const interval of this.heartbeats)
|
|
420
|
+
clearInterval(interval);
|
|
421
|
+
this.heartbeats.clear();
|
|
422
|
+
this.inboundByHost.clear();
|
|
328
423
|
// Inbound: WebSocketServer.close() blocks until every accepted
|
|
329
424
|
// socket disconnects. Forcibly terminate them so the close
|
|
330
425
|
// callback fires within the test/CI timeout window.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"quic-loader.js","sourceRoot":"","sources":["../../src/transport/quic-loader.ts"],"names":[],"mappings":"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","sourcesContent":["// QUIC Transport Loader with WebSocket fallback\n//\n// Backported from outer repo loader pattern. Exposes a single\n// loadQuicTransport(config) entry point that:\n//\n// 1. Tries to load the WASM-backed QuicClient/QuicServer (real QUIC if a\n// native build is wired in the future).\n// 2. Falls back to WebSocketFallbackTransport when QUIC is unavailable\n// OR when the WASM stub is detected (current state — see\n// crates/agentic-flow-quic/src/wasm.rs comment: WASM build is a stub\n// because browsers can't do raw UDP/QUIC; production QUIC needs\n// native Node.js builds which haven't shipped yet).\n//\n// The fallback uses standard WebSocket (ws://) so it works on all Node\n// versions without complex native dependencies. Same async send/receive\n// API surface as QuicTransport.\n//\n// Federation use case (ruvnet/ruflo ADR-104): two peers on the same\n// tailnet can call loadQuicTransport({ serverName: 'peer.tailnet' }) and\n// exchange signed envelopes today, with zero code change required when\n// the native QUIC build lands later.\n\nimport { logger } from '../utils/logger.js';\nimport WebSocket, { WebSocketServer, type RawData } from 'ws';\nimport { createHash } from 'node:crypto';\nimport { createServer as createHttpsServer } from 'node:https';\nimport { readFileSync } from 'node:fs';\nimport type { TLSSocket } from 'node:tls';\n\n/** TLS configuration for wss:// peers (ADR-107). */\nexport interface TlsConfig {\n /** Path to PEM cert file (server side — bind certs for the listener). */\n certPath?: string;\n /** Path to PEM key file (server side). */\n keyPath?: string;\n /**\n * Pinned `sha256/<base64>` fingerprints of acceptable peer certs\n * (client side — outbound connections).\n *\n * When set, ONLY these exact certs are accepted. CA validation is\n * skipped — the fingerprint IS the trust anchor. Fail-closed: if the\n * peer's cert rotates and the fingerprint doesn't match, the\n * connection is refused (operator must update config + restart).\n *\n * This prevents:\n * - Compromised public CAs issuing rogue certs for our domain\n * - TLS-MITM attacks where the attacker holds a valid cert chain\n */\n pinnedFingerprints?: string[];\n /**\n * Optional CA bundle path for non-pinned mode (e.g. private CA).\n * Used only when `pinnedFingerprints` is empty/unset.\n */\n caPath?: string;\n}\n\n/** Caller-facing config — minimal common surface across both backends. */\nexport interface QuicTransportConfig {\n serverName?: string;\n maxIdleTimeoutMs?: number;\n maxConcurrentStreams?: number;\n enable0Rtt?: boolean;\n /** TLS materials for wss:// listeners + clients (ADR-107). */\n tls?: TlsConfig;\n}\n\nexport interface AgentMessage {\n id: string;\n type: 'task' | 'result' | 'status' | 'coordination' | 'heartbeat' | string;\n payload: unknown;\n metadata?: Record<string, unknown>;\n /**\n * Stream multiplexing identifier. Messages with different streamIds\n * to the same peer are independent — receive queues and onMessage\n * handlers can scope per-stream, eliminating head-of-line blocking\n * for sequential `await` patterns on a single peer connection.\n *\n * Defaults to `'default'` if omitted (backward compat). Common\n * patterns:\n * - One stream per logical request type (`'rpc'`, `'event'`,\n * `'control'`)\n * - One stream per task (`taskId` doubled as streamId)\n * - One stream per priority class (`'high'`, `'normal'`, `'low'`)\n *\n * Maps cleanly to native QUIC streams when AGENTIC_FLOW_QUIC_NATIVE=1\n * (each app-layer streamId becomes a QUIC stream id at that point).\n */\n streamId?: string | number;\n}\n\n/** Default streamId when caller omits it. Backward-compat sentinel. */\nexport const DEFAULT_STREAM_ID = 'default';\n\nexport interface PoolStatistics {\n active: number;\n idle: number;\n created: number;\n closed: number;\n}\n\n/** Inbound message handler — called for every received message. */\nexport type InboundMessageHandler = (\n address: string,\n message: AgentMessage,\n) => void | Promise<void>;\n\n/**\n * Per-stream subscription options. Pass to `onMessage` to scope a\n * handler to a specific streamId (only fires for messages with that\n * exact streamId). Omit to receive all streams.\n */\nexport interface OnMessageOptions {\n readonly streamId?: string | number;\n}\n\n/** Common interface both real-QUIC and fallback transports satisfy. */\nexport interface AgentTransport {\n send(address: string, message: AgentMessage): Promise<void>;\n /**\n * Receive the next message from a peer. Optional `streamId` scopes\n * to that stream's queue (independent of other streams to the same\n * peer). Omit to use the default stream — backward-compat behavior.\n */\n receive(address: string, streamId?: string | number): Promise<AgentMessage>;\n request(address: string, message: AgentMessage): Promise<AgentMessage>;\n sendBatch(address: string, messages: AgentMessage[]): Promise<void>;\n getStats(): Promise<PoolStatistics>;\n close(): Promise<void>;\n /**\n * Subscribe to inbound messages. The handler fires for every received\n * message that matches `options.streamId` (if provided) or for every\n * message regardless of streamId (if options omitted).\n *\n * Multiple handlers may be registered (per-stream OR all-streams or\n * a mix). Errors thrown by a handler are logged but do not stop\n * delivery to other handlers.\n *\n * Optional method — implementations that don't support push-style\n * delivery may omit it. Callers should use `transport.onMessage?.(h)`\n * to gracefully degrade.\n */\n onMessage?(handler: InboundMessageHandler, options?: OnMessageOptions): void;\n}\n\n/**\n * WebSocket fallback transport.\n *\n * Spec compliance: implements the AgentTransport interface using\n * `ws://` (or `wss://` if address starts with `wss://`). Each call to\n * `send` lazily opens (or reuses) a connection to `address`. The\n * `receive(address)` call drains the next queued message for that\n * address; if none is queued it polls every 100ms until one arrives.\n *\n * Limits vs real QUIC: no 0-RTT resumption, no multiplexed streams\n * (one TCP connection per peer), TLS handled by the WS layer (use\n * `wss://` for encryption). Performance is \"good enough\" for federation\n * messages at human/agent rates (≤ 100 RPS per peer).\n */\nclass WebSocketFallbackTransport implements AgentTransport {\n private connections = new Map<string, WebSocket>();\n /**\n * Per-(address, streamId) message queue. Composite key shape\n * `${address}#${streamId}` — see {@link queueKey}. Each stream gets\n * its own FIFO so receive(addr, streamA) is independent of\n * receive(addr, streamB) — eliminates head-of-line blocking on a\n * single peer connection.\n */\n private messageQueue = new Map<string, AgentMessage[]>();\n private connectionsCreated = 0;\n private connectionsClosed = 0;\n private servers = new Map<number, WebSocketServer>();\n /**\n * Inbound handlers. Each entry is { handler, streamId? }. When\n * streamId is undefined the handler receives ALL messages\n * regardless of stream; otherwise only messages with the matching\n * streamId. Lets callers register both per-stream + catch-all.\n */\n private inboundHandlers = new Set<{\n handler: InboundMessageHandler;\n streamId?: string | number;\n }>();\n\n /** Compose the per-(address, streamId) queue key. */\n private queueKey(address: string, streamId: string | number): string {\n return `${address}#${streamId}`;\n }\n\n /** Resolve the streamId for a message — defaults to DEFAULT_STREAM_ID. */\n private streamOf(message: AgentMessage): string | number {\n return message.streamId ?? DEFAULT_STREAM_ID;\n }\n\n constructor(private readonly config: Required<QuicTransportConfig>) {}\n\n static async create(config: QuicTransportConfig = {}): Promise<WebSocketFallbackTransport> {\n const fullConfig: Required<QuicTransportConfig> = {\n serverName: config.serverName ?? 'localhost',\n maxIdleTimeoutMs: config.maxIdleTimeoutMs ?? 30000,\n maxConcurrentStreams: config.maxConcurrentStreams ?? 100,\n // Not applicable for WebSocket — record but ignore\n enable0Rtt: config.enable0Rtt ?? false,\n tls: config.tls ?? {},\n };\n return new WebSocketFallbackTransport(fullConfig);\n }\n\n /**\n * Bind a server-side listener so this transport instance can RECEIVE\n * messages from a remote peer (in addition to sending). Federation\n * peers run BOTH a listener and a client — calling listen(9100) plus\n * send('peer:9100', ...) gives bidirectional connectivity.\n *\n * Enables `permessage-deflate` compression with thresholds chosen\n * for federation envelopes (typically JSON, 100B-10KB):\n * - threshold: 256B — don't waste CPU compressing tiny pings\n * - level: 3 — balanced compression vs CPU (zlib's BEST_SPEED→6 range)\n * - serverNoContextTakeover: true — bound per-conn memory growth\n */\n async listen(port: number, host = '0.0.0.0'): Promise<void> {\n if (this.servers.has(port)) return;\n return new Promise((resolve, reject) => {\n const tls = this.config.tls;\n const wssOpts: ConstructorParameters<typeof WebSocketServer>[0] = {\n perMessageDeflate: {\n threshold: 256,\n zlibDeflateOptions: { level: 3 },\n serverNoContextTakeover: true,\n clientNoContextTakeover: true,\n },\n };\n // ADR-107: if cert+key paths are configured, bind via https.Server\n // (wss://). Otherwise bind plain ws:// directly.\n if (tls?.certPath && tls?.keyPath) {\n const cert = readFileSync(tls.certPath);\n const key = readFileSync(tls.keyPath);\n const httpsServer = createHttpsServer({ cert, key });\n httpsServer.listen(port, host, () => {\n const wss = new WebSocketServer({ ...wssOpts, server: httpsServer });\n this.attachServerHandlers(wss);\n this.servers.set(port, wss);\n resolve();\n });\n httpsServer.on('error', reject);\n return;\n }\n const wss = new WebSocketServer({ ...wssOpts, port, host });\n wss.on('listening', () => {\n this.servers.set(port, wss);\n resolve();\n });\n wss.on('error', reject);\n this.attachServerHandlers(wss);\n });\n }\n\n /**\n * Wire the server's `connection` and per-socket `message` handlers.\n * Extracted so the wss:// path (where the WebSocketServer is attached\n * to a pre-created https.Server) can share the same logic.\n */\n private attachServerHandlers(wss: WebSocketServer): void {\n wss.on('connection', (ws, req) => {\n const remoteAddr = `${req.socket.remoteAddress}:${req.socket.remotePort}`;\n ws.on('message', (raw: RawData) => {\n try {\n const message = JSON.parse(raw.toString()) as AgentMessage;\n // Per-stream queue (default stream when message.streamId omitted)\n const key = this.queueKey(remoteAddr, this.streamOf(message));\n const queue = this.messageQueue.get(key) ?? [];\n queue.push(message);\n this.messageQueue.set(key, queue);\n this.dispatchInbound(remoteAddr, message);\n } catch (err) {\n logger.warn('Dropped malformed inbound WS message', { remoteAddr, err });\n }\n });\n });\n }\n\n private async getOrCreateConnection(address: string): Promise<WebSocket> {\n return new Promise((resolve, reject) => {\n const existing = this.connections.get(address);\n if (existing && existing.readyState === WebSocket.OPEN) {\n resolve(existing);\n return;\n }\n\n const url = address.startsWith('ws://') || address.startsWith('wss://')\n ? address\n : `ws://${address}`;\n\n // ADR-107: cert pinning for wss:// peers. Build the WS options\n // with both compression (perf) and TLS hooks (security).\n const tls = this.config.tls;\n const isWss = url.startsWith('wss://');\n const wsOpts: WebSocket.ClientOptions = {\n perMessageDeflate: {\n threshold: 256,\n zlibDeflateOptions: { level: 3 },\n serverNoContextTakeover: true,\n clientNoContextTakeover: true,\n },\n };\n\n if (isWss && tls?.pinnedFingerprints && tls.pinnedFingerprints.length > 0) {\n // Fail-closed pinning: ONLY accept the listed cert fingerprints.\n // CA path validation is irrelevant — the fingerprint IS the trust\n // anchor. If the cert rotates, the operator must update config.\n const pinned = new Set(tls.pinnedFingerprints);\n // Cast: ws's `checkServerIdentity` is typed as returning boolean\n // but the underlying `tls.checkServerIdentity` (which it\n // forwards to) accepts `Error | undefined`. The runtime\n // semantics are: throw OR return Error → reject; otherwise accept.\n // The boolean type signature is overly restrictive.\n (wsOpts as unknown as { checkServerIdentity: unknown }).checkServerIdentity = (\n _host: string,\n cert: { raw: Buffer },\n ): Error | undefined => {\n // cert.raw is the DER-encoded cert bytes; sha256/<base64> matches\n // common pinning notation (and what `openssl x509 -fingerprint\n // -sha256` outputs after base64-encoding).\n const fp = `sha256/${createHash('sha256').update(cert.raw).digest('base64')}`;\n if (!pinned.has(fp)) {\n return new Error(\n `Federation TLS: peer cert fingerprint ${fp} not in pinned set ` +\n `(${pinned.size} fingerprint(s) configured)`,\n );\n }\n return undefined; // accept\n };\n // When pinning is on, we explicitly DON'T want CA validation\n // (the fingerprint check above is the real auth). But we also\n // DON'T want to silently accept ANY cert — the checkServerIdentity\n // above is still called.\n wsOpts.rejectUnauthorized = false;\n } else if (isWss && tls?.caPath) {\n // Non-pinned mode: validate against the configured CA bundle.\n wsOpts.ca = readFileSync(tls.caPath);\n wsOpts.rejectUnauthorized = true;\n }\n // (no tls config + ws:// → plain unencrypted; ADR-104 documents\n // tailnet-as-TLS as the recommended path)\n\n const ws = new WebSocket(url, wsOpts);\n\n ws.on('open', () => {\n this.connections.set(address, ws);\n this.connectionsCreated++;\n resolve(ws);\n });\n\n ws.on('error', (error: Error) => {\n reject(new Error(`WebSocket connection to ${url} failed: ${error.message}`));\n });\n\n ws.on('close', () => {\n this.connectionsClosed++;\n this.connections.delete(address);\n });\n\n ws.on('message', (raw: RawData) => {\n try {\n const message = JSON.parse(raw.toString()) as AgentMessage;\n const key = this.queueKey(address, this.streamOf(message));\n const queue = this.messageQueue.get(key) ?? [];\n queue.push(message);\n this.messageQueue.set(key, queue);\n this.dispatchInbound(address, message);\n } catch (err) {\n logger.warn('Dropped malformed WebSocket message', { address, err });\n }\n });\n });\n }\n\n async send(address: string, message: AgentMessage): Promise<void> {\n const ws = await this.getOrCreateConnection(address);\n ws.send(JSON.stringify(message));\n }\n\n /**\n * Register an inbound handler. Optional `options.streamId` scopes\n * the handler to a specific stream (only fires for messages with\n * matching streamId). Omit to subscribe to ALL streams.\n *\n * Patterns:\n * onMessage(h) — receives all\n * onMessage(h, { streamId: 'rpc' }) — receives only rpc\n * onMessage(h, { streamId: 'event' }) — receives only event\n * (both registered) — both fire on\n * their respective streams\n */\n onMessage(handler: InboundMessageHandler, options: OnMessageOptions = {}): void {\n this.inboundHandlers.add({ handler, streamId: options.streamId });\n }\n\n /**\n * Fire all matching handlers for a received message. Stream-scoped\n * handlers only fire when the message's streamId matches; all-stream\n * handlers always fire. Errors thrown sync OR async-rejected by one\n * handler don't stop delivery to others.\n */\n private dispatchInbound(address: string, message: AgentMessage): void {\n if (this.inboundHandlers.size === 0) return;\n const msgStream = this.streamOf(message);\n for (const entry of this.inboundHandlers) {\n // Stream filter: scoped handler only fires on matching streamId\n if (entry.streamId !== undefined && entry.streamId !== msgStream) continue;\n try {\n const r = entry.handler(address, message);\n if (r && typeof (r as Promise<void>).catch === 'function') {\n (r as Promise<void>).catch((err) => {\n logger.warn('Inbound handler rejected', { address, err });\n });\n }\n } catch (err) {\n logger.warn('Inbound handler threw', { address, err });\n }\n }\n }\n\n async receive(address: string, streamId: string | number = DEFAULT_STREAM_ID): Promise<AgentMessage> {\n const key = this.queueKey(address, streamId);\n // Fast path\n const queue = this.messageQueue.get(key) ?? [];\n if (queue.length > 0) return queue.shift()!;\n\n // Poll (caller must time out externally if they don't want to wait)\n return new Promise((resolve) => {\n const interval = setInterval(() => {\n const q = this.messageQueue.get(key) ?? [];\n if (q.length > 0) {\n clearInterval(interval);\n resolve(q.shift()!);\n }\n }, 100);\n });\n }\n\n async request(address: string, message: AgentMessage): Promise<AgentMessage> {\n await this.send(address, message);\n return this.receive(address);\n }\n\n async sendBatch(address: string, messages: AgentMessage[]): Promise<void> {\n await Promise.all(messages.map((m) => this.send(address, m)));\n }\n\n async getStats(): Promise<PoolStatistics> {\n return {\n active: this.connections.size,\n idle: 0,\n created: this.connectionsCreated,\n closed: this.connectionsClosed,\n };\n }\n\n async close(): Promise<void> {\n // Outbound clients first.\n for (const ws of this.connections.values()) {\n ws.terminate();\n }\n this.connections.clear();\n this.messageQueue.clear();\n\n // Inbound: WebSocketServer.close() blocks until every accepted\n // socket disconnects. Forcibly terminate them so the close\n // callback fires within the test/CI timeout window.\n for (const wss of this.servers.values()) {\n for (const client of wss.clients) {\n try {\n client.terminate();\n } catch {\n /* socket already gone */\n }\n }\n await new Promise<void>((resolve) => wss.close(() => resolve()));\n }\n this.servers.clear();\n }\n}\n\n/**\n * Detect whether the WASM-backed QUIC transport is \"real\" (i.e. it\n * actually moves bytes on the wire) vs the current stub. The stub\n * returns 0ms for connect+send and never increments the server's\n * received-bytes counter. We probe by observing a documented marker\n * on the WASM module: when it's truly wired the loader function\n * `defaultConfig` returns an object whose round-trip through\n * `WasmQuicClient.new` actually opens a UDP socket — failing fast on\n * an OS that blocks UDP outbound (e.g. some sandboxed CI envs).\n *\n * Until the native build lands this returns false; the loader picks\n * WebSocket. When the native binding is wired this returns true and\n * the loader picks real QUIC. Callers get the same API either way.\n */\nasync function isRealQuicAvailable(): Promise<boolean> {\n try {\n // The WASM file is published in `wasm/quic/` of this package. We\n // do NOT use it for federation today (per the wasm.rs note: it's a\n // stub since browsers can't do UDP). When a native binding is added\n // this probe should switch to detect that binding instead.\n const native = process.env.AGENTIC_FLOW_QUIC_NATIVE === '1';\n return native;\n } catch {\n return false;\n }\n}\n\n/**\n * Public API — load a working transport, preferring real QUIC when\n * available, falling back to WebSocket otherwise. The returned object\n * satisfies the AgentTransport interface in both cases.\n *\n * Example:\n * const t = await loadQuicTransport({ serverName: 'ruvultra:9100' });\n * await t.send('ruvultra:9100', { id: '1', type: 'task', payload: {...} });\n *\n * Federation v1 ships on the WebSocket fallback (this is the actual\n * working transport today). When the native QUIC binding lands, set\n * the AGENTIC_FLOW_QUIC_NATIVE=1 environment variable and the same\n * code path picks up the upgrade with no API changes.\n */\nexport async function loadQuicTransport(\n config: QuicTransportConfig = {},\n): Promise<AgentTransport> {\n if (await isRealQuicAvailable()) {\n // Future: wire to the native binding here.\n logger.info('QUIC transport: native binding selected');\n } else {\n if (process.env.NODE_ENV !== 'test') {\n logger.warn(\n 'QUIC native binding not available; using WebSocket fallback. ' +\n 'Set AGENTIC_FLOW_QUIC_NATIVE=1 once a native build is installed.',\n );\n }\n }\n return WebSocketFallbackTransport.create(config);\n}\n\n/** Quick capability probe for the doctor / health surface. */\nexport async function isQuicAvailable(): Promise<boolean> {\n return isRealQuicAvailable();\n}\n\nexport interface TransportCapabilities {\n quicAvailable: boolean;\n webSocketFallbackAvailable: true;\n selectedBackend: 'quic' | 'websocket';\n}\n\nexport async function getTransportCapabilities(): Promise<TransportCapabilities> {\n const quic = await isRealQuicAvailable();\n return {\n quicAvailable: quic,\n webSocketFallbackAvailable: true,\n selectedBackend: quic ? 'quic' : 'websocket',\n };\n}\n\nexport { WebSocketFallbackTransport };\n"]}
|
|
1
|
+
{"version":3,"file":"quic-loader.js","sourceRoot":"","sources":["../../src/transport/quic-loader.ts"],"names":[],"mappings":"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","sourcesContent":["// QUIC Transport Loader with WebSocket fallback\n//\n// Backported from outer repo loader pattern. Exposes a single\n// loadQuicTransport(config) entry point that:\n//\n// 1. Tries to load the WASM-backed QuicClient/QuicServer (real QUIC if a\n// native build is wired in the future).\n// 2. Falls back to WebSocketFallbackTransport when QUIC is unavailable\n// OR when the WASM stub is detected (current state — see\n// crates/agentic-flow-quic/src/wasm.rs comment: WASM build is a stub\n// because browsers can't do raw UDP/QUIC; production QUIC needs\n// native Node.js builds which haven't shipped yet).\n//\n// The fallback uses standard WebSocket (ws://) so it works on all Node\n// versions without complex native dependencies. Same async send/receive\n// API surface as QuicTransport.\n//\n// Federation use case (ruvnet/ruflo ADR-104): two peers on the same\n// tailnet can call loadQuicTransport({ serverName: 'peer.tailnet' }) and\n// exchange signed envelopes today, with zero code change required when\n// the native QUIC build lands later.\n\nimport { logger } from '../utils/logger.js';\nimport WebSocket, { WebSocketServer, type RawData } from 'ws';\nimport { createHash } from 'node:crypto';\nimport { createServer as createHttpsServer } from 'node:https';\nimport { readFileSync } from 'node:fs';\n\n/** TLS configuration for wss:// peers (ADR-107). */\nexport interface TlsConfig {\n /** Path to PEM cert file (server side — bind certs for the listener). */\n certPath?: string;\n /** Path to PEM key file (server side). */\n keyPath?: string;\n /**\n * Pinned `sha256/<base64>` fingerprints of acceptable peer certs\n * (client side — outbound connections).\n *\n * When set, ONLY these exact certs are accepted. CA validation is\n * skipped — the fingerprint IS the trust anchor. Fail-closed: if the\n * peer's cert rotates and the fingerprint doesn't match, the\n * connection is refused (operator must update config + restart).\n *\n * This prevents:\n * - Compromised public CAs issuing rogue certs for our domain\n * - TLS-MITM attacks where the attacker holds a valid cert chain\n */\n pinnedFingerprints?: string[];\n /**\n * Optional CA bundle path for non-pinned mode (e.g. private CA).\n * Used only when `pinnedFingerprints` is empty/unset.\n */\n caPath?: string;\n}\n\n/** Caller-facing config — minimal common surface across both backends. */\nexport interface QuicTransportConfig {\n serverName?: string;\n maxIdleTimeoutMs?: number;\n maxConcurrentStreams?: number;\n enable0Rtt?: boolean;\n /** TLS materials for wss:// listeners + clients (ADR-107). */\n tls?: TlsConfig;\n}\n\nexport interface AgentMessage {\n id: string;\n type: 'task' | 'result' | 'status' | 'coordination' | 'heartbeat' | string;\n payload: unknown;\n metadata?: Record<string, unknown>;\n /**\n * Stream multiplexing identifier. Messages with different streamIds\n * to the same peer are independent — receive queues and onMessage\n * handlers can scope per-stream, eliminating head-of-line blocking\n * for sequential `await` patterns on a single peer connection.\n *\n * Defaults to `'default'` if omitted (backward compat). Common\n * patterns:\n * - One stream per logical request type (`'rpc'`, `'event'`,\n * `'control'`)\n * - One stream per task (`taskId` doubled as streamId)\n * - One stream per priority class (`'high'`, `'normal'`, `'low'`)\n *\n * Maps cleanly to native QUIC streams when AGENTIC_FLOW_QUIC_NATIVE=1\n * (each app-layer streamId becomes a QUIC stream id at that point).\n */\n streamId?: string | number;\n}\n\n/** Default streamId when caller omits it. Backward-compat sentinel. */\nexport const DEFAULT_STREAM_ID = 'default';\n\nexport interface PoolStatistics {\n active: number;\n idle: number;\n created: number;\n closed: number;\n}\n\n/** Inbound message handler — called for every received message. */\nexport type InboundMessageHandler = (\n address: string,\n message: AgentMessage,\n) => void | Promise<void>;\n\n/**\n * Per-stream subscription options. Pass to `onMessage` to scope a\n * handler to a specific streamId (only fires for messages with that\n * exact streamId). Omit to receive all streams.\n */\nexport interface OnMessageOptions {\n readonly streamId?: string | number;\n}\n\n/** Common interface both real-QUIC and fallback transports satisfy. */\nexport interface AgentTransport {\n send(address: string, message: AgentMessage): Promise<void>;\n /**\n * Receive the next message from a peer. Optional `streamId` scopes\n * to that stream's queue (independent of other streams to the same\n * peer). Omit to use the default stream — backward-compat behavior.\n */\n receive(address: string, streamId?: string | number): Promise<AgentMessage>;\n request(address: string, message: AgentMessage): Promise<AgentMessage>;\n sendBatch(address: string, messages: AgentMessage[]): Promise<void>;\n getStats(): Promise<PoolStatistics>;\n close(): Promise<void>;\n /**\n * Subscribe to inbound messages. The handler fires for every received\n * message that matches `options.streamId` (if provided) or for every\n * message regardless of streamId (if options omitted).\n *\n * Multiple handlers may be registered (per-stream OR all-streams or\n * a mix). Errors thrown by a handler are logged but do not stop\n * delivery to other handlers.\n *\n * Optional method — implementations that don't support push-style\n * delivery may omit it. Callers should use `transport.onMessage?.(h)`\n * to gracefully degrade.\n */\n onMessage?(handler: InboundMessageHandler, options?: OnMessageOptions): void;\n}\n\n/**\n * WebSocket fallback transport.\n *\n * Spec compliance: implements the AgentTransport interface using\n * `ws://` (or `wss://` if address starts with `wss://`). Each call to\n * `send` lazily opens (or reuses) a connection to `address`. The\n * `receive(address)` call drains the next queued message for that\n * address; if none is queued it polls every 100ms until one arrives.\n *\n * Limits vs real QUIC: no 0-RTT resumption, no multiplexed streams\n * (one TCP connection per peer), TLS handled by the WS layer (use\n * `wss://` for encryption). Performance is \"good enough\" for federation\n * messages at human/agent rates (≤ 100 RPS per peer).\n */\nclass WebSocketFallbackTransport implements AgentTransport {\n private connections = new Map<string, WebSocket>();\n /**\n * Per-(address, streamId) message queue. Composite key shape\n * `${address}#${streamId}` — see {@link queueKey}. Each stream gets\n * its own FIFO so receive(addr, streamA) is independent of\n * receive(addr, streamB) — eliminates head-of-line blocking on a\n * single peer connection.\n */\n private messageQueue = new Map<string, AgentMessage[]>();\n private connectionsCreated = 0;\n private connectionsClosed = 0;\n private servers = new Map<number, WebSocketServer>();\n /**\n * Inbound handlers. Each entry is { handler, streamId? }. When\n * streamId is undefined the handler receives ALL messages\n * regardless of stream; otherwise only messages with the matching\n * streamId. Lets callers register both per-stream + catch-all.\n */\n private inboundHandlers = new Set<{\n handler: InboundMessageHandler;\n streamId?: string | number;\n }>();\n /**\n * Server-accepted (inbound) sockets indexed by peer host (#162). A\n * peer that already dialed us has a live, full-duplex socket here;\n * {@link getOrCreateConnection} reuses it for replies instead of\n * opening a fresh outbound connection — which, on a direct\n * point-to-point link, can hit a per-process stale scoped route\n * (`EHOSTUNREACH`) even while the inbound direction is healthy.\n */\n private inboundByHost = new Map<string, WebSocket>();\n /** Active liveness-probe intervals, cleared on close (#162). */\n private heartbeats = new Set<ReturnType<typeof setInterval>>();\n\n /**\n * Liveness ping cadence, derived from `maxIdleTimeoutMs` (#162 — this\n * config was previously accepted but never used). Pings at 1/6 of the\n * idle timeout so a dead socket is caught within ~1/3 of it. Default\n * 30000ms → 5000ms.\n */\n private get pingIntervalMs(): number {\n return Math.max(1000, Math.floor(this.config.maxIdleTimeoutMs / 6));\n }\n\n /**\n * Normalize an address or socket `remoteAddress` to a bare host key\n * for {@link inboundByHost} lookups. Strips `ws(s)://` scheme, an\n * IPv6-mapped IPv4 prefix (`::ffff:`), bracketed IPv6 (`[::1]:port`),\n * and a trailing `:port` on plain `host:port` forms. Bare IPv6\n * (multiple colons, no brackets) is returned unchanged.\n */\n private hostOf(addr: string): string {\n let s = addr.replace(/^wss?:\\/\\//i, '');\n const bracket = s.match(/^\\[([^\\]]+)\\](?::\\d+)?$/);\n if (bracket) s = bracket[1];\n else if (/^[^:]+:\\d+$/.test(s)) s = s.slice(0, s.lastIndexOf(':'));\n return s.replace(/^::ffff:/i, '');\n }\n\n /**\n * Liveness probe for a socket (#162). Pings on {@link pingIntervalMs};\n * a socket that misses a ping/pong window is terminated so a stale\n * connection can't silently black-hole sends. The interval is unref'd\n * (never keeps the process alive on its own) and cleared on close/error.\n */\n private startHeartbeat(ws: WebSocket): void {\n let alive = true;\n ws.on('pong', () => { alive = true; });\n const interval = setInterval(() => {\n if (!alive) {\n try { ws.terminate(); } catch { /* already gone */ }\n return; // 'close' fires → stop() clears this interval\n }\n alive = false;\n try { ws.ping(); } catch { /* socket is closing */ }\n }, this.pingIntervalMs);\n (interval as { unref?: () => void }).unref?.();\n this.heartbeats.add(interval);\n const stop = () => {\n clearInterval(interval);\n this.heartbeats.delete(interval);\n };\n ws.on('close', stop);\n ws.on('error', stop);\n }\n\n /** Compose the per-(address, streamId) queue key. */\n private queueKey(address: string, streamId: string | number): string {\n return `${address}#${streamId}`;\n }\n\n /** Resolve the streamId for a message — defaults to DEFAULT_STREAM_ID. */\n private streamOf(message: AgentMessage): string | number {\n return message.streamId ?? DEFAULT_STREAM_ID;\n }\n\n constructor(private readonly config: Required<QuicTransportConfig>) {}\n\n static async create(config: QuicTransportConfig = {}): Promise<WebSocketFallbackTransport> {\n const fullConfig: Required<QuicTransportConfig> = {\n serverName: config.serverName ?? 'localhost',\n maxIdleTimeoutMs: config.maxIdleTimeoutMs ?? 30000,\n maxConcurrentStreams: config.maxConcurrentStreams ?? 100,\n // Not applicable for WebSocket — record but ignore\n enable0Rtt: config.enable0Rtt ?? false,\n tls: config.tls ?? {},\n };\n return new WebSocketFallbackTransport(fullConfig);\n }\n\n /**\n * Bind a server-side listener so this transport instance can RECEIVE\n * messages from a remote peer (in addition to sending). Federation\n * peers run BOTH a listener and a client — calling listen(9100) plus\n * send('peer:9100', ...) gives bidirectional connectivity.\n *\n * Enables `permessage-deflate` compression with thresholds chosen\n * for federation envelopes (typically JSON, 100B-10KB):\n * - threshold: 256B — don't waste CPU compressing tiny pings\n * - level: 3 — balanced compression vs CPU (zlib's BEST_SPEED→6 range)\n * - serverNoContextTakeover: true — bound per-conn memory growth\n */\n async listen(port: number, host = '0.0.0.0'): Promise<void> {\n if (this.servers.has(port)) return;\n return new Promise((resolve, reject) => {\n const tls = this.config.tls;\n const wssOpts: ConstructorParameters<typeof WebSocketServer>[0] = {\n perMessageDeflate: {\n threshold: 256,\n zlibDeflateOptions: { level: 3 },\n serverNoContextTakeover: true,\n clientNoContextTakeover: true,\n },\n };\n // ADR-107: if cert+key paths are configured, bind via https.Server\n // (wss://). Otherwise bind plain ws:// directly.\n if (tls?.certPath && tls?.keyPath) {\n const cert = readFileSync(tls.certPath);\n const key = readFileSync(tls.keyPath);\n const httpsServer = createHttpsServer({ cert, key });\n httpsServer.listen(port, host, () => {\n const wss = new WebSocketServer({ ...wssOpts, server: httpsServer });\n this.attachServerHandlers(wss);\n this.servers.set(port, wss);\n resolve();\n });\n httpsServer.on('error', reject);\n return;\n }\n const wss = new WebSocketServer({ ...wssOpts, port, host });\n wss.on('listening', () => {\n this.servers.set(port, wss);\n resolve();\n });\n wss.on('error', reject);\n this.attachServerHandlers(wss);\n });\n }\n\n /**\n * Wire the server's `connection` and per-socket `message` handlers.\n * Extracted so the wss:// path (where the WebSocketServer is attached\n * to a pre-created https.Server) can share the same logic.\n */\n private attachServerHandlers(wss: WebSocketServer): void {\n wss.on('connection', (ws, req) => {\n const remoteAddr = `${req.socket.remoteAddress}:${req.socket.remotePort}`;\n // #162: register this accepted socket for outbound reuse, and probe\n // it for liveness. Reusing it for replies avoids a fresh (stale-route\n // prone) outbound dial back to the same peer.\n const host = this.hostOf(req.socket.remoteAddress ?? remoteAddr);\n this.inboundByHost.set(host, ws);\n this.startHeartbeat(ws);\n const dropInbound = () => {\n if (this.inboundByHost.get(host) === ws) this.inboundByHost.delete(host);\n };\n ws.on('close', dropInbound);\n ws.on('error', dropInbound);\n\n ws.on('message', (raw: RawData) => {\n try {\n const message = JSON.parse(raw.toString()) as AgentMessage;\n // Per-stream queue (default stream when message.streamId omitted)\n const key = this.queueKey(remoteAddr, this.streamOf(message));\n const queue = this.messageQueue.get(key) ?? [];\n queue.push(message);\n this.messageQueue.set(key, queue);\n this.dispatchInbound(remoteAddr, message);\n } catch (err) {\n logger.warn('Dropped malformed inbound WS message', { remoteAddr, err });\n }\n });\n });\n }\n\n private async getOrCreateConnection(address: string): Promise<WebSocket> {\n return new Promise((resolve, reject) => {\n const existing = this.connections.get(address);\n if (existing && existing.readyState === WebSocket.OPEN) {\n resolve(existing);\n return;\n }\n\n // #162: prefer an already-open INBOUND socket from the same peer host\n // before dialing. WebSocket is full-duplex, so the server-accepted\n // socket can carry our replies — and the inbound direction stays\n // healthy when a fresh outbound dial would hit a stale scoped route.\n const inbound = this.inboundByHost.get(this.hostOf(address));\n if (inbound && inbound.readyState === WebSocket.OPEN) {\n resolve(inbound);\n return;\n }\n\n const url = address.startsWith('ws://') || address.startsWith('wss://')\n ? address\n : `ws://${address}`;\n\n // ADR-107: cert pinning for wss:// peers. Build the WS options\n // with both compression (perf) and TLS hooks (security).\n const tls = this.config.tls;\n const isWss = url.startsWith('wss://');\n const wsOpts: WebSocket.ClientOptions = {\n perMessageDeflate: {\n threshold: 256,\n zlibDeflateOptions: { level: 3 },\n serverNoContextTakeover: true,\n clientNoContextTakeover: true,\n },\n };\n\n if (isWss && tls?.pinnedFingerprints && tls.pinnedFingerprints.length > 0) {\n // Fail-closed pinning: ONLY accept the listed cert fingerprints.\n // CA path validation is irrelevant — the fingerprint IS the trust\n // anchor. If the cert rotates, the operator must update config.\n const pinned = new Set(tls.pinnedFingerprints);\n // Cast: ws's `checkServerIdentity` is typed as returning boolean\n // but the underlying `tls.checkServerIdentity` (which it\n // forwards to) accepts `Error | undefined`. The runtime\n // semantics are: throw OR return Error → reject; otherwise accept.\n // The boolean type signature is overly restrictive.\n (wsOpts as unknown as { checkServerIdentity: unknown }).checkServerIdentity = (\n _host: string,\n cert: { raw: Buffer },\n ): Error | undefined => {\n // cert.raw is the DER-encoded cert bytes; sha256/<base64> matches\n // common pinning notation (and what `openssl x509 -fingerprint\n // -sha256` outputs after base64-encoding).\n const fp = `sha256/${createHash('sha256').update(cert.raw).digest('base64')}`;\n if (!pinned.has(fp)) {\n return new Error(\n `Federation TLS: peer cert fingerprint ${fp} not in pinned set ` +\n `(${pinned.size} fingerprint(s) configured)`,\n );\n }\n return undefined; // accept\n };\n // When pinning is on, we explicitly DON'T want CA validation\n // (the fingerprint check above is the real auth). But we also\n // DON'T want to silently accept ANY cert — the checkServerIdentity\n // above is still called.\n wsOpts.rejectUnauthorized = false;\n } else if (isWss && tls?.caPath) {\n // Non-pinned mode: validate against the configured CA bundle.\n wsOpts.ca = readFileSync(tls.caPath);\n wsOpts.rejectUnauthorized = true;\n }\n // (no tls config + ws:// → plain unencrypted; ADR-104 documents\n // tailnet-as-TLS as the recommended path)\n\n const ws = new WebSocket(url, wsOpts);\n\n ws.on('open', () => {\n this.connections.set(address, ws);\n this.connectionsCreated++;\n this.startHeartbeat(ws); // #162: keep the outbound link probed\n resolve(ws);\n });\n\n ws.on('error', (error: Error) => {\n reject(new Error(`WebSocket connection to ${url} failed: ${error.message}`));\n });\n\n ws.on('close', () => {\n this.connectionsClosed++;\n this.connections.delete(address);\n });\n\n ws.on('message', (raw: RawData) => {\n try {\n const message = JSON.parse(raw.toString()) as AgentMessage;\n const key = this.queueKey(address, this.streamOf(message));\n const queue = this.messageQueue.get(key) ?? [];\n queue.push(message);\n this.messageQueue.set(key, queue);\n this.dispatchInbound(address, message);\n } catch (err) {\n logger.warn('Dropped malformed WebSocket message', { address, err });\n }\n });\n });\n }\n\n async send(address: string, message: AgentMessage): Promise<void> {\n const ws = await this.getOrCreateConnection(address);\n ws.send(JSON.stringify(message));\n }\n\n /**\n * Register an inbound handler. Optional `options.streamId` scopes\n * the handler to a specific stream (only fires for messages with\n * matching streamId). Omit to subscribe to ALL streams.\n *\n * Patterns:\n * onMessage(h) — receives all\n * onMessage(h, { streamId: 'rpc' }) — receives only rpc\n * onMessage(h, { streamId: 'event' }) — receives only event\n * (both registered) — both fire on\n * their respective streams\n */\n onMessage(handler: InboundMessageHandler, options: OnMessageOptions = {}): void {\n this.inboundHandlers.add({ handler, streamId: options.streamId });\n }\n\n /**\n * Fire all matching handlers for a received message. Stream-scoped\n * handlers only fire when the message's streamId matches; all-stream\n * handlers always fire. Errors thrown sync OR async-rejected by one\n * handler don't stop delivery to others.\n */\n private dispatchInbound(address: string, message: AgentMessage): void {\n if (this.inboundHandlers.size === 0) return;\n const msgStream = this.streamOf(message);\n for (const entry of this.inboundHandlers) {\n // Stream filter: scoped handler only fires on matching streamId\n if (entry.streamId !== undefined && entry.streamId !== msgStream) continue;\n try {\n const r = entry.handler(address, message);\n if (r && typeof (r as Promise<void>).catch === 'function') {\n (r as Promise<void>).catch((err) => {\n logger.warn('Inbound handler rejected', { address, err });\n });\n }\n } catch (err) {\n logger.warn('Inbound handler threw', { address, err });\n }\n }\n }\n\n async receive(address: string, streamId: string | number = DEFAULT_STREAM_ID): Promise<AgentMessage> {\n const key = this.queueKey(address, streamId);\n // Fast path\n const queue = this.messageQueue.get(key) ?? [];\n if (queue.length > 0) return queue.shift()!;\n\n // Poll (caller must time out externally if they don't want to wait)\n return new Promise((resolve) => {\n const interval = setInterval(() => {\n const q = this.messageQueue.get(key) ?? [];\n if (q.length > 0) {\n clearInterval(interval);\n resolve(q.shift()!);\n }\n }, 100);\n });\n }\n\n async request(address: string, message: AgentMessage): Promise<AgentMessage> {\n await this.send(address, message);\n return this.receive(address);\n }\n\n async sendBatch(address: string, messages: AgentMessage[]): Promise<void> {\n await Promise.all(messages.map((m) => this.send(address, m)));\n }\n\n async getStats(): Promise<PoolStatistics> {\n return {\n active: this.connections.size,\n idle: 0,\n created: this.connectionsCreated,\n closed: this.connectionsClosed,\n };\n }\n\n async close(): Promise<void> {\n // Outbound clients first.\n for (const ws of this.connections.values()) {\n ws.terminate();\n }\n this.connections.clear();\n this.messageQueue.clear();\n\n // Stop all liveness probes and drop the inbound index (#162).\n for (const interval of this.heartbeats) clearInterval(interval);\n this.heartbeats.clear();\n this.inboundByHost.clear();\n\n // Inbound: WebSocketServer.close() blocks until every accepted\n // socket disconnects. Forcibly terminate them so the close\n // callback fires within the test/CI timeout window.\n for (const wss of this.servers.values()) {\n for (const client of wss.clients) {\n try {\n client.terminate();\n } catch {\n /* socket already gone */\n }\n }\n await new Promise<void>((resolve) => wss.close(() => resolve()));\n }\n this.servers.clear();\n }\n}\n\n/**\n * Detect whether the WASM-backed QUIC transport is \"real\" (i.e. it\n * actually moves bytes on the wire) vs the current stub. The stub\n * returns 0ms for connect+send and never increments the server's\n * received-bytes counter. We probe by observing a documented marker\n * on the WASM module: when it's truly wired the loader function\n * `defaultConfig` returns an object whose round-trip through\n * `WasmQuicClient.new` actually opens a UDP socket — failing fast on\n * an OS that blocks UDP outbound (e.g. some sandboxed CI envs).\n *\n * Until the native build lands this returns false; the loader picks\n * WebSocket. When the native binding is wired this returns true and\n * the loader picks real QUIC. Callers get the same API either way.\n */\nasync function isRealQuicAvailable(): Promise<boolean> {\n try {\n // The WASM file is published in `wasm/quic/` of this package. We\n // do NOT use it for federation today (per the wasm.rs note: it's a\n // stub since browsers can't do UDP). When a native binding is added\n // this probe should switch to detect that binding instead.\n const native = process.env.AGENTIC_FLOW_QUIC_NATIVE === '1';\n return native;\n } catch {\n return false;\n }\n}\n\n/**\n * Public API — load a working transport, preferring real QUIC when\n * available, falling back to WebSocket otherwise. The returned object\n * satisfies the AgentTransport interface in both cases.\n *\n * Example:\n * const t = await loadQuicTransport({ serverName: 'ruvultra:9100' });\n * await t.send('ruvultra:9100', { id: '1', type: 'task', payload: {...} });\n *\n * Federation v1 ships on the WebSocket fallback (this is the actual\n * working transport today). When the native QUIC binding lands, set\n * the AGENTIC_FLOW_QUIC_NATIVE=1 environment variable and the same\n * code path picks up the upgrade with no API changes.\n */\nexport async function loadQuicTransport(\n config: QuicTransportConfig = {},\n): Promise<AgentTransport> {\n if (await isRealQuicAvailable()) {\n // Future: wire to the native binding here.\n logger.info('QUIC transport: native binding selected');\n } else {\n if (process.env.NODE_ENV !== 'test') {\n logger.warn(\n 'QUIC native binding not available; using WebSocket fallback. ' +\n 'Set AGENTIC_FLOW_QUIC_NATIVE=1 once a native build is installed.',\n );\n }\n }\n return WebSocketFallbackTransport.create(config);\n}\n\n/** Quick capability probe for the doctor / health surface. */\nexport async function isQuicAvailable(): Promise<boolean> {\n return isRealQuicAvailable();\n}\n\nexport interface TransportCapabilities {\n quicAvailable: boolean;\n webSocketFallbackAvailable: true;\n selectedBackend: 'quic' | 'websocket';\n}\n\nexport async function getTransportCapabilities(): Promise<TransportCapabilities> {\n const quic = await isRealQuicAvailable();\n return {\n quicAvailable: quic,\n webSocketFallbackAvailable: true,\n selectedBackend: quic ? 'quic' : 'websocket',\n };\n}\n\nexport { WebSocketFallbackTransport };\n"]}
|
|
@@ -162,7 +162,7 @@ export async function getCachedOnnxEmbedder() {
|
|
|
162
162
|
}
|
|
163
163
|
export async function getCachedTransformersPipeline(task = 'feature-extraction', model = 'Xenova/all-MiniLM-L6-v2') {
|
|
164
164
|
return modelCache.getOrLoad(`transformers:${task}:${model}`, async () => {
|
|
165
|
-
const { pipeline } = await import('@
|
|
165
|
+
const { pipeline } = await import('@huggingface/transformers');
|
|
166
166
|
return pipeline(task, model);
|
|
167
167
|
}, 200 * 1024 * 1024 // 200MB estimate for transformers model
|
|
168
168
|
);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"model-cache.js","sourceRoot":"","sources":["../../src/utils/model-cache.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Global Model Cache\n *\n * Caches loaded models in memory to avoid repeated initialization overhead.\n * Supports ONNX embeddings, transformers.js pipelines, and other heavy models.\n */\n\ninterface CachedModel {\n model: any;\n loadedAt: number;\n lastUsed: number;\n useCount: number;\n sizeEstimate: number; // bytes\n}\n\ninterface CacheStats {\n models: number;\n totalSize: number;\n size?: number;\n totalHits: number;\n totalMisses: number;\n hits: number;\n misses: number;\n hitRate: number;\n}\n\nclass ModelCache {\n private cache = new Map<string, CachedModel>();\n private maxSize: number; // Max cache size in bytes\n private totalHits = 0;\n private totalMisses = 0;\n\n constructor(maxSizeMB: number = 512) {\n this.maxSize = maxSizeMB * 1024 * 1024;\n }\n\n /**\n * Get a cached model or load it\n */\n async getOrLoad<T>(\n key: string,\n loader: () => Promise<T>,\n sizeEstimate: number = 50 * 1024 * 1024 // Default 50MB estimate\n ): Promise<T> {\n const cached = this.cache.get(key);\n if (cached) {\n cached.lastUsed = Date.now();\n cached.useCount++;\n this.totalHits++;\n return cached.model as T;\n }\n\n this.totalMisses++;\n\n // Evict if needed\n this.evictIfNeeded(sizeEstimate);\n\n // Load model\n const model = await loader();\n\n this.cache.set(key, {\n model,\n loadedAt: Date.now(),\n lastUsed: Date.now(),\n useCount: 1,\n sizeEstimate\n });\n\n return model;\n }\n\n /**\n * Check if model is cached\n */\n has(key: string): boolean {\n return this.cache.has(key);\n }\n\n /**\n * Get cached model without loading\n */\n get<T>(key: string): T | undefined {\n const cached = this.cache.get(key);\n if (cached) {\n cached.lastUsed = Date.now();\n cached.useCount++;\n return cached.model as T;\n }\n return undefined;\n }\n\n /**\n * Manually cache a model\n */\n set(key: string, model: any, sizeEstimate: number = 50 * 1024 * 1024): void {\n this.evictIfNeeded(sizeEstimate);\n this.cache.set(key, {\n model,\n loadedAt: Date.now(),\n lastUsed: Date.now(),\n useCount: 1,\n sizeEstimate\n });\n }\n\n /**\n * Remove a model from cache\n */\n delete(key: string): boolean {\n return this.cache.delete(key);\n }\n\n /**\n * Clear all cached models\n */\n clear(): void {\n this.cache.clear();\n }\n\n /**\n * Get cache statistics\n */\n getStats(): CacheStats {\n let totalSize = 0;\n for (const cached of this.cache.values()) {\n totalSize += cached.sizeEstimate;\n }\n const hits = this.totalHits;\n const misses = this.totalMisses;\n const total = hits + misses;\n return {\n models: this.cache.size,\n totalSize,\n size: this.cache.size,\n totalHits: hits,\n totalMisses: misses,\n hits,\n misses,\n hitRate: total > 0 ? hits / total : 0\n };\n }\n\n /**\n * Evict least recently used models if cache is full\n */\n private evictIfNeeded(newSize: number): void {\n let currentSize = 0;\n for (const cached of this.cache.values()) {\n currentSize += cached.sizeEstimate;\n }\n\n if (currentSize + newSize <= this.maxSize) {\n return;\n }\n\n // Sort by last used time (LRU)\n const entries = Array.from(this.cache.entries())\n .sort((a, b) => a[1].lastUsed - b[1].lastUsed);\n\n // Evict until we have space\n for (const [key, cached] of entries) {\n if (currentSize + newSize <= this.maxSize) {\n break;\n }\n this.cache.delete(key);\n currentSize -= cached.sizeEstimate;\n }\n }\n}\n\n// Global singleton\nexport const modelCache = new ModelCache();\n\n// Convenience functions\nexport async function getCachedOnnxEmbedder(): Promise<any> {\n return modelCache.getOrLoad(\n 'onnx-embeddings',\n async () => {\n // Suppress experimental warning for WASM\n const originalEmit = process.emit;\n // @ts-ignore\n process.emit = function (name, data, ...args) {\n if (name === 'warning' && typeof data === 'object' &&\n (data as any).name === 'ExperimentalWarning' &&\n (data as any).message?.includes('Import')) {\n return false;\n }\n // @ts-ignore\n return originalEmit.apply(process, [name, data, ...args]);\n };\n\n try {\n const onnxModule = await import('ruvector-onnx-embeddings-wasm') as any;\n const EmbedderClass = onnxModule.OnnxEmbeddings || onnxModule.default;\n if (EmbedderClass) {\n const embedder = new EmbedderClass();\n await embedder.initialize?.();\n return embedder;\n }\n } finally {\n process.emit = originalEmit;\n }\n return null;\n },\n 100 * 1024 * 1024 // 100MB estimate for ONNX model\n );\n}\n\nexport async function getCachedTransformersPipeline(\n task: string = 'feature-extraction',\n model: string = 'Xenova/all-MiniLM-L6-v2'\n): Promise<any> {\n return modelCache.getOrLoad(\n `transformers:${task}:${model}`,\n async () => {\n const { pipeline } = await import('@xenova/transformers');\n return pipeline(task as any, model);\n },\n 200 * 1024 * 1024 // 200MB estimate for transformers model\n );\n}\n\nexport async function getCachedRuvectorCore(): Promise<any> {\n return modelCache.getOrLoad(\n 'ruvector-core',\n async () => {\n const ruvector = await import('ruvector');\n return ruvector;\n },\n 50 * 1024 * 1024\n );\n}\n"]}
|
|
1
|
+
{"version":3,"file":"model-cache.js","sourceRoot":"","sources":["../../src/utils/model-cache.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Global Model Cache\n *\n * Caches loaded models in memory to avoid repeated initialization overhead.\n * Supports ONNX embeddings, transformers.js pipelines, and other heavy models.\n */\n\ninterface CachedModel {\n model: any;\n loadedAt: number;\n lastUsed: number;\n useCount: number;\n sizeEstimate: number; // bytes\n}\n\ninterface CacheStats {\n models: number;\n totalSize: number;\n size?: number;\n totalHits: number;\n totalMisses: number;\n hits: number;\n misses: number;\n hitRate: number;\n}\n\nclass ModelCache {\n private cache = new Map<string, CachedModel>();\n private maxSize: number; // Max cache size in bytes\n private totalHits = 0;\n private totalMisses = 0;\n\n constructor(maxSizeMB: number = 512) {\n this.maxSize = maxSizeMB * 1024 * 1024;\n }\n\n /**\n * Get a cached model or load it\n */\n async getOrLoad<T>(\n key: string,\n loader: () => Promise<T>,\n sizeEstimate: number = 50 * 1024 * 1024 // Default 50MB estimate\n ): Promise<T> {\n const cached = this.cache.get(key);\n if (cached) {\n cached.lastUsed = Date.now();\n cached.useCount++;\n this.totalHits++;\n return cached.model as T;\n }\n\n this.totalMisses++;\n\n // Evict if needed\n this.evictIfNeeded(sizeEstimate);\n\n // Load model\n const model = await loader();\n\n this.cache.set(key, {\n model,\n loadedAt: Date.now(),\n lastUsed: Date.now(),\n useCount: 1,\n sizeEstimate\n });\n\n return model;\n }\n\n /**\n * Check if model is cached\n */\n has(key: string): boolean {\n return this.cache.has(key);\n }\n\n /**\n * Get cached model without loading\n */\n get<T>(key: string): T | undefined {\n const cached = this.cache.get(key);\n if (cached) {\n cached.lastUsed = Date.now();\n cached.useCount++;\n return cached.model as T;\n }\n return undefined;\n }\n\n /**\n * Manually cache a model\n */\n set(key: string, model: any, sizeEstimate: number = 50 * 1024 * 1024): void {\n this.evictIfNeeded(sizeEstimate);\n this.cache.set(key, {\n model,\n loadedAt: Date.now(),\n lastUsed: Date.now(),\n useCount: 1,\n sizeEstimate\n });\n }\n\n /**\n * Remove a model from cache\n */\n delete(key: string): boolean {\n return this.cache.delete(key);\n }\n\n /**\n * Clear all cached models\n */\n clear(): void {\n this.cache.clear();\n }\n\n /**\n * Get cache statistics\n */\n getStats(): CacheStats {\n let totalSize = 0;\n for (const cached of this.cache.values()) {\n totalSize += cached.sizeEstimate;\n }\n const hits = this.totalHits;\n const misses = this.totalMisses;\n const total = hits + misses;\n return {\n models: this.cache.size,\n totalSize,\n size: this.cache.size,\n totalHits: hits,\n totalMisses: misses,\n hits,\n misses,\n hitRate: total > 0 ? hits / total : 0\n };\n }\n\n /**\n * Evict least recently used models if cache is full\n */\n private evictIfNeeded(newSize: number): void {\n let currentSize = 0;\n for (const cached of this.cache.values()) {\n currentSize += cached.sizeEstimate;\n }\n\n if (currentSize + newSize <= this.maxSize) {\n return;\n }\n\n // Sort by last used time (LRU)\n const entries = Array.from(this.cache.entries())\n .sort((a, b) => a[1].lastUsed - b[1].lastUsed);\n\n // Evict until we have space\n for (const [key, cached] of entries) {\n if (currentSize + newSize <= this.maxSize) {\n break;\n }\n this.cache.delete(key);\n currentSize -= cached.sizeEstimate;\n }\n }\n}\n\n// Global singleton\nexport const modelCache = new ModelCache();\n\n// Convenience functions\nexport async function getCachedOnnxEmbedder(): Promise<any> {\n return modelCache.getOrLoad(\n 'onnx-embeddings',\n async () => {\n // Suppress experimental warning for WASM\n const originalEmit = process.emit;\n // @ts-ignore\n process.emit = function (name, data, ...args) {\n if (name === 'warning' && typeof data === 'object' &&\n (data as any).name === 'ExperimentalWarning' &&\n (data as any).message?.includes('Import')) {\n return false;\n }\n // @ts-ignore\n return originalEmit.apply(process, [name, data, ...args]);\n };\n\n try {\n const onnxModule = await import('ruvector-onnx-embeddings-wasm') as any;\n const EmbedderClass = onnxModule.OnnxEmbeddings || onnxModule.default;\n if (EmbedderClass) {\n const embedder = new EmbedderClass();\n await embedder.initialize?.();\n return embedder;\n }\n } finally {\n process.emit = originalEmit;\n }\n return null;\n },\n 100 * 1024 * 1024 // 100MB estimate for ONNX model\n );\n}\n\nexport async function getCachedTransformersPipeline(\n task: string = 'feature-extraction',\n model: string = 'Xenova/all-MiniLM-L6-v2'\n): Promise<any> {\n return modelCache.getOrLoad(\n `transformers:${task}:${model}`,\n async () => {\n const { pipeline } = await import('@huggingface/transformers');\n return pipeline(task as any, model);\n },\n 200 * 1024 * 1024 // 200MB estimate for transformers model\n );\n}\n\nexport async function getCachedRuvectorCore(): Promise<any> {\n return modelCache.getOrLoad(\n 'ruvector-core',\n async () => {\n const ruvector = await import('ruvector');\n return ruvector;\n },\n 50 * 1024 * 1024\n );\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agentic-flow",
|
|
3
|
-
"version": "2.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "2.1.1",
|
|
4
|
+
"description": "The agentic meta-harness — freeze the model, evolve the harness. An open runtime that routes each query to the cost-optimal model, evolves its own harness (planner/context/reviewer/retry/tool/memory/score policy) and autonomously repairs code, then orchestrates 66 specialized agents, 213 MCP tools, ReasoningBank memory, and multi-agent swarms on top. Built by @ruvnet on the Claude Agent SDK.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"bin": {
|
|
8
8
|
"agentic-flow": "dist/cli-proxy.js",
|
|
9
|
-
"agentdb": "dist/agentdb/cli/agentdb-cli.js"
|
|
9
|
+
"agentdb": "dist/agentdb/cli/agentdb-cli.js",
|
|
10
|
+
"agentic-flow-repair": "dist/repair/cli.js"
|
|
10
11
|
},
|
|
11
12
|
"exports": {
|
|
12
13
|
".": "./dist/index.js",
|
|
@@ -21,6 +22,11 @@
|
|
|
21
22
|
"./reasoningbank/backend-selector": "./dist/reasoningbank/backend-selector.js",
|
|
22
23
|
"./reasoningbank/wasm-adapter": "./dist/reasoningbank/wasm-adapter.js",
|
|
23
24
|
"./router": "./dist/router/index.js",
|
|
25
|
+
"./router/cost-optimal": "./dist/router/cost-optimal-router.js",
|
|
26
|
+
"./repair": "./dist/repair/darwin-repair.js",
|
|
27
|
+
"./harness/provenance": "./dist/harness/provenance.js",
|
|
28
|
+
"./harness/metaharness": "./dist/harness/metaharness.js",
|
|
29
|
+
"./harness/governance": "./dist/harness/flywheel-governance.js",
|
|
24
30
|
"./agent-booster": "./dist/agent-booster/index.js",
|
|
25
31
|
"./transport/quic": "./dist/transport/quic.js",
|
|
26
32
|
"./embeddings": "./dist/embeddings/index.js",
|
|
@@ -29,7 +35,7 @@
|
|
|
29
35
|
"scripts": {
|
|
30
36
|
"postinstall": "node scripts/postinstall.js || true",
|
|
31
37
|
"start": "node --enable-source-maps dist/index.js",
|
|
32
|
-
"build": "(npm run build:wasm || true) &&
|
|
38
|
+
"build": "(npm run build:wasm || true) && tsc -p config/tsconfig.json --skipLibCheck && mkdir -p dist/reasoningbank && (cp -r src/reasoningbank/prompts dist/reasoningbank/ 2>/dev/null || true) && (cp -r src/reasoningbank/config dist/reasoningbank/ 2>/dev/null || true)",
|
|
33
39
|
"build:wasm": "cd ../reasoningbank && wasm-pack build --target bundler --out-dir pkg/bundler crates/reasoningbank-wasm && wasm-pack build --target web --out-dir pkg/web crates/reasoningbank-wasm && mkdir -p ../agentic-flow/wasm/reasoningbank && cp -r crates/reasoningbank-wasm/pkg/bundler/* ../agentic-flow/wasm/reasoningbank/ && cp -r crates/reasoningbank-wasm/pkg/web ../agentic-flow/wasm/reasoningbank/",
|
|
34
40
|
"build:wasm:clean": "rm -rf ../reasoningbank/crates/reasoningbank-wasm/pkg && rm -rf wasm/reasoningbank",
|
|
35
41
|
"dev": "tsx src/index.ts",
|
|
@@ -63,9 +69,18 @@
|
|
|
63
69
|
"proxy:quic:dev": "tsx src/proxy/quic-proxy.ts",
|
|
64
70
|
"benchmark:embeddings": "tsx src/benchmarks/embeddings-benchmark.ts",
|
|
65
71
|
"test:orchestration": "vitest run tests/orchestration/ --reporter=verbose",
|
|
72
|
+
"test:governance": "vitest run tests/harness/provenance.test.ts tests/harness/flywheel-governance.test.ts tests/mcp/mcp-policy.test.ts tests/embeddings/huggingface-offline.test.ts tests/repair/darwin-repair.test.ts",
|
|
66
73
|
"test:orchestration:smoke": "tsx tests/orchestration/orchestration-api.smoke.ts && tsx tests/orchestration/loop-policy.smoke.ts && tsx tests/orchestration/memory-plane.smoke.ts"
|
|
67
74
|
},
|
|
68
75
|
"keywords": [
|
|
76
|
+
"meta-harness",
|
|
77
|
+
"metaharness",
|
|
78
|
+
"agent-harness",
|
|
79
|
+
"harness-evolution",
|
|
80
|
+
"model-routing",
|
|
81
|
+
"cost-optimal-routing",
|
|
82
|
+
"self-improving",
|
|
83
|
+
"darwin",
|
|
69
84
|
"ai",
|
|
70
85
|
"ai-agents",
|
|
71
86
|
"ai-orchestration",
|
|
@@ -146,19 +161,20 @@
|
|
|
146
161
|
},
|
|
147
162
|
"homepage": "https://github.com/ruvnet/agentic-flow#readme",
|
|
148
163
|
"engines": {
|
|
149
|
-
"node": ">=
|
|
164
|
+
"node": ">=20.0.0"
|
|
150
165
|
},
|
|
151
166
|
"dependencies": {
|
|
152
167
|
"@anthropic-ai/claude-agent-sdk": "^0.1.5",
|
|
153
168
|
"@anthropic-ai/sdk": "^0.65.0",
|
|
154
169
|
"@google/genai": "^1.22.0",
|
|
170
|
+
"@metaharness/router": "^0.3.2",
|
|
155
171
|
"@ruvector/core": "^0.1.29",
|
|
156
172
|
"@ruvector/edge-full": "^0.1.0",
|
|
157
|
-
"@ruvector/router": "^0.1.
|
|
158
|
-
"@ruvector/ruvllm": "^
|
|
173
|
+
"@ruvector/router": "^0.1.30",
|
|
174
|
+
"@ruvector/ruvllm": "^2.5.5",
|
|
159
175
|
"@ruvector/tiny-dancer": "^0.1.17",
|
|
160
176
|
"@supabase/supabase-js": "^2.78.0",
|
|
161
|
-
"@
|
|
177
|
+
"@huggingface/transformers": "^3.8.1",
|
|
162
178
|
"axios": "^1.12.2",
|
|
163
179
|
"dotenv": "^16.4.5",
|
|
164
180
|
"express": "^5.1.0",
|
|
@@ -166,7 +182,7 @@
|
|
|
166
182
|
"glob": "^13.0.0",
|
|
167
183
|
"gun": "^0.2020.1241",
|
|
168
184
|
"http-proxy-middleware": "^3.0.5",
|
|
169
|
-
"ruvector": "
|
|
185
|
+
"ruvector": "0.2.40",
|
|
170
186
|
"ruvector-onnx-embeddings-wasm": "^0.1.2",
|
|
171
187
|
"tiktoken": "^1.0.22",
|
|
172
188
|
"ulid": "^3.0.1",
|
|
@@ -181,12 +197,12 @@
|
|
|
181
197
|
"agentdb": "^3.0.0-alpha.14",
|
|
182
198
|
"better-sqlite3": "^11.10.0",
|
|
183
199
|
"onnxruntime-node": "^1.23.2",
|
|
184
|
-
"sharp": "^0.
|
|
200
|
+
"sharp": "^0.34.3",
|
|
185
201
|
"sql.js": "^1.11.0"
|
|
186
202
|
},
|
|
187
203
|
"overrides": {
|
|
188
204
|
"protobufjs": ">=7.5.5",
|
|
189
|
-
"@
|
|
205
|
+
"@huggingface/transformers": {
|
|
190
206
|
"sharp": "$sharp"
|
|
191
207
|
}
|
|
192
208
|
},
|