instar 1.3.528 → 1.3.530
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/dist/commands/server.d.ts.map +1 -1
- package/dist/commands/server.js +114 -0
- package/dist/commands/server.js.map +1 -1
- package/dist/config/ConfigDefaults.d.ts.map +1 -1
- package/dist/config/ConfigDefaults.js +29 -0
- package/dist/config/ConfigDefaults.js.map +1 -1
- package/dist/core/CoherenceJournal.d.ts +1 -1
- package/dist/core/CoherenceJournal.d.ts.map +1 -1
- package/dist/core/CoherenceJournal.js +27 -2
- package/dist/core/CoherenceJournal.js.map +1 -1
- package/dist/core/EvolutionManager.d.ts +35 -0
- package/dist/core/EvolutionManager.d.ts.map +1 -1
- package/dist/core/EvolutionManager.js +49 -0
- package/dist/core/EvolutionManager.js.map +1 -1
- package/dist/core/KnowledgeReplicatedStore.d.ts +310 -0
- package/dist/core/KnowledgeReplicatedStore.d.ts.map +1 -0
- package/dist/core/KnowledgeReplicatedStore.js +526 -0
- package/dist/core/KnowledgeReplicatedStore.js.map +1 -0
- package/dist/core/LearningsReplicatedStore.d.ts +298 -0
- package/dist/core/LearningsReplicatedStore.d.ts.map +1 -0
- package/dist/core/LearningsReplicatedStore.js +574 -0
- package/dist/core/LearningsReplicatedStore.js.map +1 -0
- package/dist/core/PostUpdateMigrator.d.ts.map +1 -1
- package/dist/core/PostUpdateMigrator.js +49 -1
- package/dist/core/PostUpdateMigrator.js.map +1 -1
- package/dist/core/devGatedFeatures.d.ts.map +1 -1
- package/dist/core/devGatedFeatures.js +10 -0
- package/dist/core/devGatedFeatures.js.map +1 -1
- package/dist/knowledge/KnowledgeManager.d.ts +37 -0
- package/dist/knowledge/KnowledgeManager.d.ts.map +1 -1
- package/dist/knowledge/KnowledgeManager.js +45 -0
- package/dist/knowledge/KnowledgeManager.js.map +1 -1
- package/dist/scaffold/templates.d.ts.map +1 -1
- package/dist/scaffold/templates.js +3 -1
- package/dist/scaffold/templates.js.map +1 -1
- package/package.json +1 -1
- package/src/data/builtin-manifest.json +20 -20
- package/src/scaffold/templates.ts +3 -1
- package/upgrades/1.3.529.md +33 -0
- package/upgrades/1.3.530.md +33 -0
- package/upgrades/side-effects/ws22-learnings.md +74 -0
- package/upgrades/side-effects/ws24-knowledge.md +77 -0
|
@@ -0,0 +1,526 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* KnowledgeReplicatedStore — the FOURTH concrete consumer of the HLC replicated-store
|
|
3
|
+
* foundation (WS2.4) and the THIRD memory-family kind (after WS2.3 relationships and
|
|
4
|
+
* WS2.2 learnings). It layers the `knowledge-record` replicated kind onto the generic
|
|
5
|
+
* substrate (ReplicatedRecordEnvelope / UnionReader / ConflictStore / RollbackUnmerge /
|
|
6
|
+
* ReplicationBudget / StoreSnapshot) so that a knowledge SOURCE the agent ingested on
|
|
7
|
+
* machine A is known on machine B — ONE knowledge catalog, not one-per-machine.
|
|
8
|
+
*
|
|
9
|
+
* It is the literal analog of `LearningsReplicatedStore.ts` (the WS2.2 memory-family
|
|
10
|
+
* reference consumer) and `RelationshipsReplicatedStore.ts` (the WS2.3 PII reference
|
|
11
|
+
* consumer). A knowledge source is a POINTER + SUMMARY (a `title`/`url`/`type` +
|
|
12
|
+
* `summary`/`tags`/`wordCount`), lower-PII than a relationship — but its
|
|
13
|
+
* `title`/`summary`/`url` CAN reference people or content, so it REUSES the established
|
|
14
|
+
* PII machinery (type-clamp, disclosure-min projection, tombstones, flag-coherence)
|
|
15
|
+
* rather than reinventing or downgrading it. THIS IS PURE LOGIC. No fs, no Date directly,
|
|
16
|
+
* no network. It defines:
|
|
17
|
+
*
|
|
18
|
+
* A. The `knowledge-record` store schema — a STRICT typed validator that
|
|
19
|
+
* TYPE-CLAMPS every known field: `ingestedAt` ISO-8601-only, `type` ∈ the
|
|
20
|
+
* {article,transcript,doc} enum, `wordCount` a finite number, `tags[]`/free text
|
|
21
|
+
* length-clamped. The schema is a DISCRIMINATED UNION on `op` — an `op:'put'` VALUE
|
|
22
|
+
* schema AND an `op:'delete'` TOMBSTONE schema coexist under the one kind, so a
|
|
23
|
+
* tombstone is never marked invalid by the value schema.
|
|
24
|
+
*
|
|
25
|
+
* B. The disclosure-minimized PROJECTION (fork #2) — `buildKnowledgeRecordData` emits
|
|
26
|
+
* ONLY the enumerated catalog-metadata fields, NEVER the markdown file BODY (the
|
|
27
|
+
* separate `filePath` file can be a huge transcript — full-content sync is a
|
|
28
|
+
* TRACKED follow-up, CMT-1416) and NEVER the local generated `id` OR the local
|
|
29
|
+
* `filePath` (a per-machine artifact path — meaningless and a mild info-leak on a
|
|
30
|
+
* peer). `recordKey` is the cross-machine IDENTITY SURFACE, derived deterministically
|
|
31
|
+
* from the stable content (normalize(url || title) + normalize(type)) — never the
|
|
32
|
+
* per-machine `generateId()` id (the cross-machine-UNSTABLE id, exactly the
|
|
33
|
+
* relationship-UUID / LRN-id trap the prior kinds solved with a stable identity
|
|
34
|
+
* surface). The SAME article ingested on two machines collapses to ONE record.
|
|
35
|
+
*
|
|
36
|
+
* C. The TOMBSTONE builder — `buildKnowledgeTombstoneData` emits an `op:'delete'`
|
|
37
|
+
* record `{ recordKey, op, hlc, origin, deletedAt }` so a `remove()` propagates as a
|
|
38
|
+
* positive signal across an offline-then-rejoining peer instead of a record absence.
|
|
39
|
+
* CRITICAL: the KnowledgeManager.remove() path MUST emit a tombstone per removed
|
|
40
|
+
* source, else a peer re-replicates the locally-removed source forever (resurrection).
|
|
41
|
+
*
|
|
42
|
+
* D. The union-aware read — `mergeUnionToKnowledge` collapses a
|
|
43
|
+
* `Map<recordKey, UnionResult>` into the merged catalog view. Knowledge is
|
|
44
|
+
* HIGH-impact at the REPLICATION layer (a concurrent divergent edit to the SAME
|
|
45
|
+
* recordKey — e.g. two machines re-summarize the same url differently — goes through
|
|
46
|
+
* APPEND-BOTH-AND-FLAG; both versions surface, never a silent clobber). The CONSUMER
|
|
47
|
+
* READ path is ADVISORY (fork #3): it injects BOTH variants of an open conflict as
|
|
48
|
+
* guidance — a knowledge source is REFERENCE, not authority — and NEVER blocks on an
|
|
49
|
+
* unresolved conflict. The read NEVER writes a foreign record into the local store.
|
|
50
|
+
*
|
|
51
|
+
* E. Foreign-record render safety — `renderForeignKnowledgeContext` wraps a replicated
|
|
52
|
+
* record in an explicit `<replicated-untrusted-data origin="…">` envelope and
|
|
53
|
+
* sanitizes EVERY rendered field. There is no "trusted because machine-set" render
|
|
54
|
+
* slot for a foreign record.
|
|
55
|
+
*
|
|
56
|
+
* DECIDED FORKS (Echo, 2026-06-13 — recorded verbatim in the PR ELI16):
|
|
57
|
+
* 1. recordKey = a content fingerprint over the STABLE source identity
|
|
58
|
+
* (sha256(normalize(url || title) + '\x1f' + normalize(type))), NEVER the local
|
|
59
|
+
* generated `id` (cross-machine identity surface — see deriveKnowledgeRecordKey).
|
|
60
|
+
* 2. Replicate the CATALOG ENTRY (metadata) ONLY — NOT the markdown file body. The
|
|
61
|
+
* projection carries { title, url, type, ingestedAt, tags[], summary, wordCount }
|
|
62
|
+
* and STRIPS the local `id` + `filePath`. Full-content-body sync is a TRACKED
|
|
63
|
+
* follow-up (CMT-1416).
|
|
64
|
+
* 3. Impact tier = HIGH at the REPLICATION layer (append-both-and-flag), ADVISORY at
|
|
65
|
+
* the READ layer (both variants injected as hints, never blocking) — a knowledge
|
|
66
|
+
* source is reference, not authority. See mergeUnionToKnowledge + KNOWLEDGE_IMPACT_TIER.
|
|
67
|
+
*
|
|
68
|
+
* SAFETY POSTURE: MECHANISM, dark by default. Nothing here blocks a user-initiated
|
|
69
|
+
* action. The local `id` + `filePath` are NEVER part of the replicated schema and are
|
|
70
|
+
* stripped from every emitted projection (disclosure minimization).
|
|
71
|
+
*/
|
|
72
|
+
import { createHash } from 'node:crypto';
|
|
73
|
+
import { jailStoreStringField } from './ReplicatedRecordEnvelope.js';
|
|
74
|
+
// ───────────────────────────────────────────────────────────────────────────
|
|
75
|
+
// A. Identity, tier, schema, bounds, caps
|
|
76
|
+
// ───────────────────────────────────────────────────────────────────────────
|
|
77
|
+
/** The stateSync config sub-key + advert suffix for this store (e.g.
|
|
78
|
+
* `multiMachine.stateSync.knowledge.enabled`). Equal to the advert flag key
|
|
79
|
+
* `stateSyncReceive['knowledge']`. */
|
|
80
|
+
export const KNOWLEDGE_STORE_KEY = 'knowledge';
|
|
81
|
+
/** The JournalKind string this store rides — the DUAL-REGISTRY's dynamic half.
|
|
82
|
+
* MUST also be present in CoherenceJournal.JOURNAL_KINDS (the static half), or the
|
|
83
|
+
* store advertises receive=true yet serves/applies/pulls nothing. */
|
|
84
|
+
export const KNOWLEDGE_RECORD_KIND = 'knowledge-record';
|
|
85
|
+
/**
|
|
86
|
+
* Knowledge is HIGH-impact at the REPLICATION layer (fork #3): a concurrent divergent
|
|
87
|
+
* VALUE edit to the SAME recordKey from different origins goes through
|
|
88
|
+
* APPEND-BOTH-AND-FLAG — both versions preserved, ONE deduped conflict, never a silent
|
|
89
|
+
* overwrite. The READ path (mergeUnionToKnowledge) is ADVISORY — both variants surface as
|
|
90
|
+
* guidance hints, the read never blocks on an open conflict — a knowledge source is
|
|
91
|
+
* reference, not authority. Operator resolution via POST /state/resolve-conflict is
|
|
92
|
+
* OPTIONAL cleanup that collapses the flag, never a gate on the hint.
|
|
93
|
+
*/
|
|
94
|
+
export const KNOWLEDGE_IMPACT_TIER = 'high';
|
|
95
|
+
/** The valid `type` enum for a knowledge source (KnowledgeManager.KnowledgeSource). A
|
|
96
|
+
* foreign record whose `type` is outside this set is REJECTED (markup cannot survive an
|
|
97
|
+
* enum slot). */
|
|
98
|
+
export const KNOWLEDGE_TYPES = Object.freeze(['article', 'transcript', 'doc']);
|
|
99
|
+
// ── Local-record caps mirrored on RECEIVE (length-clamp discipline). A value over a
|
|
100
|
+
// cap REJECTS the whole record (never truncate-and-accept), EXCEPT free text which
|
|
101
|
+
// is length-clamped on receive (a flood is bounded, not record-rejected). ───────
|
|
102
|
+
/** A knowledge `summary` can be a paragraph. Clamp on receive (NOT the file body — the
|
|
103
|
+
* body is never replicated; only this short catalog summary crosses the wire). */
|
|
104
|
+
export const MAX_SUMMARY_LENGTH = 20_000;
|
|
105
|
+
/** Per-free-text-string clamp for title / each tag. */
|
|
106
|
+
export const MAX_FREETEXT_LENGTH = 2_000;
|
|
107
|
+
/** A url is bounded (the catalog url; never a file body). */
|
|
108
|
+
export const MAX_URL_LENGTH = 2_048;
|
|
109
|
+
/** Tags cap (mirrors a reasonable per-source tag count). */
|
|
110
|
+
export const MAX_TAGS = 50;
|
|
111
|
+
/**
|
|
112
|
+
* Per-kind replication bounds. The knowledge store is FEW + bounded (a catalog of
|
|
113
|
+
* ingested sources), so the per-store retention mirrors the learning-record sibling (a
|
|
114
|
+
* small window with a few archives). NEVER `rotateKeep: 0` (rotate-but-never-delete
|
|
115
|
+
* would be a compliance defect for any memory-family kind). The rate cap COALESCES
|
|
116
|
+
* (latest state per recordKey per interval) so a churny re-ingest loop does not flood
|
|
117
|
+
* the stream.
|
|
118
|
+
*/
|
|
119
|
+
export const KNOWLEDGE_RECORD_BOUNDS = {
|
|
120
|
+
retention: { maxFileBytes: 4 * 1024 * 1024, rotateKeep: 4 },
|
|
121
|
+
// Few records, coalesced: capacity is the burst, refill the sustained rate.
|
|
122
|
+
rateCap: { capacity: 30, refillPerSec: 5 },
|
|
123
|
+
};
|
|
124
|
+
/**
|
|
125
|
+
* Per-entry size cap RAISED to 64KB for this kind. The default
|
|
126
|
+
* APPLIER_MAX_ENTRY_BYTES = 8KB is SMALLER than a fat knowledge summary (a 20K summary
|
|
127
|
+
* alone exceeds it), so under it the longest summaries would never replicate AND would
|
|
128
|
+
* wedge the stream. 64KB is provably above the disclosure-minimized projection's maximum:
|
|
129
|
+
* summary(20k) is the dominant term — we additionally enforce a HARD post-projection
|
|
130
|
+
* ceiling: a record that STILL exceeds 64KB after projection is REJECTED with a named
|
|
131
|
+
* error (never silent-truncate, never suspect-wedge). See assertProjectionUnderCap.
|
|
132
|
+
*/
|
|
133
|
+
export const KNOWLEDGE_MAX_ENTRY_BYTES = 64 * 1024;
|
|
134
|
+
/**
|
|
135
|
+
* The store-specific field names the `knowledge-record` VALUE schema OWNS (the
|
|
136
|
+
* unknown-field counter's allowlist). The local generated `id` AND the local `filePath`
|
|
137
|
+
* are DELIBERATELY ABSENT — `id` is per-machine + generated and `filePath` is a local
|
|
138
|
+
* artifact path; NEITHER is replicated (the recordKey keys on the content fingerprint,
|
|
139
|
+
* not the id; the body the filePath points at is a tracked follow-up, CMT-1416).
|
|
140
|
+
* `recordKey`/`hlc`/`op`/`origin`/`observed` are reserved envelope fields, never store
|
|
141
|
+
* fields.
|
|
142
|
+
*/
|
|
143
|
+
export const KNOWLEDGE_STORE_KNOWN_FIELDS = Object.freeze([
|
|
144
|
+
'title',
|
|
145
|
+
'url',
|
|
146
|
+
'type',
|
|
147
|
+
'ingestedAt',
|
|
148
|
+
'tags',
|
|
149
|
+
'summary',
|
|
150
|
+
'wordCount',
|
|
151
|
+
]);
|
|
152
|
+
/** The tombstone's store-owned fields beyond the reserved envelope set. `deletedAt`
|
|
153
|
+
* is the only store field a delete carries. */
|
|
154
|
+
export const KNOWLEDGE_TOMBSTONE_KNOWN_FIELDS = Object.freeze([
|
|
155
|
+
'deletedAt',
|
|
156
|
+
]);
|
|
157
|
+
/** The full set of known store fields across BOTH op-branches (the schema's
|
|
158
|
+
* knownFields the registry uses for unknown-field counting — a field legal in EITHER
|
|
159
|
+
* branch is "known", and the branch validate() enforces which is legal for THIS op). */
|
|
160
|
+
const ALL_KNOWN_FIELDS = Object.freeze([
|
|
161
|
+
...KNOWLEDGE_STORE_KNOWN_FIELDS,
|
|
162
|
+
...KNOWLEDGE_TOMBSTONE_KNOWN_FIELDS,
|
|
163
|
+
]);
|
|
164
|
+
// ── ISO-8601 type-clamp: ingestedAt is the load-bearing date field. On a foreign
|
|
165
|
+
// record it MUST validate as a real date or be normalized, so markup cannot survive
|
|
166
|
+
// the clamp. ──────────────────────────────────────────────────────────────────
|
|
167
|
+
/** Is `v` a valid ISO-8601 date string (and ONLY a date — no smuggled markup)? A
|
|
168
|
+
* string Date.parse rejects, or that contains an injection char (`<`, `>`, `"`), is
|
|
169
|
+
* not a clean ISO date. */
|
|
170
|
+
export function isIso8601(v) {
|
|
171
|
+
if (typeof v !== 'string' || v.length === 0 || v.length > 64)
|
|
172
|
+
return false;
|
|
173
|
+
const ms = Date.parse(v);
|
|
174
|
+
if (!Number.isFinite(ms))
|
|
175
|
+
return false;
|
|
176
|
+
if (v.includes('<') || v.includes('>') || v.includes('"'))
|
|
177
|
+
return false;
|
|
178
|
+
return true;
|
|
179
|
+
}
|
|
180
|
+
function clampFreeText(v, max = MAX_FREETEXT_LENGTH) {
|
|
181
|
+
if (typeof v !== 'string')
|
|
182
|
+
return null;
|
|
183
|
+
return v.length > max ? v.slice(0, max) : v;
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* The `knowledge-record` store schema — a DISCRIMINATED UNION on `op`. Strict typed
|
|
187
|
+
* validation on top of the envelope: reject free text beyond the known fields,
|
|
188
|
+
* TYPE-CLAMP every known field (`ingestedAt` ISO-8601, `type` enum, `wordCount` finite
|
|
189
|
+
* number, `tags`/`summary`/`title` length-clamped, `url` jailed against a path-shaped
|
|
190
|
+
* value) so markup cannot smuggle through a render slot that bypasses sanitize().
|
|
191
|
+
* Returns the validated store-specific object (known fields only), or null to reject the
|
|
192
|
+
* WHOLE record. PURE (no I/O, no mutation of `raw`).
|
|
193
|
+
*
|
|
194
|
+
* The envelope validator has ALREADY validated `op` ∈ {put,delete} before calling this.
|
|
195
|
+
* We branch on it so a tombstone `{recordKey, op:'delete', hlc, origin, deletedAt}`
|
|
196
|
+
* passes (only `deletedAt` is a legal store field for a delete) WITHOUT being marked
|
|
197
|
+
* invalid by the rich VALUE schema.
|
|
198
|
+
*/
|
|
199
|
+
export const knowledgeRecordStoreSchema = {
|
|
200
|
+
knownFields: ALL_KNOWN_FIELDS,
|
|
201
|
+
validate(raw, ctx) {
|
|
202
|
+
const op = raw.op;
|
|
203
|
+
// ── DELETE (tombstone) branch. Only `deletedAt` is a legal store field; any
|
|
204
|
+
// VALUE field present is counted as a dropped field but does not reject — the
|
|
205
|
+
// tombstone's recordKey + hlc + op (envelope, already validated) carry the
|
|
206
|
+
// suppression. ────────────────────────────────────────────────────────────
|
|
207
|
+
if (op === 'delete') {
|
|
208
|
+
const deletedAt = isIso8601(raw.deletedAt) ? raw.deletedAt : undefined;
|
|
209
|
+
for (const k of Object.keys(raw)) {
|
|
210
|
+
if (k === 'op' || k === 'deletedAt')
|
|
211
|
+
continue;
|
|
212
|
+
if (KNOWLEDGE_STORE_KNOWN_FIELDS.includes(k))
|
|
213
|
+
ctx.countDroppedField();
|
|
214
|
+
}
|
|
215
|
+
return deletedAt !== undefined ? { deletedAt } : {};
|
|
216
|
+
}
|
|
217
|
+
// ── VALUE (put) branch. ──────────────────────────────────────────────────
|
|
218
|
+
// title — required non-empty free text, clamped.
|
|
219
|
+
const title = clampFreeText(raw.title);
|
|
220
|
+
if (title === null || title.length === 0)
|
|
221
|
+
return null;
|
|
222
|
+
// type — required enum membership (markup cannot survive an enum slot).
|
|
223
|
+
if (typeof raw.type !== 'string' || !KNOWLEDGE_TYPES.includes(raw.type))
|
|
224
|
+
return null;
|
|
225
|
+
const type = raw.type;
|
|
226
|
+
// ingestedAt — required ISO-8601. A non-date coerces to epoch-0 (tolerant-read
|
|
227
|
+
// posture, the manager treats catalog dates as soft) — never record-rejects, but
|
|
228
|
+
// markup can never survive the clamp.
|
|
229
|
+
const ingestedAt = isIso8601(raw.ingestedAt) ? raw.ingestedAt : new Date(0).toISOString();
|
|
230
|
+
// url — OPTIONAL (null when absent). A url is path-sensitive: jail it against a
|
|
231
|
+
// path-shaped value (defense-in-depth — a peer could smuggle a relative artifact
|
|
232
|
+
// path into `url`). null when absent, jailed-out, or over-cap-clamped.
|
|
233
|
+
let url = null;
|
|
234
|
+
if (typeof raw.url === 'string' && raw.url.length > 0) {
|
|
235
|
+
const clampedUrl = raw.url.length > MAX_URL_LENGTH ? raw.url.slice(0, MAX_URL_LENGTH) : raw.url;
|
|
236
|
+
url = jailStoreStringField(clampedUrl, ctx) !== null ? clampedUrl : null;
|
|
237
|
+
}
|
|
238
|
+
// summary — free text, length-clamped on receive (a flood is bounded). NEVER the
|
|
239
|
+
// markdown file body — only this short catalog summary crosses the wire (fork #2).
|
|
240
|
+
const summary = typeof raw.summary === 'string'
|
|
241
|
+
? (raw.summary.length > MAX_SUMMARY_LENGTH ? raw.summary.slice(0, MAX_SUMMARY_LENGTH) : raw.summary)
|
|
242
|
+
: '';
|
|
243
|
+
// wordCount — a FINITE number (a non-finite/non-number coerces to 0; markup cannot
|
|
244
|
+
// survive a numeric slot).
|
|
245
|
+
const wordCount = typeof raw.wordCount === 'number' && Number.isFinite(raw.wordCount) ? raw.wordCount : 0;
|
|
246
|
+
// tags — array of clamped strings, ≤ MAX_TAGS.
|
|
247
|
+
const tags = Array.isArray(raw.tags)
|
|
248
|
+
? raw.tags
|
|
249
|
+
.filter((t) => typeof t === 'string')
|
|
250
|
+
.slice(0, MAX_TAGS)
|
|
251
|
+
.map((t) => (t.length > MAX_FREETEXT_LENGTH ? t.slice(0, MAX_FREETEXT_LENGTH) : t))
|
|
252
|
+
: [];
|
|
253
|
+
return {
|
|
254
|
+
title,
|
|
255
|
+
url,
|
|
256
|
+
type,
|
|
257
|
+
ingestedAt,
|
|
258
|
+
tags,
|
|
259
|
+
summary,
|
|
260
|
+
wordCount,
|
|
261
|
+
};
|
|
262
|
+
},
|
|
263
|
+
};
|
|
264
|
+
// ───────────────────────────────────────────────────────────────────────────
|
|
265
|
+
// recordKey — the cross-machine IDENTITY SURFACE (fork #1)
|
|
266
|
+
// ───────────────────────────────────────────────────────────────────────────
|
|
267
|
+
/** Normalize a string for the content fingerprint: trim + lowercase + collapse
|
|
268
|
+
* internal whitespace, so trivial formatting differences across machines do not split
|
|
269
|
+
* the same source into two records. */
|
|
270
|
+
export function normalizeForKey(v) {
|
|
271
|
+
return String(v).trim().toLowerCase().replace(/\s+/g, ' ');
|
|
272
|
+
}
|
|
273
|
+
/**
|
|
274
|
+
* Derive the cross-machine-stable recordKey for a knowledge source (fork #1). A source is
|
|
275
|
+
* "the same" across machines by its STABLE CONTENT IDENTITY, NOT by the per-machine
|
|
276
|
+
* `generateId()` id — VM-A and VM-B mint different `kb_…` ids for the same article, so an
|
|
277
|
+
* id-keyed record could never collide them (exactly the relationship-UUID / LRN-id trap
|
|
278
|
+
* the prior kinds solved with a stable identity surface).
|
|
279
|
+
*
|
|
280
|
+
* The key is a deterministic, collision-resistant hash:
|
|
281
|
+
* sha256(normalize(url || title) + '\x1f' + normalize(type))
|
|
282
|
+
* hex-truncated to 32 chars (the same shape UnionReader.conflictId uses). The `\x1f`
|
|
283
|
+
* (unit separator) is an un-typeable delimiter so two sources cannot collide by
|
|
284
|
+
* straddling the field boundary.
|
|
285
|
+
*
|
|
286
|
+
* `url` is the NATURAL identity when present (the same article fetched on two machines
|
|
287
|
+
* has the same url even if its title was edited); when ABSENT we fall back to the title.
|
|
288
|
+
* Returns null when the identity anchor (url || title) is empty (a degenerate record with
|
|
289
|
+
* no stable identity surface — the caller skips emission; it can never collide a stranger
|
|
290
|
+
* by an empty key).
|
|
291
|
+
*
|
|
292
|
+
* COLLISION SAFETY: two DIFFERENT sources share a key ONLY if they share the EXACT same
|
|
293
|
+
* normalized (url || title) AND type — which IS the definition of "the same source".
|
|
294
|
+
* SPLIT-IDENTITY SAFETY: the same source derives the SAME key on both machines IFF both
|
|
295
|
+
* hold the same anchor + type; the normalization absorbs trivial formatting drift.
|
|
296
|
+
*/
|
|
297
|
+
export function deriveKnowledgeRecordKey(title, url, type) {
|
|
298
|
+
const anchor = (typeof url === 'string' && url.trim().length > 0)
|
|
299
|
+
? normalizeForKey(url)
|
|
300
|
+
: normalizeForKey(title ?? '');
|
|
301
|
+
const t = normalizeForKey(type ?? '');
|
|
302
|
+
if (anchor.length === 0)
|
|
303
|
+
return null;
|
|
304
|
+
const h = createHash('sha256');
|
|
305
|
+
h.update(`${anchor}\x1f${t}`);
|
|
306
|
+
return h.digest('hex').slice(0, 32);
|
|
307
|
+
}
|
|
308
|
+
/** The named error a record-over-cap surfaces: not silent-truncate, not suspect-wedge. */
|
|
309
|
+
export class KnowledgeRecordTooLargeError extends Error {
|
|
310
|
+
recordKey;
|
|
311
|
+
bytes;
|
|
312
|
+
constructor(recordKey, bytes) {
|
|
313
|
+
super(`knowledge-record ${recordKey} is ${bytes} bytes after projection — over the ${KNOWLEDGE_MAX_ENTRY_BYTES}-byte per-entry cap; not replicated`);
|
|
314
|
+
this.recordKey = recordKey;
|
|
315
|
+
this.bytes = bytes;
|
|
316
|
+
this.name = 'KnowledgeRecordTooLargeError';
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
function clampFreeTextEmit(v, max = MAX_FREETEXT_LENGTH) {
|
|
320
|
+
return typeof v === 'string' && v.length > max ? v.slice(0, max) : (v ?? '');
|
|
321
|
+
}
|
|
322
|
+
/**
|
|
323
|
+
* Build the disclosure-minimized `knowledge-record` envelope `data` for an `op:'put'`
|
|
324
|
+
* (fork #2). Emits ONLY the enumerated CATALOG-METADATA projection — NEVER the markdown
|
|
325
|
+
* file BODY (the `filePath` file), NEVER the local generated `id`, NEVER the local
|
|
326
|
+
* `filePath` (a local artifact path — meaningless + a mild info-leak on a peer).
|
|
327
|
+
* recordKey = the derived content-fingerprint identity surface (fork #1).
|
|
328
|
+
*
|
|
329
|
+
* Returns null when the record has no stable identity surface (empty url+title ⇒
|
|
330
|
+
* deriveKnowledgeRecordKey null — the caller skips emission). Throws
|
|
331
|
+
* KnowledgeRecordTooLargeError when the projection STILL exceeds the 64KB per-entry cap
|
|
332
|
+
* (a NAMED, surfaced rejection — never silent-truncate).
|
|
333
|
+
*/
|
|
334
|
+
export function buildKnowledgeRecordData(input) {
|
|
335
|
+
const { record, hlc, origin, observed } = input;
|
|
336
|
+
const recordKey = deriveKnowledgeRecordKey(record.title, record.url, record.type);
|
|
337
|
+
if (recordKey === null)
|
|
338
|
+
return null;
|
|
339
|
+
const data = {
|
|
340
|
+
title: clampFreeTextEmit(record.title),
|
|
341
|
+
// url — emit as a string when present, else null (a NULL url is a legal absence; the
|
|
342
|
+
// local filePath is NEVER emitted — fork #2).
|
|
343
|
+
url: typeof record.url === 'string' && record.url.length > 0
|
|
344
|
+
? clampFreeTextEmit(record.url, MAX_URL_LENGTH)
|
|
345
|
+
: null,
|
|
346
|
+
type: record.type,
|
|
347
|
+
ingestedAt: record.ingestedAt,
|
|
348
|
+
tags: Array.isArray(record.tags) ? record.tags.slice(0, MAX_TAGS).map((t) => clampFreeTextEmit(t)) : [],
|
|
349
|
+
summary: typeof record.summary === 'string'
|
|
350
|
+
? (record.summary.length > MAX_SUMMARY_LENGTH ? record.summary.slice(0, MAX_SUMMARY_LENGTH) : record.summary)
|
|
351
|
+
: '',
|
|
352
|
+
wordCount: typeof record.wordCount === 'number' && Number.isFinite(record.wordCount) ? record.wordCount : 0,
|
|
353
|
+
// envelope fields (recordKey = identity surface).
|
|
354
|
+
recordKey,
|
|
355
|
+
hlc,
|
|
356
|
+
op: 'put',
|
|
357
|
+
origin,
|
|
358
|
+
...(observed !== undefined ? { observed } : {}),
|
|
359
|
+
};
|
|
360
|
+
assertProjectionUnderCap(recordKey, data);
|
|
361
|
+
return data;
|
|
362
|
+
}
|
|
363
|
+
/** Throw KnowledgeRecordTooLargeError if the projected data serializes over the
|
|
364
|
+
* per-entry cap. The cap is set so a legal disclosure-minimized record can never reach
|
|
365
|
+
* it; this is the belt-and-suspenders named rejection. */
|
|
366
|
+
export function assertProjectionUnderCap(recordKey, data) {
|
|
367
|
+
const bytes = Buffer.byteLength(JSON.stringify(data), 'utf-8');
|
|
368
|
+
if (bytes > KNOWLEDGE_MAX_ENTRY_BYTES) {
|
|
369
|
+
throw new KnowledgeRecordTooLargeError(recordKey, bytes);
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
/**
|
|
373
|
+
* Build an `op:'delete'` TOMBSTONE `data` for a knowledge-source removal. recordKey = the
|
|
374
|
+
* SAME content-fingerprint identity surface the value records key on, so the tombstone
|
|
375
|
+
* reaches the same source's record on every machine even though the local ids differ.
|
|
376
|
+
* Returns null when the identity anchor (url || title) is empty (no identity surface to
|
|
377
|
+
* tombstone).
|
|
378
|
+
*
|
|
379
|
+
* CRITICAL (fork-adjacent): the KnowledgeManager.remove() path MUST call this for the
|
|
380
|
+
* removed source, else a peer re-replicates the locally-removed source forever
|
|
381
|
+
* (resurrection). The delete-resurrection guard lives in the merge (a later `delete` hlc
|
|
382
|
+
* wins over an earlier `put`).
|
|
383
|
+
*/
|
|
384
|
+
export function buildKnowledgeTombstoneData(input) {
|
|
385
|
+
const recordKey = deriveKnowledgeRecordKey(input.title, input.url, input.type);
|
|
386
|
+
if (recordKey === null)
|
|
387
|
+
return null;
|
|
388
|
+
return {
|
|
389
|
+
deletedAt: input.deletedAt,
|
|
390
|
+
recordKey,
|
|
391
|
+
hlc: input.hlc,
|
|
392
|
+
op: 'delete',
|
|
393
|
+
origin: input.origin,
|
|
394
|
+
...(input.observed !== undefined ? { observed: input.observed } : {}),
|
|
395
|
+
};
|
|
396
|
+
}
|
|
397
|
+
/** Reconstruct a MergedKnowledgeView from an OriginRecord (the envelope stripped). */
|
|
398
|
+
function viewFromOriginRecord(rec, conflicted) {
|
|
399
|
+
return { recordKey: rec.envelope.recordKey, origin: rec.origin, data: rec.data, conflicted };
|
|
400
|
+
}
|
|
401
|
+
/**
|
|
402
|
+
* Collapse a `Map<recordKey, UnionResult>` into the merged knowledge view.
|
|
403
|
+
* HIGH-impact-at-replication / ADVISORY-at-read contract (fork #3):
|
|
404
|
+
* - A resolved single value ⇒ that one view entry.
|
|
405
|
+
* - An OPEN concurrent conflict ⇒ BOTH (all) `put` variants as separate entries
|
|
406
|
+
* (append-both — both surface as ADVISORY guidance; the read NEVER suppresses a
|
|
407
|
+
* usable view AND NEVER BLOCKS waiting on operator resolution — a knowledge source is
|
|
408
|
+
* reference, not authority). A `delete` variant contributes nothing to display.
|
|
409
|
+
* - A delete-resolved key (every origin's latest is a tombstone) ⇒ nothing (the
|
|
410
|
+
* delete-resurrection guard: a later delete wins over an earlier put).
|
|
411
|
+
* The read is READ-ONLY: a replicated record NEVER clobbers a divergent local record —
|
|
412
|
+
* the local store files are never written here.
|
|
413
|
+
*/
|
|
414
|
+
export function mergeUnionToKnowledge(union) {
|
|
415
|
+
const out = [];
|
|
416
|
+
for (const result of union.values()) {
|
|
417
|
+
if (result.conflict) {
|
|
418
|
+
for (const v of result.conflict.versions) {
|
|
419
|
+
if (v.envelope.op === 'delete')
|
|
420
|
+
continue;
|
|
421
|
+
out.push(viewFromOriginRecord(v, true));
|
|
422
|
+
}
|
|
423
|
+
continue;
|
|
424
|
+
}
|
|
425
|
+
if (result.value && result.value.envelope.op !== 'delete') {
|
|
426
|
+
out.push(viewFromOriginRecord(result.value, false));
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
return out;
|
|
430
|
+
}
|
|
431
|
+
// ───────────────────────────────────────────────────────────────────────────
|
|
432
|
+
// E. Foreign-record render safety — quoted untrusted data
|
|
433
|
+
// ───────────────────────────────────────────────────────────────────────────
|
|
434
|
+
/** Sanitize a string for inclusion in a context block (escape the envelope-break +
|
|
435
|
+
* markup vectors). */
|
|
436
|
+
function sanitize(s) {
|
|
437
|
+
return String(s).replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"');
|
|
438
|
+
}
|
|
439
|
+
/**
|
|
440
|
+
* Render a FOREIGN (replicated) knowledge record into a session-context block, wrapped in
|
|
441
|
+
* an explicit `<replicated-untrusted-data origin="…">` envelope so the session model
|
|
442
|
+
* treats it as a PEER'S knowledge source to re-ground against, never a directive. EVERY
|
|
443
|
+
* rendered field is escaped — there is no "trusted because machine-set" slot. A null
|
|
444
|
+
* `data.title` (a malformed view) yields null.
|
|
445
|
+
*/
|
|
446
|
+
export function renderForeignKnowledgeContext(view) {
|
|
447
|
+
const d = view.data;
|
|
448
|
+
if (typeof d.title !== 'string' || d.title.length === 0)
|
|
449
|
+
return null;
|
|
450
|
+
const safeOrigin = sanitize(view.origin);
|
|
451
|
+
const lines = [
|
|
452
|
+
`<replicated-untrusted-data origin="${safeOrigin}">`,
|
|
453
|
+
`Knowledge source: ${sanitize(d.title)}`,
|
|
454
|
+
];
|
|
455
|
+
if (typeof d.type === 'string')
|
|
456
|
+
lines.push(`Type: ${sanitize(d.type)}`);
|
|
457
|
+
if (typeof d.url === 'string' && d.url.length > 0)
|
|
458
|
+
lines.push(`URL: ${sanitize(d.url)}`);
|
|
459
|
+
if (typeof d.ingestedAt === 'string')
|
|
460
|
+
lines.push(`Ingested: ${sanitize(d.ingestedAt)}`);
|
|
461
|
+
if (Array.isArray(d.tags) && d.tags.length > 0)
|
|
462
|
+
lines.push(`Tags: ${d.tags.map(sanitize).join(', ')}`);
|
|
463
|
+
if (typeof d.wordCount === 'number')
|
|
464
|
+
lines.push(`Word count: ${d.wordCount}`);
|
|
465
|
+
if (typeof d.summary === 'string' && d.summary.length > 0)
|
|
466
|
+
lines.push(`Summary: ${sanitize(d.summary)}`);
|
|
467
|
+
lines.push('</replicated-untrusted-data>');
|
|
468
|
+
return lines.join('\n');
|
|
469
|
+
}
|
|
470
|
+
// ───────────────────────────────────────────────────────────────────────────
|
|
471
|
+
// Own-origin materialization for the union reader (mirrors WS2.2)
|
|
472
|
+
// ───────────────────────────────────────────────────────────────────────────
|
|
473
|
+
/**
|
|
474
|
+
* Build an OriginRecord for the OWN knowledge store (the single-origin materialization
|
|
475
|
+
* the union reader merges against peer replicas). recordKey = derived content-fingerprint
|
|
476
|
+
* identity surface; the envelope carries a SYNTHETIC own-origin HLC stamp derived
|
|
477
|
+
* deterministically from `ingestedAt` (physical) so the own record has a well-formed,
|
|
478
|
+
* stable position relative to peer records. Returns null for a degenerate record (no
|
|
479
|
+
* identity surface). The local `id` + `filePath` are NEVER carried into the replicated
|
|
480
|
+
* namespace.
|
|
481
|
+
*/
|
|
482
|
+
export function knowledgeToOriginRecord(record, origin) {
|
|
483
|
+
const recordKey = deriveKnowledgeRecordKey(record.title, record.url, record.type);
|
|
484
|
+
if (recordKey === null)
|
|
485
|
+
return null;
|
|
486
|
+
const physical = Date.parse(record.ingestedAt ?? '');
|
|
487
|
+
const hlc = {
|
|
488
|
+
physical: Number.isFinite(physical) ? physical : 0,
|
|
489
|
+
logical: 0,
|
|
490
|
+
node: origin,
|
|
491
|
+
};
|
|
492
|
+
const data = {
|
|
493
|
+
title: record.title,
|
|
494
|
+
url: typeof record.url === 'string' && record.url.length > 0 ? record.url : null,
|
|
495
|
+
type: record.type,
|
|
496
|
+
ingestedAt: record.ingestedAt,
|
|
497
|
+
tags: Array.isArray(record.tags) ? record.tags : [],
|
|
498
|
+
summary: record.summary ?? '',
|
|
499
|
+
wordCount: typeof record.wordCount === 'number' && Number.isFinite(record.wordCount) ? record.wordCount : 0,
|
|
500
|
+
};
|
|
501
|
+
const envelope = { recordKey, hlc, op: 'put', origin };
|
|
502
|
+
return { origin, envelope, data };
|
|
503
|
+
}
|
|
504
|
+
// ───────────────────────────────────────────────────────────────────────────
|
|
505
|
+
// Registration descriptor (consumed by server.ts to register the dual registry)
|
|
506
|
+
// ───────────────────────────────────────────────────────────────────────────
|
|
507
|
+
/** The ReplicatedKindRegistry registration for the `knowledge-record` store. server.ts
|
|
508
|
+
* registers this onto the shared registry; the dual-registry coupling test asserts
|
|
509
|
+
* `kind` is also present in JOURNAL_KINDS. */
|
|
510
|
+
export const KNOWLEDGE_KIND_REGISTRATION = {
|
|
511
|
+
kind: KNOWLEDGE_RECORD_KIND,
|
|
512
|
+
store: KNOWLEDGE_STORE_KEY,
|
|
513
|
+
schema: knowledgeRecordStoreSchema,
|
|
514
|
+
};
|
|
515
|
+
/** Convenience: the store's contributing journal kinds (for rollback-unmerge's
|
|
516
|
+
* kindsForStore('knowledge') wiring). */
|
|
517
|
+
export function knowledgeContributingKinds() {
|
|
518
|
+
return [KNOWLEDGE_RECORD_KIND];
|
|
519
|
+
}
|
|
520
|
+
/** The store's impact tier resolver, for ReplicatedStoreReader.tierOf. Returns HIGH for
|
|
521
|
+
* the `knowledge` store (and HIGH for any unknown store — the conservative
|
|
522
|
+
* append-both-and-flag direction, never a silent clobber). */
|
|
523
|
+
export function knowledgeTierOf(_store) {
|
|
524
|
+
return KNOWLEDGE_IMPACT_TIER;
|
|
525
|
+
}
|
|
526
|
+
//# sourceMappingURL=KnowledgeReplicatedStore.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"KnowledgeReplicatedStore.js","sourceRoot":"","sources":["../../src/core/KnowledgeReplicatedStore.ts"],"names":[],"mappings":"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"}
|