@oortmem/core 0.2.1 → 0.4.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/agents.d.ts +38 -0
- package/dist/agents.js +67 -0
- package/dist/config.d.ts +1 -0
- package/dist/config.js +7 -0
- package/dist/consolidate.d.ts +4 -0
- package/dist/consolidate.js +15 -3
- 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 +122 -15
- package/dist/grant.js +233 -26
- package/dist/index.d.ts +6 -1
- package/dist/index.js +6 -1
- package/dist/isolation.d.ts +48 -2
- package/dist/isolation.js +362 -71
- package/dist/levels.d.ts +79 -0
- package/dist/levels.js +140 -0
- package/dist/memory-store.js +27 -23
- package/dist/model.d.ts +17 -0
- package/dist/observe.d.ts +9 -0
- package/dist/observe.js +27 -3
- package/dist/recall.d.ts +27 -1
- package/dist/recall.js +163 -17
- package/dist/store-conformance.d.ts +12 -0
- package/dist/store-conformance.js +366 -2
- package/dist/temporal.js +12 -7
- package/package.json +1 -1
package/dist/date-cue.js
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
const WEEKDAYS = 'monday|tuesday|wednesday|thursday|friday|saturday|sunday|mon|tues?|wed|thu|thurs?|fri|sat|sun|weekdays?|weekends?';
|
|
2
|
+
const MONTHS = 'january|february|march|april|june|july|august|september|october|november|december|jan|feb|mar|apr|jun|jul|aug|sept?|oct|nov|dec';
|
|
3
|
+
const CASUAL = 'today|tonight|tomorrow|tmrw?|yesterday|overmorrow|noon|midday|midnight|mornings?|afternoons?|evenings?|nights?|ago';
|
|
4
|
+
/** Time units, chrono's abbreviations included ("2h", "a few mins", "this am"). */
|
|
5
|
+
const UNITS = 'sec(?:ond)?s?|min(?:ute)?s?|h(?:ou)?rs?|hours?|days?|w(?:ee)?ks?|fortnights?|months?|mos?|quarters?|qtrs?|y(?:ea)?rs?|years?|decades?|[smhdwy]|a\\.?m|p\\.?m';
|
|
6
|
+
const ORDINAL = '(?:(?:twenty|thirty)[\\s-]?)?(?:first|second|third|fourth|fifth|sixth|seventh|eighth|ninth|tenth|eleventh|twelfth|thirteenth|fourteenth|fifteenth|sixteenth|seventeenth|eighteenth|nineteenth|twentieth|thirtieth)|twenty|thirty';
|
|
7
|
+
/**
|
|
8
|
+
* "May" the month, not the verb: chrono reads it with a day or ordinal near it, after "in",
|
|
9
|
+
* before a comma or "of", or at the end of the text.
|
|
10
|
+
*/
|
|
11
|
+
const MAY = `in\\s+may|may\\s+(?:until|till|to|through)\\s+may|may\\s*(?:,|\\.\\s*\\d|of\\b|$|\\d|${ORDINAL})|(?:\\d|${ORDINAL})[\\s,]*(?:[a-z]+[\\s,]+)?may`;
|
|
12
|
+
const QUANTITY = 'a|an|the|one|two|three|four|five|six|seven|eight|nine|ten|eleven|twelve|few|several|couple|half|this|last|next|past|coming|previous|upcoming|within|in|for';
|
|
13
|
+
/** Words that make a following "now" a validity bound ("from now on", "until now"). */
|
|
14
|
+
const NOW_CUE = 'until|till|through|thru|expires?|expiring|ends?|since|from|starting|starts|as\\s+of|effective|beginning|begins';
|
|
15
|
+
const CUES = [
|
|
16
|
+
// Day and month names, also run into digits as chrono reads them ("17may", "sept11").
|
|
17
|
+
`(?<![a-z])(?:${WEEKDAYS}|${MONTHS}|${CASUAL}|${MAY})(?![a-z])`,
|
|
18
|
+
// "a week", "3 days", "a couple of days", "half an hour", "the end of the month".
|
|
19
|
+
`\\b(?:\\d+|${QUANTITY})\\s+(?:[a-z]+\\s+){0,2}?(?:${UNITS})\\b`,
|
|
20
|
+
// Times and dates: 17:30, 1/2, 2026-11-20, 12.5.2026, and "2026 11 3".
|
|
21
|
+
'\\d\\s*[:/.?\\-]\\s*\\d',
|
|
22
|
+
'\\d[\\s,]+\\d{1,2}[\\s,]+\\d',
|
|
23
|
+
// Ranges, read as times: "6 to 8".
|
|
24
|
+
'\\d\\s+to\\s+\\d',
|
|
25
|
+
// Suffixed numbers: 5pm, 5 a, 24h, 30m, 1d, 3rd, 1990s, 5 o'clock, 5 a.m., 21 utc.
|
|
26
|
+
`\\d\\s*(?:${UNITS}|a|st|nd|rd|th|o['’]?\\s*clock|utc|gmt)\\b`,
|
|
27
|
+
// A bare number a preposition makes a time: "at 5", "from 9 to 5".
|
|
28
|
+
'\\b(?:at|by|from|until|till|through|before|after|since|on|around|about|between)\\s*\\d',
|
|
29
|
+
'\\bhalf\\s+a',
|
|
30
|
+
`\\b(?:${NOW_CUE})\\s+(?:on\\s+)?(?:right\\s+)?now\\b`,
|
|
31
|
+
// Quarters and halves: Q1, H2.
|
|
32
|
+
'\\b[qh][1-4]\\b',
|
|
33
|
+
];
|
|
34
|
+
const CUE = new RegExp(CUES.join('|'), 'i');
|
|
35
|
+
/** A time with a zone abbreviation ("21 PST", "9 CEST"); upper case only, as zones are written. */
|
|
36
|
+
const ZONED = /\d\s*[A-Z]{1,4}T\b/;
|
|
37
|
+
/**
|
|
38
|
+
* Whether `text` may hold a date or time expression chrono-node would read. False means
|
|
39
|
+
* chrono reads none that the temporal functions act on (a bare "now" is the present, and
|
|
40
|
+
* only counts after a bound word such as "from" or "until"). True may be a false alarm: that
|
|
41
|
+
* only costs loading chrono.
|
|
42
|
+
*/
|
|
43
|
+
export function mayHaveDateCue(text) {
|
|
44
|
+
return CUE.test(text) || ZONED.test(text);
|
|
45
|
+
}
|
|
46
|
+
let chrono;
|
|
47
|
+
/** Whether chrono-node has been loaded in this process. */
|
|
48
|
+
export function chronoLoaded() {
|
|
49
|
+
return chrono !== undefined;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Loads chrono-node ahead of need. Node never has to: the temporal functions load it
|
|
53
|
+
* synchronously on first use. A bundled runtime without `require` (a Worker) awaits this once
|
|
54
|
+
* at startup.
|
|
55
|
+
*/
|
|
56
|
+
export async function preloadChrono() {
|
|
57
|
+
chrono ??= await import('chrono-node');
|
|
58
|
+
}
|
|
59
|
+
/** chrono-node, loaded synchronously on first call. */
|
|
60
|
+
export function loadChrono() {
|
|
61
|
+
if (chrono !== undefined)
|
|
62
|
+
return chrono;
|
|
63
|
+
const mod = globalThis.process?.getBuiltinModule?.('node:module');
|
|
64
|
+
if (mod === undefined) {
|
|
65
|
+
throw new Error('chrono-node is not loaded: await preloadChrono() at startup');
|
|
66
|
+
}
|
|
67
|
+
chrono = mod.createRequire(import.meta.url)('chrono-node');
|
|
68
|
+
return chrono;
|
|
69
|
+
}
|
|
70
|
+
//# sourceMappingURL=date-cue.js.map
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Oort export format (docs/export-format.md): JSON Lines, a manifest line,
|
|
3
|
+
* then one memory per line. Version 2 (written by default) adds `address`,
|
|
4
|
+
* `author` and `provenance` to every line and keeps `scope` on Planet
|
|
5
|
+
* memories; version 1 has `scope` only and can hold Planet memories only.
|
|
6
|
+
* Readers accept both, version 1 forever.
|
|
7
|
+
*/
|
|
8
|
+
import { type Address } from './address.js';
|
|
9
|
+
import type { Memory, Scope } from './model.js';
|
|
10
|
+
/** The version `oort export` writes. */
|
|
11
|
+
export declare const EXPORT_FORMAT_VERSION = 2;
|
|
12
|
+
export type ExportVersion = 1 | 2;
|
|
13
|
+
export interface ExportManifestV1 {
|
|
14
|
+
oort: 'export';
|
|
15
|
+
version: 1;
|
|
16
|
+
exportedAt: string;
|
|
17
|
+
memories: number;
|
|
18
|
+
/** The distinct scopes of the exported memories. */
|
|
19
|
+
scopes: Scope[];
|
|
20
|
+
}
|
|
21
|
+
export interface ExportManifestV2 {
|
|
22
|
+
oort: 'export';
|
|
23
|
+
version: 2;
|
|
24
|
+
exportedAt: string;
|
|
25
|
+
memories: number;
|
|
26
|
+
/** The distinct scopes of the exported Planet memories. */
|
|
27
|
+
scopes: Scope[];
|
|
28
|
+
/** The distinct addresses of every exported memory. */
|
|
29
|
+
addresses: Address[];
|
|
30
|
+
}
|
|
31
|
+
export type ExportManifest = ExportManifestV1 | ExportManifestV2;
|
|
32
|
+
/** Output-only flags a line may carry (see the format's Limitations). */
|
|
33
|
+
export interface HiddenLinkFlags {
|
|
34
|
+
supersedesHidden?: true;
|
|
35
|
+
supersededByHidden?: true;
|
|
36
|
+
}
|
|
37
|
+
/** Raised for a memory a format version cannot hold, or a file that is not a readable export. */
|
|
38
|
+
export declare class ExportFormatError extends Error {
|
|
39
|
+
constructor(message: string);
|
|
40
|
+
}
|
|
41
|
+
/** True when version 1 can hold `m`: a Planet memory whose author is its scope's agent. */
|
|
42
|
+
export declare function fitsVersion1(m: Memory): boolean;
|
|
43
|
+
/**
|
|
44
|
+
* One memory as an export line of `version`: the embedding as base64 of its
|
|
45
|
+
* int8 bytes. Version 2 always writes `address`, `author` and `provenance`
|
|
46
|
+
* (null when none), and `scope` for Planet memories only.
|
|
47
|
+
*/
|
|
48
|
+
export declare function exportLine(m: Memory & HiddenLinkFlags, version: ExportVersion): Record<string, unknown>;
|
|
49
|
+
/** The manifest for `memories` written as `version`. */
|
|
50
|
+
export declare function exportManifest(memories: Memory[], version: ExportVersion, exportedAt?: Date): ExportManifest;
|
|
51
|
+
/** A whole export: the manifest line, then one line per memory, each ending in `\n`. */
|
|
52
|
+
export declare function formatExport(memories: (Memory & HiddenLinkFlags)[], version?: ExportVersion, exportedAt?: Date): string;
|
|
53
|
+
export interface ParsedExport {
|
|
54
|
+
version: ExportVersion;
|
|
55
|
+
manifest: ExportManifest;
|
|
56
|
+
/** In the shape stores return (see `canonicalMemory`), embeddings decoded. */
|
|
57
|
+
memories: Memory[];
|
|
58
|
+
}
|
|
59
|
+
export interface ParseExportOptions {
|
|
60
|
+
/**
|
|
61
|
+
* The importing person's account: Planet memories move to this owner and
|
|
62
|
+
* customer (agent, project and everything else kept), the way an import
|
|
63
|
+
* into another account rewrites the principal. Other levels keep their
|
|
64
|
+
* addresses. Unset: addresses are kept as written.
|
|
65
|
+
*/
|
|
66
|
+
planet?: {
|
|
67
|
+
orgId: string;
|
|
68
|
+
customerId: string;
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Reads an export of version 1 or 2. A version 1 line's `scope` names the
|
|
73
|
+
* memory's Planet and its agent the author. Throws `ExportFormatError` for an
|
|
74
|
+
* unknown version, a count that does not match, or a line that is not a
|
|
75
|
+
* memory (with its line number, never its content).
|
|
76
|
+
*/
|
|
77
|
+
export declare function parseExport(text: string, opts?: ParseExportOptions): ParsedExport;
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Oort export format (docs/export-format.md): JSON Lines, a manifest line,
|
|
3
|
+
* then one memory per line. Version 2 (written by default) adds `address`,
|
|
4
|
+
* `author` and `provenance` to every line and keeps `scope` on Planet
|
|
5
|
+
* memories; version 1 has `scope` only and can hold Planet memories only.
|
|
6
|
+
* Readers accept both, version 1 forever.
|
|
7
|
+
*/
|
|
8
|
+
import { addressOf, authorOf, canonicalMemory, planetOf, resolveAddressing, scopeOf, } from './address.js';
|
|
9
|
+
/** The version `oort export` writes. */
|
|
10
|
+
export const EXPORT_FORMAT_VERSION = 2;
|
|
11
|
+
/** Raised for a memory a format version cannot hold, or a file that is not a readable export. */
|
|
12
|
+
export class ExportFormatError extends Error {
|
|
13
|
+
constructor(message) {
|
|
14
|
+
super(message);
|
|
15
|
+
this.name = 'ExportFormatError';
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
const base64 = (v) => Buffer.from(v.buffer, v.byteOffset, v.byteLength).toString('base64');
|
|
19
|
+
/** True when version 1 can hold `m`: a Planet memory whose author is its scope's agent. */
|
|
20
|
+
export function fitsVersion1(m) {
|
|
21
|
+
const author = authorOf(m);
|
|
22
|
+
return (addressOf(m).level === 'user' &&
|
|
23
|
+
author.accountRef === undefined &&
|
|
24
|
+
author.agentId === m.scope.agentId &&
|
|
25
|
+
(m.provenance === undefined || m.provenance === null));
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* One memory as an export line of `version`: the embedding as base64 of its
|
|
29
|
+
* int8 bytes. Version 2 always writes `address`, `author` and `provenance`
|
|
30
|
+
* (null when none), and `scope` for Planet memories only.
|
|
31
|
+
*/
|
|
32
|
+
export function exportLine(m, version) {
|
|
33
|
+
const { embedding, address: _a, author: _w, provenance, scope, id, ...rest } = m;
|
|
34
|
+
const tail = embedding === undefined ? {} : { embedding: base64(embedding) };
|
|
35
|
+
if (version === 1) {
|
|
36
|
+
if (!fitsVersion1(m)) {
|
|
37
|
+
throw new ExportFormatError(`memory ${id} is not a Planet memory; version 1 cannot hold it`);
|
|
38
|
+
}
|
|
39
|
+
return { id, scope, ...rest, ...tail };
|
|
40
|
+
}
|
|
41
|
+
const address = addressOf(m);
|
|
42
|
+
return {
|
|
43
|
+
id,
|
|
44
|
+
...(address.level === 'user' && { scope }),
|
|
45
|
+
address,
|
|
46
|
+
author: authorOf(m),
|
|
47
|
+
provenance: provenance ?? null,
|
|
48
|
+
...rest,
|
|
49
|
+
...tail,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
const keyOf = (x) => JSON.stringify(Object.entries(x).sort());
|
|
53
|
+
/** The manifest for `memories` written as `version`. */
|
|
54
|
+
export function exportManifest(memories, version, exportedAt = new Date()) {
|
|
55
|
+
const scopes = new Map();
|
|
56
|
+
const addresses = new Map();
|
|
57
|
+
for (const m of memories) {
|
|
58
|
+
const a = addressOf(m);
|
|
59
|
+
if (a.level === 'user')
|
|
60
|
+
scopes.set(keyOf(m.scope), m.scope);
|
|
61
|
+
addresses.set(keyOf(a), a);
|
|
62
|
+
}
|
|
63
|
+
const base = {
|
|
64
|
+
oort: 'export',
|
|
65
|
+
exportedAt: exportedAt.toISOString(),
|
|
66
|
+
memories: memories.length,
|
|
67
|
+
scopes: [...scopes.values()],
|
|
68
|
+
};
|
|
69
|
+
return version === 1
|
|
70
|
+
? { ...base, version: 1 }
|
|
71
|
+
: { ...base, version: 2, addresses: [...addresses.values()] };
|
|
72
|
+
}
|
|
73
|
+
/** A whole export: the manifest line, then one line per memory, each ending in `\n`. */
|
|
74
|
+
export function formatExport(memories, version = EXPORT_FORMAT_VERSION, exportedAt = new Date()) {
|
|
75
|
+
return [
|
|
76
|
+
exportManifest(memories, version, exportedAt),
|
|
77
|
+
...memories.map((m) => exportLine(m, version)),
|
|
78
|
+
]
|
|
79
|
+
.map((x) => `${JSON.stringify(x)}\n`)
|
|
80
|
+
.join('');
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Reads an export of version 1 or 2. A version 1 line's `scope` names the
|
|
84
|
+
* memory's Planet and its agent the author. Throws `ExportFormatError` for an
|
|
85
|
+
* unknown version, a count that does not match, or a line that is not a
|
|
86
|
+
* memory (with its line number, never its content).
|
|
87
|
+
*/
|
|
88
|
+
export function parseExport(text, opts = {}) {
|
|
89
|
+
const lines = text.split('\n').filter((l) => l.trim() !== '');
|
|
90
|
+
const head = parseJson(lines[0], 1);
|
|
91
|
+
if (head?.oort !== 'export')
|
|
92
|
+
throw new ExportFormatError('line 1 is not an Oort export manifest');
|
|
93
|
+
if (head.version !== 1 && head.version !== 2) {
|
|
94
|
+
throw new ExportFormatError(`export version ${String(head.version)} is not supported`);
|
|
95
|
+
}
|
|
96
|
+
const version = head.version;
|
|
97
|
+
if (head.memories !== lines.length - 1) {
|
|
98
|
+
throw new ExportFormatError(`the manifest counts ${String(head.memories)} memories, the file has ${lines.length - 1}`);
|
|
99
|
+
}
|
|
100
|
+
const memories = lines.slice(1).map((l, i) => {
|
|
101
|
+
const n = i + 2;
|
|
102
|
+
const raw = parseJson(l, n);
|
|
103
|
+
if (typeof raw !== 'object' || raw === null || typeof raw['id'] !== 'string') {
|
|
104
|
+
throw new ExportFormatError(`line ${n} is not a memory`);
|
|
105
|
+
}
|
|
106
|
+
try {
|
|
107
|
+
return readLine(raw, version, opts);
|
|
108
|
+
}
|
|
109
|
+
catch (e) {
|
|
110
|
+
if (e instanceof RangeError || e instanceof TypeError) {
|
|
111
|
+
throw new ExportFormatError(`line ${n}: ${e.message}`);
|
|
112
|
+
}
|
|
113
|
+
throw e;
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
return { version, manifest: head, memories };
|
|
117
|
+
}
|
|
118
|
+
function parseJson(line, n) {
|
|
119
|
+
try {
|
|
120
|
+
return JSON.parse(line ?? '');
|
|
121
|
+
}
|
|
122
|
+
catch {
|
|
123
|
+
throw new ExportFormatError(`line ${n} is not JSON`);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
function readLine(raw, version, opts) {
|
|
127
|
+
const { embedding, address: rawAddress, author: rawAuthor, provenance, scope: rawScope, supersedesHidden: _sh, supersededByHidden: _bh, ...rest } = raw;
|
|
128
|
+
let address;
|
|
129
|
+
let author;
|
|
130
|
+
if (version === 1) {
|
|
131
|
+
const scope = rawScope;
|
|
132
|
+
if (typeof scope !== 'object' || scope === null)
|
|
133
|
+
throw new RangeError('a version 1 line needs scope');
|
|
134
|
+
address = planetOf(scope);
|
|
135
|
+
author = { agentId: scope.agentId };
|
|
136
|
+
}
|
|
137
|
+
else {
|
|
138
|
+
({ address, author } = checked(rawAddress, rawAuthor, rawScope));
|
|
139
|
+
}
|
|
140
|
+
if (opts.planet !== undefined && address.level === 'user') {
|
|
141
|
+
address = { ...address, orgId: opts.planet.orgId, customerId: opts.planet.customerId };
|
|
142
|
+
}
|
|
143
|
+
const m = {
|
|
144
|
+
...rest,
|
|
145
|
+
scope: scopeOf(address, author),
|
|
146
|
+
address,
|
|
147
|
+
author,
|
|
148
|
+
...(provenance !== undefined && provenance !== null && { provenance }),
|
|
149
|
+
...(typeof embedding === 'string' && {
|
|
150
|
+
embedding: Int8Array.from(Buffer.from(embedding, 'base64')),
|
|
151
|
+
}),
|
|
152
|
+
};
|
|
153
|
+
return canonicalMemory(m);
|
|
154
|
+
}
|
|
155
|
+
/** A version 2 line's address and author, checked, with its scope (when present) matching them. */
|
|
156
|
+
function checked(rawAddress, rawAuthor, rawScope) {
|
|
157
|
+
if (rawAddress === undefined || rawAuthor === undefined) {
|
|
158
|
+
throw new RangeError('a version 2 line needs address and author');
|
|
159
|
+
}
|
|
160
|
+
const probe = { address: rawAddress, author: rawAuthor };
|
|
161
|
+
const want = scopeOf(probe.address, probe.author);
|
|
162
|
+
const { address, author } = resolveAddressing({
|
|
163
|
+
...probe,
|
|
164
|
+
scope: rawScope === undefined ? want : rawScope,
|
|
165
|
+
});
|
|
166
|
+
return { address, author };
|
|
167
|
+
}
|
|
168
|
+
//# sourceMappingURL=export-format.js.map
|
package/dist/grant.d.ts
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
|
+
import { type Address, type Author, type NodeRef } from './address.js';
|
|
1
2
|
import type { ScopeFilter } from './adapters.js';
|
|
2
|
-
import type { Scope } from './model.js';
|
|
3
|
-
/**
|
|
3
|
+
import type { Memory, Scope } from './model.js';
|
|
4
|
+
/**
|
|
5
|
+
* Who a grant acts for: an account (`orgId`, `customerId`) and the agent
|
|
6
|
+
* acting for it. Cursors belong to the principal.
|
|
7
|
+
*/
|
|
4
8
|
export interface Principal {
|
|
5
9
|
orgId: string;
|
|
6
10
|
customerId: string;
|
|
@@ -11,49 +15,152 @@ export interface Principal {
|
|
|
11
15
|
* no project) or `'all'`, every project of the principal plus global.
|
|
12
16
|
*/
|
|
13
17
|
export type GrantProjects = readonly (string | null)[] | 'all';
|
|
14
|
-
/**
|
|
18
|
+
/**
|
|
19
|
+
* One side (read or write) of a pre-level grant: the principal's Planet,
|
|
20
|
+
* written by the principal's agent, in `projectIds`.
|
|
21
|
+
* @deprecated Stores read `Grant.readNodes` and `Grant.writeNodes`.
|
|
22
|
+
*/
|
|
15
23
|
export interface GrantFilter extends Readonly<Principal> {
|
|
16
24
|
readonly projectIds: GrantProjects;
|
|
17
25
|
}
|
|
26
|
+
/**
|
|
27
|
+
* One node a grant covers (CON-61): the node, the project bindings inside it,
|
|
28
|
+
* and optionally one author. Matching is structural and exact on every field
|
|
29
|
+
* (never a prefix). `authorAgentId` restricts the pattern to memories that
|
|
30
|
+
* agent wrote. The local constructors (`localGrant`, `principalGrant`) name
|
|
31
|
+
* the principal's Planet with no author restriction (a person's agents share
|
|
32
|
+
* their Planet, CON-62) and the principal's agent's own Star.
|
|
33
|
+
*/
|
|
34
|
+
export interface NodePattern {
|
|
35
|
+
readonly node: NodeRef;
|
|
36
|
+
readonly projectIds: GrantProjects;
|
|
37
|
+
readonly authorAgentId?: string;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Where an agent's new memories land by default (its home node, spec 5.1):
|
|
41
|
+
* the person's Planet, or the agent's own Star when it is configured as one.
|
|
42
|
+
*/
|
|
43
|
+
export type Home = 'planet' | 'star';
|
|
44
|
+
/**
|
|
45
|
+
* The agent id of a caller that named no agent (an MCP entry written before agents existed, a
|
|
46
|
+
* terminal without `--agent`). It reads and writes the person's Planet only: it has no Star, so
|
|
47
|
+
* it can neither see nor create one. No configured agent may use this id.
|
|
48
|
+
*/
|
|
49
|
+
export declare const ANONYMOUS_AGENT = "anonymous";
|
|
18
50
|
/**
|
|
19
51
|
* The permission every `MemoryStore` method takes. Opaque: built only by
|
|
20
|
-
* `localGrant` or `
|
|
52
|
+
* `localGrant`, `principalGrant` or `nodeGrant`, frozen, and nominal, so a raw
|
|
21
53
|
* `ScopeFilter` (or any look-alike object) is rejected by the type checker
|
|
22
54
|
* and, for JavaScript callers, by `assertGrant` inside every store.
|
|
23
55
|
*
|
|
24
|
-
* `
|
|
25
|
-
* `coldGet`, `recordAccess`); `
|
|
26
|
-
* `forget`, `setTier`, `expireMany`, `coldPut`).
|
|
27
|
-
*
|
|
56
|
+
* `readNodes` bounds what can be seen (`get`, `lexical`, `dense`, `list`,
|
|
57
|
+
* `coldGet`, `recordAccess`); `writeNodes` bounds what can change (`upsert`,
|
|
58
|
+
* `forget`, `setTier`, `expireMany`, `coldPut`). A memory is covered when
|
|
59
|
+
* any pattern of the side covers its address and author. Cursors belong to
|
|
60
|
+
* the principal. Anything outside a grant behaves exactly like something that
|
|
28
61
|
* does not exist.
|
|
29
62
|
*/
|
|
30
63
|
export declare class Grant {
|
|
31
64
|
#private;
|
|
32
65
|
readonly principal: Readonly<Principal>;
|
|
66
|
+
/** @deprecated The pre-level view of `readNodes`; see `GrantFilter`. */
|
|
33
67
|
readonly read: GrantFilter;
|
|
68
|
+
/** @deprecated The pre-level view of `writeNodes`; see `GrantFilter`. */
|
|
34
69
|
readonly write: GrantFilter;
|
|
70
|
+
readonly readNodes: readonly NodePattern[];
|
|
71
|
+
readonly writeNodes: readonly NodePattern[];
|
|
72
|
+
/**
|
|
73
|
+
* The node new memories are written to (the home node): the principal's
|
|
74
|
+
* Planet unless the agent keeps a Star. Engine code that creates a memory
|
|
75
|
+
* (observe, remember) addresses it here; edits of existing memories keep
|
|
76
|
+
* their own address. Covered by `writeNodes` whenever they are not empty.
|
|
77
|
+
*/
|
|
78
|
+
readonly home: NodeRef;
|
|
35
79
|
private constructor();
|
|
36
80
|
/** @internal Use `localGrant` or `principalGrant`. */
|
|
37
|
-
static issue(principal: Principal, read: GrantProjects, write: GrantProjects): Grant;
|
|
81
|
+
static issue(principal: Principal, read: GrantProjects, write: GrantProjects, home?: Home): Grant;
|
|
82
|
+
/** @internal Use `nodeGrant` or `narrowGrant`. */
|
|
83
|
+
static issueNodes(principal: Principal, read: readonly NodePattern[], write: readonly NodePattern[], home?: NodeRef): Grant;
|
|
38
84
|
/** True for a real grant (not a copy, not a look-alike). */
|
|
39
85
|
static is(x: unknown): x is Grant;
|
|
40
86
|
}
|
|
87
|
+
/** The Star of a principal's agent (owned by the principal's owner). */
|
|
88
|
+
export declare function starNode(p: Pick<Principal, 'orgId' | 'agentId'>): NodeRef;
|
|
41
89
|
/**
|
|
42
|
-
* The grant for
|
|
43
|
-
*
|
|
44
|
-
* `write`
|
|
90
|
+
* The grant for one agent of one person (CON-62): read and write the
|
|
91
|
+
* person's Planet (memories any of their agents wrote) and this agent's own
|
|
92
|
+
* Star, in the projects `read` names; `opts.write` may narrow the write side.
|
|
93
|
+
* Another agent's Star is never covered. `opts.home` picks where new memories
|
|
94
|
+
* land: the Planet (default) or the agent's Star; with `star` the Planet is
|
|
95
|
+
* read-only (the agent writes only its Star). `ANONYMOUS_AGENT` gets the
|
|
96
|
+
* Planet alone. `write` must name the same principal as `read`.
|
|
97
|
+
*
|
|
98
|
+
* Before CON-62 the Planet side was restricted to memories the principal's
|
|
99
|
+
* own agent wrote. Every local memory so far was written as `claude-code`,
|
|
100
|
+
* so a local store sees exactly what it saw; nothing is rewritten.
|
|
45
101
|
*/
|
|
46
102
|
export declare function localGrant(read: ScopeFilter, opts?: {
|
|
47
103
|
write?: ScopeFilter;
|
|
104
|
+
home?: Home;
|
|
48
105
|
}): Grant;
|
|
49
|
-
/**
|
|
50
|
-
|
|
106
|
+
/**
|
|
107
|
+
* Every project of `p` (and its global memories), for read and write:
|
|
108
|
+
* maintenance such as export. Covers the same nodes as `localGrant`: the
|
|
109
|
+
* person's Planet and this agent's own Star.
|
|
110
|
+
*/
|
|
111
|
+
export declare function principalGrant(p: Principal, opts?: {
|
|
112
|
+
home?: Home;
|
|
113
|
+
}): Grant;
|
|
114
|
+
/**
|
|
115
|
+
* A grant over explicit node patterns, possibly across owners and levels:
|
|
116
|
+
* `read` bounds what is seen, `write` (default: `read`) what may change.
|
|
117
|
+
* Trusted: whoever builds one has already decided the principal may use
|
|
118
|
+
* every node it names (local configuration, or membership checked by a
|
|
119
|
+
* control plane). Nothing in the engine builds one for its own callers yet:
|
|
120
|
+
* the CLI, hooks and MCP server keep the pre-level grants.
|
|
121
|
+
*/
|
|
122
|
+
export declare function nodeGrant(opts: {
|
|
123
|
+
principal: Principal;
|
|
124
|
+
read: readonly NodePattern[];
|
|
125
|
+
write?: readonly NodePattern[];
|
|
126
|
+
/** Where new memories land (default: the principal's Planet if written, else the first node written). */
|
|
127
|
+
home?: NodeRef;
|
|
128
|
+
}): Grant;
|
|
129
|
+
/**
|
|
130
|
+
* `g` narrowed to the nodes `keep` names (by `nodeKey`), on both sides: the
|
|
131
|
+
* grant recall uses for one node's first stage. Never wider than `g`. The
|
|
132
|
+
* home stays when it is kept, else the default for what remains.
|
|
133
|
+
*/
|
|
134
|
+
export declare function narrowGrant(g: Grant, keep: readonly NodeRef[]): Grant;
|
|
135
|
+
/** The distinct nodes `g` reads, in the order its patterns name them. */
|
|
136
|
+
export declare function readableNodes(g: Grant): NodeRef[];
|
|
137
|
+
/** The address a new memory gets under `g`: its home node, bound to `projectId` when given. */
|
|
138
|
+
export declare function homeAddress(g: Grant, projectId?: string): Address;
|
|
51
139
|
/** Rejects anything that is not a real `Grant`; every store calls it first. */
|
|
52
140
|
export declare function assertGrant(g: unknown): asserts g is Grant;
|
|
53
|
-
/**
|
|
141
|
+
/**
|
|
142
|
+
* True iff a memory with `scope` lies inside `f`. Extra scope fields do not widen or narrow it.
|
|
143
|
+
* @deprecated Use `grantReads` / `grantWrites`.
|
|
144
|
+
*/
|
|
54
145
|
export declare function filterCovers(f: GrantFilter, scope: Scope): boolean;
|
|
146
|
+
/** True iff `p` covers a memory at `address` written by `author`. */
|
|
147
|
+
export declare function patternCovers(p: NodePattern, address: Address, author: Author): boolean;
|
|
148
|
+
/** True iff `g` may read `m` (by its address and author). */
|
|
149
|
+
export declare const grantReads: (g: Grant, m: Pick<Memory, 'scope' | 'address' | 'author'>) => boolean;
|
|
150
|
+
/** True iff `g` may change `m` (by its address and author). */
|
|
151
|
+
export declare const grantWrites: (g: Grant, m: Pick<Memory, 'scope' | 'address' | 'author'>) => boolean;
|
|
152
|
+
/** True iff `g` may read a Planet memory with `scope` (`scope.agentId` as its author). */
|
|
55
153
|
export declare const canRead: (g: Grant, scope: Scope) => boolean;
|
|
154
|
+
/** True iff `g` may change a Planet memory with `scope` (`scope.agentId` as its author). */
|
|
56
155
|
export declare const canWrite: (g: Grant, scope: Scope) => boolean;
|
|
156
|
+
/**
|
|
157
|
+
* Two records occupy the same place: the same address, the same author and
|
|
158
|
+
* the same scope (extra scope fields included). A write that would change any
|
|
159
|
+
* of them for an existing id is refused.
|
|
160
|
+
*/
|
|
161
|
+
export declare function sameRecordAddress(a: Pick<Memory, 'scope' | 'address' | 'author'>, b: Pick<Memory, 'scope' | 'address' | 'author'>): boolean;
|
|
162
|
+
/** The Planet node of a principal: what pre-level grants cover. */
|
|
163
|
+
export declare function planetNode(p: Pick<Principal, 'orgId' | 'customerId'>): NodeRef;
|
|
57
164
|
/**
|
|
58
165
|
* The key a store keeps a cursor under. The local principal (the CLI's own,
|
|
59
166
|
* `local`/`self`/`claude-code`) keeps the bare key, so cursors written before
|