@oortmem/core 0.2.0 → 0.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/dist/address.d.ts +161 -0
- package/dist/address.js +225 -0
- package/dist/date-cue.d.ts +26 -0
- package/dist/date-cue.js +70 -0
- package/dist/export-format.d.ts +77 -0
- package/dist/export-format.js +168 -0
- package/dist/grant.d.ts +69 -9
- package/dist/grant.js +126 -17
- package/dist/index.d.ts +4 -1
- package/dist/index.js +4 -1
- package/dist/isolation.d.ts +24 -1
- package/dist/isolation.js +146 -16
- package/dist/memory-store.js +27 -23
- package/dist/model.d.ts +17 -0
- package/dist/store-conformance.d.ts +12 -0
- package/dist/store-conformance.js +312 -1
- package/dist/temporal.js +12 -7
- package/package.json +1 -1
package/dist/isolation.js
CHANGED
|
@@ -1,4 +1,11 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Isolation checks for `MemoryStore` implementations (CON-60). Plain
|
|
3
|
+
* functions that return the violations they find, so the conformance suite
|
|
4
|
+
* can require an empty list and a test can prove a broken store is caught.
|
|
5
|
+
* No vitest here; `store-conformance.ts` wires these into `it` blocks.
|
|
6
|
+
*/
|
|
7
|
+
import { addressed, addressOf, authorOf, GALAXY_ROOT, nodeOf, } from './address.js';
|
|
8
|
+
import { GrantError, localGrant, nodeGrant } from './grant.js';
|
|
2
9
|
const NOW = '2026-11-04T12:00:00.000Z';
|
|
3
10
|
/** A memory for the isolation checks: text, stub and entities all carry `marker`. */
|
|
4
11
|
export function isoMemory(id, scope, marker, embedding) {
|
|
@@ -31,6 +38,27 @@ const principal = (s) => ({ orgId: s.orgId, customerId: s.customerId, agentId: s
|
|
|
31
38
|
export function soleGrant(s) {
|
|
32
39
|
return localGrant({ ...principal(s), projectIds: [s.projectId ?? null] });
|
|
33
40
|
}
|
|
41
|
+
/** The narrowest grant for one memory at any level: its node, its one project, its author. */
|
|
42
|
+
export function soleGrantFor(m) {
|
|
43
|
+
const address = addressOf(m);
|
|
44
|
+
const author = authorOf(m);
|
|
45
|
+
const pattern = {
|
|
46
|
+
node: nodeOf(address),
|
|
47
|
+
projectIds: [address.projectId ?? null],
|
|
48
|
+
authorAgentId: author.agentId,
|
|
49
|
+
};
|
|
50
|
+
return nodeGrant({ principal: ISO_PRINCIPAL, read: [pattern] });
|
|
51
|
+
}
|
|
52
|
+
/** `m` moved to `scope`: its address and author dropped, so the scope's Planet is the address. */
|
|
53
|
+
export function movedTo(m, scope) {
|
|
54
|
+
const { address: _a, author: _w, provenance: _p, ...rest } = m;
|
|
55
|
+
return { ...rest, scope };
|
|
56
|
+
}
|
|
57
|
+
/** A memory at `address`, written by `author`, for the isolation checks. */
|
|
58
|
+
export function isoAt(id, address, author, marker, embedding) {
|
|
59
|
+
const at = addressed(address, author);
|
|
60
|
+
return { ...isoMemory(id, at.scope, marker, embedding), ...at };
|
|
61
|
+
}
|
|
34
62
|
/** A stable rendering of a record (embedding as plain numbers) for before/after comparison. */
|
|
35
63
|
function render(m) {
|
|
36
64
|
if (m === null)
|
|
@@ -59,6 +87,51 @@ export const ISO_SCOPES = {
|
|
|
59
87
|
otherAgent: { orgId: 'local', customerId: 'self', agentId: 'another-agent', projectId: 'pa' },
|
|
60
88
|
otherOrg: { orgId: 'another-org', customerId: 'self', agentId: 'claude-code', projectId: 'pa' },
|
|
61
89
|
};
|
|
90
|
+
/** Who the level checks act as: the caller of `ISO_SCOPES.mine`. */
|
|
91
|
+
const ISO_PRINCIPAL = { orgId: 'local', customerId: 'self', agentId: 'claude-code' };
|
|
92
|
+
/**
|
|
93
|
+
* Victims at the other levels, each next to the caller's own Planet: a Planet
|
|
94
|
+
* memory another of the caller's agents wrote, the caller's agent's own Star,
|
|
95
|
+
* an Orbit, a Flyby, a Constellation and a Galaxy root of an organisation, and
|
|
96
|
+
* one of the caller's own owner. A pre-level grant sees none of them.
|
|
97
|
+
*/
|
|
98
|
+
export const ISO_LEVELS = {
|
|
99
|
+
planetOtherAuthor: {
|
|
100
|
+
address: { level: 'user', orgId: 'local', customerId: 'self', projectId: 'pa' },
|
|
101
|
+
author: { agentId: 'cursor' },
|
|
102
|
+
},
|
|
103
|
+
star: {
|
|
104
|
+
address: { level: 'agent', orgId: 'local', agentId: 'claude-code', projectId: 'pa' },
|
|
105
|
+
author: { agentId: 'claude-code' },
|
|
106
|
+
},
|
|
107
|
+
orbit: {
|
|
108
|
+
address: { level: 'agent-user', orgId: 'local', agentId: 'claude-code', customerId: 'self' },
|
|
109
|
+
author: { agentId: 'claude-code' },
|
|
110
|
+
},
|
|
111
|
+
flyby: {
|
|
112
|
+
address: {
|
|
113
|
+
level: 'run',
|
|
114
|
+
orgId: 'local',
|
|
115
|
+
agentId: 'claude-code',
|
|
116
|
+
runId: 'r1',
|
|
117
|
+
customerId: 'self',
|
|
118
|
+
projectId: 'pa',
|
|
119
|
+
},
|
|
120
|
+
author: { agentId: 'claude-code' },
|
|
121
|
+
},
|
|
122
|
+
ownHive: {
|
|
123
|
+
address: { level: 'hive', orgId: 'local', hiveId: 'self', projectId: 'pa' },
|
|
124
|
+
author: { agentId: 'claude-code' },
|
|
125
|
+
},
|
|
126
|
+
constellation: {
|
|
127
|
+
address: { level: 'hive', orgId: 'org:acme', hiveId: 'checkout', projectId: 'pa' },
|
|
128
|
+
author: { agentId: 'claude-code', accountRef: 'acct:member' },
|
|
129
|
+
},
|
|
130
|
+
galaxy: {
|
|
131
|
+
address: { level: 'hive', orgId: 'org:acme', hiveId: GALAXY_ROOT },
|
|
132
|
+
author: { agentId: 'claude-code' },
|
|
133
|
+
},
|
|
134
|
+
};
|
|
62
135
|
/**
|
|
63
136
|
* Every `MemoryStore` method, called with a grant for `ISO_SCOPES.mine`
|
|
64
137
|
* against memories (warm and cold) in each other scope: nothing is seen,
|
|
@@ -76,13 +149,23 @@ export async function isolationViolations(make) {
|
|
|
76
149
|
await store.upsert(mine, isoMemory('mine-1', ISO_SCOPES.mine, 'shared', vec(1)));
|
|
77
150
|
const victims = [];
|
|
78
151
|
let n = 2;
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
152
|
+
const targets = [
|
|
153
|
+
...Object.entries(ISO_SCOPES)
|
|
154
|
+
.filter(([name]) => name !== 'mine')
|
|
155
|
+
.map(([name, scope]) => [
|
|
156
|
+
name,
|
|
157
|
+
(id, marker, v) => isoMemory(id, scope, marker, v),
|
|
158
|
+
]),
|
|
159
|
+
...Object.entries(ISO_LEVELS).map(([name, at]) => [
|
|
160
|
+
name,
|
|
161
|
+
(id, marker, v) => isoAt(id, at.address, at.author, marker, v),
|
|
162
|
+
]),
|
|
163
|
+
];
|
|
164
|
+
for (const [name, build] of targets) {
|
|
82
165
|
for (const cold of [false, true]) {
|
|
83
166
|
const id = `victim-${name}-${cold ? 'cold' : 'warm'}`;
|
|
84
|
-
const
|
|
85
|
-
const
|
|
167
|
+
const m = build(id, `shared canary${name}`, vec(n++));
|
|
168
|
+
const g = soleGrantFor(m);
|
|
86
169
|
await store.upsert(g, m);
|
|
87
170
|
if (cold) {
|
|
88
171
|
const { skipped } = await store.coldPut(g, m);
|
|
@@ -128,7 +211,7 @@ export async function isolationViolations(make) {
|
|
|
128
211
|
if (!('err' in direct) || !(direct.err instanceof GrantError)) {
|
|
129
212
|
fail('upsert of its own scope was not refused with GrantError');
|
|
130
213
|
}
|
|
131
|
-
const moved = await settle(store.upsert(mine,
|
|
214
|
+
const moved = await settle(store.upsert(mine, movedTo(v.m, ISO_SCOPES.mine)));
|
|
132
215
|
if (!('err' in moved) || !(moved.err instanceof GrantError)) {
|
|
133
216
|
fail('upsert moving its id into my scope was not refused with GrantError');
|
|
134
217
|
}
|
|
@@ -138,7 +221,7 @@ export async function isolationViolations(make) {
|
|
|
138
221
|
}
|
|
139
222
|
const batch = await settle(store.upsertMany(mine, [
|
|
140
223
|
isoMemory(`batch-${id}`, ISO_SCOPES.mine, 'batch'),
|
|
141
|
-
|
|
224
|
+
movedTo(v.m, ISO_SCOPES.mine),
|
|
142
225
|
]));
|
|
143
226
|
if (!('err' in batch))
|
|
144
227
|
fail('a batch reaching it was not refused');
|
|
@@ -253,12 +336,53 @@ const WORDS = ['orbit', 'comet', 'nebula', 'pulsar', 'quasar', 'photon', 'kuiper
|
|
|
253
336
|
* keys (callers use UUIDs), so a tenant that reuses another tenant's id is
|
|
254
337
|
* refused, which is a collision, not a leak of content.
|
|
255
338
|
*/
|
|
256
|
-
function opSequence(seed, steps) {
|
|
339
|
+
function opSequence(seed, steps, multiNode) {
|
|
257
340
|
const rng = seededRng(seed);
|
|
258
|
-
const
|
|
259
|
-
|
|
341
|
+
const planetA = ['pa', 'pa2', null].map((projectId) => (id, t, e) => isoMemory(id, projectId === null ? ISO_SCOPES.global : { ...ISO_SCOPES.mine, projectId }, t, e));
|
|
342
|
+
// With multiNode, A also reads and writes a Constellation of another owner and its
|
|
343
|
+
// agent's own Star; B (another person) its own Planet and a sibling Constellation.
|
|
344
|
+
const aBuild = multiNode
|
|
345
|
+
? [
|
|
346
|
+
...planetA,
|
|
347
|
+
(id, t, e) => isoAt(id, ISO_LEVELS['constellation'].address, { agentId: 'claude-code' }, t, e),
|
|
348
|
+
(id, t, e) => isoAt(id, ISO_LEVELS['star'].address, { agentId: 'claude-code' }, t, e),
|
|
349
|
+
]
|
|
350
|
+
: planetA;
|
|
351
|
+
const aGrant = multiNode
|
|
352
|
+
? nodeGrant({
|
|
353
|
+
principal: ISO_PRINCIPAL,
|
|
354
|
+
read: [
|
|
355
|
+
{
|
|
356
|
+
node: { level: 'user', orgId: 'local', customerId: 'self' },
|
|
357
|
+
projectIds: ['pa', 'pa2', null],
|
|
358
|
+
authorAgentId: 'claude-code',
|
|
359
|
+
},
|
|
360
|
+
{ node: { level: 'hive', orgId: 'org:acme', hiveId: 'checkout' }, projectIds: 'all' },
|
|
361
|
+
{ node: { level: 'agent', orgId: 'local', agentId: 'claude-code' }, projectIds: 'all' },
|
|
362
|
+
],
|
|
363
|
+
})
|
|
364
|
+
: localGrant({ ...principal(ISO_SCOPES.mine), projectIds: ['pa', 'pa2', null] });
|
|
260
365
|
const bScope = ISO_SCOPES.otherCustomer;
|
|
261
|
-
const
|
|
366
|
+
const payments = { level: 'hive', orgId: 'org:acme', hiveId: 'payments' };
|
|
367
|
+
const bBuild = multiNode
|
|
368
|
+
? [
|
|
369
|
+
(id, t, e) => isoMemory(id, bScope, t, e),
|
|
370
|
+
(id, t, e) => isoAt(id, payments, { agentId: 'claude-code' }, t, e),
|
|
371
|
+
]
|
|
372
|
+
: [(id, t, e) => isoMemory(id, bScope, t, e)];
|
|
373
|
+
const bGrant = multiNode
|
|
374
|
+
? nodeGrant({
|
|
375
|
+
principal: principal(bScope),
|
|
376
|
+
read: [
|
|
377
|
+
{
|
|
378
|
+
node: { level: 'user', orgId: 'local', customerId: 'someone-else' },
|
|
379
|
+
projectIds: ['pa'],
|
|
380
|
+
authorAgentId: 'claude-code',
|
|
381
|
+
},
|
|
382
|
+
{ node: nodeOf(payments), projectIds: 'all' },
|
|
383
|
+
],
|
|
384
|
+
})
|
|
385
|
+
: soleGrant(bScope);
|
|
262
386
|
const ops = [];
|
|
263
387
|
const aIds = [];
|
|
264
388
|
const bIds = [];
|
|
@@ -278,8 +402,10 @@ function opSequence(seed, steps) {
|
|
|
278
402
|
if (kind <= 2 || own.length === 0) {
|
|
279
403
|
const id = `${who}-${i}`;
|
|
280
404
|
own.push(id);
|
|
281
|
-
const
|
|
282
|
-
|
|
405
|
+
const builds = who === 'A' ? aBuild : bBuild;
|
|
406
|
+
// One builder draws no number, so the pre-level sequences replay exactly.
|
|
407
|
+
const build = builds.length === 1 ? builds[0] : builds[rng.int(builds.length)];
|
|
408
|
+
const m = { ...build(id, text(), emb()), salience: rng.int(10) / 10 };
|
|
283
409
|
ops.push({ who, run: (s) => s.upsert(grant, m) });
|
|
284
410
|
}
|
|
285
411
|
else if (kind === 3) {
|
|
@@ -337,9 +463,13 @@ function opSequence(seed, steps) {
|
|
|
337
463
|
* order: SQLite FTS5's bm25 uses table-wide term statistics, so another
|
|
338
464
|
* tenant's text shifts scores (a known side channel; see the PR notes).
|
|
339
465
|
* Every other result is compared exactly, dense scores included.
|
|
466
|
+
*
|
|
467
|
+
* `multiNode`: A's grant spans its Planet, its agent's Star and a
|
|
468
|
+
* Constellation of another owner; B's spans its own Planet and a sibling
|
|
469
|
+
* Constellation. Same property.
|
|
340
470
|
*/
|
|
341
|
-
export async function nonInterference(make, seed, steps) {
|
|
342
|
-
const ops = opSequence(seed, steps);
|
|
471
|
+
export async function nonInterference(make, seed, steps, opts = {}) {
|
|
472
|
+
const ops = opSequence(seed, steps, opts.multiNode === true);
|
|
343
473
|
const observe = async (withB) => {
|
|
344
474
|
const s = await make();
|
|
345
475
|
const seen = [];
|
package/dist/memory-store.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { appendAccess } from './activation.js';
|
|
2
|
-
import {
|
|
2
|
+
import { canonicalMemory } from './address.js';
|
|
3
|
+
import { assertGrant, cursorKey, grantReads, grantWrites, GrantError, sameRecordAddress, } from './grant.js';
|
|
3
4
|
import { isCurrent } from './model.js';
|
|
4
5
|
import { tokenizeLexical as tokens } from './tokenize.js';
|
|
5
6
|
function cosine(a, b) {
|
|
@@ -46,26 +47,32 @@ export function createMemoryStore(opts = {}) {
|
|
|
46
47
|
if (m.tier !== 'cold')
|
|
47
48
|
cold.delete(m.id);
|
|
48
49
|
}
|
|
49
|
-
function scoped(
|
|
50
|
-
return [...main.values()].filter((m) =>
|
|
50
|
+
function scoped(grant) {
|
|
51
|
+
return [...main.values()].filter((m) => grantReads(grant, m));
|
|
51
52
|
}
|
|
52
53
|
/** The live record if `grant` may read it; `undefined` for unknown and out-of-grant alike. */
|
|
53
54
|
function readable(grant, id) {
|
|
54
55
|
const m = main.get(id);
|
|
55
|
-
return m !== undefined &&
|
|
56
|
+
return m !== undefined && grantReads(grant, m) ? m : undefined;
|
|
56
57
|
}
|
|
57
58
|
/** The live record if `grant` may change it. */
|
|
58
59
|
function writable(grant, id) {
|
|
59
60
|
const m = main.get(id);
|
|
60
|
-
return m !== undefined &&
|
|
61
|
+
return m !== undefined && grantWrites(grant, m) ? m : undefined;
|
|
61
62
|
}
|
|
62
|
-
/**
|
|
63
|
-
|
|
64
|
-
|
|
63
|
+
/**
|
|
64
|
+
* Every check `upsert` makes, before anything is written: a legal address
|
|
65
|
+
* that matches the scope (`RangeError` otherwise), inside `grant.write`,
|
|
66
|
+
* and the same address, author and scope as any record already under the id.
|
|
67
|
+
* Returns the record as stored.
|
|
68
|
+
*/
|
|
69
|
+
function checkWrite(grant, m, existing) {
|
|
70
|
+
const c = canonicalMemory(m);
|
|
71
|
+
if (!grantWrites(grant, c))
|
|
65
72
|
throw new GrantError();
|
|
66
|
-
|
|
67
|
-
if (existing !== undefined && !sameScope(existing.scope, m.scope))
|
|
73
|
+
if (existing !== undefined && !sameRecordAddress(existing, c))
|
|
68
74
|
throw new GrantError();
|
|
75
|
+
return c;
|
|
69
76
|
}
|
|
70
77
|
/** True while the live record still matches the snapshot a guarded write was computed from. */
|
|
71
78
|
function unchanged(live, snap) {
|
|
@@ -116,8 +123,7 @@ export function createMemoryStore(opts = {}) {
|
|
|
116
123
|
open();
|
|
117
124
|
assertGrant(grant);
|
|
118
125
|
const c = structuredClone(m);
|
|
119
|
-
checkWrite(grant, c);
|
|
120
|
-
put(c);
|
|
126
|
+
put(checkWrite(grant, c, main.get(c.id)));
|
|
121
127
|
},
|
|
122
128
|
/** Atomic trivially: checks and clones every record before writing any. */
|
|
123
129
|
async upsertMany(grant, ms) {
|
|
@@ -127,15 +133,13 @@ export function createMemoryStore(opts = {}) {
|
|
|
127
133
|
// Later entries may move an id the batch itself created: check against
|
|
128
134
|
// the batch's own earlier entries as well as the store.
|
|
129
135
|
const batch = new Map();
|
|
136
|
+
const checked = [];
|
|
130
137
|
for (const c of cs) {
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
if (existing !== undefined && !sameScope(existing.scope, c.scope))
|
|
135
|
-
throw new GrantError();
|
|
136
|
-
batch.set(c.id, c);
|
|
138
|
+
const ok = checkWrite(grant, c, batch.get(c.id) ?? main.get(c.id));
|
|
139
|
+
batch.set(c.id, ok);
|
|
140
|
+
checked.push(ok);
|
|
137
141
|
}
|
|
138
|
-
for (const c of
|
|
142
|
+
for (const c of checked)
|
|
139
143
|
put(c);
|
|
140
144
|
},
|
|
141
145
|
async get(grant, id) {
|
|
@@ -164,7 +168,7 @@ export function createMemoryStore(opts = {}) {
|
|
|
164
168
|
if (query.size === 0)
|
|
165
169
|
return [];
|
|
166
170
|
const scored = [];
|
|
167
|
-
for (const m of scoped(grant
|
|
171
|
+
for (const m of scoped(grant)) {
|
|
168
172
|
const fields = m.tier === 'cold' ? [m.stub, ...m.entities] : [m.text, m.stub, ...m.entities];
|
|
169
173
|
const doc = new Set(tokens(fields.join(' ')));
|
|
170
174
|
let score = 0;
|
|
@@ -180,7 +184,7 @@ export function createMemoryStore(opts = {}) {
|
|
|
180
184
|
open();
|
|
181
185
|
assertGrant(grant);
|
|
182
186
|
const scored = [];
|
|
183
|
-
for (const m of scoped(grant
|
|
187
|
+
for (const m of scoped(grant)) {
|
|
184
188
|
if (m.tier === 'cold' || m.embedding === undefined)
|
|
185
189
|
continue;
|
|
186
190
|
if (m.embedding.length !== v.length) {
|
|
@@ -204,7 +208,7 @@ export function createMemoryStore(opts = {}) {
|
|
|
204
208
|
assertGrant(grant);
|
|
205
209
|
const validOnly = listOpts.validOnly ?? true;
|
|
206
210
|
const now = clock();
|
|
207
|
-
return scoped(grant
|
|
211
|
+
return scoped(grant)
|
|
208
212
|
.filter((m) => !validOnly || isCurrent(m, now))
|
|
209
213
|
.filter((m) => listOpts.tier === undefined || m.tier === listOpts.tier)
|
|
210
214
|
.filter((m) => listOpts.pinned !== true || m.pinned)
|
|
@@ -225,7 +229,7 @@ export function createMemoryStore(opts = {}) {
|
|
|
225
229
|
if (readable(grant, id) === undefined)
|
|
226
230
|
return null;
|
|
227
231
|
const m = cold.get(id);
|
|
228
|
-
return m === undefined || !
|
|
232
|
+
return m === undefined || !grantReads(grant, m) ? null : structuredClone(m);
|
|
229
233
|
},
|
|
230
234
|
async setTier(grant, id, tier, snapshot) {
|
|
231
235
|
open();
|
package/dist/model.d.ts
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
|
+
import type { Address, Author, Provenance } from './address.js';
|
|
1
2
|
export type MemoryType = 'semantic' | 'episodic' | 'procedural' | 'preference';
|
|
2
3
|
export type Tier = 'hot' | 'warm' | 'cold';
|
|
4
|
+
/**
|
|
5
|
+
* The pre-level view of where a memory lives. For a Planet memory (every
|
|
6
|
+
* memory written before levels) it is the whole story: the Planet is
|
|
7
|
+
* (org, customer, project) and `agentId` is the author. For other levels see
|
|
8
|
+
* `scopeOf` in `address.ts`. Grants are checked against `address` and `author`.
|
|
9
|
+
*/
|
|
3
10
|
export interface Scope {
|
|
4
11
|
orgId: string;
|
|
5
12
|
customerId: string;
|
|
@@ -38,6 +45,16 @@ export interface MemorySource {
|
|
|
38
45
|
export interface Memory {
|
|
39
46
|
id: string;
|
|
40
47
|
scope: Scope;
|
|
48
|
+
/**
|
|
49
|
+
* Where the memory lives. Absent: the Planet `scope` names, which is where
|
|
50
|
+
* every memory written before levels lives, so stores return it only for
|
|
51
|
+
* other levels. Read it with `addressOf(m)`.
|
|
52
|
+
*/
|
|
53
|
+
address?: Address;
|
|
54
|
+
/** Who wrote it. Absent: the agent `scope.agentId` names. Read it with `authorOf(m)`. */
|
|
55
|
+
author?: Author;
|
|
56
|
+
/** Where a shared copy came from; absent (or null) on ordinary memories. */
|
|
57
|
+
provenance?: Provenance | null;
|
|
41
58
|
type: MemoryType;
|
|
42
59
|
text: string;
|
|
43
60
|
stub: string;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type Address, type Author, type NodeRef, type Provenance } from './address.js';
|
|
1
2
|
import type { MemoryStore } from './adapters.js';
|
|
2
3
|
import { type Grant } from './grant.js';
|
|
3
4
|
import type { Memory, Scope } from './model.js';
|
|
@@ -22,3 +23,14 @@ export declare const TG: Grant;
|
|
|
22
23
|
/** The narrowest grant that can read and write a memory at `scope`. */
|
|
23
24
|
export declare function grantFor(scope: Scope): Grant;
|
|
24
25
|
export declare function storeConformance(name: string, make: () => Promise<MemoryStore>): void;
|
|
26
|
+
/** One memory per level (and a Planet memory by another agent), each with a distinct embedding. */
|
|
27
|
+
export declare const LEVEL_CASES: {
|
|
28
|
+
name: string;
|
|
29
|
+
address: Address;
|
|
30
|
+
author: Author;
|
|
31
|
+
provenance?: Provenance;
|
|
32
|
+
}[];
|
|
33
|
+
/** A memory at `address` by `author` (scope filled to match). */
|
|
34
|
+
export declare function memoryAt(id: string, address: Address, author: Author, overrides?: Partial<Memory>): Memory;
|
|
35
|
+
/** Read and write every project of `nodes` (no author restriction). */
|
|
36
|
+
export declare function nodesGrant(...nodes: NodeRef[]): Grant;
|