@ijfw/memory-server 1.3.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/bin/ijfw +27 -0
- package/bin/ijfw-dashboard +180 -0
- package/bin/ijfw-dispatch-plan +41 -0
- package/bin/ijfw-memorize +273 -0
- package/bin/ijfw-memory +51 -0
- package/fixtures/demo-target.js +28 -0
- package/package.json +53 -0
- package/src/api-client.js +190 -0
- package/src/audit-roster.js +315 -0
- package/src/caps.js +37 -0
- package/src/cold-scan-runner.mjs +37 -0
- package/src/compute/edges.js +155 -0
- package/src/compute/extract.js +560 -0
- package/src/compute/fts5.js +420 -0
- package/src/compute/graph-auto-index.js +191 -0
- package/src/compute/graph-lock.js +114 -0
- package/src/compute/index.js +18 -0
- package/src/compute/migration-runner.js +116 -0
- package/src/compute/migrations/001-initial.js +23 -0
- package/src/compute/migrations/002-porter-stemming-source.js +139 -0
- package/src/compute/migrations/003-tier-semantic.js +69 -0
- package/src/compute/migrations/004-kg-tables.js +83 -0
- package/src/compute/migrations/005-stale-candidate.js +72 -0
- package/src/compute/python-resolver.js +106 -0
- package/src/compute/runner-vm.js +185 -0
- package/src/compute/runner.js +416 -0
- package/src/compute/sandbox-detect.js +122 -0
- package/src/compute/sandbox-linux.js +164 -0
- package/src/compute/sandbox-macos.js +167 -0
- package/src/compute/sandbox-windows.js +63 -0
- package/src/compute/schema.sql +118 -0
- package/src/compute/staleness.js +239 -0
- package/src/compute/synonyms.js +367 -0
- package/src/compute/traverse.js +180 -0
- package/src/cost/aggregator.js +229 -0
- package/src/cost/pricing.js +134 -0
- package/src/cost/readers/claude.js +179 -0
- package/src/cost/readers/codex.js +131 -0
- package/src/cost/readers/gemini.js +111 -0
- package/src/cost/savings.js +243 -0
- package/src/cross-dispatcher.js +437 -0
- package/src/cross-orchestrator-cli.js +1885 -0
- package/src/cross-orchestrator.js +598 -0
- package/src/cross-project-search.js +114 -0
- package/src/dashboard-client.html +1180 -0
- package/src/dashboard-server.js +895 -0
- package/src/design-companion.js +81 -0
- package/src/dispatch/colon-syntax.js +732 -0
- package/src/dispatch-planner.js +235 -0
- package/src/dream/cooldown.js +105 -0
- package/src/dream/runner.mjs +373 -0
- package/src/dream/staleness-wiring.js +195 -0
- package/src/feedback-detector.js +57 -0
- package/src/hero-line.js +115 -0
- package/src/importers/claude-mem.js +152 -0
- package/src/importers/cli.js +311 -0
- package/src/importers/common.js +84 -0
- package/src/importers/discover.js +235 -0
- package/src/importers/rtk.js +107 -0
- package/src/intent-router.js +221 -0
- package/src/lib/atomic-io.js +201 -0
- package/src/lib/cache.js +33 -0
- package/src/lib/npm-view.js +104 -0
- package/src/lib/status-card.js +95 -0
- package/src/lib/token.js +85 -0
- package/src/memory/fts5.js +349 -0
- package/src/memory/migration-runner.js +116 -0
- package/src/memory/migrations/001-fts5-init.js +26 -0
- package/src/memory/migrations/002-tier-semantic.js +60 -0
- package/src/memory/migrations/003-stale-candidate.js +60 -0
- package/src/memory/reader.js +300 -0
- package/src/memory/recall-counter.js +76 -0
- package/src/memory/schema.sql +79 -0
- package/src/memory/search.js +431 -0
- package/src/memory/staleness.js +237 -0
- package/src/memory/tier-promotion.js +377 -0
- package/src/memory/tokenize.js +63 -0
- package/src/project-type-detector.js +866 -0
- package/src/prompt-check.js +171 -0
- package/src/ralph-allowlist.js +88 -0
- package/src/receipts.js +129 -0
- package/src/redactor.js +107 -0
- package/src/sandbox.js +275 -0
- package/src/sanitizer.js +69 -0
- package/src/scan-resume.js +167 -0
- package/src/schema.js +82 -0
- package/src/search-bm25.js +108 -0
- package/src/server.js +1414 -0
- package/src/swarm-config.js +80 -0
- package/src/trident/dispatch.js +211 -0
- package/src/trident/lens-health.js +253 -0
- package/src/update-apply.js +79 -0
- package/src/update-check.js +136 -0
- package/src/vectors.js +178 -0
- package/templates/design/bento-grid.md +84 -0
- package/templates/design/brutalist-luxe.md +82 -0
- package/templates/design/cinematic-dark.md +82 -0
- package/templates/design/data-dense-dashboard.md +88 -0
- package/templates/design/editorial-warm.md +81 -0
- package/templates/design/glassmorphic.md +84 -0
- package/templates/design/magazine-editorial.md +84 -0
- package/templates/design/maximalist-vibrant.md +85 -0
- package/templates/design/neo-swiss-tech.md +85 -0
- package/templates/design/swiss-minimal.md +80 -0
- package/templates/design/terminal-native.md +83 -0
- package/templates/design/warm-organic.md +84 -0
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
// IJFW v1.3.0 -- D2 edge writer.
|
|
2
|
+
//
|
|
3
|
+
// Source authority: D-PILLAR-SPEC.md section 2 (edge weight formula) +
|
|
4
|
+
// section 3 (redactor ordering).
|
|
5
|
+
//
|
|
6
|
+
// Pipeline:
|
|
7
|
+
// 1. extractEntities(body) yields candidates with redacted flag.
|
|
8
|
+
// 2. ./edges.js writes kg_nodes (find-or-create by (kind, name)),
|
|
9
|
+
// flagging redacted=1 for secret-shaped entities.
|
|
10
|
+
// 3. For each unordered pair of CLEAN entities (redacted=0 on BOTH):
|
|
11
|
+
// - INSERT OR IGNORE kg_edges row with co_occurrence_count=1
|
|
12
|
+
// - On conflict, UPDATE: increment co_occurrence_count, refresh ts,
|
|
13
|
+
// recompute weight via section 2 formula.
|
|
14
|
+
// 4. Locking: caller wraps the whole write in acquireGraphWriteLock().
|
|
15
|
+
//
|
|
16
|
+
// Edge weight formula (section 2):
|
|
17
|
+
// weight = clamp_01(
|
|
18
|
+
// log1p(co_occurrence_count) * 0.4 +
|
|
19
|
+
// recency_decay_factor * 0.4 +
|
|
20
|
+
// redactor_clean_factor * 0.2
|
|
21
|
+
// )
|
|
22
|
+
// recency_decay_factor = exp(-age_days / 30)
|
|
23
|
+
// redactor_clean_factor = 1.0 if both clean, 0.0 if either redacted
|
|
24
|
+
// (the latter never reaches this code path)
|
|
25
|
+
// clamp_01 = max(0, min(1, x))
|
|
26
|
+
|
|
27
|
+
const RECENCY_HALF_LIFE_DAYS = 30;
|
|
28
|
+
const MS_PER_DAY = 24 * 60 * 60 * 1000;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* upsertNode(db, ent, ts) -> { id, redacted }
|
|
32
|
+
*
|
|
33
|
+
* Find-or-create kg_nodes row by (kind, name). Updates last_seen on hit.
|
|
34
|
+
* Returns the row id and the persisted redacted flag.
|
|
35
|
+
*/
|
|
36
|
+
export function upsertNode(db, ent, ts) {
|
|
37
|
+
const now = Number(ts) || Date.now();
|
|
38
|
+
const redactedFlag = ent.redacted ? 1 : 0;
|
|
39
|
+
|
|
40
|
+
// Find first.
|
|
41
|
+
const found = db.prepare(
|
|
42
|
+
`SELECT id, redacted FROM kg_nodes WHERE kind = ? AND name = ?`
|
|
43
|
+
).get(ent.kind, ent.name);
|
|
44
|
+
|
|
45
|
+
if (found) {
|
|
46
|
+
db.prepare(
|
|
47
|
+
`UPDATE kg_nodes SET last_seen = ?, redacted = ? WHERE id = ?`
|
|
48
|
+
).run(now, redactedFlag, found.id);
|
|
49
|
+
return { id: Number(found.id), redacted: redactedFlag };
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const insert = db.prepare(
|
|
53
|
+
`INSERT INTO kg_nodes (kind, name, first_seen, last_seen, redacted) ` +
|
|
54
|
+
`VALUES (?, ?, ?, ?, ?)`
|
|
55
|
+
);
|
|
56
|
+
const info = insert.run(ent.kind, ent.name, now, now, redactedFlag);
|
|
57
|
+
return { id: Number(info.lastInsertRowid), redacted: redactedFlag };
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* computeEdgeWeight({ co_occurrence_count, age_days, redactor_clean }) -> number
|
|
62
|
+
*
|
|
63
|
+
* Section 2 formula. Caller is responsible for computing age_days from
|
|
64
|
+
* a stored ts. Returns clamped [0, 1] weight.
|
|
65
|
+
*/
|
|
66
|
+
export function computeEdgeWeight({ co_occurrence_count, age_days, redactor_clean }) {
|
|
67
|
+
const co = Math.max(0, Number(co_occurrence_count) || 0);
|
|
68
|
+
const age = Math.max(0, Number(age_days) || 0);
|
|
69
|
+
const clean = redactor_clean ? 1 : 0;
|
|
70
|
+
|
|
71
|
+
const log1p = Math.log1p(co);
|
|
72
|
+
const recency = Math.exp(-age / RECENCY_HALF_LIFE_DAYS);
|
|
73
|
+
const raw = log1p * 0.4 + recency * 0.4 + clean * 0.2;
|
|
74
|
+
return Math.max(0, Math.min(1, raw));
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* writeEdges(db, observationId, entities, opts?) -> { nodes, edgesAdded, edgesUpdated, redactedSkipped }
|
|
79
|
+
*
|
|
80
|
+
* Co-occurrence pairs all clean entities within the observation. Edge
|
|
81
|
+
* weight follows D-PILLAR-SPEC section 2. Redacted entities are stored
|
|
82
|
+
* as nodes (with redacted=1) but participate in NO edges (redactor_clean
|
|
83
|
+
* factor is 0 for those endpoints, and per section 3 we skip the edge
|
|
84
|
+
* write entirely rather than emit a zero-weight edge).
|
|
85
|
+
*
|
|
86
|
+
* `observationId` is included in the return value so callers can link
|
|
87
|
+
* back to the observation ledger; it is not stored on edges directly.
|
|
88
|
+
*
|
|
89
|
+
* opts.ts (default Date.now()) -- co-occurrence timestamp; used for
|
|
90
|
+
* recency decay calculation. Tests inject a fixed ts.
|
|
91
|
+
*/
|
|
92
|
+
export function writeEdges(db, observationId, entities, opts = {}) {
|
|
93
|
+
const ts = Number(opts.ts) || Date.now();
|
|
94
|
+
const nodes = [];
|
|
95
|
+
let redactedSkipped = 0;
|
|
96
|
+
for (const ent of entities) {
|
|
97
|
+
const persisted = upsertNode(db, ent, ts);
|
|
98
|
+
nodes.push({ ...ent, id: persisted.id });
|
|
99
|
+
if (persisted.redacted) redactedSkipped++;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
let edgesAdded = 0;
|
|
103
|
+
let edgesUpdated = 0;
|
|
104
|
+
|
|
105
|
+
const cleanNodes = nodes.filter(n => !n.redacted);
|
|
106
|
+
for (let i = 0; i < cleanNodes.length; i++) {
|
|
107
|
+
for (let j = i + 1; j < cleanNodes.length; j++) {
|
|
108
|
+
// Stable ordering: src.id < dst.id ensures UPSERT keying matches
|
|
109
|
+
// even if the caller iterates entities in a different order.
|
|
110
|
+
const srcNode = cleanNodes[i].id < cleanNodes[j].id ? cleanNodes[i] : cleanNodes[j];
|
|
111
|
+
const dstNode = cleanNodes[i].id < cleanNodes[j].id ? cleanNodes[j] : cleanNodes[i];
|
|
112
|
+
|
|
113
|
+
const existing = db.prepare(
|
|
114
|
+
`SELECT co_occurrence_count, ts FROM kg_edges ` +
|
|
115
|
+
`WHERE src = ? AND dst = ? AND kind = ?`
|
|
116
|
+
).get(srcNode.id, dstNode.id, 'co_occurs');
|
|
117
|
+
|
|
118
|
+
if (existing) {
|
|
119
|
+
const newCount = Number(existing.co_occurrence_count) + 1;
|
|
120
|
+
const ageDays = (ts - Number(existing.ts)) / MS_PER_DAY;
|
|
121
|
+
const weight = computeEdgeWeight({
|
|
122
|
+
co_occurrence_count: newCount,
|
|
123
|
+
age_days: Math.max(0, ageDays),
|
|
124
|
+
redactor_clean: true,
|
|
125
|
+
});
|
|
126
|
+
db.prepare(
|
|
127
|
+
`UPDATE kg_edges SET co_occurrence_count = ?, weight = ?, ts = ? ` +
|
|
128
|
+
`WHERE src = ? AND dst = ? AND kind = ?`
|
|
129
|
+
).run(newCount, weight, ts, srcNode.id, dstNode.id, 'co_occurs');
|
|
130
|
+
edgesUpdated++;
|
|
131
|
+
} else {
|
|
132
|
+
const weight = computeEdgeWeight({
|
|
133
|
+
co_occurrence_count: 1,
|
|
134
|
+
age_days: 0,
|
|
135
|
+
redactor_clean: true,
|
|
136
|
+
});
|
|
137
|
+
db.prepare(
|
|
138
|
+
`INSERT INTO kg_edges (src, dst, kind, weight, co_occurrence_count, ts) ` +
|
|
139
|
+
`VALUES (?, ?, ?, ?, ?, ?)`
|
|
140
|
+
).run(srcNode.id, dstNode.id, 'co_occurs', weight, 1, ts);
|
|
141
|
+
edgesAdded++;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
return {
|
|
147
|
+
observationId,
|
|
148
|
+
nodes,
|
|
149
|
+
edgesAdded,
|
|
150
|
+
edgesUpdated,
|
|
151
|
+
redactedSkipped,
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
export const __test = { RECENCY_HALF_LIFE_DAYS, MS_PER_DAY };
|