crbro-memory 1.6.1 → 1.8.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.
@@ -0,0 +1,178 @@
1
+ "use strict";
2
+ // ─── CRBRO Shared Memory: the git transport ──────────────────────
3
+ //
4
+ // Git is the whole backend. No service to run, no account to create, no bill:
5
+ // a private repository is already storage, history, access control and a merge
6
+ // engine, and the user picks where it lives.
7
+ //
8
+ // Two things about running git from inside an MCP server matter more than the
9
+ // commands themselves. It must never wait for a human — there is no terminal
10
+ // on the other end of stdio, so a credential prompt would hang the assistant
11
+ // forever. And it must never rewrite what it stores: git on Windows converts
12
+ // line endings by default, which turns one appended line into two different
13
+ // lines on two machines, and the logs here are append-only.
14
+ var __importDefault = (this && this.__importDefault) || function (mod) {
15
+ return (mod && mod.__esModule) ? mod : { "default": mod };
16
+ };
17
+ Object.defineProperty(exports, "__esModule", { value: true });
18
+ exports.GITIGNORE = exports.GITATTRIBUTES = void 0;
19
+ exports.git = git;
20
+ exports.gitAvailable = gitAvailable;
21
+ exports.hardenRepo = hardenRepo;
22
+ exports.initSpace = initSpace;
23
+ exports.cloneSpace = cloneSpace;
24
+ exports.syncSpace = syncSpace;
25
+ const node_child_process_1 = require("node:child_process");
26
+ const node_fs_1 = require("node:fs");
27
+ const node_path_1 = __importDefault(require("node:path"));
28
+ /**
29
+ * Everything that could make git stop and ask a person something, turned off.
30
+ * Without this the server hangs on the first private repository it meets.
31
+ */
32
+ const SILENT_ENV = {
33
+ GIT_TERMINAL_PROMPT: '0',
34
+ GIT_ASKPASS: '',
35
+ SSH_ASKPASS: '',
36
+ GIT_SSH_COMMAND: 'ssh -o BatchMode=yes -o StrictHostKeyChecking=accept-new',
37
+ GCM_INTERACTIVE: 'never',
38
+ };
39
+ function classify(stderr) {
40
+ const s = stderr.toLowerCase();
41
+ if (s.includes('could not resolve host') || s.includes('unable to access') ||
42
+ s.includes('network is unreachable') || s.includes('operation timed out'))
43
+ return 'offline';
44
+ if (s.includes('authentication failed') || s.includes('permission denied') ||
45
+ s.includes('could not read username') || s.includes('terminal prompts disabled'))
46
+ return 'auth';
47
+ if (s.includes('conflict'))
48
+ return 'conflict';
49
+ return undefined;
50
+ }
51
+ function git(args, cwd, timeoutMs = 20_000) {
52
+ const r = (0, node_child_process_1.spawnSync)('git', args, {
53
+ cwd,
54
+ encoding: 'utf-8',
55
+ timeout: timeoutMs,
56
+ windowsHide: true,
57
+ env: { ...process.env, ...SILENT_ENV },
58
+ });
59
+ if (r.error) {
60
+ const code = r.error.code;
61
+ return {
62
+ ok: false,
63
+ stdout: '',
64
+ stderr: String(r.error.message || ''),
65
+ reason: code === 'ENOENT' ? 'no_git' : 'timeout',
66
+ };
67
+ }
68
+ if (r.signal) {
69
+ return { ok: false, stdout: r.stdout || '', stderr: r.stderr || '', reason: 'timeout' };
70
+ }
71
+ const stderr = r.stderr || '';
72
+ return {
73
+ ok: r.status === 0,
74
+ stdout: r.stdout || '',
75
+ stderr,
76
+ reason: r.status === 0 ? undefined : classify(stderr),
77
+ };
78
+ }
79
+ /** Is git installed at all? */
80
+ function gitAvailable() {
81
+ return git(['--version'], process.cwd(), 5_000).ok;
82
+ }
83
+ /**
84
+ * Settings that have to be on the repository itself, not on the machine.
85
+ *
86
+ * `core.autocrlf` ships enabled on Windows — verified true at system level on
87
+ * the machine this was built on — and it rewrites files on checkout. With
88
+ * append-only logs merged by union, the same line with two different endings
89
+ * becomes two lines, and the memory quietly duplicates itself. `* -text` in
90
+ * .gitattributes plus this setting stops it at both ends.
91
+ */
92
+ function hardenRepo(dir) {
93
+ git(['config', 'core.autocrlf', 'false'], dir);
94
+ git(['config', 'core.safecrlf', 'false'], dir);
95
+ git(['config', 'merge.ours.driver', 'true'], dir);
96
+ // Identity, so committing works on a machine with no global git config.
97
+ if (!git(['config', 'user.email'], dir).stdout.trim()) {
98
+ git(['config', 'user.email', 'crbro@localhost'], dir);
99
+ }
100
+ if (!git(['config', 'user.name'], dir).stdout.trim()) {
101
+ git(['config', 'user.name', 'CRBRO'], dir);
102
+ }
103
+ }
104
+ exports.GITATTRIBUTES = '# CRBRO shared memory. Do not edit.\n' +
105
+ '# -text keeps git from rewriting line endings: these logs are append-only\n' +
106
+ '# and a rewritten ending turns one line into two on the next merge.\n' +
107
+ '* -text\n' +
108
+ '*.jsonl merge=union\n';
109
+ exports.GITIGNORE = '# Local bookkeeping. Never shared.\n' +
110
+ '.local/\n';
111
+ /** Prepare a brand-new space so the first person to clone finds real history. */
112
+ async function initSpace(dir, remote, branch = 'main') {
113
+ await node_fs_1.promises.mkdir(dir, { recursive: true });
114
+ const init = git(['init', '-b', branch], dir);
115
+ if (!init.ok && !init.stdout.includes('Reinitialized')) {
116
+ const legacy = git(['init'], dir);
117
+ if (!legacy.ok)
118
+ return legacy;
119
+ git(['checkout', '-b', branch], dir);
120
+ }
121
+ hardenRepo(dir);
122
+ await node_fs_1.promises.writeFile(node_path_1.default.join(dir, '.gitattributes'), exports.GITATTRIBUTES, 'utf-8');
123
+ await node_fs_1.promises.writeFile(node_path_1.default.join(dir, '.gitignore'), exports.GITIGNORE, 'utf-8');
124
+ git(['remote', 'remove', 'origin'], dir);
125
+ const add = git(['remote', 'add', 'origin', remote], dir);
126
+ if (!add.ok)
127
+ return add;
128
+ git(['add', '-A', '.'], dir);
129
+ git(['commit', '-q', '-m', 'CRBRO shared space'], dir);
130
+ // Push the first commit before anyone clones. Skipping this is how two
131
+ // people end up with unrelated histories and a merge that refuses to run —
132
+ // each of them keeping their own half of the memory without noticing.
133
+ return git(['push', '-u', 'origin', `HEAD:${branch}`], dir, 30_000);
134
+ }
135
+ /** Clone an existing space. */
136
+ function cloneSpace(dir, remote, branch = 'main') {
137
+ const parent = node_path_1.default.dirname(dir);
138
+ const name = node_path_1.default.basename(dir);
139
+ const r = git(['clone', '--branch', branch, remote, name], parent, 60_000);
140
+ if (r.ok)
141
+ hardenRepo(dir);
142
+ return r;
143
+ }
144
+ /**
145
+ * Bring in everyone else's notes and send ours.
146
+ * Offline is a normal outcome, not an error: the work stays local and goes out
147
+ * next time. Nothing here can lose a note, because nobody edits anyone's file.
148
+ */
149
+ function syncSpace(dir, author, branch = 'main', timeoutMs = 30_000) {
150
+ git(['add', '-A', '.'], dir);
151
+ const hayCambios = !git(['diff', '--cached', '--quiet'], dir).ok;
152
+ if (hayCambios) {
153
+ git(['commit', '-q', '-m', `crbro ${author} ${new Date().toISOString()}`], dir);
154
+ }
155
+ const fetched = git(['fetch', 'origin', branch], dir, timeoutMs);
156
+ if (!fetched.ok)
157
+ return { pulled: fetched, pushed: null };
158
+ const merged = git(['merge', '--no-edit', '--allow-unrelated-histories', `origin/${branch}`], dir, timeoutMs);
159
+ if (!merged.ok) {
160
+ // Union merging makes this rare, but a hand-edited file could still clash.
161
+ // Keeping both sides and stopping beats guessing.
162
+ git(['merge', '--abort'], dir);
163
+ return { pulled: { ...merged, reason: 'conflict' }, pushed: null };
164
+ }
165
+ // Push, and if someone landed first, take their work and try once more.
166
+ let pushed = git(['push', 'origin', `HEAD:${branch}`], dir, timeoutMs);
167
+ for (let intento = 0; intento < 2 && !pushed.ok && pushed.reason !== 'auth'; intento++) {
168
+ if (!git(['fetch', 'origin', branch], dir, timeoutMs).ok)
169
+ break;
170
+ if (!git(['merge', '--no-edit', `origin/${branch}`], dir, timeoutMs).ok) {
171
+ git(['merge', '--abort'], dir);
172
+ break;
173
+ }
174
+ pushed = git(['push', 'origin', `HEAD:${branch}`], dir, timeoutMs);
175
+ }
176
+ return { pulled: merged, pushed };
177
+ }
178
+ //# sourceMappingURL=git.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"git.js","sourceRoot":"","sources":["../../src/sync/git.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,26 @@
1
+ import type { Neuron } from '../types/index.js';
2
+ import type { Op } from './ops.js';
3
+ /** Fields that disagreed and were left alone, so a human can look. */
4
+ export interface Divergence {
5
+ field: string;
6
+ mine: string;
7
+ theirs: string;
8
+ from: string;
9
+ }
10
+ export interface MergeReport {
11
+ facts_added: number;
12
+ facts_retracted: number;
13
+ facts_superseded: number;
14
+ decisions_added: number;
15
+ patterns_added: number;
16
+ tags_added: number;
17
+ authors: string[];
18
+ divergence: Divergence[];
19
+ }
20
+ export declare function applyOps(base: Neuron | null, ops: Op[], fallback?: {
21
+ id: string;
22
+ }): {
23
+ neuron: Neuron;
24
+ report: MergeReport;
25
+ };
26
+ //# sourceMappingURL=materialize.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"materialize.d.ts","sourceRoot":"","sources":["../../src/sync/materialize.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,MAAM,EAA8B,MAAM,mBAAmB,CAAC;AAC5E,OAAO,KAAK,EAAE,EAAE,EAAgC,MAAM,UAAU,CAAC;AAGjE,sEAAsE;AACtE,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,WAAW;IAC1B,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe,EAAE,MAAM,CAAC;IACxB,gBAAgB,EAAE,MAAM,CAAC;IACzB,eAAe,EAAE,MAAM,CAAC;IACxB,cAAc,EAAE,MAAM,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,UAAU,EAAE,UAAU,EAAE,CAAC;CAC1B;AAqBD,wBAAgB,QAAQ,CACtB,IAAI,EAAE,MAAM,GAAG,IAAI,EACnB,GAAG,EAAE,EAAE,EAAE,EACT,QAAQ,CAAC,EAAE;IAAE,EAAE,EAAE,MAAM,CAAA;CAAE,GACxB;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,WAAW,CAAA;CAAE,CA6JzC"}
@@ -0,0 +1,192 @@
1
+ "use strict";
2
+ // ─── CRBRO Shared Memory: rebuilding a neuron from the logs ──────
3
+ //
4
+ // Everything hard about sharing memory lives in this file, and it is one pure
5
+ // function. Given whatever the neuron looks like locally plus every note
6
+ // anyone has written, produce the neuron. No disk, no network, no git.
7
+ //
8
+ // Three properties make the merge automatic, and every rule below is chosen to
9
+ // preserve them:
10
+ // - order does not matter,
11
+ // - applying the same note twice changes nothing,
12
+ // - applying half the notes now and the rest later ends up the same.
13
+ // Anything that needed a clock, a "latest wins", or a tie-break on wall time
14
+ // would break them, which is why none of that is here.
15
+ Object.defineProperty(exports, "__esModule", { value: true });
16
+ exports.applyOps = applyOps;
17
+ const fs_js_1 = require("../utils/fs.js");
18
+ const ops_js_1 = require("./ops.js");
19
+ /**
20
+ * How far along a fact's life it is. Merging takes the maximum, so a fact
21
+ * someone retracted can never come back to life because a stale log still
22
+ * calls it active. Truth about what is no longer true only moves one way.
23
+ */
24
+ const RANK = { active: 0, superseded: 1, retracted: 2 };
25
+ const BY_RANK = ['active', 'superseded', 'retracted'];
26
+ function rankOf(s) {
27
+ return RANK[s || 'active'] ?? 0;
28
+ }
29
+ /** The earlier of two ISO dates. Empty strings lose. */
30
+ function earliest(a, b) {
31
+ if (!a)
32
+ return b;
33
+ if (!b)
34
+ return a;
35
+ return a < b ? a : b;
36
+ }
37
+ function applyOps(base, ops, fallback) {
38
+ const report = {
39
+ facts_added: 0,
40
+ facts_retracted: 0,
41
+ facts_superseded: 0,
42
+ decisions_added: 0,
43
+ patterns_added: 0,
44
+ tags_added: 0,
45
+ authors: [],
46
+ divergence: [],
47
+ };
48
+ const neuronOp = ops.find(o => o.op === 'neuron');
49
+ const id = base?.id || neuronOp?.nid || fallback?.id || '';
50
+ // Start from what we already have. If we have nothing, the announcement
51
+ // gives us a name; if we do have it, ours stands and any disagreement is
52
+ // reported rather than resolved. Overwriting someone's local name because a
53
+ // teammate spelled it differently is not a merge, it is a stomp.
54
+ const neuron = base
55
+ ? { ...base, facts: [...base.facts], decisions: [...base.decisions], patterns: [...base.patterns], preferences: [...base.preferences], tags: [...base.tags], connections: [...base.connections] }
56
+ : {
57
+ id,
58
+ name: neuronOp && neuronOp.op === 'neuron' ? neuronOp.name : id,
59
+ domain: neuronOp && neuronOp.op === 'neuron' ? neuronOp.domain : 'general',
60
+ type: (neuronOp && neuronOp.op === 'neuron' ? neuronOp.ntype : 'project'),
61
+ created: (0, fs_js_1.now)(),
62
+ last_accessed: (0, fs_js_1.now)(),
63
+ access_count: 0,
64
+ heat: 0.5,
65
+ summary: '',
66
+ facts: [],
67
+ decisions: [],
68
+ patterns: [],
69
+ preferences: [],
70
+ connections: [],
71
+ tags: [],
72
+ };
73
+ if (base && neuronOp && neuronOp.op === 'neuron') {
74
+ if (neuronOp.name && base.name && (0, ops_js_1.normalizeText)(neuronOp.name) !== (0, ops_js_1.normalizeText)(base.name)) {
75
+ report.divergence.push({ field: 'name', mine: base.name, theirs: neuronOp.name, from: neuronOp.by });
76
+ }
77
+ if (neuronOp.domain && base.domain && neuronOp.domain !== base.domain && base.domain !== 'general') {
78
+ report.divergence.push({ field: 'domain', mine: base.domain, theirs: neuronOp.domain, from: neuronOp.by });
79
+ }
80
+ }
81
+ // ─── Facts: union by id, then the furthest-along status ───────
82
+ const byFid = new Map();
83
+ for (const f of neuron.facts) {
84
+ byFid.set(f.id || '', f);
85
+ }
86
+ // Facts stored before ids existed: index them by their text so an incoming
87
+ // note about the same sentence lands on the same fact instead of duplicating.
88
+ const byText = new Map();
89
+ for (const f of neuron.facts) {
90
+ byText.set((0, ops_js_1.normalizeText)(f.text), f);
91
+ }
92
+ const statusOps = [];
93
+ for (const op of ops) {
94
+ if (op.op === 'fact') {
95
+ const fo = op;
96
+ const existing = byFid.get(fo.fid) || byText.get((0, ops_js_1.normalizeText)(fo.text));
97
+ if (existing) {
98
+ // Same fact from two people. Keep the earliest date so provenance
99
+ // reflects who knew it first, and the higher confidence.
100
+ existing.id = existing.id || fo.fid;
101
+ existing.added = earliest(existing.added, fo.at);
102
+ existing.confidence = Math.max(existing.confidence ?? 1, fo.conf ?? 1);
103
+ continue;
104
+ }
105
+ const fact = {
106
+ text: fo.text,
107
+ confidence: fo.conf ?? 1,
108
+ added: fo.at,
109
+ source: `team:${fo.by}`,
110
+ id: fo.fid,
111
+ status: 'active',
112
+ };
113
+ neuron.facts.push(fact);
114
+ byFid.set(fo.fid, fact);
115
+ byText.set((0, ops_js_1.normalizeText)(fo.text), fact);
116
+ report.facts_added++;
117
+ }
118
+ else if (op.op === 'status') {
119
+ statusOps.push(op);
120
+ }
121
+ }
122
+ // Status is applied after every fact exists, so a note retracting a fact
123
+ // works no matter which log it arrived in or in what order.
124
+ for (const so of statusOps) {
125
+ const target = byFid.get(so.fid);
126
+ if (!target)
127
+ continue;
128
+ const antes = rankOf(target.status);
129
+ const despues = Math.max(antes, rankOf(so.to));
130
+ if (despues === antes)
131
+ continue;
132
+ target.status = BY_RANK[despues];
133
+ target.revised = so.at;
134
+ if (so.why)
135
+ target.revision_note = so.why;
136
+ if (target.status === 'retracted')
137
+ report.facts_retracted++;
138
+ else
139
+ report.facts_superseded++;
140
+ }
141
+ // ─── Decisions: union by (id, author) ────────────────────────
142
+ // Two people can reach the same decision for different reasons, and both
143
+ // reasons are worth keeping. Keying on the pair means neither is lost.
144
+ const seenDecision = new Set();
145
+ for (const d of neuron.decisions) {
146
+ seenDecision.add(`${d.id || (0, ops_js_1.normalizeText)(d.text)}|${d.by || ''}`);
147
+ }
148
+ for (const op of ops) {
149
+ if (op.op !== 'decision')
150
+ continue;
151
+ const dop = op;
152
+ const key = `${dop.did}|${dop.by}`;
153
+ if (seenDecision.has(key))
154
+ continue;
155
+ seenDecision.add(key);
156
+ neuron.decisions.push({
157
+ text: dop.text,
158
+ date: dop.at,
159
+ rationale: dop.why || '',
160
+ id: dop.did,
161
+ by: dop.by,
162
+ });
163
+ report.decisions_added++;
164
+ }
165
+ // ─── Patterns and tags: plain set union ──────────────────────
166
+ for (const op of ops) {
167
+ if (op.op === 'pattern') {
168
+ if (!neuron.patterns.some(p => (0, ops_js_1.normalizeText)(p) === (0, ops_js_1.normalizeText)(op.text))) {
169
+ neuron.patterns.push(op.text);
170
+ report.patterns_added++;
171
+ }
172
+ }
173
+ else if (op.op === 'tag') {
174
+ if (!neuron.tags.some(t => (0, ops_js_1.normalizeText)(t) === (0, ops_js_1.normalizeText)(op.text))) {
175
+ neuron.tags.push(op.text);
176
+ report.tags_added++;
177
+ }
178
+ }
179
+ }
180
+ // Deterministic order, so two machines holding the same knowledge produce
181
+ // byte-identical files. Without this, every sync would look like a change.
182
+ neuron.facts.sort((a, b) => {
183
+ const d = String(a.added || '').localeCompare(String(b.added || ''));
184
+ return d !== 0 ? d : String(a.id || '').localeCompare(String(b.id || ''));
185
+ });
186
+ neuron.decisions.sort((a, b) => String(a.date || '').localeCompare(String(b.date || '')));
187
+ neuron.patterns.sort();
188
+ neuron.tags.sort();
189
+ report.authors = [...new Set(ops.map(o => o.by).filter(Boolean))].sort();
190
+ return { neuron, report };
191
+ }
192
+ //# sourceMappingURL=materialize.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"materialize.js","sourceRoot":"","sources":["../../src/sync/materialize.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,76 @@
1
+ /** Bumped only if the shape changes in a way older clients cannot read. */
2
+ export declare const OPS_VERSION = 1;
3
+ export type OpKind = 'neuron' | 'fact' | 'status' | 'decision' | 'pattern' | 'tag';
4
+ interface OpBase {
5
+ v: number;
6
+ op: OpKind;
7
+ /** Neuron this applies to. */
8
+ nid: string;
9
+ /** Who wrote it. Not a security claim — anyone with push access can write any name. */
10
+ by: string;
11
+ /** ISO timestamp. Used for display and for keeping the earliest date, never to decide a winner. */
12
+ at: string;
13
+ }
14
+ /** Announces a neuron. Its name/type/domain are only used if the receiver has no such neuron. */
15
+ export interface NeuronOp extends OpBase {
16
+ op: 'neuron';
17
+ name: string;
18
+ ntype: string;
19
+ domain: string;
20
+ }
21
+ export interface FactOp extends OpBase {
22
+ op: 'fact';
23
+ fid: string;
24
+ text: string;
25
+ conf: number;
26
+ src?: string;
27
+ }
28
+ /**
29
+ * A fact stopped being current. Only ever moves forward:
30
+ * active → superseded → retracted, never back.
31
+ */
32
+ export interface StatusOp extends OpBase {
33
+ op: 'status';
34
+ fid: string;
35
+ to: 'superseded' | 'retracted';
36
+ why?: string;
37
+ }
38
+ export interface DecisionOp extends OpBase {
39
+ op: 'decision';
40
+ did: string;
41
+ text: string;
42
+ why?: string;
43
+ }
44
+ export interface PatternOp extends OpBase {
45
+ op: 'pattern';
46
+ text: string;
47
+ }
48
+ export interface TagOp extends OpBase {
49
+ op: 'tag';
50
+ text: string;
51
+ }
52
+ export type Op = NeuronOp | FactOp | StatusOp | DecisionOp | PatternOp | TagOp;
53
+ /**
54
+ * Same wording, same id, on every machine.
55
+ *
56
+ * Normalising first matters: one person's copy of a sentence may differ from
57
+ * another's by a stray double space or a different Unicode composition, and
58
+ * without this they would become two facts saying the same thing.
59
+ */
60
+ export declare function normalizeText(text: string): string;
61
+ export declare function entryId(text: string): string;
62
+ /** Serialise one operation as a single line. */
63
+ export declare function encodeOp(op: Op): string;
64
+ /**
65
+ * Parse a log. Unreadable lines are skipped rather than thrown, because a log
66
+ * can be cut in half by a process dying mid-append or by a merge landing while
67
+ * we read. One bad line must never cost the other nine hundred.
68
+ */
69
+ export declare function decodeOps(content: string): {
70
+ ops: Op[];
71
+ skipped: number;
72
+ };
73
+ /** Where a person's log for a neuron lives inside the space. */
74
+ export declare function opsRelPath(neuronId: string, author: string, device: string): string;
75
+ export {};
76
+ //# sourceMappingURL=ops.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ops.d.ts","sourceRoot":"","sources":["../../src/sync/ops.ts"],"names":[],"mappings":"AAcA,2EAA2E;AAC3E,eAAO,MAAM,WAAW,IAAI,CAAC;AAE7B,MAAM,MAAM,MAAM,GAAG,QAAQ,GAAG,MAAM,GAAG,QAAQ,GAAG,UAAU,GAAG,SAAS,GAAG,KAAK,CAAC;AAEnF,UAAU,MAAM;IACd,CAAC,EAAE,MAAM,CAAC;IACV,EAAE,EAAE,MAAM,CAAC;IACX,8BAA8B;IAC9B,GAAG,EAAE,MAAM,CAAC;IACZ,uFAAuF;IACvF,EAAE,EAAE,MAAM,CAAC;IACX,mGAAmG;IACnG,EAAE,EAAE,MAAM,CAAC;CACZ;AAED,iGAAiG;AACjG,MAAM,WAAW,QAAS,SAAQ,MAAM;IACtC,EAAE,EAAE,QAAQ,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,MAAO,SAAQ,MAAM;IACpC,EAAE,EAAE,MAAM,CAAC;IACX,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED;;;GAGG;AACH,MAAM,WAAW,QAAS,SAAQ,MAAM;IACtC,EAAE,EAAE,QAAQ,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,EAAE,EAAE,YAAY,GAAG,WAAW,CAAC;IAC/B,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,UAAW,SAAQ,MAAM;IACxC,EAAE,EAAE,UAAU,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,SAAU,SAAQ,MAAM;IACvC,EAAE,EAAE,SAAS,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,KAAM,SAAQ,MAAM;IACnC,EAAE,EAAE,KAAK,CAAC;IACV,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,MAAM,EAAE,GAAG,QAAQ,GAAG,MAAM,GAAG,QAAQ,GAAG,UAAU,GAAG,SAAS,GAAG,KAAK,CAAC;AAE/E;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAElD;AAED,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE5C;AAED,gDAAgD;AAChD,wBAAgB,QAAQ,CAAC,EAAE,EAAE,EAAE,GAAG,MAAM,CAEvC;AAED;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,OAAO,EAAE,MAAM,GAAG;IAAE,GAAG,EAAE,EAAE,EAAE,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAyBzE;AAED,gEAAgE;AAChE,wBAAgB,UAAU,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAEnF"}
@@ -0,0 +1,75 @@
1
+ "use strict";
2
+ // ─── CRBRO Shared Memory: the operation log ──────────────────────
3
+ //
4
+ // Two people cannot share a neuron by copying the file back and forth: the
5
+ // last save wins and the other person's work vanishes. So nobody ever shares
6
+ // the neuron. Each person appends notes to their own log — "I added this
7
+ // fact", "I retracted that one" — and every machine rebuilds the neuron from
8
+ // all the logs it has.
9
+ //
10
+ // Nobody writes to anybody else's file, so there is nothing to collide. That
11
+ // is the whole trick, and it is what makes the merge automatic: no conflict
12
+ // can arise if no two writers touch the same bytes.
13
+ Object.defineProperty(exports, "__esModule", { value: true });
14
+ exports.OPS_VERSION = void 0;
15
+ exports.normalizeText = normalizeText;
16
+ exports.entryId = entryId;
17
+ exports.encodeOp = encodeOp;
18
+ exports.decodeOps = decodeOps;
19
+ exports.opsRelPath = opsRelPath;
20
+ const hash_js_1 = require("../utils/hash.js");
21
+ /** Bumped only if the shape changes in a way older clients cannot read. */
22
+ exports.OPS_VERSION = 1;
23
+ /**
24
+ * Same wording, same id, on every machine.
25
+ *
26
+ * Normalising first matters: one person's copy of a sentence may differ from
27
+ * another's by a stray double space or a different Unicode composition, and
28
+ * without this they would become two facts saying the same thing.
29
+ */
30
+ function normalizeText(text) {
31
+ return (text || '').normalize('NFC').replace(/\s+/g, ' ').trim();
32
+ }
33
+ function entryId(text) {
34
+ return (0, hash_js_1.contentHash)(normalizeText(text));
35
+ }
36
+ /** Serialise one operation as a single line. */
37
+ function encodeOp(op) {
38
+ return JSON.stringify(op);
39
+ }
40
+ /**
41
+ * Parse a log. Unreadable lines are skipped rather than thrown, because a log
42
+ * can be cut in half by a process dying mid-append or by a merge landing while
43
+ * we read. One bad line must never cost the other nine hundred.
44
+ */
45
+ function decodeOps(content) {
46
+ const ops = [];
47
+ let skipped = 0;
48
+ for (const line of (content || '').split('\n')) {
49
+ const t = line.trim();
50
+ if (!t)
51
+ continue;
52
+ try {
53
+ const parsed = JSON.parse(t);
54
+ if (!parsed || typeof parsed !== 'object' || !parsed.op || !parsed.nid) {
55
+ skipped++;
56
+ continue;
57
+ }
58
+ if (parsed.v > exports.OPS_VERSION) {
59
+ // Written by a newer CRBRO. Skipping is safer than half-understanding it.
60
+ skipped++;
61
+ continue;
62
+ }
63
+ ops.push(parsed);
64
+ }
65
+ catch {
66
+ skipped++;
67
+ }
68
+ }
69
+ return { ops, skipped };
70
+ }
71
+ /** Where a person's log for a neuron lives inside the space. */
72
+ function opsRelPath(neuronId, author, device) {
73
+ return `neurons/${neuronId}/ops/${author}.${device}.jsonl`;
74
+ }
75
+ //# sourceMappingURL=ops.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ops.js","sourceRoot":"","sources":["../../src/sync/ops.ts"],"names":[],"mappings":";AAAA,oEAAoE;AACpE,EAAE;AACF,2EAA2E;AAC3E,6EAA6E;AAC7E,yEAAyE;AACzE,6EAA6E;AAC7E,uBAAuB;AACvB,EAAE;AACF,6EAA6E;AAC7E,4EAA4E;AAC5E,oDAAoD;;;AAyEpD,sCAEC;AAED,0BAEC;AAGD,4BAEC;AAOD,8BAyBC;AAGD,gCAEC;AAvHD,8CAA+C;AAE/C,2EAA2E;AAC9D,QAAA,WAAW,GAAG,CAAC,CAAC;AA6D7B;;;;;;GAMG;AACH,SAAgB,aAAa,CAAC,IAAY;IACxC,OAAO,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;AACnE,CAAC;AAED,SAAgB,OAAO,CAAC,IAAY;IAClC,OAAO,IAAA,qBAAW,EAAC,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC;AAC1C,CAAC;AAED,gDAAgD;AAChD,SAAgB,QAAQ,CAAC,EAAM;IAC7B,OAAO,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;AAC5B,CAAC;AAED;;;;GAIG;AACH,SAAgB,SAAS,CAAC,OAAe;IACvC,MAAM,GAAG,GAAS,EAAE,CAAC;IACrB,IAAI,OAAO,GAAG,CAAC,CAAC;IAEhB,KAAK,MAAM,IAAI,IAAI,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;QAC/C,MAAM,CAAC,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;QACtB,IAAI,CAAC,CAAC;YAAE,SAAS;QACjB,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAO,CAAC;YACnC,IAAI,CAAC,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,CAAC,MAAM,CAAC,EAAE,IAAI,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC;gBACvE,OAAO,EAAE,CAAC;gBACV,SAAS;YACX,CAAC;YACD,IAAI,MAAM,CAAC,CAAC,GAAG,mBAAW,EAAE,CAAC;gBAC3B,0EAA0E;gBAC1E,OAAO,EAAE,CAAC;gBACV,SAAS;YACX,CAAC;YACD,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACnB,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,EAAE,CAAC;QACZ,CAAC;IACH,CAAC;IAED,OAAO,EAAE,GAAG,EAAE,OAAO,EAAE,CAAC;AAC1B,CAAC;AAED,gEAAgE;AAChE,SAAgB,UAAU,CAAC,QAAgB,EAAE,MAAc,EAAE,MAAc;IACzE,OAAO,WAAW,QAAQ,QAAQ,MAAM,IAAI,MAAM,QAAQ,CAAC;AAC7D,CAAC"}