mirror of
https://github.com/ruvnet/ruflo.git
synced 2026-11-09 05:35:21 +09:00
* feat(agent): add bounded managed dynamic workflows and verified lifecycle observation * feat(plugins): integrate managed workflows with mod policy and capability discovery * feat(managed): unify console runs and verified advisory learning * fix(managed): hydrate native recall receipts validated on ruOS * docs(managed): record authenticated console host blocker * docs(managed): qualify installed console and compatible native host * feat(managed): integrate verified cycles and isolate native memory * fix(managed): enforce offline native models and qualify feedback E2E * fix(managed): independent security and correctness review findings - gate managed_agent_interrupt through the managed_agent_control policy; terminate obeys the allowlist and deletes only its own environment when RUFLO_MANAGED_CONTROL=1 - track only events from the acknowledged message so a later poll cannot report the previous turn as finished - require the canonical base64 verifier signature so one receipt cannot fill the ledger - register RUFLO_MANAGED_AGENTS_BASE_URL as a trust-boundary env var (env-precedence audit) and key the credential scope marker with a per-process HMAC (CodeQL) - best-effort local snapshot writes; report messageId when a prompt fails after it was sent - console: rescheduling is running, budget/action-blocked sessions are active, not finished - document the remaining known limits in ADR 0004 Co-Authored-By: RuFlo <ruv@ruv.net> * docs: user guide section for managed cloud agents; correct ruflo-agent README counts Co-Authored-By: RuFlo <ruv@ruv.net> * fix(managed): scope marker is a generation number, never a hash of the API key (CodeQL) Co-Authored-By: RuFlo <ruv@ruv.net> --------- Co-authored-by: r <r@x>
57 lines
3.4 KiB
JavaScript
57 lines
3.4 KiB
JavaScript
/** Deterministic event-processing benchmark, not a model-quality benchmark.
|
|
* Node >=22.18: node scripts/bench-managed-workflows.mjs
|
|
*/
|
|
import { performance } from 'node:perf_hooks';
|
|
import { createHash } from 'node:crypto';
|
|
import { readFileSync } from 'node:fs';
|
|
import { execFileSync } from 'node:child_process';
|
|
import { ManagedProgress } from '../v3/@claude-flow/cli/src/services/managed-workflows.ts';
|
|
|
|
const baselineCommit = '973d0ab497c9138d7bee1dc3c7a4d3985e37bf9e';
|
|
const baselinePath = 'v3/@claude-flow/cli/src/mcp-tools/managed-agent-tools.ts';
|
|
const baselineSource = execFileSync('git', ['show', `${baselineCommit}:${baselinePath}`], { encoding: 'utf8' });
|
|
// Exercise the exact original summarizer from the frozen source, compiled by
|
|
// Node's strip-types, rather than substituting a slower baseline algorithm.
|
|
const { stripTypeScriptTypes } = await import('node:module');
|
|
const original = baselineSource.slice(baselineSource.indexOf('function summarizeEvents('), baselineSource.indexOf('async function fetchSessionEvents('));
|
|
const summarize = new Function(`${stripTypeScriptTypes(original)}; return summarizeEvents;`)();
|
|
const hash = value => createHash('sha256').update(value).digest('hex');
|
|
const candidateSource = readFileSync('v3/@claude-flow/cli/src/services/managed-workflows.ts');
|
|
const events = Array.from({ length: 5000 }, (_, i) => ({
|
|
id: `e${i}`, type: 'agent.message', processed_at: new Date(1792843200000 + i).toISOString(),
|
|
content: [{ type: 'text', text: 'bounded result\n' }],
|
|
}));
|
|
events.push({ id: 'idle', type: 'session.status_idle', processed_at: '2026-10-24T13:00:00Z', stop_reason: { type: 'end_turn' } });
|
|
|
|
function run(mode) {
|
|
const p = new ManagedProgress(); let result;
|
|
const start = performance.now();
|
|
for (let n = 100; n < events.length + 100; n += 100) {
|
|
result = mode === 'baseline' ? summarize(events.slice(0, n)) : (p.accept(events.slice(n - 100, n)), p.snapshot());
|
|
}
|
|
return { ms: performance.now() - start, result };
|
|
}
|
|
for (let i = 0; i < 3; i++) { run('baseline'); run('incremental'); }
|
|
const baseline = [], candidate = [];
|
|
for (let i = 0; i < 9; i++) {
|
|
// Alternate order to reduce systematic warmup bias.
|
|
const order = i % 2 ? ['baseline', 'incremental'] : ['incremental', 'baseline'];
|
|
for (const mode of order) {
|
|
const r = run(mode); (mode === 'baseline' ? baseline : candidate).push(r.ms);
|
|
if (r.result.assistantText !== events.slice(0, -1).map(e => e.content[0].text).join('')) throw new Error('Output mismatch');
|
|
}
|
|
}
|
|
const median = xs => [...xs].sort((a, b) => a - b)[Math.floor(xs.length / 2)];
|
|
const polls = Math.ceil(events.length / 100);
|
|
const baselineVisits = Array.from({ length: polls }, (_, i) => Math.min(events.length, (i + 1) * 100)).reduce((a, b) => a + b, 0);
|
|
console.log(JSON.stringify({
|
|
kind: 'synthetic event processing only', node: process.version, baselineCommit,
|
|
baselineSha256: hash(baselineSource), candidateSha256: hash(candidateSource),
|
|
events: events.length, polls, repeats: 9,
|
|
baselineMedianMs: median(baseline), candidateMedianMs: median(candidate),
|
|
latencyRatio: median(baseline) / median(candidate),
|
|
baselineEventVisits: baselineVisits, incrementalEventVisits: events.length,
|
|
eventVisitReduction: 1 - events.length / baselineVisits,
|
|
limitations: 'No provider latency, token savings, dollar savings or bug-finding improvement measured. Snapshot output assembly is included in both timings.',
|
|
}, null, 2));
|