@agentproto/memory 0.0.0-stage → 0.1.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/LICENSE +202 -0
- package/README.md +124 -2
- package/dist/index.d.ts +678 -0
- package/dist/index.mjs +894 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +60 -3
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,678 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* AIP-72 memory/v1 document types.
|
|
5
|
+
*
|
|
6
|
+
* Hand-tuned on top of the shape `json-schema-to-typescript` generates
|
|
7
|
+
* from `resources/aip-72/draft/MEMORY.schema.json`: the `$defs` get
|
|
8
|
+
* `Memory*` names, and the document carries its markdown `body` as a
|
|
9
|
+
* required string (the schema's `body` property stands for the text
|
|
10
|
+
* after the closing frontmatter fence).
|
|
11
|
+
*/
|
|
12
|
+
declare const MEMORY_TYPES: readonly ["user", "feedback", "project", "reference"];
|
|
13
|
+
/** The closed memory type set (`type`, and `kind` on a memory/v1 item). */
|
|
14
|
+
type MemoryType = (typeof MEMORY_TYPES)[number];
|
|
15
|
+
declare const MEMORY_STATUSES: readonly ["active", "superseded", "archived"];
|
|
16
|
+
/** Lifecycle status. Absent means `active` (AIP-72 rule 9). */
|
|
17
|
+
type MemoryStatus = (typeof MEMORY_STATUSES)[number];
|
|
18
|
+
interface MemoryAnchor {
|
|
19
|
+
kind: "global" | "project" | "task" | "session" | "pr" | "contact" | "agent" | "node" | "operator";
|
|
20
|
+
/** Anchor target slug (project, contact, agent). */
|
|
21
|
+
slug?: string;
|
|
22
|
+
/**
|
|
23
|
+
* Anchor target id (task, session, node, operator). A node's is its
|
|
24
|
+
* AIP-34 id (`@<owner>/<slug>`), an operator's the body of its AIP-54
|
|
25
|
+
* `operator:` ref (`<slug>` or `<slug>@<workspace>`). Section 10.
|
|
26
|
+
*/
|
|
27
|
+
id?: string;
|
|
28
|
+
/** Repository `owner/name` for a pr anchor. */
|
|
29
|
+
repo?: string;
|
|
30
|
+
/** Pull-request number for a pr anchor. */
|
|
31
|
+
number?: number;
|
|
32
|
+
}
|
|
33
|
+
interface MemoryLifetime {
|
|
34
|
+
kind: "durable" | "until" | "while" | "session";
|
|
35
|
+
/** Expiry timestamp, REQUIRED for kind `until`. */
|
|
36
|
+
at?: string;
|
|
37
|
+
/** Governing anchor, REQUIRED for kind `while`. */
|
|
38
|
+
anchor?: MemoryAnchor;
|
|
39
|
+
}
|
|
40
|
+
interface MemoryDecidedBy {
|
|
41
|
+
rule?: string;
|
|
42
|
+
source?: string;
|
|
43
|
+
}
|
|
44
|
+
interface MemoryProvenance {
|
|
45
|
+
source?: string;
|
|
46
|
+
/** Omitted, never empty, when the source has no session. */
|
|
47
|
+
sessionId?: string;
|
|
48
|
+
/** Omitted, never empty, when the source has none. */
|
|
49
|
+
ref?: string;
|
|
50
|
+
quote?: string;
|
|
51
|
+
at?: string;
|
|
52
|
+
extractorVersion?: string;
|
|
53
|
+
}
|
|
54
|
+
interface MemoryCapture {
|
|
55
|
+
mode?: "live" | "batch";
|
|
56
|
+
producer?: string;
|
|
57
|
+
capturedAt?: string;
|
|
58
|
+
}
|
|
59
|
+
interface MemoryEvidence {
|
|
60
|
+
sourceKind?: string;
|
|
61
|
+
trust?: string;
|
|
62
|
+
explicitness?: string;
|
|
63
|
+
corroborations?: number;
|
|
64
|
+
contradictsActive?: boolean;
|
|
65
|
+
impact?: string;
|
|
66
|
+
lifetime?: MemoryLifetime;
|
|
67
|
+
anchorsResolved?: string[];
|
|
68
|
+
ageHours?: number;
|
|
69
|
+
flags?: string[];
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* The YAML frontmatter of a memory/v1 item. Every field is optional at
|
|
73
|
+
* the type level: the schema accepts either the canonical AIP-10 base
|
|
74
|
+
* fields (`schema`, `slug`, `kind`, `title`, `updated_at`) or the legacy
|
|
75
|
+
* trio (`id`, `name`, `type`), which `validateMemory` checks.
|
|
76
|
+
*/
|
|
77
|
+
interface MemoryFrontmatter {
|
|
78
|
+
schema?: "memory/v1";
|
|
79
|
+
slug?: string;
|
|
80
|
+
kind?: string;
|
|
81
|
+
title?: string;
|
|
82
|
+
updated_at?: string;
|
|
83
|
+
id?: string;
|
|
84
|
+
name?: string;
|
|
85
|
+
type?: MemoryType;
|
|
86
|
+
description?: string;
|
|
87
|
+
tags?: string[];
|
|
88
|
+
polarity?: "do" | "dont";
|
|
89
|
+
lifetime?: MemoryLifetime;
|
|
90
|
+
status?: MemoryStatus;
|
|
91
|
+
created?: string;
|
|
92
|
+
written?: string;
|
|
93
|
+
decidedBy?: MemoryDecidedBy;
|
|
94
|
+
provenance?: MemoryProvenance;
|
|
95
|
+
evidence?: MemoryEvidence;
|
|
96
|
+
capture?: MemoryCapture;
|
|
97
|
+
}
|
|
98
|
+
/** One memory/v1 item: frontmatter fields plus the markdown body. */
|
|
99
|
+
interface MemoryDocument extends MemoryFrontmatter {
|
|
100
|
+
body: string;
|
|
101
|
+
}
|
|
102
|
+
/** `defineMemory` input: `body` may be omitted for an item with no prose. */
|
|
103
|
+
type MemoryDefinition = MemoryFrontmatter & {
|
|
104
|
+
body?: string;
|
|
105
|
+
};
|
|
106
|
+
/** The frozen item `defineMemory` returns, with `status` made explicit. */
|
|
107
|
+
type MemoryHandle = Readonly<MemoryDocument & {
|
|
108
|
+
status: MemoryStatus;
|
|
109
|
+
}>;
|
|
110
|
+
/**
|
|
111
|
+
* One validation finding. `path` is the dot path of the offending field
|
|
112
|
+
* (`provenance.sessionId`, `lifetime.at`, `tags.0`), `""` for the whole
|
|
113
|
+
* document. `rule` is `schema` for a MEMORY.schema.json constraint,
|
|
114
|
+
* `section-2` for the memory/v1 `kind` set, or `rule-<n>` for AIP-72
|
|
115
|
+
* conformance rule n.
|
|
116
|
+
*/
|
|
117
|
+
interface MemoryIssue {
|
|
118
|
+
path: string;
|
|
119
|
+
message: string;
|
|
120
|
+
rule: string;
|
|
121
|
+
}
|
|
122
|
+
interface MemoryValidationResult {
|
|
123
|
+
ok: boolean;
|
|
124
|
+
errors: MemoryIssue[];
|
|
125
|
+
/** SHOULD-level findings (rule 8). Never make `ok` false. */
|
|
126
|
+
warnings: MemoryIssue[];
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* AIP-72 reference implementation of `defineMemory`: the writer path.
|
|
131
|
+
*
|
|
132
|
+
* Validates in canonical mode (rule 1: a writer sets `schema`, `slug`,
|
|
133
|
+
* `kind`, `title`, `updated_at` on every item it creates) and throws a
|
|
134
|
+
* `MemoryValidationError` whose `field` / `issues` name the offending
|
|
135
|
+
* field paths. Only two values are filled in: `status` becomes `active`
|
|
136
|
+
* when absent (section 3: a writer creates an item `active`), and an
|
|
137
|
+
* absent `body` becomes `""`. Nothing else is defaulted; in particular
|
|
138
|
+
* `tags` is not set to `[]`, since rule 4 omits an empty optional field.
|
|
139
|
+
*
|
|
140
|
+
* Built on `createDoctype` for the shared slug check and top-level
|
|
141
|
+
* freeze; the handle is frozen deeply and does not alias the input.
|
|
142
|
+
*/
|
|
143
|
+
declare function defineMemory(input: MemoryDefinition): MemoryHandle;
|
|
144
|
+
|
|
145
|
+
interface ValidateMemoryOptions {
|
|
146
|
+
/**
|
|
147
|
+
* Require the canonical fields (`schema`, `slug`, `kind`, `title`,
|
|
148
|
+
* `updated_at`), as rule 1 asks of a writer creating an item. Off by
|
|
149
|
+
* default: a reader MUST accept legacy items (rule 9).
|
|
150
|
+
*/
|
|
151
|
+
canonical?: boolean;
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Validate one memory/v1 document (frontmatter fields plus `body`).
|
|
155
|
+
*
|
|
156
|
+
* Runs MEMORY.schema.json (through the generated zod, plus the
|
|
157
|
+
* `anyOf` and lifetime `if/then` branches the codegen cannot express)
|
|
158
|
+
* and the AIP-72 conformance rules checkable on a single document:
|
|
159
|
+
* section 2 (`kind` set), rules 1, 2, 4 and 5 as errors, rule 8 as
|
|
160
|
+
* warnings. Rules 6 and 7 compare two versions of an item and belong to
|
|
161
|
+
* the store; `isAllowedStatusTransition` covers rule 7's table.
|
|
162
|
+
*
|
|
163
|
+
* Defaults: MEMORY.schema.json gives `tags` (and `evidence.anchorsResolved`,
|
|
164
|
+
* `evidence.flags`) `default: []`, and the generated zod carries it. This
|
|
165
|
+
* function only checks: it never returns the parsed data, so the default
|
|
166
|
+
* is never filled in. An item without `tags` stays without `tags` and
|
|
167
|
+
* reads as untagged (the digest treats it as `global`, AIP-72 section 9).
|
|
168
|
+
* An explicit `tags: []` validates: rule 4 rejects empty strings and empty
|
|
169
|
+
* objects, not empty arrays, though a writer omits it (`defineMemory`).
|
|
170
|
+
*/
|
|
171
|
+
declare function validateMemory(doc: unknown, opts?: ValidateMemoryOptions): MemoryValidationResult;
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Split a MEMORY.md file into its frontmatter fields and `body`.
|
|
175
|
+
*
|
|
176
|
+
* The frontmatter is read as YAML 1.2 core schema, so a date-time stays
|
|
177
|
+
* a string and an unquoted `true`, `null` or `2026` in a string field
|
|
178
|
+
* reaches `validateMemory` as a non-string and fails there (rule 3).
|
|
179
|
+
* This only parses: it does not validate, so a reader can still load an
|
|
180
|
+
* item it will then inspect. The body is free (rule 12): one blank line
|
|
181
|
+
* after the closing fence is the separator, everything after it is kept
|
|
182
|
+
* byte for byte.
|
|
183
|
+
*/
|
|
184
|
+
declare function parseMemory(markdown: string): MemoryDocument;
|
|
185
|
+
/**
|
|
186
|
+
* Render a memory/v1 document as MEMORY.md. Throws a
|
|
187
|
+
* `MemoryValidationError` if the document does not validate.
|
|
188
|
+
*
|
|
189
|
+
* Every string value is double-quoted, so a YAML 1.1 or 1.2 reader
|
|
190
|
+
* reads it back as a string (rule 3); undefined fields are omitted
|
|
191
|
+
* (rule 4); key order is the document's own. So re-rendering a file this
|
|
192
|
+
* function wrote, with only `status` changed, changes only the `status`
|
|
193
|
+
* line (rule 6). That does not hold for a file another writer produced
|
|
194
|
+
* (unquoted strings, other key styles, comments): re-rendering it
|
|
195
|
+
* normalizes every line, which is why the store rewrites the status line
|
|
196
|
+
* in place instead (`setStatusLine`, `files-store.ts`).
|
|
197
|
+
* `parseMemory(renderMemory(d))` deep-equals `d`.
|
|
198
|
+
*/
|
|
199
|
+
declare function renderMemory(doc: MemoryDocument): string;
|
|
200
|
+
|
|
201
|
+
/** The item's status, reading an absent `status` as `active` (rule 9). */
|
|
202
|
+
declare function memoryStatus(doc: Pick<MemoryFrontmatter, "status">): MemoryStatus;
|
|
203
|
+
/**
|
|
204
|
+
* Rule 7: a writer only moves `status` from `active` (or absent) to
|
|
205
|
+
* `superseded` or `archived`, and never out of either. Rewriting the
|
|
206
|
+
* same status is not a transition and is allowed.
|
|
207
|
+
*/
|
|
208
|
+
declare function isAllowedStatusTransition(from: MemoryStatus | undefined, to: MemoryStatus): boolean;
|
|
209
|
+
/**
|
|
210
|
+
* Whether a reader may present the item as in force at `now`.
|
|
211
|
+
*
|
|
212
|
+
* Rule 10: a `superseded` or `archived` item is not. Rule 11: an
|
|
213
|
+
* `until` lifetime past its `at` is not. A `while` anchor or a
|
|
214
|
+
* `session` lifetime needs context outside the document (does the
|
|
215
|
+
* anchor still hold, which session is current), so this check treats
|
|
216
|
+
* them as holding; the store that has that context narrows further.
|
|
217
|
+
*/
|
|
218
|
+
declare function isInForce(doc: Pick<MemoryFrontmatter, "status" | "lifetime">, now?: Date): boolean;
|
|
219
|
+
|
|
220
|
+
/** Thrown by `defineMemory` and `renderMemory` on an invalid item. */
|
|
221
|
+
declare class MemoryValidationError extends Error {
|
|
222
|
+
/** Every error found, each naming its field path. */
|
|
223
|
+
readonly issues: readonly MemoryIssue[];
|
|
224
|
+
/** Path of the first offending field (`""` for the whole document). */
|
|
225
|
+
readonly field: string;
|
|
226
|
+
constructor(prefix: string, issues: readonly MemoryIssue[]);
|
|
227
|
+
}
|
|
228
|
+
/** Thrown by `parseMemory` when the markdown has no readable frontmatter. */
|
|
229
|
+
declare class MemoryParseError extends Error {
|
|
230
|
+
constructor(message: string);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* AIP-72 memory/v1 frontmatter zod schema.
|
|
235
|
+
*
|
|
236
|
+
* Codegen output of `scripts/scaffold-aip.mjs --aip 72 --slug memory
|
|
237
|
+
* --doctype MEMORY --schema-only --inline-refs` over
|
|
238
|
+
* `specs/resources/aip-72/draft/MEMORY.schema.json`. Do not edit the code
|
|
239
|
+
* below by hand: change the JSON draft, then regenerate.
|
|
240
|
+
* `scripts/check-spec-drift.mjs` fails CI when the code drifts from the
|
|
241
|
+
* draft (it does not compare this comment).
|
|
242
|
+
*
|
|
243
|
+
* Only `validate.ts` imports this schema. `validateMemory` runs it, and
|
|
244
|
+
* `defineMemory` (`define-memory.ts`) and `renderMemory` (`markdown.ts`)
|
|
245
|
+
* validate through `validateMemory`. `index.ts` re-exports it.
|
|
246
|
+
*
|
|
247
|
+
* What the codegen cannot express also lives in `validate.ts`: the
|
|
248
|
+
* root `anyOf` (canonical fields or the legacy id/name/type trio), the
|
|
249
|
+
* `$defs.lifetime` if/then rules (`until` needs `at`, `while` needs
|
|
250
|
+
* `anchor`), the unknown-key check on the root and `lifetime` objects
|
|
251
|
+
* (zod 4 drops it inside the `.and(...)` intersections below), and the
|
|
252
|
+
* AIP-72 conformance rules that apply to a single document.
|
|
253
|
+
*/
|
|
254
|
+
|
|
255
|
+
declare const memoryFrontmatterSchema: z.ZodIntersection<z.ZodObject<{
|
|
256
|
+
schema: z.ZodOptional<z.ZodLiteral<"memory/v1">>;
|
|
257
|
+
slug: z.ZodOptional<z.ZodString>;
|
|
258
|
+
kind: z.ZodOptional<z.ZodString>;
|
|
259
|
+
title: z.ZodOptional<z.ZodString>;
|
|
260
|
+
updated_at: z.ZodOptional<z.ZodString>;
|
|
261
|
+
id: z.ZodOptional<z.ZodString>;
|
|
262
|
+
name: z.ZodOptional<z.ZodString>;
|
|
263
|
+
type: z.ZodOptional<z.ZodEnum<{
|
|
264
|
+
user: "user";
|
|
265
|
+
feedback: "feedback";
|
|
266
|
+
project: "project";
|
|
267
|
+
reference: "reference";
|
|
268
|
+
}>>;
|
|
269
|
+
description: z.ZodOptional<z.ZodString>;
|
|
270
|
+
tags: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
271
|
+
polarity: z.ZodOptional<z.ZodEnum<{
|
|
272
|
+
do: "do";
|
|
273
|
+
dont: "dont";
|
|
274
|
+
}>>;
|
|
275
|
+
lifetime: z.ZodOptional<z.ZodIntersection<z.ZodObject<{
|
|
276
|
+
kind: z.ZodEnum<{
|
|
277
|
+
session: "session";
|
|
278
|
+
durable: "durable";
|
|
279
|
+
until: "until";
|
|
280
|
+
while: "while";
|
|
281
|
+
}>;
|
|
282
|
+
at: z.ZodOptional<z.ZodString>;
|
|
283
|
+
anchor: z.ZodOptional<z.ZodObject<{
|
|
284
|
+
kind: z.ZodEnum<{
|
|
285
|
+
project: "project";
|
|
286
|
+
global: "global";
|
|
287
|
+
task: "task";
|
|
288
|
+
session: "session";
|
|
289
|
+
pr: "pr";
|
|
290
|
+
contact: "contact";
|
|
291
|
+
agent: "agent";
|
|
292
|
+
node: "node";
|
|
293
|
+
operator: "operator";
|
|
294
|
+
}>;
|
|
295
|
+
slug: z.ZodOptional<z.ZodString>;
|
|
296
|
+
id: z.ZodOptional<z.ZodString>;
|
|
297
|
+
repo: z.ZodOptional<z.ZodString>;
|
|
298
|
+
number: z.ZodOptional<z.ZodNumber>;
|
|
299
|
+
}, z.core.$strict>>;
|
|
300
|
+
}, z.core.$strict>, z.ZodIntersection<z.ZodAny, z.ZodAny>>>;
|
|
301
|
+
status: z.ZodOptional<z.ZodEnum<{
|
|
302
|
+
active: "active";
|
|
303
|
+
superseded: "superseded";
|
|
304
|
+
archived: "archived";
|
|
305
|
+
}>>;
|
|
306
|
+
created: z.ZodOptional<z.ZodString>;
|
|
307
|
+
written: z.ZodOptional<z.ZodString>;
|
|
308
|
+
decidedBy: z.ZodOptional<z.ZodObject<{
|
|
309
|
+
rule: z.ZodOptional<z.ZodString>;
|
|
310
|
+
source: z.ZodOptional<z.ZodString>;
|
|
311
|
+
}, z.core.$strict>>;
|
|
312
|
+
provenance: z.ZodOptional<z.ZodObject<{
|
|
313
|
+
source: z.ZodOptional<z.ZodString>;
|
|
314
|
+
sessionId: z.ZodOptional<z.ZodString>;
|
|
315
|
+
ref: z.ZodOptional<z.ZodString>;
|
|
316
|
+
quote: z.ZodOptional<z.ZodString>;
|
|
317
|
+
at: z.ZodOptional<z.ZodString>;
|
|
318
|
+
extractorVersion: z.ZodOptional<z.ZodString>;
|
|
319
|
+
}, z.core.$strict>>;
|
|
320
|
+
evidence: z.ZodOptional<z.ZodObject<{
|
|
321
|
+
sourceKind: z.ZodOptional<z.ZodString>;
|
|
322
|
+
trust: z.ZodOptional<z.ZodString>;
|
|
323
|
+
explicitness: z.ZodOptional<z.ZodString>;
|
|
324
|
+
corroborations: z.ZodOptional<z.ZodNumber>;
|
|
325
|
+
contradictsActive: z.ZodOptional<z.ZodBoolean>;
|
|
326
|
+
impact: z.ZodOptional<z.ZodString>;
|
|
327
|
+
lifetime: z.ZodOptional<z.ZodIntersection<z.ZodObject<{
|
|
328
|
+
kind: z.ZodEnum<{
|
|
329
|
+
session: "session";
|
|
330
|
+
durable: "durable";
|
|
331
|
+
until: "until";
|
|
332
|
+
while: "while";
|
|
333
|
+
}>;
|
|
334
|
+
at: z.ZodOptional<z.ZodString>;
|
|
335
|
+
anchor: z.ZodOptional<z.ZodObject<{
|
|
336
|
+
kind: z.ZodEnum<{
|
|
337
|
+
project: "project";
|
|
338
|
+
global: "global";
|
|
339
|
+
task: "task";
|
|
340
|
+
session: "session";
|
|
341
|
+
pr: "pr";
|
|
342
|
+
contact: "contact";
|
|
343
|
+
agent: "agent";
|
|
344
|
+
node: "node";
|
|
345
|
+
operator: "operator";
|
|
346
|
+
}>;
|
|
347
|
+
slug: z.ZodOptional<z.ZodString>;
|
|
348
|
+
id: z.ZodOptional<z.ZodString>;
|
|
349
|
+
repo: z.ZodOptional<z.ZodString>;
|
|
350
|
+
number: z.ZodOptional<z.ZodNumber>;
|
|
351
|
+
}, z.core.$strict>>;
|
|
352
|
+
}, z.core.$strict>, z.ZodIntersection<z.ZodAny, z.ZodAny>>>;
|
|
353
|
+
anchorsResolved: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
354
|
+
ageHours: z.ZodOptional<z.ZodNumber>;
|
|
355
|
+
flags: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
356
|
+
}, z.core.$strict>>;
|
|
357
|
+
capture: z.ZodOptional<z.ZodObject<{
|
|
358
|
+
mode: z.ZodOptional<z.ZodEnum<{
|
|
359
|
+
live: "live";
|
|
360
|
+
batch: "batch";
|
|
361
|
+
}>>;
|
|
362
|
+
producer: z.ZodOptional<z.ZodString>;
|
|
363
|
+
capturedAt: z.ZodOptional<z.ZodString>;
|
|
364
|
+
}, z.core.$strict>>;
|
|
365
|
+
body: z.ZodOptional<z.ZodString>;
|
|
366
|
+
}, z.core.$strict>, z.ZodUnion<readonly [z.ZodAny, z.ZodAny]>>;
|
|
367
|
+
|
|
368
|
+
/**
|
|
369
|
+
* AIP-72 section 8: the memory store port.
|
|
370
|
+
*
|
|
371
|
+
* The shapes below are the ones section 8 fixes, name for name. A store
|
|
372
|
+
* keys items by `id`; `slug`, when present, is a second handle that is
|
|
373
|
+
* unique among active items (rule 14). Writes are compare-and-set on the
|
|
374
|
+
* opaque `revision` (rule 17), and nothing is ever hard deleted (rule 16).
|
|
375
|
+
*/
|
|
376
|
+
/** One stored item, or one pending proposal. */
|
|
377
|
+
interface MemoryRecord {
|
|
378
|
+
item: MemoryDocument;
|
|
379
|
+
/** Opaque; changes on every byte change of the stored document. */
|
|
380
|
+
revision: string;
|
|
381
|
+
/** Set on a proposal, absent on an item. */
|
|
382
|
+
proposalId?: string;
|
|
383
|
+
}
|
|
384
|
+
/**
|
|
385
|
+
* AIP-72 section 10. Built by the host from the calling session, never by
|
|
386
|
+
* the store, and handed in on every operation made on a session's behalf.
|
|
387
|
+
* A call with no `scope` is a host call (rule 30).
|
|
388
|
+
*/
|
|
389
|
+
interface MemoryScope {
|
|
390
|
+
/**
|
|
391
|
+
* The node projection: AIP-34 node ids, nearest first, already cut at a
|
|
392
|
+
* sealed or hard node. Empty: the session has no node scope.
|
|
393
|
+
*/
|
|
394
|
+
readonly nodes: readonly string[];
|
|
395
|
+
/** Id of the root node of the session's tree. */
|
|
396
|
+
readonly tree?: string;
|
|
397
|
+
/** The operator anchor id, when the session runs an operator. */
|
|
398
|
+
readonly operator?: string;
|
|
399
|
+
}
|
|
400
|
+
interface MemoryFilter {
|
|
401
|
+
type?: MemoryType | readonly MemoryType[];
|
|
402
|
+
status?: MemoryStatus | readonly MemoryStatus[];
|
|
403
|
+
/** Matches at most one active item (rule 14, rule 20 overlap). */
|
|
404
|
+
slug?: string;
|
|
405
|
+
/** Match any, e.g. `["global", "project:acme"]`. */
|
|
406
|
+
tags?: readonly string[];
|
|
407
|
+
/** Rules 10 and 11, at the store's clock. */
|
|
408
|
+
inForce?: boolean;
|
|
409
|
+
/** List proposals instead of items. */
|
|
410
|
+
proposals?: boolean;
|
|
411
|
+
/** Section 10: only the items in the scope's node projection. Absent: a host call. */
|
|
412
|
+
scope?: MemoryScope;
|
|
413
|
+
}
|
|
414
|
+
interface MemorySearchHit {
|
|
415
|
+
source: "memory" | "knowledge";
|
|
416
|
+
/** REQUIRED when `source` is `"memory"`. */
|
|
417
|
+
id?: string;
|
|
418
|
+
slug?: string;
|
|
419
|
+
title: string;
|
|
420
|
+
snippet?: string;
|
|
421
|
+
score?: number;
|
|
422
|
+
/** Provider pointer when `source` is `"knowledge"`. */
|
|
423
|
+
ref?: string;
|
|
424
|
+
}
|
|
425
|
+
interface MemoryWriteOptions {
|
|
426
|
+
mode: "propose" | "commit";
|
|
427
|
+
/** The revision the caller last read (rule 17). */
|
|
428
|
+
ifRevision?: string;
|
|
429
|
+
/** The proposal this commit promotes; dropped once the item is written (rule 21). */
|
|
430
|
+
fromProposal?: string;
|
|
431
|
+
/** Section 10: the calling session's scope. Absent: a host call. */
|
|
432
|
+
scope?: MemoryScope;
|
|
433
|
+
/**
|
|
434
|
+
* Rule 32, on a create. `"operator"` (the default) owns the item by the
|
|
435
|
+
* session's operator; `"node"` shares it with every operator of the node.
|
|
436
|
+
*/
|
|
437
|
+
owner?: "operator" | "node";
|
|
438
|
+
}
|
|
439
|
+
interface MemorySearchOptions {
|
|
440
|
+
filter?: MemoryFilter;
|
|
441
|
+
limit?: number;
|
|
442
|
+
}
|
|
443
|
+
interface MemoryStore {
|
|
444
|
+
write(item: MemoryDocument, opts: MemoryWriteOptions): Promise<MemoryRecord>;
|
|
445
|
+
read(id: string, opts?: {
|
|
446
|
+
scope?: MemoryScope;
|
|
447
|
+
}): Promise<MemoryRecord | null>;
|
|
448
|
+
list(filter?: MemoryFilter): Promise<readonly MemoryRecord[]>;
|
|
449
|
+
search(query: string, opts?: MemorySearchOptions): Promise<readonly MemorySearchHit[]>;
|
|
450
|
+
supersede(oldId: string, next: MemoryDocument, opts?: {
|
|
451
|
+
ifRevision?: string;
|
|
452
|
+
scope?: MemoryScope;
|
|
453
|
+
}): Promise<{
|
|
454
|
+
old: MemoryRecord;
|
|
455
|
+
next: MemoryRecord;
|
|
456
|
+
}>;
|
|
457
|
+
archive(id: string, opts?: {
|
|
458
|
+
ifRevision?: string;
|
|
459
|
+
scope?: MemoryScope;
|
|
460
|
+
}): Promise<MemoryRecord>;
|
|
461
|
+
dismiss(proposalId: string, opts?: {
|
|
462
|
+
scope?: MemoryScope;
|
|
463
|
+
}): Promise<void>;
|
|
464
|
+
}
|
|
465
|
+
/** The section 8 port under the `I*` name its knowledge sibling uses. */
|
|
466
|
+
type IMemoryStore = MemoryStore;
|
|
467
|
+
type MemoryStoreErrorCode = "conflict" | "not-found" | "invalid-transition" | "slug-taken";
|
|
468
|
+
/**
|
|
469
|
+
* Base of the store's typed failures. An invalid document is rejected
|
|
470
|
+
* with the document package's `MemoryValidationError` instead, which
|
|
471
|
+
* names the field (rule 13).
|
|
472
|
+
*/
|
|
473
|
+
declare class MemoryStoreError extends Error {
|
|
474
|
+
readonly code: MemoryStoreErrorCode;
|
|
475
|
+
constructor(code: MemoryStoreErrorCode, message: string);
|
|
476
|
+
}
|
|
477
|
+
/** Rule 17: `ifRevision` missing or stale on a write that needs it. */
|
|
478
|
+
declare class MemoryConflictError extends MemoryStoreError {
|
|
479
|
+
readonly id: string;
|
|
480
|
+
/** The revision the caller sent, if any. */
|
|
481
|
+
readonly expected: string | undefined;
|
|
482
|
+
/** The revision on disk, `undefined` when the item does not exist. */
|
|
483
|
+
readonly actual: string | undefined;
|
|
484
|
+
constructor(id: string, expected: string | undefined, actual: string | undefined);
|
|
485
|
+
}
|
|
486
|
+
/** No item (or proposal) with that id. */
|
|
487
|
+
declare class MemoryNotFoundError extends MemoryStoreError {
|
|
488
|
+
readonly id: string;
|
|
489
|
+
readonly what: "item" | "proposal";
|
|
490
|
+
constructor(what: "item" | "proposal", id: string);
|
|
491
|
+
}
|
|
492
|
+
/** Rules 7 and 15: a status move the store does not allow. */
|
|
493
|
+
declare class MemoryTransitionError extends MemoryStoreError {
|
|
494
|
+
readonly id: string;
|
|
495
|
+
readonly from: MemoryStatus;
|
|
496
|
+
readonly to: MemoryStatus;
|
|
497
|
+
constructor(id: string, from: MemoryStatus, to: MemoryStatus, detail?: string);
|
|
498
|
+
}
|
|
499
|
+
/** Rule 14: another active item already holds the slug. */
|
|
500
|
+
declare class MemorySlugTakenError extends MemoryStoreError {
|
|
501
|
+
readonly slug: string;
|
|
502
|
+
/** The active item holding it. */
|
|
503
|
+
readonly holder: string;
|
|
504
|
+
constructor(slug: string, holder: string);
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
type Tagged = Pick<MemoryFrontmatter, "tags">;
|
|
508
|
+
/** `node:<id>`, `id` being the AIP-34 id of a node (`@<owner>/<slug>`). */
|
|
509
|
+
declare function nodeTag(id: string): string;
|
|
510
|
+
/** `operator:<id>`, `id` being the body of an AIP-54 `operator:` ref. */
|
|
511
|
+
declare function operatorTag(id: string): string;
|
|
512
|
+
/** The ids of the item's node anchors (an item carries at most one). */
|
|
513
|
+
declare function nodeAnchorIds(item: Tagged): string[];
|
|
514
|
+
/** The ids of the item's operator anchors (an item carries at most one). */
|
|
515
|
+
declare function operatorAnchorIds(item: Tagged): string[];
|
|
516
|
+
/** Whether a tag is a node or operator anchor (neither counts as an anchor for section 9). */
|
|
517
|
+
declare function isNodeOrOperatorTag(tag: string): boolean;
|
|
518
|
+
interface NodeProjectionOptions {
|
|
519
|
+
/**
|
|
520
|
+
* Operator anchor ids the store's AIP-38 read grants let the session
|
|
521
|
+
* read (condition 4). A grant never extends condition 2.
|
|
522
|
+
*/
|
|
523
|
+
grantedOperators?: Iterable<string>;
|
|
524
|
+
}
|
|
525
|
+
/**
|
|
526
|
+
* Conditions 2 and 4 of the node projection (section 10), the two that
|
|
527
|
+
* read an item's node and operator anchors. Conditions 1 (in force) and 3
|
|
528
|
+
* (the session's flat anchors) stay with the caller, which has the clock
|
|
529
|
+
* and the anchors; the conditions are an intersection.
|
|
530
|
+
*
|
|
531
|
+
* - With a node scope the item must carry a node anchor, and every node
|
|
532
|
+
* anchor it carries must be in `scope.nodes`; an item with none is out.
|
|
533
|
+
* - Without one, an item with a node anchor is out and one with none is
|
|
534
|
+
* judged by the other conditions alone.
|
|
535
|
+
* - An operator anchor needs the session to run that operator, or a grant.
|
|
536
|
+
*/
|
|
537
|
+
declare function isInNodeProjection(item: Tagged, scope: MemoryScope, opts?: NodeProjectionOptions): boolean;
|
|
538
|
+
interface WriteScopeOptions {
|
|
539
|
+
/** Rule 32. Default `"operator"`; `"node"` adds no operator anchor. */
|
|
540
|
+
owner?: "operator" | "node";
|
|
541
|
+
/**
|
|
542
|
+
* The item this write replaces or supersedes. A document that names no
|
|
543
|
+
* node anchor keeps the basis's, and a document that names no owner keeps
|
|
544
|
+
* the basis's kind of ownership, so an edit never moves or hands over an
|
|
545
|
+
* item by default (rule 34: nothing promotes an item by itself).
|
|
546
|
+
*/
|
|
547
|
+
basis?: Tagged;
|
|
548
|
+
/** Rule 33: the host declares trees, so a document with no node anchor is refused. */
|
|
549
|
+
declaresTrees?: boolean;
|
|
550
|
+
}
|
|
551
|
+
/**
|
|
552
|
+
* The document a write stores: the node and operator anchors added by
|
|
553
|
+
* default (rules 31 to 33), or a validation error naming `tags`. `scope`
|
|
554
|
+
* absent is a host call (rule 30): nothing is added or confined, but a
|
|
555
|
+
* store whose host declares trees still refuses a document with no node
|
|
556
|
+
* anchor (rule 33).
|
|
557
|
+
*/
|
|
558
|
+
declare function applyWriteScope(item: MemoryDocument, scope: MemoryScope | undefined, opts?: WriteScopeOptions): MemoryDocument;
|
|
559
|
+
/**
|
|
560
|
+
* Rule 34. A commit that moves an item to another node changes that anchor
|
|
561
|
+
* and nothing else: not the operator anchor, not the other anchors. (That
|
|
562
|
+
* the new node is in `scope.nodes` is `applyWriteScope`'s check; a node above
|
|
563
|
+
* a sealed or hard wall never is, the projection being cut there.)
|
|
564
|
+
*/
|
|
565
|
+
declare function assertRetagOnly(before: Tagged, after: Tagged): void;
|
|
566
|
+
|
|
567
|
+
/**
|
|
568
|
+
* One hit of a knowledge provider, as much of the AIP-10
|
|
569
|
+
* `KnowledgeHit` as the store reads.
|
|
570
|
+
*/
|
|
571
|
+
interface MemoryKnowledgeHit {
|
|
572
|
+
readonly sourceId: string;
|
|
573
|
+
readonly chunkId?: string;
|
|
574
|
+
readonly text: string;
|
|
575
|
+
readonly score: number;
|
|
576
|
+
/** `memoryId` (string) names the memory item the hit was indexed from. */
|
|
577
|
+
readonly metadata?: Readonly<Record<string, unknown>>;
|
|
578
|
+
}
|
|
579
|
+
/**
|
|
580
|
+
* The slice of an AIP-10 `IKnowledgeProvider` (`@agentproto/knowledge-engine`)
|
|
581
|
+
* the store delegates search to. Declared structurally so this package
|
|
582
|
+
* takes no dependency on the knowledge engine: any `IKnowledgeProvider`
|
|
583
|
+
* fits as is.
|
|
584
|
+
*/
|
|
585
|
+
interface MemoryKnowledgeProvider {
|
|
586
|
+
query(q: {
|
|
587
|
+
readonly query: string;
|
|
588
|
+
readonly topK?: number;
|
|
589
|
+
}): Promise<{
|
|
590
|
+
readonly hits: readonly MemoryKnowledgeHit[];
|
|
591
|
+
}>;
|
|
592
|
+
}
|
|
593
|
+
interface FilesMemoryStoreOptions {
|
|
594
|
+
/** The store directory: `items/<type>/<id>.md` and `proposals/<id>.md` below it. */
|
|
595
|
+
root: string;
|
|
596
|
+
/**
|
|
597
|
+
* Optional search delegate. Its hits are merged with the built-in
|
|
598
|
+
* keyword match: a hit naming an item of this store is checked against
|
|
599
|
+
* the files (rule 22), anything else comes back as `source: "knowledge"`.
|
|
600
|
+
*/
|
|
601
|
+
knowledge?: MemoryKnowledgeProvider;
|
|
602
|
+
/** The store's clock: `updated_at` on commits, and the in-force check. */
|
|
603
|
+
now?: () => Date;
|
|
604
|
+
/**
|
|
605
|
+
* The host declares AIP-34 trees (section 10, rule 33): a host call that
|
|
606
|
+
* creates an item or a proposal with no node anchor is refused. Off: the
|
|
607
|
+
* store behaves as sections 8 and 9 alone.
|
|
608
|
+
*/
|
|
609
|
+
declaresTrees?: boolean;
|
|
610
|
+
}
|
|
611
|
+
/**
|
|
612
|
+
* AIP-72 section 8 reference store over a directory:
|
|
613
|
+
*
|
|
614
|
+
* ```
|
|
615
|
+
* <root>/items/<type>/<id>.md one memory/v1 item per file
|
|
616
|
+
* <root>/proposals/<sha256>.md pending proposals, not items
|
|
617
|
+
* ```
|
|
618
|
+
*
|
|
619
|
+
* The files are the source of truth and the store keeps no index.
|
|
620
|
+
* `revision` is the SHA-256 (hex) of the file bytes, a proposal id the
|
|
621
|
+
* SHA-256 of the rendered proposal. Files are written to a temp file
|
|
622
|
+
* then renamed into place. Item files are never deleted: a type change
|
|
623
|
+
* renames the file, supersede and archive rewrite the `status` line
|
|
624
|
+
* only. Mutations through one instance are serialized, so a
|
|
625
|
+
* compare-and-set cannot interleave with another write of the same
|
|
626
|
+
* store object; several processes writing one directory are not
|
|
627
|
+
* coordinated.
|
|
628
|
+
*/
|
|
629
|
+
declare class FilesMemoryStore implements MemoryStore {
|
|
630
|
+
#private;
|
|
631
|
+
constructor(options: FilesMemoryStoreOptions);
|
|
632
|
+
write(item: MemoryDocument, opts: MemoryWriteOptions): Promise<MemoryRecord>;
|
|
633
|
+
read(id: string, opts?: {
|
|
634
|
+
scope?: MemoryScope;
|
|
635
|
+
}): Promise<MemoryRecord | null>;
|
|
636
|
+
list(filter?: MemoryFilter): Promise<readonly MemoryRecord[]>;
|
|
637
|
+
search(query: string, opts?: MemorySearchOptions): Promise<readonly MemorySearchHit[]>;
|
|
638
|
+
supersede(oldId: string, next: MemoryDocument, opts?: {
|
|
639
|
+
ifRevision?: string;
|
|
640
|
+
scope?: MemoryScope;
|
|
641
|
+
}): Promise<{
|
|
642
|
+
old: MemoryRecord;
|
|
643
|
+
next: MemoryRecord;
|
|
644
|
+
}>;
|
|
645
|
+
archive(id: string, opts?: {
|
|
646
|
+
ifRevision?: string;
|
|
647
|
+
scope?: MemoryScope;
|
|
648
|
+
}): Promise<MemoryRecord>;
|
|
649
|
+
dismiss(proposalId: string, opts?: {
|
|
650
|
+
scope?: MemoryScope;
|
|
651
|
+
}): Promise<void>;
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
/**
|
|
655
|
+
* @agentproto/memory: AIP-72 memory/v1 reference implementation.
|
|
656
|
+
*
|
|
657
|
+
* One small typed assertion (user, feedback, project, reference) about a
|
|
658
|
+
* principal or its work, with a lifecycle, stored as a markdown file
|
|
659
|
+
* with YAML frontmatter.
|
|
660
|
+
*
|
|
661
|
+
* Spec: https://agentproto.sh/docs/aip-72
|
|
662
|
+
*
|
|
663
|
+
* Authoring paths:
|
|
664
|
+
* - TS: `defineMemory({...})` -> frozen `MemoryHandle` (writer, canonical)
|
|
665
|
+
* - MD: `parseMemory(src)` -> `MemoryDocument`, then `validateMemory(doc)`
|
|
666
|
+
* - out: `renderMemory(doc)` -> MEMORY.md text
|
|
667
|
+
*
|
|
668
|
+
* Store (section 8): the `MemoryStore` port and `FilesMemoryStore`, the
|
|
669
|
+
* reference layout (`items/<type>/<id>.md`, `proposals/<id>.md`).
|
|
670
|
+
*
|
|
671
|
+
* Node scope (section 10): `MemoryScope`, the `node:` and `operator:`
|
|
672
|
+
* anchors, and the projection and write rules over a scope the host
|
|
673
|
+
* computed (`isInNodeProjection`, `applyWriteScope`).
|
|
674
|
+
*/
|
|
675
|
+
declare const SPEC_NAME: "memory/v1";
|
|
676
|
+
declare const SPEC_VERSION: "1.0.0-alpha";
|
|
677
|
+
|
|
678
|
+
export { FilesMemoryStore, type FilesMemoryStoreOptions, type IMemoryStore, MEMORY_STATUSES, MEMORY_TYPES, type MemoryAnchor, type MemoryCapture, MemoryConflictError, type MemoryDecidedBy, type MemoryDefinition, type MemoryDocument, type MemoryEvidence, type MemoryFilter, type MemoryFrontmatter, type MemoryHandle, type MemoryIssue, type MemoryKnowledgeHit, type MemoryKnowledgeProvider, type MemoryLifetime, MemoryNotFoundError, MemoryParseError, type MemoryProvenance, type MemoryRecord, type MemoryScope, type MemorySearchHit, type MemorySearchOptions, MemorySlugTakenError, type MemoryStatus, type MemoryStore, MemoryStoreError, type MemoryStoreErrorCode, MemoryTransitionError, type MemoryType, MemoryValidationError, type MemoryValidationResult, type MemoryWriteOptions, type NodeProjectionOptions, SPEC_NAME, SPEC_VERSION, type ValidateMemoryOptions, type WriteScopeOptions, applyWriteScope, assertRetagOnly, defineMemory, isAllowedStatusTransition, isInForce, isInNodeProjection, isNodeOrOperatorTag, memoryFrontmatterSchema, memoryStatus, nodeAnchorIds, nodeTag, operatorAnchorIds, operatorTag, parseMemory, renderMemory, validateMemory };
|