@claude-flow/cli 3.38.23 → 3.39.0
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.
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"manifest": {
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.39.0",
|
|
4
4
|
"files": {
|
|
5
5
|
"auto-memory-hook.mjs": "85fe05c757421c52137c0bc8545a0896bab6b4714538c2a11d1d0835bfcc8c1c",
|
|
6
6
|
"hook-handler.cjs": "209d9fafe10e17d1be0866727f6f9cf9ac66f9a0793f1c793a4f58319e8e4583",
|
|
@@ -8,6 +8,6 @@
|
|
|
8
8
|
"statusline.cjs": "4a48353b4f1566fa4379b00fd0321b6676a22b6cc91fbbd8380ac5183d619468"
|
|
9
9
|
}
|
|
10
10
|
},
|
|
11
|
-
"signature": "
|
|
11
|
+
"signature": "ezHXe7aYuhhtcjCgjytboA5ImEY+bpGDC7eJXkqXPmlVHtNyG6I0DPoBUV2SPSeyf4VRRjME3gdANH35XSMmDg==",
|
|
12
12
|
"algorithm": "ed25519"
|
|
13
13
|
}
|
package/README.md
CHANGED
|
@@ -21,6 +21,11 @@
|
|
|
21
21
|
|
|
22
22
|
**An agent meta-harness for Claude Code and Codex.**
|
|
23
23
|
|
|
24
|
+
[](docs/ruflo-explained.md)
|
|
25
|
+
|
|
26
|
+
**[📖 RuFlo Explained — Build an AI Team That Plans, Remembers, Tests, and Improves](docs/ruflo-explained.md)**
|
|
27
|
+
A 14-chapter guide: from the basic idea to a first useful task, then memory, agent teams, plugins, cost and verification.
|
|
28
|
+
|
|
24
29
|
</div>
|
|
25
30
|
|
|
26
31
|
> **Agent = Model + Harness.** The model writes; the harness gives it tools, memory, loops, sandboxes, and controls so it can actually work. **Ruflo is the harness** — the execution layer around Claude Code and Codex that adds 100+ specialized agents, coordinated swarms, self-learning memory, federated comms across machines, and enterprise security guardrails. So agents don't just run, they collaborate.
|
package/catalog-manifest.json
CHANGED
|
@@ -139,9 +139,44 @@ export declare function collectUnknownReceiptFields(receipt: FlywheelEvaluationR
|
|
|
139
139
|
*/
|
|
140
140
|
export declare function canonicalizeJcs(value: unknown): string;
|
|
141
141
|
export declare function sha256Ref(value: string | Buffer): string;
|
|
142
|
+
/**
|
|
143
|
+
* Enforce ADR-322C rule 2 / conformance-checklist A3 over a receipt payload:
|
|
144
|
+
* "Every fractional value is a canonical decimal string, not a binary float."
|
|
145
|
+
*
|
|
146
|
+
* This lives at the RECEIPT boundary rather than inside `canonicalizeJcs`,
|
|
147
|
+
* which is shared with the proposer envelope (`flywheel-proposer.ts`) and the
|
|
148
|
+
* promotion ledger (`flywheel-transaction.ts`) — structures the contract does
|
|
149
|
+
* not govern. A first attempt put the check in the canonicalizer and broke
|
|
150
|
+
* those callers, which is the reason the scope is spelled out here.
|
|
151
|
+
*
|
|
152
|
+
* Integers and scaled integers stay JSON numbers (currency micros, durations,
|
|
153
|
+
* iteration counts). A fractional JSON number anywhere in the payload is a
|
|
154
|
+
* contract violation, and the error names the path — the original bug
|
|
155
|
+
* (ruvnet/ruflo#3229) needed a live fixture to find precisely because neither
|
|
156
|
+
* verifier said which field was wrong.
|
|
157
|
+
*/
|
|
158
|
+
export declare function assertReceiptNumberDomain(value: unknown, path?: string): void;
|
|
142
159
|
export declare function policyCandidateId(policy: Record<string, unknown>): string;
|
|
143
160
|
/** UUIDv7 with a 48-bit millisecond timestamp and RFC-4122 variant bits. */
|
|
144
161
|
export declare function uuidV7(now?: number): string;
|
|
162
|
+
/**
|
|
163
|
+
* Encode an opaque policy object so every fractional value is a scale-12
|
|
164
|
+
* decimal string, per ADR-322C rule 2 and conformance-checklist A3.
|
|
165
|
+
*
|
|
166
|
+
* `candidatePolicy` is declared "Opaque to this contract; its shape is owned by
|
|
167
|
+
* policySchemaVersion. Must still satisfy the ADR-322C number rules" — a rule
|
|
168
|
+
* the JSON Schema cannot express for an opaque object, so nothing enforced it
|
|
169
|
+
* and the producer wrote binary floats (`{"alpha": 0.3, "mmrLambda": 0.5}`).
|
|
170
|
+
* Ruflo's own verifier accepted them because `assertJsonValue` only checked
|
|
171
|
+
* finite-and-not-negative-zero; autogenous's stricter verifier correctly
|
|
172
|
+
* rejected the receipt (ruvnet/ruflo#3229, ruvnet/autogenous#15).
|
|
173
|
+
*
|
|
174
|
+
* Integers stay JSON numbers and only non-integers are encoded, which is what
|
|
175
|
+
* the contract's own example shows: `{"hnswEf": 128, "hybridWeight": "0.65"}`.
|
|
176
|
+
* Recurses through nested objects and arrays, because "every fractional value"
|
|
177
|
+
* is not limited to the top level.
|
|
178
|
+
*/
|
|
179
|
+
export declare function encodePolicyFractions(value: unknown): unknown;
|
|
145
180
|
export declare function computePromotionStatistics(input: {
|
|
146
181
|
baselineScore: number;
|
|
147
182
|
candidateScore: number;
|
|
@@ -123,8 +123,47 @@ export function canonicalizeJcs(value) {
|
|
|
123
123
|
export function sha256Ref(value) {
|
|
124
124
|
return `sha256:${createHash('sha256').update(value).digest('hex')}`;
|
|
125
125
|
}
|
|
126
|
+
/**
|
|
127
|
+
* Enforce ADR-322C rule 2 / conformance-checklist A3 over a receipt payload:
|
|
128
|
+
* "Every fractional value is a canonical decimal string, not a binary float."
|
|
129
|
+
*
|
|
130
|
+
* This lives at the RECEIPT boundary rather than inside `canonicalizeJcs`,
|
|
131
|
+
* which is shared with the proposer envelope (`flywheel-proposer.ts`) and the
|
|
132
|
+
* promotion ledger (`flywheel-transaction.ts`) — structures the contract does
|
|
133
|
+
* not govern. A first attempt put the check in the canonicalizer and broke
|
|
134
|
+
* those callers, which is the reason the scope is spelled out here.
|
|
135
|
+
*
|
|
136
|
+
* Integers and scaled integers stay JSON numbers (currency micros, durations,
|
|
137
|
+
* iteration counts). A fractional JSON number anywhere in the payload is a
|
|
138
|
+
* contract violation, and the error names the path — the original bug
|
|
139
|
+
* (ruvnet/ruflo#3229) needed a live fixture to find precisely because neither
|
|
140
|
+
* verifier said which field was wrong.
|
|
141
|
+
*/
|
|
142
|
+
export function assertReceiptNumberDomain(value, path = '$') {
|
|
143
|
+
if (typeof value === 'number') {
|
|
144
|
+
if (!Number.isInteger(value)) {
|
|
145
|
+
throw new Error(`fractional number at ${path} must be a scale-12 decimal string (ADR-322C rule 2)`);
|
|
146
|
+
}
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
if (Array.isArray(value)) {
|
|
150
|
+
value.forEach((v, i) => assertReceiptNumberDomain(v, `${path}[${i}]`));
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
if (value && typeof value === 'object') {
|
|
154
|
+
for (const [k, v] of Object.entries(value)) {
|
|
155
|
+
assertReceiptNumberDomain(v, `${path}.${k}`);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
126
159
|
export function policyCandidateId(policy) {
|
|
127
|
-
|
|
160
|
+
// Encode at the hashing boundary so the content ID is always over the
|
|
161
|
+
// CANONICAL form, whether the caller passed a raw config or an
|
|
162
|
+
// already-encoded policy. `encodePolicyFractions` is idempotent — an
|
|
163
|
+
// encoded value is a string and passes through untouched — so
|
|
164
|
+
// policyCandidateId(raw) === policyCandidateId(encoded), which is what lets
|
|
165
|
+
// `verify` recompute the ID from the payload and still match.
|
|
166
|
+
return sha256Ref(canonicalizeJcs(encodePolicyFractions(policy)));
|
|
128
167
|
}
|
|
129
168
|
/** UUIDv7 with a 48-bit millisecond timestamp and RFC-4122 variant bits. */
|
|
130
169
|
export function uuidV7(now = Date.now()) {
|
|
@@ -143,6 +182,38 @@ const decimal = (value, scale = 12) => {
|
|
|
143
182
|
const normalized = value.toFixed(scale).replace(/\.?0+$/, '');
|
|
144
183
|
return normalized === '-0' || normalized === '' ? '0' : normalized;
|
|
145
184
|
};
|
|
185
|
+
/**
|
|
186
|
+
* Encode an opaque policy object so every fractional value is a scale-12
|
|
187
|
+
* decimal string, per ADR-322C rule 2 and conformance-checklist A3.
|
|
188
|
+
*
|
|
189
|
+
* `candidatePolicy` is declared "Opaque to this contract; its shape is owned by
|
|
190
|
+
* policySchemaVersion. Must still satisfy the ADR-322C number rules" — a rule
|
|
191
|
+
* the JSON Schema cannot express for an opaque object, so nothing enforced it
|
|
192
|
+
* and the producer wrote binary floats (`{"alpha": 0.3, "mmrLambda": 0.5}`).
|
|
193
|
+
* Ruflo's own verifier accepted them because `assertJsonValue` only checked
|
|
194
|
+
* finite-and-not-negative-zero; autogenous's stricter verifier correctly
|
|
195
|
+
* rejected the receipt (ruvnet/ruflo#3229, ruvnet/autogenous#15).
|
|
196
|
+
*
|
|
197
|
+
* Integers stay JSON numbers and only non-integers are encoded, which is what
|
|
198
|
+
* the contract's own example shows: `{"hnswEf": 128, "hybridWeight": "0.65"}`.
|
|
199
|
+
* Recurses through nested objects and arrays, because "every fractional value"
|
|
200
|
+
* is not limited to the top level.
|
|
201
|
+
*/
|
|
202
|
+
export function encodePolicyFractions(value) {
|
|
203
|
+
if (typeof value === 'number') {
|
|
204
|
+
if (!Number.isFinite(value))
|
|
205
|
+
throw new Error('policy value must be finite');
|
|
206
|
+
// Integers are exact in JSON and the contract permits scaled integers, so
|
|
207
|
+
// they are left alone; -0 is normalized away by `decimal`.
|
|
208
|
+
return Number.isInteger(value) && !Object.is(value, -0) ? value : decimal(value);
|
|
209
|
+
}
|
|
210
|
+
if (Array.isArray(value))
|
|
211
|
+
return value.map(encodePolicyFractions);
|
|
212
|
+
if (value && typeof value === 'object') {
|
|
213
|
+
return Object.fromEntries(Object.entries(value).map(([k, v]) => [k, encodePolicyFractions(v)]));
|
|
214
|
+
}
|
|
215
|
+
return value;
|
|
216
|
+
}
|
|
146
217
|
function seedFrom(parts) {
|
|
147
218
|
const digest = createHash('sha256').update(parts.join('')).digest();
|
|
148
219
|
return { seed: digest.readUInt32BE(0), hex: digest.toString('hex') };
|
|
@@ -239,6 +310,10 @@ function receiptIdentityPayload(payload) {
|
|
|
239
310
|
return payload;
|
|
240
311
|
}
|
|
241
312
|
function signedBytes(payload) {
|
|
313
|
+
// Assert at the signing boundary: a receipt must never be SIGNED unless it
|
|
314
|
+
// satisfies the number domain it claims. Covers both produce and verify,
|
|
315
|
+
// since both paths sign or re-derive these bytes.
|
|
316
|
+
assertReceiptNumberDomain(payload);
|
|
242
317
|
return Buffer.concat([
|
|
243
318
|
Buffer.from(RECEIPT_DOMAIN, 'utf8'),
|
|
244
319
|
Buffer.from([0]),
|
|
@@ -249,7 +324,12 @@ export function createFlywheelReceipt(input) {
|
|
|
249
324
|
const now = input.now ?? Date.now();
|
|
250
325
|
const evaluationRunId = input.evaluationRunId ?? uuidV7(now);
|
|
251
326
|
const lineageId = input.lineageId ?? uuidV7(now);
|
|
252
|
-
|
|
327
|
+
// Encode ONCE and use the encoded object for both the candidate content ID
|
|
328
|
+
// and the payload, so `verify`'s recomputation of policyCandidateId still
|
|
329
|
+
// matches. Encoding after the ID were computed would make every receipt fail
|
|
330
|
+
// its own 'candidate content ID mismatch' check.
|
|
331
|
+
const candidatePolicy = encodePolicyFractions(input.candidatePolicy);
|
|
332
|
+
const candidateId = policyCandidateId(candidatePolicy);
|
|
253
333
|
// Verifiers recompute the statistics from the payload's scale-12 decimal
|
|
254
334
|
// strings — the only values they ever have. The encoded values are therefore
|
|
255
335
|
// the statistical inputs of record: compute the decision from them, not from
|
|
@@ -277,9 +357,23 @@ export function createFlywheelReceipt(input) {
|
|
|
277
357
|
evaluationRunId,
|
|
278
358
|
baselineRef: input.baselineRef,
|
|
279
359
|
expectedLedgerHead: input.expectedLedgerHead ?? GENESIS_LEDGER_HEAD,
|
|
280
|
-
candidatePolicy
|
|
360
|
+
candidatePolicy,
|
|
281
361
|
gateVersion: input.gateVersion ?? statistics.ruleVersion,
|
|
282
|
-
|
|
362
|
+
// v1 -> v2: the encoding of `candidatePolicy` changed (#3229). Fixing it
|
|
363
|
+
// changes the candidate content ID, so a pre-fix receipt can never match a
|
|
364
|
+
// post-fix champion reference. Bumping the schema version makes the
|
|
365
|
+
// promotion gate refuse those receipts with an ACCURATE reason —
|
|
366
|
+
// 'policy schema changed' (flywheel-transaction.ts:468) — instead of a
|
|
367
|
+
// confusing 'stale baseline' hash mismatch.
|
|
368
|
+
//
|
|
369
|
+
// Signed receipts are deliberately NOT migrated: re-encoding changes the
|
|
370
|
+
// content, which changes the ID, which invalidates the signature. A
|
|
371
|
+
// migration would mean re-signing, i.e. minting new receipts that claim to
|
|
372
|
+
// be old ones, which is what the receipt design exists to prevent. Existing
|
|
373
|
+
// receipts stay valid as history and unpromotable; the champion is
|
|
374
|
+
// re-established through the explicit reset path, which already requires
|
|
375
|
+
// confirmation and a recorded reason.
|
|
376
|
+
policySchemaVersion: input.policySchemaVersion ?? 'ruflo.retrieval-policy/v2',
|
|
283
377
|
safetyEnvelopeRef: input.safetyEnvelopeRef,
|
|
284
378
|
...(input.anchorRef ? { anchorRef: input.anchorRef } : {}),
|
|
285
379
|
requestedProposer: input.requestedProposer ?? 'local',
|
|
@@ -342,6 +436,17 @@ export function createFlywheelReceipt(input) {
|
|
|
342
436
|
return receipt;
|
|
343
437
|
}
|
|
344
438
|
export function verifyFlywheelReceipt(receipt, trustedPublicKeys) {
|
|
439
|
+
// The enforcement half of #3229. Before this, ruflo verified its own
|
|
440
|
+
// non-conforming receipts because the only number rule was
|
|
441
|
+
// finite-and-not-negative-zero, while autogenous's stricter verifier
|
|
442
|
+
// correctly rejected them. Reported as an error rather than thrown so the
|
|
443
|
+
// caller gets it alongside every other finding.
|
|
444
|
+
try {
|
|
445
|
+
assertReceiptNumberDomain(receipt.payload);
|
|
446
|
+
}
|
|
447
|
+
catch (err) {
|
|
448
|
+
return { valid: false, signed: !!receipt.signature, errors: [err.message] };
|
|
449
|
+
}
|
|
345
450
|
const errors = [];
|
|
346
451
|
try {
|
|
347
452
|
if (receipt.payload.schemaVersion !== RECEIPT_SCHEMA)
|
|
@@ -216,7 +216,12 @@ export async function evaluateFlywheelCandidate(projectRoot, deps) {
|
|
|
216
216
|
evaluationRunId: deps.evaluationRunId,
|
|
217
217
|
baselineRef: refOf(baseline),
|
|
218
218
|
expectedLedgerHead: txState.ledgerHead,
|
|
219
|
-
|
|
219
|
+
// `as unknown as` erased RetrievalConfig's `number` fields and laundered
|
|
220
|
+
// binary floats into a receipt claiming ADR-322C conformance (#3229).
|
|
221
|
+
// createFlywheelReceipt now encodes fractional values to scale-12
|
|
222
|
+
// decimal strings, so a single structural cast is enough and the
|
|
223
|
+
// type-erasing double cast is gone.
|
|
224
|
+
candidatePolicy: { ...candidate },
|
|
220
225
|
safetyEnvelopeRef,
|
|
221
226
|
anchorRef: deps.anchorRef,
|
|
222
227
|
requestedProposer: deps.requestedProposer ?? 'local',
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@claude-flow/cli",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.39.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Ruflo CLI - Enterprise AI agent orchestration with 60+ specialized agents, swarm coordination, MCP server, self-learning hooks, and vector memory for Claude Code",
|
|
6
6
|
"main": "dist/src/index.js",
|