@remnic/core 9.3.595 → 9.3.597
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/access-cli.js +14 -14
- package/dist/access-http.js +6 -6
- package/dist/access-mcp.js +5 -5
- package/dist/access-schema.d.ts +7 -7
- package/dist/access-service.js +4 -4
- package/dist/briefing.js +2 -2
- package/dist/causal-consolidation.js +3 -3
- package/dist/{chunk-ARY5OOLG.js → chunk-557IAFPD.js} +2 -2
- package/dist/{chunk-VFB2G5YL.js → chunk-5BUGGPBR.js} +4 -4
- package/dist/{chunk-USYGGIJZ.js → chunk-D2MMMTDV.js} +2 -2
- package/dist/{chunk-XM7BYXT7.js → chunk-D65TSG24.js} +2 -2
- package/dist/{chunk-FHBEL473.js → chunk-DOX2CG6Y.js} +54 -5
- package/dist/chunk-DOX2CG6Y.js.map +1 -0
- package/dist/{chunk-DARLGSFX.js → chunk-ELKI4BB6.js} +4 -4
- package/dist/{chunk-QRWZOCJN.js → chunk-F4LM4ULA.js} +12 -12
- package/dist/{chunk-JIBCUYIP.js → chunk-IEFHBIU2.js} +10 -10
- package/dist/{chunk-KDUFBSBF.js → chunk-IK34DVAC.js} +2 -2
- package/dist/{chunk-OPYFD6PD.js → chunk-IK7DCC5H.js} +2 -2
- package/dist/{chunk-574MU2Y3.js → chunk-JTDRJQ3K.js} +2 -2
- package/dist/{chunk-LAL7WBLY.js → chunk-LYPDMKUT.js} +3 -3
- package/dist/{chunk-GBXGCFRH.js → chunk-MA5MWGKP.js} +2 -2
- package/dist/{chunk-HQO5EBUC.js → chunk-MLT75J5S.js} +3 -3
- package/dist/{chunk-7X7TBJRX.js → chunk-NOMEVTUD.js} +2 -2
- package/dist/{chunk-SUTSSOYU.js → chunk-OD5LFAPZ.js} +2 -2
- package/dist/{chunk-XT7XVA53.js → chunk-OI27U2HT.js} +2 -2
- package/dist/{chunk-MQEIWDYW.js → chunk-QDDHYAKV.js} +2 -2
- package/dist/{chunk-ZY6UPHNY.js → chunk-TYICDVQW.js} +3 -3
- package/dist/{chunk-XRWTAEZM.js → chunk-W5O2FQTZ.js} +2 -2
- package/dist/{chunk-V3RXWQIE.js → chunk-WXACKLKP.js} +209 -59
- package/dist/chunk-WXACKLKP.js.map +1 -0
- package/dist/{chunk-IRFF6LSF.js → chunk-YFS5OEKO.js} +36 -1
- package/dist/chunk-YFS5OEKO.js.map +1 -0
- package/dist/cli.js +15 -15
- package/dist/compounding/engine.js +2 -2
- package/dist/connectors/codex-materialize-runner.js +2 -2
- package/dist/connectors/index.js +2 -2
- package/dist/entity-retrieval.js +2 -2
- package/dist/index.js +22 -22
- package/dist/maintenance/memory-governance.js +2 -2
- package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +2 -2
- package/dist/maintenance/rebuild-memory-projection.js +3 -3
- package/dist/namespaces/migrate.js +3 -3
- package/dist/namespaces/storage.js +2 -2
- package/dist/operator-toolkit.js +5 -5
- package/dist/orchestrator.js +11 -11
- package/dist/retrieval-agents.js +2 -2
- package/dist/schemas.d.ts +22 -22
- package/dist/semantic-consolidation.js +3 -3
- package/dist/semantic-rule-promotion.js +2 -2
- package/dist/semantic-rule-verifier.js +2 -2
- package/dist/storage.d.ts +2 -0
- package/dist/storage.js +1 -1
- package/dist/temporal-index.js +1 -1
- package/dist/transfer/types.d.ts +12 -12
- package/dist/verified-recall.js +2 -2
- package/package.json +1 -1
- package/src/entity-retrieval.ts +64 -3
- package/src/storage.ts +40 -0
- package/src/temporal-index.test.ts +191 -0
- package/src/temporal-index.ts +291 -100
- package/dist/chunk-FHBEL473.js.map +0 -1
- package/dist/chunk-IRFF6LSF.js.map +0 -1
- package/dist/chunk-V3RXWQIE.js.map +0 -1
- /package/dist/{chunk-ARY5OOLG.js.map → chunk-557IAFPD.js.map} +0 -0
- /package/dist/{chunk-VFB2G5YL.js.map → chunk-5BUGGPBR.js.map} +0 -0
- /package/dist/{chunk-USYGGIJZ.js.map → chunk-D2MMMTDV.js.map} +0 -0
- /package/dist/{chunk-XM7BYXT7.js.map → chunk-D65TSG24.js.map} +0 -0
- /package/dist/{chunk-DARLGSFX.js.map → chunk-ELKI4BB6.js.map} +0 -0
- /package/dist/{chunk-QRWZOCJN.js.map → chunk-F4LM4ULA.js.map} +0 -0
- /package/dist/{chunk-JIBCUYIP.js.map → chunk-IEFHBIU2.js.map} +0 -0
- /package/dist/{chunk-KDUFBSBF.js.map → chunk-IK34DVAC.js.map} +0 -0
- /package/dist/{chunk-OPYFD6PD.js.map → chunk-IK7DCC5H.js.map} +0 -0
- /package/dist/{chunk-574MU2Y3.js.map → chunk-JTDRJQ3K.js.map} +0 -0
- /package/dist/{chunk-LAL7WBLY.js.map → chunk-LYPDMKUT.js.map} +0 -0
- /package/dist/{chunk-GBXGCFRH.js.map → chunk-MA5MWGKP.js.map} +0 -0
- /package/dist/{chunk-HQO5EBUC.js.map → chunk-MLT75J5S.js.map} +0 -0
- /package/dist/{chunk-7X7TBJRX.js.map → chunk-NOMEVTUD.js.map} +0 -0
- /package/dist/{chunk-SUTSSOYU.js.map → chunk-OD5LFAPZ.js.map} +0 -0
- /package/dist/{chunk-XT7XVA53.js.map → chunk-OI27U2HT.js.map} +0 -0
- /package/dist/{chunk-MQEIWDYW.js.map → chunk-QDDHYAKV.js.map} +0 -0
- /package/dist/{chunk-ZY6UPHNY.js.map → chunk-TYICDVQW.js.map} +0 -0
- /package/dist/{chunk-XRWTAEZM.js.map → chunk-W5O2FQTZ.js.map} +0 -0
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import { spawn } from "node:child_process";
|
|
3
|
+
import { mkdir, mkdtemp, readFile, rm, symlink, utimes, writeFile } from "node:fs/promises";
|
|
4
|
+
import { tmpdir } from "node:os";
|
|
5
|
+
import { join } from "node:path";
|
|
6
|
+
import test from "node:test";
|
|
7
|
+
import { setTimeout as delay } from "node:timers/promises";
|
|
8
|
+
|
|
9
|
+
import { queryByDateRangeAsync, queryByTagsAsync } from "./temporal-index.js";
|
|
10
|
+
|
|
11
|
+
async function runIndexWorker(moduleUrl: string, memoryDir: string, workerId: number, count: number): Promise<void> {
|
|
12
|
+
const workerSource = `
|
|
13
|
+
const { indexMemory } = await import(process.argv[1]);
|
|
14
|
+
const memoryDir = process.argv[2];
|
|
15
|
+
const workerId = Number(process.argv[3]);
|
|
16
|
+
const count = Number(process.argv[4]);
|
|
17
|
+
for (let i = 0; i < count; i += 1) {
|
|
18
|
+
indexMemory(
|
|
19
|
+
memoryDir,
|
|
20
|
+
\`/tmp/remnic-temporal-worker-\${workerId}-memory-\${i}.md\`,
|
|
21
|
+
"2026-03-09T12:00:00.000Z",
|
|
22
|
+
["concurrency/shared", \`concurrency/worker-\${workerId}\`],
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
`;
|
|
26
|
+
|
|
27
|
+
await new Promise<void>((resolve, reject) => {
|
|
28
|
+
const child = spawn(
|
|
29
|
+
process.execPath,
|
|
30
|
+
["--import", "tsx", "-e", workerSource, moduleUrl, memoryDir, String(workerId), String(count)],
|
|
31
|
+
{
|
|
32
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
33
|
+
}
|
|
34
|
+
);
|
|
35
|
+
let stderr = "";
|
|
36
|
+
child.stderr.setEncoding("utf8");
|
|
37
|
+
child.stderr.on("data", (chunk) => {
|
|
38
|
+
stderr += chunk;
|
|
39
|
+
});
|
|
40
|
+
child.on("error", reject);
|
|
41
|
+
child.on("close", (code) => {
|
|
42
|
+
if (code === 0) {
|
|
43
|
+
resolve();
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
reject(new Error(`index worker ${workerId} exited ${code}: ${stderr}`));
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
test("temporal index concurrent writers retain every date and tag path", async () => {
|
|
52
|
+
const memoryDir = await mkdtemp(join(tmpdir(), "remnic-temporal-index-concurrent-"));
|
|
53
|
+
const moduleUrl = new URL("./temporal-index.ts", import.meta.url).href;
|
|
54
|
+
const workerCount = 4;
|
|
55
|
+
const entriesPerWorker = 12;
|
|
56
|
+
const expectedPaths = new Set<string>();
|
|
57
|
+
|
|
58
|
+
for (let workerId = 0; workerId < workerCount; workerId += 1) {
|
|
59
|
+
for (let i = 0; i < entriesPerWorker; i += 1) {
|
|
60
|
+
expectedPaths.add(`/tmp/remnic-temporal-worker-${workerId}-memory-${i}.md`);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
await Promise.all(
|
|
65
|
+
Array.from({ length: workerCount }, (_, workerId) =>
|
|
66
|
+
runIndexWorker(moduleUrl, memoryDir, workerId, entriesPerWorker)
|
|
67
|
+
)
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
const dateMatches = await queryByDateRangeAsync(memoryDir, "2026-03-09", "2026-03-10");
|
|
71
|
+
const tagMatches = await queryByTagsAsync(memoryDir, ["concurrency/shared"]);
|
|
72
|
+
|
|
73
|
+
assert.deepEqual(dateMatches, expectedPaths);
|
|
74
|
+
assert.deepEqual(tagMatches, expectedPaths);
|
|
75
|
+
|
|
76
|
+
const timeIndex = JSON.parse(await readFile(join(memoryDir, "state", "index_time.json"), "utf8"));
|
|
77
|
+
const tagIndex = JSON.parse(await readFile(join(memoryDir, "state", "index_tags.json"), "utf8"));
|
|
78
|
+
assert.equal(timeIndex.dates["2026-03-09"].length, expectedPaths.size);
|
|
79
|
+
assert.equal(tagIndex.tags["concurrency/shared"].paths.length, expectedPaths.size);
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
test("temporal index writers wait for old locks owned by live processes", async () => {
|
|
83
|
+
const memoryDir = await mkdtemp(join(tmpdir(), "remnic-temporal-index-live-lock-"));
|
|
84
|
+
const stateDir = join(memoryDir, "state");
|
|
85
|
+
const lockDir = join(stateDir, "index_time.json.lock.d");
|
|
86
|
+
const moduleUrl = new URL("./temporal-index.ts", import.meta.url).href;
|
|
87
|
+
await mkdir(lockDir, { recursive: true });
|
|
88
|
+
await writeFile(join(lockDir, "owner.json"), JSON.stringify({ pid: process.pid }), "utf8");
|
|
89
|
+
const oldLockTime = new Date(Date.now() - 120_000);
|
|
90
|
+
await utimes(lockDir, oldLockTime, oldLockTime);
|
|
91
|
+
|
|
92
|
+
const workerSource = `
|
|
93
|
+
const { indexMemory } = await import(process.argv[1]);
|
|
94
|
+
indexMemory(
|
|
95
|
+
process.argv[2],
|
|
96
|
+
"/tmp/remnic-temporal-live-lock-memory.md",
|
|
97
|
+
"2026-03-10T12:00:00.000Z",
|
|
98
|
+
["concurrency/live-lock"],
|
|
99
|
+
);
|
|
100
|
+
`;
|
|
101
|
+
let closed = false;
|
|
102
|
+
const workerDone = new Promise<void>((resolve, reject) => {
|
|
103
|
+
const child = spawn(process.execPath, ["--import", "tsx", "-e", workerSource, moduleUrl, memoryDir], {
|
|
104
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
105
|
+
});
|
|
106
|
+
let stderr = "";
|
|
107
|
+
child.stderr.setEncoding("utf8");
|
|
108
|
+
child.stderr.on("data", (chunk) => {
|
|
109
|
+
stderr += chunk;
|
|
110
|
+
});
|
|
111
|
+
child.on("error", reject);
|
|
112
|
+
child.on("close", (code) => {
|
|
113
|
+
closed = true;
|
|
114
|
+
if (code === 0) {
|
|
115
|
+
resolve();
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
reject(new Error(`live-lock worker exited ${code}: ${stderr}`));
|
|
119
|
+
});
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
await delay(150);
|
|
123
|
+
assert.equal(closed, false);
|
|
124
|
+
await rm(lockDir, { recursive: true, force: true });
|
|
125
|
+
await workerDone;
|
|
126
|
+
|
|
127
|
+
const dateMatches = await queryByDateRangeAsync(memoryDir, "2026-03-10", "2026-03-11");
|
|
128
|
+
const tagMatches = await queryByTagsAsync(memoryDir, ["concurrency/live-lock"]);
|
|
129
|
+
assert.deepEqual(dateMatches, new Set(["/tmp/remnic-temporal-live-lock-memory.md"]));
|
|
130
|
+
assert.deepEqual(tagMatches, new Set(["/tmp/remnic-temporal-live-lock-memory.md"]));
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
test("temporal index writers clear stale locks whose owner pid was recycled", async () => {
|
|
134
|
+
const memoryDir = await mkdtemp(join(tmpdir(), "remnic-temporal-index-recycled-pid-"));
|
|
135
|
+
const stateDir = join(memoryDir, "state");
|
|
136
|
+
const lockDir = join(stateDir, "index_time.json.lock.d");
|
|
137
|
+
const moduleUrl = new URL("./temporal-index.ts", import.meta.url).href;
|
|
138
|
+
await mkdir(lockDir, { recursive: true });
|
|
139
|
+
await writeFile(
|
|
140
|
+
join(lockDir, "owner.json"),
|
|
141
|
+
JSON.stringify({
|
|
142
|
+
pid: process.pid,
|
|
143
|
+
processStartedAtMs: Date.now() - 7 * 86_400_000,
|
|
144
|
+
createdAt: new Date(Date.now() - 120_000).toISOString(),
|
|
145
|
+
}),
|
|
146
|
+
"utf8"
|
|
147
|
+
);
|
|
148
|
+
const oldLockTime = new Date(Date.now() - 120_000);
|
|
149
|
+
await utimes(lockDir, oldLockTime, oldLockTime);
|
|
150
|
+
|
|
151
|
+
await runIndexWorker(moduleUrl, memoryDir, 99, 1);
|
|
152
|
+
|
|
153
|
+
const dateMatches = await queryByDateRangeAsync(memoryDir, "2026-03-09", "2026-03-10");
|
|
154
|
+
const tagMatches = await queryByTagsAsync(memoryDir, ["concurrency/shared"]);
|
|
155
|
+
assert.deepEqual(dateMatches, new Set(["/tmp/remnic-temporal-worker-99-memory-0.md"]));
|
|
156
|
+
assert.deepEqual(tagMatches, new Set(["/tmp/remnic-temporal-worker-99-memory-0.md"]));
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
test("temporal index writers remove regular file lock blockers", async () => {
|
|
160
|
+
const memoryDir = await mkdtemp(join(tmpdir(), "remnic-temporal-index-file-lock-"));
|
|
161
|
+
const stateDir = join(memoryDir, "state");
|
|
162
|
+
const lockPath = join(stateDir, "index_time.json.lock.d");
|
|
163
|
+
const moduleUrl = new URL("./temporal-index.ts", import.meta.url).href;
|
|
164
|
+
await mkdir(stateDir, { recursive: true });
|
|
165
|
+
await writeFile(lockPath, "not a lock directory", "utf8");
|
|
166
|
+
|
|
167
|
+
await runIndexWorker(moduleUrl, memoryDir, 100, 1);
|
|
168
|
+
|
|
169
|
+
const dateMatches = await queryByDateRangeAsync(memoryDir, "2026-03-09", "2026-03-10");
|
|
170
|
+
const tagMatches = await queryByTagsAsync(memoryDir, ["concurrency/shared"]);
|
|
171
|
+
assert.deepEqual(dateMatches, new Set(["/tmp/remnic-temporal-worker-100-memory-0.md"]));
|
|
172
|
+
assert.deepEqual(tagMatches, new Set(["/tmp/remnic-temporal-worker-100-memory-0.md"]));
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
test("temporal index writers fail open on symlink lock blockers", async () => {
|
|
176
|
+
const memoryDir = await mkdtemp(join(tmpdir(), "remnic-temporal-index-symlink-lock-"));
|
|
177
|
+
const stateDir = join(memoryDir, "state");
|
|
178
|
+
const lockPath = join(stateDir, "index_time.json.lock.d");
|
|
179
|
+
const symlinkTarget = join(memoryDir, "outside-lock-target");
|
|
180
|
+
const moduleUrl = new URL("./temporal-index.ts", import.meta.url).href;
|
|
181
|
+
await mkdir(stateDir, { recursive: true });
|
|
182
|
+
await writeFile(symlinkTarget, "do not follow", "utf8");
|
|
183
|
+
await symlink(symlinkTarget, lockPath);
|
|
184
|
+
|
|
185
|
+
await runIndexWorker(moduleUrl, memoryDir, 101, 1);
|
|
186
|
+
|
|
187
|
+
const dateMatches = await queryByDateRangeAsync(memoryDir, "2026-03-09", "2026-03-10");
|
|
188
|
+
const tagMatches = await queryByTagsAsync(memoryDir, ["concurrency/shared"]);
|
|
189
|
+
assert.equal(dateMatches, null);
|
|
190
|
+
assert.deepEqual(tagMatches, new Set(["/tmp/remnic-temporal-worker-101-memory-0.md"]));
|
|
191
|
+
});
|