mindforge-cc 10.7.0 → 11.2.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.
- package/.agent/hooks/mindforge-statusline.js +2 -2
- package/.mindforge/MINDFORGE-V2-SCHEMA.json +43 -10
- package/.mindforge/config.json +18 -4
- package/CHANGELOG.md +165 -0
- package/MINDFORGE.md +3 -3
- package/README.md +49 -4
- package/RELEASENOTES.md +81 -1
- package/SECURITY.md +20 -8
- package/bin/autonomous/audit-writer.js +105 -70
- package/bin/autonomous/auto-runner.js +377 -34
- package/bin/autonomous/context-refactorer.js +26 -11
- package/bin/autonomous/dependency-dag.js +59 -0
- package/bin/autonomous/state-manager.js +62 -6
- package/bin/autonomous/stuck-monitor.js +46 -7
- package/bin/autonomous/wave-executor.js +86 -26
- package/bin/council-cli.js +161 -0
- package/bin/dashboard/api-router.js +43 -0
- package/bin/dashboard/approval-handler.js +3 -1
- package/bin/dashboard/metrics-aggregator.js +28 -1
- package/bin/dashboard/server.js +68 -5
- package/bin/dashboard/sse-bridge.js +10 -13
- package/bin/engine/council-runtime.js +124 -0
- package/bin/engine/feedback-loop.js +8 -0
- package/bin/engine/intelligence-interlock.js +32 -15
- package/bin/engine/logic-drift-detector.js +2 -1
- package/bin/engine/nexus-tracer.js +3 -2
- package/bin/engine/otel-exporter.js +123 -0
- package/bin/engine/remediation-engine.js +155 -32
- package/bin/engine/self-corrective-synthesizer.js +84 -10
- package/bin/engine/sre-manager.js +12 -4
- package/bin/engine/temporal-cli.js +4 -2
- package/bin/engine/temporal-hub.js +131 -34
- package/bin/engine/verification-runner.js +131 -0
- package/bin/engine/verify-cli.js +34 -0
- package/bin/eval/eval-harness.js +82 -0
- package/bin/eval/golden-set-retrieval.json +46 -0
- package/bin/governance/approve.js +41 -5
- package/bin/governance/audit-hash.js +12 -0
- package/bin/governance/audit-verifier.js +60 -0
- package/bin/governance/impact-analyzer.js +28 -0
- package/bin/governance/policy-engine.js +10 -3
- package/bin/governance/quantum-crypto.js +95 -28
- package/bin/governance/rbac-manager.js +74 -2
- package/bin/governance/ztai-manager.js +79 -9
- package/bin/hindsight-injector.js +8 -9
- package/bin/hooks/instinct-capture-hook.js +186 -0
- package/bin/memory/auto-shadow.js +32 -3
- package/bin/memory/eis-client.js +71 -34
- package/bin/memory/embedding-engine.js +61 -0
- package/bin/memory/identity-synthesizer.js +2 -2
- package/bin/memory/knowledge-graph.js +58 -5
- package/bin/memory/knowledge-indexer.js +53 -6
- package/bin/memory/knowledge-store.js +52 -6
- package/bin/memory/retrieval-fusion.js +58 -0
- package/bin/memory/semantic-hub.js +2 -2
- package/bin/memory/vector-hub.js +111 -6
- package/bin/migrations/10.7.0-to-11.0.0.js +110 -0
- package/bin/migrations/schema-versions.js +13 -0
- package/bin/mindforge-cli.js +4 -5
- package/bin/models/anthropic-provider.js +58 -4
- package/bin/models/cloud-broker.js +68 -20
- package/bin/models/cost-tracker.js +3 -1
- package/bin/models/difficulty-scorer.js +54 -0
- package/bin/models/gemini-provider.js +57 -2
- package/bin/models/model-client.js +20 -0
- package/bin/models/model-router.js +59 -26
- package/bin/models/openai-provider.js +50 -3
- package/bin/models/pricing-registry.js +128 -0
- package/bin/review/ads-engine.js +1 -1
- package/bin/security/trust-boundaries.js +102 -0
- package/bin/security/trust-gate-hook.js +39 -0
- package/bin/skill-registry.js +3 -2
- package/bin/skills-builder/marketplace-cli.js +5 -3
- package/bin/skills-builder/skill-registrar.js +4 -6
- package/bin/sre/sentinel.js +7 -5
- package/bin/utils/append-queue.js +55 -0
- package/bin/utils/file-io.js +90 -38
- package/bin/utils/index.js +58 -0
- package/bin/utils/version-check.js +59 -0
- package/bin/verify-audit.js +12 -0
- package/bin/wizard/theme.js +1 -2
- package/docs/getting-started.md +1 -1
- package/docs/user-guide.md +2 -2
- package/package.json +2 -2
- package/bin/dashboard/team-tracker.js +0 -0
|
@@ -1,90 +1,125 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* MindForge — Audit Writer (
|
|
3
|
-
*
|
|
4
|
-
*
|
|
2
|
+
* MindForge — Audit Writer (Synchronous Durable, Hash-Chained)
|
|
3
|
+
*
|
|
4
|
+
* Provides the ONE unified audit-append primitive {@link appendAuditEntrySync}
|
|
5
|
+
* that every audit write site funnels through, producing a single verifiable
|
|
6
|
+
* hash chain (UC-04 / UC-04b).
|
|
7
|
+
*
|
|
8
|
+
* Retired in UC-04b: the old buffered async writer (`createAuditWriter`) and its
|
|
9
|
+
* AuditRotator-based 5000-line rotation. Rotation BROKE the hash chain — archiving
|
|
10
|
+
* + truncating AUDIT.jsonl orphaned the carried head's `previous_hash` from an entry
|
|
11
|
+
* no longer on disk, so the verifier failed closed on a rotated-but-untampered file.
|
|
12
|
+
* As a result AUDIT.jsonl now grows UNBOUNDED. That is an accepted short-term tradeoff
|
|
13
|
+
* at single-operator/dev scale. The proper fix — chain-aware compaction (archive old
|
|
14
|
+
* entries AND re-anchor the first carried entry to previous_hash=null so the live tail
|
|
15
|
+
* verifies standalone) — is a DEFERRED future feature, intentionally NOT in scope here.
|
|
5
16
|
*/
|
|
6
17
|
'use strict';
|
|
7
18
|
|
|
8
19
|
const fs = require('fs');
|
|
20
|
+
const path = require('path');
|
|
9
21
|
const crypto = require('crypto');
|
|
10
|
-
|
|
11
|
-
const FLUSH_INTERVAL_MS = 100;
|
|
12
|
-
const FLUSH_THRESHOLD = 10;
|
|
22
|
+
const { hashAuditEntry } = require('../governance/audit-hash');
|
|
13
23
|
|
|
14
24
|
/**
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
25
|
+
* Computes the SHA-256 hash of an entry chained to its predecessor (UC-04).
|
|
26
|
+
* Delegates to the canonical {@link hashAuditEntry} (bin/governance/audit-hash.js)
|
|
27
|
+
* so the writer and verifier share ONE hasher — material drift is impossible.
|
|
28
|
+
* The material is {...entry, previous_hash} where `entry` does NOT contain `_hash`.
|
|
29
|
+
* @param {object} entry — entry WITHOUT a `_hash` field
|
|
30
|
+
* @param {string|null} previousHash — prior entry's `_hash` (null for the first link)
|
|
31
|
+
* @returns {string} hex-encoded SHA-256 digest
|
|
18
32
|
*/
|
|
19
|
-
function
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
let isClosed = false;
|
|
23
|
-
|
|
24
|
-
function scheduleFlush() {
|
|
25
|
-
if (flushTimer !== null) return;
|
|
26
|
-
flushTimer = setTimeout(async () => {
|
|
27
|
-
flushTimer = null;
|
|
28
|
-
await flush();
|
|
29
|
-
}, FLUSH_INTERVAL_MS);
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* Adds an entry to the buffer. Triggers flush if threshold reached.
|
|
34
|
-
* Stamps entry with id and timestamp if missing (immutable — creates new object).
|
|
35
|
-
*/
|
|
36
|
-
function write(entry) {
|
|
37
|
-
if (isClosed) {
|
|
38
|
-
throw new Error('AuditWriter is closed — cannot write after close()');
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
const stamped = Object.assign(Object.create(null), entry, {
|
|
42
|
-
id: entry.id || crypto.randomBytes(8).toString('hex'),
|
|
43
|
-
timestamp: entry.timestamp || new Date().toISOString(),
|
|
44
|
-
});
|
|
33
|
+
function hashEntry(entry, previousHash) {
|
|
34
|
+
return hashAuditEntry(entry, previousHash);
|
|
35
|
+
}
|
|
45
36
|
|
|
46
|
-
|
|
37
|
+
/**
|
|
38
|
+
* Reads the `_hash` of the last entry already on disk so a re-opened writer
|
|
39
|
+
* continues the existing chain instead of starting a fresh, disconnected one.
|
|
40
|
+
* @param {string} auditPath — path to the AUDIT.jsonl file
|
|
41
|
+
* @returns {string|null} last `_hash`, or null if absent/unreadable/empty
|
|
42
|
+
*/
|
|
43
|
+
function readLastHash(auditPath) {
|
|
44
|
+
try {
|
|
45
|
+
const lines = fs.readFileSync(auditPath, 'utf8').split('\n').filter(Boolean);
|
|
46
|
+
if (lines.length === 0) return null;
|
|
47
|
+
const last = JSON.parse(lines[lines.length - 1]);
|
|
48
|
+
return last._hash || null;
|
|
49
|
+
} catch { return null; }
|
|
50
|
+
}
|
|
47
51
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
52
|
+
// ── Unified synchronous-durable chained append (UC-04b) ───────────────────────
|
|
53
|
+
// ONE primitive that ALL audit write sites funnel through, producing a single
|
|
54
|
+
// verifiable chain via the canonical hashAuditEntry. Synchronous + fsync'd so an
|
|
55
|
+
// acknowledged write is on disk before the call returns (UC-09 durability) — this
|
|
56
|
+
// is what lets us delete the old raw `appendFileSync` shadow-writes: the durable
|
|
57
|
+
// sync write gives in-process consumers (e.g. StuckMonitor, which is fed the event
|
|
58
|
+
// object directly but may also re-read the file) immediate, durable data.
|
|
59
|
+
//
|
|
60
|
+
// Chain head caching: re-reading the file's tail on every append is O(file) — bad
|
|
61
|
+
// on hot paths. Instead we keep a per-path in-memory lastHash (Map keyed by the
|
|
62
|
+
// RESOLVED absolute path), seeded ONCE from the file's last entry on the first
|
|
63
|
+
// append, then advanced in-process for O(1) appends. If the cache is cold (new
|
|
64
|
+
// process, or a path never written in this process) we seed from disk — so a
|
|
65
|
+
// second process correctly continues the on-disk chain from its tail.
|
|
66
|
+
//
|
|
67
|
+
// Concurrency: within a process this is fully synchronous, so calls cannot
|
|
68
|
+
// interleave and the cached lastHash is always current. ACROSS processes, each
|
|
69
|
+
// process seeds from the file tail on its first append; this is correct only under
|
|
70
|
+
// the single-operator model (no two processes appending CONCURRENTLY to the same
|
|
71
|
+
// audit file). MindForge runs one autonomous operator at a time, so this holds.
|
|
72
|
+
const _lastHashCache = new Map(); // resolvedPath -> last `_hash` written/seen
|
|
58
73
|
|
|
59
|
-
|
|
60
|
-
|
|
74
|
+
/**
|
|
75
|
+
* Synchronously appends ONE hash-chained, durable entry to an audit JSONL file.
|
|
76
|
+
* This is the single unified append used by every audit write site (UC-04b).
|
|
77
|
+
*
|
|
78
|
+
* NOTE: performs an fsync (openSync('a') + writeSync + fsyncSync + closeSync) on
|
|
79
|
+
* EVERY call. This is deliberate for audit integrity/durability — but it makes the
|
|
80
|
+
* call relatively expensive, so it is intended for audit-grade events, NOT for
|
|
81
|
+
* high-frequency telemetry. Hot-path callers (e.g. nexus-tracer span/reasoning
|
|
82
|
+
* events) pay one fsync per event; keep that cost in mind before adding new hot
|
|
83
|
+
* write sites.
|
|
84
|
+
*
|
|
85
|
+
* @param {string} auditPath — path to the AUDIT.jsonl file
|
|
86
|
+
* @param {object} event — the event payload (id/timestamp stamped if missing)
|
|
87
|
+
* @returns {object} the stamped + chained entry actually written
|
|
88
|
+
*/
|
|
89
|
+
function appendAuditEntrySync(auditPath, event) {
|
|
90
|
+
const resolved = path.resolve(auditPath);
|
|
61
91
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
92
|
+
// 1. Stamp id + timestamp if missing — immutable (new object, never mutate input).
|
|
93
|
+
const stamped = {
|
|
94
|
+
...event,
|
|
95
|
+
id: event.id || crypto.randomBytes(8).toString('hex'),
|
|
96
|
+
timestamp: event.timestamp || new Date().toISOString(),
|
|
97
|
+
};
|
|
67
98
|
|
|
68
|
-
|
|
69
|
-
|
|
99
|
+
// 2. Seed previous_hash: prefer the warm in-process cache; fall back to the
|
|
100
|
+
// file's last entry when cold (first append in this process for this path).
|
|
101
|
+
let previous_hash = _lastHashCache.has(resolved)
|
|
102
|
+
? _lastHashCache.get(resolved)
|
|
103
|
+
: readLastHash(resolved);
|
|
70
104
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
}
|
|
105
|
+
// 3. Compute _hash over {...stamped, previous_hash} WITHOUT _hash in the material.
|
|
106
|
+
const _hash = hashEntry(stamped, previous_hash);
|
|
74
107
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
108
|
+
// 4. Write {...stamped, previous_hash, _hash} as one JSON line, durably+synchronously
|
|
109
|
+
// (openSync('a') + writeSync + fsyncSync + closeSync — mirrors appendDurableSync).
|
|
110
|
+
const chained = { ...stamped, previous_hash, _hash };
|
|
111
|
+
fs.mkdirSync(path.dirname(resolved), { recursive: true });
|
|
112
|
+
const fd = fs.openSync(resolved, 'a');
|
|
113
|
+
try {
|
|
114
|
+
fs.writeSync(fd, JSON.stringify(chained) + '\n');
|
|
115
|
+
fs.fsyncSync(fd);
|
|
116
|
+
} finally {
|
|
117
|
+
fs.closeSync(fd);
|
|
85
118
|
}
|
|
86
119
|
|
|
87
|
-
|
|
120
|
+
// 5. Advance the in-process chain head and return the written entry.
|
|
121
|
+
_lastHashCache.set(resolved, _hash);
|
|
122
|
+
return chained;
|
|
88
123
|
}
|
|
89
124
|
|
|
90
|
-
module.exports = {
|
|
125
|
+
module.exports = { appendAuditEntrySync };
|