@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.
@@ -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 };