@agentproto/memory 0.0.0-stage → 0.0.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 ADDED
@@ -0,0 +1,202 @@
1
+
2
+ Apache License
3
+ Version 2.0, January 2004
4
+ http://www.apache.org/licenses/
5
+
6
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
7
+
8
+ 1. Definitions.
9
+
10
+ "License" shall mean the terms and conditions for use, reproduction,
11
+ and distribution as defined by Sections 1 through 9 of this document.
12
+
13
+ "Licensor" shall mean the copyright owner or entity authorized by
14
+ the copyright owner that is granting the License.
15
+
16
+ "Legal Entity" shall mean the union of the acting entity and all
17
+ other entities that control, are controlled by, or are under common
18
+ control with that entity. For the purposes of this definition,
19
+ "control" means (i) the power, direct or indirect, to cause the
20
+ direction or management of such entity, whether by contract or
21
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
22
+ outstanding shares, or (iii) beneficial ownership of such entity.
23
+
24
+ "You" (or "Your") shall mean an individual or Legal Entity
25
+ exercising permissions granted by this License.
26
+
27
+ "Source" form shall mean the preferred form for making modifications,
28
+ including but not limited to software source code, documentation
29
+ source, and configuration files.
30
+
31
+ "Object" form shall mean any form resulting from mechanical
32
+ transformation or translation of a Source form, including but
33
+ not limited to compiled object code, generated documentation,
34
+ and conversions to other media types.
35
+
36
+ "Work" shall mean the work of authorship, whether in Source or
37
+ Object form, made available under the License, as indicated by a
38
+ copyright notice that is included in or attached to the work
39
+ (an example is provided in the Appendix below).
40
+
41
+ "Derivative Works" shall mean any work, whether in Source or Object
42
+ form, that is based on (or derived from) the Work and for which the
43
+ editorial revisions, annotations, elaborations, or other modifications
44
+ represent, as a whole, an original work of authorship. For the purposes
45
+ of this License, Derivative Works shall not include works that remain
46
+ separable from, or merely link (or bind by name) to the interfaces of,
47
+ the Work and Derivative Works thereof.
48
+
49
+ "Contribution" shall mean any work of authorship, including
50
+ the original version of the Work and any modifications or additions
51
+ to that Work or Derivative Works thereof, that is intentionally
52
+ submitted to Licensor for inclusion in the Work by the copyright owner
53
+ or by an individual or Legal Entity authorized to submit on behalf of
54
+ the copyright owner. For the purposes of this definition, "submitted"
55
+ means any form of electronic, verbal, or written communication sent
56
+ to the Licensor or its representatives, including but not limited to
57
+ communication on electronic mailing lists, source code control systems,
58
+ and issue tracking systems that are managed by, or on behalf of, the
59
+ Licensor for the purpose of discussing and improving the Work, but
60
+ excluding communication that is conspicuously marked or otherwise
61
+ designated in writing by the copyright owner as "Not a Contribution."
62
+
63
+ "Contributor" shall mean Licensor and any individual or Legal Entity
64
+ on behalf of whom a Contribution has been received by Licensor and
65
+ subsequently incorporated within the Work.
66
+
67
+ 2. Grant of Copyright License. Subject to the terms and conditions of
68
+ this License, each Contributor hereby grants to You a perpetual,
69
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
70
+ copyright license to reproduce, prepare Derivative Works of,
71
+ publicly display, publicly perform, sublicense, and distribute the
72
+ Work and such Derivative Works in Source or Object form.
73
+
74
+ 3. Grant of Patent License. Subject to the terms and conditions of
75
+ this License, each Contributor hereby grants to You a perpetual,
76
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
77
+ (except as stated in this section) patent license to make, have made,
78
+ use, offer to sell, sell, import, and otherwise transfer the Work,
79
+ where such license applies only to those patent claims licensable
80
+ by such Contributor that are necessarily infringed by their
81
+ Contribution(s) alone or by combination of their Contribution(s)
82
+ with the Work to which such Contribution(s) was submitted. If You
83
+ institute patent litigation against any entity (including a
84
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
85
+ or a Contribution incorporated within the Work constitutes direct
86
+ or contributory patent infringement, then any patent licenses
87
+ granted to You under this License for that Work shall terminate
88
+ as of the date such litigation is filed.
89
+
90
+ 4. Redistribution. You may reproduce and distribute copies of the
91
+ Work or Derivative Works thereof in any medium, with or without
92
+ modifications, and in Source or Object form, provided that You
93
+ meet the following conditions:
94
+
95
+ (a) You must give any other recipients of the Work or
96
+ Derivative Works a copy of this License; and
97
+
98
+ (b) You must cause any modified files to carry prominent notices
99
+ stating that You changed the files; and
100
+
101
+ (c) You must retain, in the Source form of any Derivative Works
102
+ that You distribute, all copyright, patent, trademark, and
103
+ attribution notices from the Source form of the Work,
104
+ excluding those notices that do not pertain to any part of
105
+ the Derivative Works; and
106
+
107
+ (d) If the Work includes a "NOTICE" text file as part of its
108
+ distribution, then any Derivative Works that You distribute must
109
+ include a readable copy of the attribution notices contained
110
+ within such NOTICE file, excluding those notices that do not
111
+ pertain to any part of the Derivative Works, in at least one
112
+ of the following places: within a NOTICE text file distributed
113
+ as part of the Derivative Works; within the Source form or
114
+ documentation, if provided along with the Derivative Works; or,
115
+ within a display generated by the Derivative Works, if and
116
+ wherever such third-party notices normally appear. The contents
117
+ of the NOTICE file are for informational purposes only and
118
+ do not modify the License. You may add Your own attribution
119
+ notices within Derivative Works that You distribute, alongside
120
+ or as an addendum to the NOTICE text from the Work, provided
121
+ that such additional attribution notices cannot be construed
122
+ as modifying the License.
123
+
124
+ You may add Your own copyright statement to Your modifications and
125
+ may provide additional or different license terms and conditions
126
+ for use, reproduction, or distribution of Your modifications, or
127
+ for any such Derivative Works as a whole, provided Your use,
128
+ reproduction, and distribution of the Work otherwise complies with
129
+ the conditions stated in this License.
130
+
131
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
132
+ any Contribution intentionally submitted for inclusion in the Work
133
+ by You to the Licensor shall be under the terms and conditions of
134
+ this License, without any additional terms or conditions.
135
+ Notwithstanding the above, nothing herein shall supersede or modify
136
+ the terms of any separate license agreement you may have executed
137
+ with Licensor regarding such Contributions.
138
+
139
+ 6. Trademarks. This License does not grant permission to use the trade
140
+ names, trademarks, service marks, or product names of the Licensor,
141
+ except as required for reasonable and customary use in describing the
142
+ origin of the Work and reproducing the content of the NOTICE file.
143
+
144
+ 7. Disclaimer of Warranty. Unless required by applicable law or
145
+ agreed to in writing, Licensor provides the Work (and each
146
+ Contributor provides its Contributions) on an "AS IS" BASIS,
147
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
148
+ implied, including, without limitation, any warranties or conditions
149
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
150
+ PARTICULAR PURPOSE. You are solely responsible for determining the
151
+ appropriateness of using or redistributing the Work and assume any
152
+ risks associated with Your exercise of permissions under this License.
153
+
154
+ 8. Limitation of Liability. In no event and under no legal theory,
155
+ whether in tort (including negligence), contract, or otherwise,
156
+ unless required by applicable law (such as deliberate and grossly
157
+ negligent acts) or agreed to in writing, shall any Contributor be
158
+ liable to You for damages, including any direct, indirect, special,
159
+ incidental, or consequential damages of any character arising as a
160
+ result of this License or out of the use or inability to use the
161
+ Work (including but not limited to damages for loss of goodwill,
162
+ work stoppage, computer failure or malfunction, or any and all
163
+ other commercial damages or losses), even if such Contributor
164
+ has been advised of the possibility of such damages.
165
+
166
+ 9. Accepting Warranty or Additional Liability. While redistributing
167
+ the Work or Derivative Works thereof, You may choose to offer,
168
+ and charge a fee for, acceptance of support, warranty, indemnity,
169
+ or other liability obligations and/or rights consistent with this
170
+ License. However, in accepting such obligations, You may act only
171
+ on Your own behalf and on Your sole responsibility, not on behalf
172
+ of any other Contributor, and only if You agree to indemnify,
173
+ defend, and hold each Contributor harmless for any liability
174
+ incurred by, or claims asserted against, such Contributor by reason
175
+ of your accepting any such warranty or additional liability.
176
+
177
+ END OF TERMS AND CONDITIONS
178
+
179
+ APPENDIX: How to apply the Apache License to your work.
180
+
181
+ To apply the Apache License to your work, attach the following
182
+ boilerplate notice, with the fields enclosed by brackets "[]"
183
+ replaced with your own identifying information. (Don't include
184
+ the brackets!) The text should be enclosed in the appropriate
185
+ comment syntax for the file format. We also recommend that a
186
+ file or class name and description of purpose be included on the
187
+ same "printed page" as the copyright notice for easier
188
+ identification within third-party archives.
189
+
190
+ Copyright [yyyy] [name of copyright owner]
191
+
192
+ Licensed under the Apache License, Version 2.0 (the "License");
193
+ you may not use this file except in compliance with the License.
194
+ You may obtain a copy of the License at
195
+
196
+ http://www.apache.org/licenses/LICENSE-2.0
197
+
198
+ Unless required by applicable law or agreed to in writing, software
199
+ distributed under the License is distributed on an "AS IS" BASIS,
200
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
201
+ See the License for the specific language governing permissions and
202
+ limitations under the License.
package/README.md CHANGED
@@ -1,3 +1,50 @@
1
- # Temporary Holding Version
1
+ # @agentproto/memory
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ AIP-72 `memory/v1` reference implementation: define, validate, parse and
4
+ render memory items. A memory item is one small typed assertion (`user`,
5
+ `feedback`, `project`, `reference`) about a principal or its work, with a
6
+ lifecycle (`active`, then `superseded` or `archived`), stored as markdown
7
+ with YAML frontmatter.
8
+
9
+ Spec: <https://agentproto.sh/docs/aip-72>. This package covers the
10
+ document only; a store port and per-harness projection come later.
11
+
12
+ ## Usage
13
+
14
+ ```ts
15
+ import {
16
+ defineMemory,
17
+ parseMemory,
18
+ renderMemory,
19
+ validateMemory,
20
+ } from "@agentproto/memory"
21
+
22
+ // Writer path: canonical fields required, throws MemoryValidationError
23
+ // (with `.field` and `.issues`) on any invalid field.
24
+ const item = defineMemory({
25
+ schema: "memory/v1",
26
+ slug: "cite-sources",
27
+ kind: "feedback",
28
+ type: "feedback",
29
+ title: "Cite sources",
30
+ updated_at: "2026-10-13T11:30:00.000Z",
31
+ polarity: "do",
32
+ provenance: { source: "converse", at: "2026-10-13T11:00:00.000Z" },
33
+ body: "Always cite sources.\n",
34
+ })
35
+
36
+ const md = renderMemory(item) // every string double-quoted
37
+ const doc = parseMemory(md) // YAML 1.2 core, does not validate
38
+
39
+ // Reader path: legacy items (id, name, type) are accepted.
40
+ const { ok, errors, warnings } = validateMemory(doc)
41
+ // errors: [{ path: "provenance.sessionId", message, rule: "schema" }, ...]
42
+ ```
43
+
44
+ `defineMemory` fills in only `status: "active"` (a writer creates an item
45
+ active) and an absent `body` as `""`. Lifecycle helpers: `memoryStatus`,
46
+ `isAllowedStatusTransition`, `isInForce`.
47
+
48
+ ## License
49
+
50
+ Apache-2.0. See [LICENSE](./LICENSE).
@@ -0,0 +1,365 @@
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";
20
+ /** Anchor target slug (project, contact, agent). */
21
+ slug?: string;
22
+ /** Anchor target id (task, session). */
23
+ id?: string;
24
+ /** Repository `owner/name` for a pr anchor. */
25
+ repo?: string;
26
+ /** Pull-request number for a pr anchor. */
27
+ number?: number;
28
+ }
29
+ interface MemoryLifetime {
30
+ kind: "durable" | "until" | "while" | "session";
31
+ /** Expiry timestamp, REQUIRED for kind `until`. */
32
+ at?: string;
33
+ /** Governing anchor, REQUIRED for kind `while`. */
34
+ anchor?: MemoryAnchor;
35
+ }
36
+ interface MemoryDecidedBy {
37
+ rule?: string;
38
+ source?: string;
39
+ }
40
+ interface MemoryProvenance {
41
+ source?: string;
42
+ /** Omitted, never empty, when the source has no session. */
43
+ sessionId?: string;
44
+ /** Omitted, never empty, when the source has none. */
45
+ ref?: string;
46
+ quote?: string;
47
+ at?: string;
48
+ extractorVersion?: string;
49
+ }
50
+ interface MemoryCapture {
51
+ mode?: "live" | "batch";
52
+ producer?: string;
53
+ capturedAt?: string;
54
+ }
55
+ interface MemoryEvidence {
56
+ sourceKind?: string;
57
+ trust?: string;
58
+ explicitness?: string;
59
+ corroborations?: number;
60
+ contradictsActive?: boolean;
61
+ impact?: string;
62
+ lifetime?: MemoryLifetime;
63
+ anchorsResolved?: string[];
64
+ ageHours?: number;
65
+ flags?: string[];
66
+ }
67
+ /**
68
+ * The YAML frontmatter of a memory/v1 item. Every field is optional at
69
+ * the type level: the schema accepts either the canonical AIP-10 base
70
+ * fields (`schema`, `slug`, `kind`, `title`, `updated_at`) or the legacy
71
+ * trio (`id`, `name`, `type`), which `validateMemory` checks.
72
+ */
73
+ interface MemoryFrontmatter {
74
+ schema?: "memory/v1";
75
+ slug?: string;
76
+ kind?: string;
77
+ title?: string;
78
+ updated_at?: string;
79
+ id?: string;
80
+ name?: string;
81
+ type?: MemoryType;
82
+ description?: string;
83
+ tags?: string[];
84
+ polarity?: "do" | "dont";
85
+ lifetime?: MemoryLifetime;
86
+ status?: MemoryStatus;
87
+ created?: string;
88
+ written?: string;
89
+ decidedBy?: MemoryDecidedBy;
90
+ provenance?: MemoryProvenance;
91
+ evidence?: MemoryEvidence;
92
+ capture?: MemoryCapture;
93
+ }
94
+ /** One memory/v1 item: frontmatter fields plus the markdown body. */
95
+ interface MemoryDocument extends MemoryFrontmatter {
96
+ body: string;
97
+ }
98
+ /** `defineMemory` input: `body` may be omitted for an item with no prose. */
99
+ type MemoryDefinition = MemoryFrontmatter & {
100
+ body?: string;
101
+ };
102
+ /** The frozen item `defineMemory` returns, with `status` made explicit. */
103
+ type MemoryHandle = Readonly<MemoryDocument & {
104
+ status: MemoryStatus;
105
+ }>;
106
+ /**
107
+ * One validation finding. `path` is the dot path of the offending field
108
+ * (`provenance.sessionId`, `lifetime.at`, `tags.0`), `""` for the whole
109
+ * document. `rule` is `schema` for a MEMORY.schema.json constraint,
110
+ * `section-2` for the memory/v1 `kind` set, or `rule-<n>` for AIP-72
111
+ * conformance rule n.
112
+ */
113
+ interface MemoryIssue {
114
+ path: string;
115
+ message: string;
116
+ rule: string;
117
+ }
118
+ interface MemoryValidationResult {
119
+ ok: boolean;
120
+ errors: MemoryIssue[];
121
+ /** SHOULD-level findings (rule 8). Never make `ok` false. */
122
+ warnings: MemoryIssue[];
123
+ }
124
+
125
+ /**
126
+ * AIP-72 reference implementation of `defineMemory`: the writer path.
127
+ *
128
+ * Validates in canonical mode (rule 1: a writer sets `schema`, `slug`,
129
+ * `kind`, `title`, `updated_at` on every item it creates) and throws a
130
+ * `MemoryValidationError` whose `field` / `issues` name the offending
131
+ * field paths. Only two values are filled in: `status` becomes `active`
132
+ * when absent (section 3: a writer creates an item `active`), and an
133
+ * absent `body` becomes `""`. Nothing else is defaulted; in particular
134
+ * `tags` is not set to `[]`, since rule 4 omits an empty optional field.
135
+ *
136
+ * Built on `createDoctype` for the shared slug check and top-level
137
+ * freeze; the handle is frozen deeply and does not alias the input.
138
+ */
139
+ declare function defineMemory(input: MemoryDefinition): MemoryHandle;
140
+
141
+ interface ValidateMemoryOptions {
142
+ /**
143
+ * Require the canonical fields (`schema`, `slug`, `kind`, `title`,
144
+ * `updated_at`), as rule 1 asks of a writer creating an item. Off by
145
+ * default: a reader MUST accept legacy items (rule 9).
146
+ */
147
+ canonical?: boolean;
148
+ }
149
+ /**
150
+ * Validate one memory/v1 document (frontmatter fields plus `body`).
151
+ *
152
+ * Runs MEMORY.schema.json (through the generated zod, plus the
153
+ * `anyOf` and lifetime `if/then` branches the codegen cannot express)
154
+ * and the AIP-72 conformance rules checkable on a single document:
155
+ * section 2 (`kind` set), rules 1, 2, 4 and 5 as errors, rule 8 as
156
+ * warnings. Rules 6 and 7 compare two versions of an item and belong to
157
+ * the store; `isAllowedStatusTransition` covers rule 7's table.
158
+ */
159
+ declare function validateMemory(doc: unknown, opts?: ValidateMemoryOptions): MemoryValidationResult;
160
+
161
+ /**
162
+ * Split a MEMORY.md file into its frontmatter fields and `body`.
163
+ *
164
+ * The frontmatter is read as YAML 1.2 core schema, so a date-time stays
165
+ * a string and an unquoted `true`, `null` or `2026` in a string field
166
+ * reaches `validateMemory` as a non-string and fails there (rule 3).
167
+ * This only parses: it does not validate, so a reader can still load an
168
+ * item it will then inspect. The body is free (rule 12): one blank line
169
+ * after the closing fence is the separator, everything after it is kept
170
+ * byte for byte.
171
+ */
172
+ declare function parseMemory(markdown: string): MemoryDocument;
173
+ /**
174
+ * Render a memory/v1 document as MEMORY.md. Throws a
175
+ * `MemoryValidationError` if the document does not validate.
176
+ *
177
+ * Every string value is double-quoted, so a YAML 1.1 or 1.2 reader
178
+ * reads it back as a string (rule 3); undefined fields are omitted
179
+ * (rule 4); key order is the document's own, so a status-only rewrite
180
+ * changes only the `status` line (rule 6). `parseMemory(renderMemory(d))`
181
+ * deep-equals `d`.
182
+ */
183
+ declare function renderMemory(doc: MemoryDocument): string;
184
+
185
+ /** The item's status, reading an absent `status` as `active` (rule 9). */
186
+ declare function memoryStatus(doc: Pick<MemoryFrontmatter, "status">): MemoryStatus;
187
+ /**
188
+ * Rule 7: a writer only moves `status` from `active` (or absent) to
189
+ * `superseded` or `archived`, and never out of either. Rewriting the
190
+ * same status is not a transition and is allowed.
191
+ */
192
+ declare function isAllowedStatusTransition(from: MemoryStatus | undefined, to: MemoryStatus): boolean;
193
+ /**
194
+ * Whether a reader may present the item as in force at `now`.
195
+ *
196
+ * Rule 10: a `superseded` or `archived` item is not. Rule 11: an
197
+ * `until` lifetime past its `at` is not. A `while` anchor or a
198
+ * `session` lifetime needs context outside the document (does the
199
+ * anchor still hold, which session is current), so this check treats
200
+ * them as holding; the store that has that context narrows further.
201
+ */
202
+ declare function isInForce(doc: Pick<MemoryFrontmatter, "status" | "lifetime">, now?: Date): boolean;
203
+
204
+ /** Thrown by `defineMemory` and `renderMemory` on an invalid item. */
205
+ declare class MemoryValidationError extends Error {
206
+ /** Every error found, each naming its field path. */
207
+ readonly issues: readonly MemoryIssue[];
208
+ /** Path of the first offending field (`""` for the whole document). */
209
+ readonly field: string;
210
+ constructor(prefix: string, issues: readonly MemoryIssue[]);
211
+ }
212
+ /** Thrown by `parseMemory` when the markdown has no readable frontmatter. */
213
+ declare class MemoryParseError extends Error {
214
+ constructor(message: string);
215
+ }
216
+
217
+ /**
218
+ * AIP-72 memory/v1 frontmatter zod schema.
219
+ *
220
+ * Codegen output of `scripts/scaffold-aip.mjs --aip 72 --slug memory
221
+ * --doctype MEMORY --schema-only --inline-refs` over
222
+ * `specs/resources/aip-72/draft/MEMORY.schema.json`. Do not edit the code
223
+ * below by hand: change the JSON draft, then regenerate.
224
+ * `scripts/check-spec-drift.mjs` fails CI when the code drifts from the
225
+ * draft (it does not compare this comment).
226
+ *
227
+ * Only `validate.ts` imports this schema. `validateMemory` runs it, and
228
+ * `defineMemory` (`define-memory.ts`) and `renderMemory` (`markdown.ts`)
229
+ * validate through `validateMemory`. `index.ts` re-exports it.
230
+ *
231
+ * What the codegen cannot express also lives in `validate.ts`: the
232
+ * root `anyOf` (canonical fields or the legacy id/name/type trio), the
233
+ * `$defs.lifetime` if/then rules (`until` needs `at`, `while` needs
234
+ * `anchor`), the unknown-key check on the root and `lifetime` objects
235
+ * (zod 4 drops it inside the `.and(...)` intersections below), and the
236
+ * AIP-72 conformance rules that apply to a single document.
237
+ */
238
+
239
+ declare const memoryFrontmatterSchema: z.ZodIntersection<z.ZodObject<{
240
+ schema: z.ZodOptional<z.ZodLiteral<"memory/v1">>;
241
+ slug: z.ZodOptional<z.ZodString>;
242
+ kind: z.ZodOptional<z.ZodString>;
243
+ title: z.ZodOptional<z.ZodString>;
244
+ updated_at: z.ZodOptional<z.ZodString>;
245
+ id: z.ZodOptional<z.ZodString>;
246
+ name: z.ZodOptional<z.ZodString>;
247
+ type: z.ZodOptional<z.ZodEnum<{
248
+ user: "user";
249
+ feedback: "feedback";
250
+ project: "project";
251
+ reference: "reference";
252
+ }>>;
253
+ description: z.ZodOptional<z.ZodString>;
254
+ tags: z.ZodDefault<z.ZodArray<z.ZodString>>;
255
+ polarity: z.ZodOptional<z.ZodEnum<{
256
+ do: "do";
257
+ dont: "dont";
258
+ }>>;
259
+ lifetime: z.ZodOptional<z.ZodIntersection<z.ZodObject<{
260
+ kind: z.ZodEnum<{
261
+ session: "session";
262
+ durable: "durable";
263
+ until: "until";
264
+ while: "while";
265
+ }>;
266
+ at: z.ZodOptional<z.ZodString>;
267
+ anchor: z.ZodOptional<z.ZodObject<{
268
+ kind: z.ZodEnum<{
269
+ project: "project";
270
+ global: "global";
271
+ task: "task";
272
+ session: "session";
273
+ pr: "pr";
274
+ contact: "contact";
275
+ agent: "agent";
276
+ }>;
277
+ slug: z.ZodOptional<z.ZodString>;
278
+ id: z.ZodOptional<z.ZodString>;
279
+ repo: z.ZodOptional<z.ZodString>;
280
+ number: z.ZodOptional<z.ZodNumber>;
281
+ }, z.core.$strict>>;
282
+ }, z.core.$strict>, z.ZodIntersection<z.ZodAny, z.ZodAny>>>;
283
+ status: z.ZodOptional<z.ZodEnum<{
284
+ active: "active";
285
+ superseded: "superseded";
286
+ archived: "archived";
287
+ }>>;
288
+ created: z.ZodOptional<z.ZodString>;
289
+ written: z.ZodOptional<z.ZodString>;
290
+ decidedBy: z.ZodOptional<z.ZodObject<{
291
+ rule: z.ZodOptional<z.ZodString>;
292
+ source: z.ZodOptional<z.ZodString>;
293
+ }, z.core.$strict>>;
294
+ provenance: z.ZodOptional<z.ZodObject<{
295
+ source: z.ZodOptional<z.ZodString>;
296
+ sessionId: z.ZodOptional<z.ZodString>;
297
+ ref: z.ZodOptional<z.ZodString>;
298
+ quote: z.ZodOptional<z.ZodString>;
299
+ at: z.ZodOptional<z.ZodString>;
300
+ extractorVersion: z.ZodOptional<z.ZodString>;
301
+ }, z.core.$strict>>;
302
+ evidence: z.ZodOptional<z.ZodObject<{
303
+ sourceKind: z.ZodOptional<z.ZodString>;
304
+ trust: z.ZodOptional<z.ZodString>;
305
+ explicitness: z.ZodOptional<z.ZodString>;
306
+ corroborations: z.ZodOptional<z.ZodNumber>;
307
+ contradictsActive: z.ZodOptional<z.ZodBoolean>;
308
+ impact: z.ZodOptional<z.ZodString>;
309
+ lifetime: z.ZodOptional<z.ZodIntersection<z.ZodObject<{
310
+ kind: z.ZodEnum<{
311
+ session: "session";
312
+ durable: "durable";
313
+ until: "until";
314
+ while: "while";
315
+ }>;
316
+ at: z.ZodOptional<z.ZodString>;
317
+ anchor: z.ZodOptional<z.ZodObject<{
318
+ kind: z.ZodEnum<{
319
+ project: "project";
320
+ global: "global";
321
+ task: "task";
322
+ session: "session";
323
+ pr: "pr";
324
+ contact: "contact";
325
+ agent: "agent";
326
+ }>;
327
+ slug: z.ZodOptional<z.ZodString>;
328
+ id: z.ZodOptional<z.ZodString>;
329
+ repo: z.ZodOptional<z.ZodString>;
330
+ number: z.ZodOptional<z.ZodNumber>;
331
+ }, z.core.$strict>>;
332
+ }, z.core.$strict>, z.ZodIntersection<z.ZodAny, z.ZodAny>>>;
333
+ anchorsResolved: z.ZodDefault<z.ZodArray<z.ZodString>>;
334
+ ageHours: z.ZodOptional<z.ZodNumber>;
335
+ flags: z.ZodDefault<z.ZodArray<z.ZodString>>;
336
+ }, z.core.$strict>>;
337
+ capture: z.ZodOptional<z.ZodObject<{
338
+ mode: z.ZodOptional<z.ZodEnum<{
339
+ live: "live";
340
+ batch: "batch";
341
+ }>>;
342
+ producer: z.ZodOptional<z.ZodString>;
343
+ capturedAt: z.ZodOptional<z.ZodString>;
344
+ }, z.core.$strict>>;
345
+ body: z.ZodOptional<z.ZodString>;
346
+ }, z.core.$strict>, z.ZodUnion<readonly [z.ZodAny, z.ZodAny]>>;
347
+
348
+ /**
349
+ * @agentproto/memory: AIP-72 memory/v1 reference implementation.
350
+ *
351
+ * One small typed assertion (user, feedback, project, reference) about a
352
+ * principal or its work, with a lifecycle, stored as a markdown file
353
+ * with YAML frontmatter.
354
+ *
355
+ * Spec: https://agentproto.sh/docs/aip-72
356
+ *
357
+ * Authoring paths:
358
+ * - TS: `defineMemory({...})` -> frozen `MemoryHandle` (writer, canonical)
359
+ * - MD: `parseMemory(src)` -> `MemoryDocument`, then `validateMemory(doc)`
360
+ * - out: `renderMemory(doc)` -> MEMORY.md text
361
+ */
362
+ declare const SPEC_NAME: "memory/v1";
363
+ declare const SPEC_VERSION: "1.0.0-alpha";
364
+
365
+ export { MEMORY_STATUSES, MEMORY_TYPES, type MemoryAnchor, type MemoryCapture, type MemoryDecidedBy, type MemoryDefinition, type MemoryDocument, type MemoryEvidence, type MemoryFrontmatter, type MemoryHandle, type MemoryIssue, type MemoryLifetime, MemoryParseError, type MemoryProvenance, type MemoryStatus, type MemoryType, MemoryValidationError, type MemoryValidationResult, SPEC_NAME, SPEC_VERSION, type ValidateMemoryOptions, defineMemory, isAllowedStatusTransition, isInForce, memoryFrontmatterSchema, memoryStatus, parseMemory, renderMemory, validateMemory };
package/dist/index.mjs ADDED
@@ -0,0 +1,256 @@
1
+ import { createDoctype } from '@agentproto/doctype';
2
+ import { z } from 'zod';
3
+ import { parseDocument, stringify } from 'yaml';
4
+
5
+ /**
6
+ * @agentproto/memory
7
+ * AIP-72 memory/v1 `defineMemory` reference implementation.
8
+ */
9
+
10
+
11
+ // src/errors.ts
12
+ var MemoryValidationError = class extends Error {
13
+ /** Every error found, each naming its field path. */
14
+ issues;
15
+ /** Path of the first offending field (`""` for the whole document). */
16
+ field;
17
+ constructor(prefix, issues) {
18
+ super(
19
+ `${prefix}: ${issues.map((i) => `${i.path || "(document)"}: ${i.message}`).join("; ")}`
20
+ );
21
+ this.name = "MemoryValidationError";
22
+ this.issues = issues;
23
+ this.field = issues[0]?.path ?? "";
24
+ }
25
+ };
26
+ var MemoryParseError = class extends Error {
27
+ constructor(message) {
28
+ super(`parseMemory (AIP-72): ${message}`);
29
+ this.name = "MemoryParseError";
30
+ }
31
+ };
32
+ var memoryFrontmatterSchema = z.object({ "schema": z.literal("memory/v1").describe("Discriminator for a memory/v1 document. Optional only to tolerate legacy items written before this doctype existed; a canonical memory/v1 document MUST set it, and when present it MUST equal 'memory/v1'.").optional(), "slug": z.string().regex(new RegExp("^[a-z][a-z0-9-]*[a-z0-9]$")).min(2).max(96).describe("Stable kebab-case identifier (AIP-10 convention). Used as the item's path stem and cross-reference target.").optional(), "kind": z.string().regex(new RegExp("^[a-z][a-z0-9-]*$")).min(2).max(32).describe("Item kind (AIP-10 convention). Common values are the memory types: 'user', 'feedback', 'project', 'reference'.").optional(), "title": z.string().min(1).max(200).describe("Human-readable title (AIP-10 convention).").optional(), "updated_at": z.string().datetime({ offset: true }).describe("ISO 8601 timestamp of the last content write (AIP-10 convention). A writer MUST set it when it creates the item and when it changes any field other than `status`; a status-only lifecycle rewrite (to `superseded` or `archived`) MAY leave it unchanged.").optional(), "id": z.string().min(1).describe("Item identifier (e.g. 'mem_qc8f407g'). Legacy alias for the AIP-10 path stem; when the base fields are omitted it takes part in identifying the item.").optional(), "name": z.string().min(1).max(1e3).describe("Slugified name, distinct from `title`. Kept as a separate field so a legacy item round-trips without loss.").optional(), "type": z.enum(["user", "feedback", "project", "reference"]).describe("Memory type. The closed type set from the memory contract; `kind` is the AIP-10-aligned spelling of the same notion.").optional(), "description": z.string().max(4e3).describe("One-line human-readable summary, independent of the body.").optional(), "tags": z.array(z.string().min(1)).describe("Free-form labels. Scope anchors are carried here in their formatted form (e.g. 'global', 'project:fhcm', 'agent:assistant', 'pr:agentproto/ts#1858').").default([]), "polarity": z.enum(["do", "dont"]).describe("Feedback polarity. Present only on items of type 'feedback'.").optional(), "lifetime": z.object({ "kind": z.enum(["durable", "until", "while", "session"]).describe("Lifetime kind. 'durable' never expires; 'until' expires at `at`; 'while' lasts as long as `anchor` holds; 'session' lasts for the session."), "at": z.string().datetime({ offset: true }).describe("Expiry timestamp for kind 'until'.").optional(), "anchor": z.object({ "kind": z.enum(["global", "project", "task", "session", "pr", "contact", "agent"]).describe("Anchor kind. 'agent' extends the conversation-contract set for per-agent anchoring."), "slug": z.string().min(1).describe("Anchor target slug (project, contact, agent).").optional(), "id": z.string().min(1).describe("Anchor target id (task, session).").optional(), "repo": z.string().min(1).describe("Repository 'owner/name' for a pr anchor.").optional(), "number": z.number().int().gte(1).describe("Pull-request number for a pr anchor.").optional() }).strict().describe("Governing anchor for kind 'while'.").optional() }).strict().and(z.intersection(z.any(), z.any())).describe("How long the item stays valid. Typical values are { kind: 'durable' } or { kind: 'while', anchor: { \u2026 } }.").optional(), "status": z.enum(["active", "superseded", "archived"]).describe("Lifecycle status. A writer creates an item 'active'. 'superseded' means a newer item replaces it; 'archived' means it is retired without a replacement (decayed, or its anchor no longer resolves). Both are terminal: an item is marked, never erased. Absent means 'active'.").optional(), "created": z.string().datetime({ offset: true }).describe("Creation timestamp.").optional(), "written": z.string().datetime({ offset: true }).describe("Last-write timestamp. Mirrors the AIP-10 `updated_at`.").optional(), "decidedBy": z.object({ "rule": z.string().min(1).describe("Classifier rule that produced the item (e.g. 'lens-confident', 'brain-memory-file').").optional(), "source": z.string().min(1).describe("Where the deciding rule came from (e.g. 'builtin').").optional() }).strict().describe("Which classifier/rule decided the item's shape and status.").optional(), "provenance": z.object({ "source": z.string().min(1).describe("Provenance source class (e.g. 'brain-transcript', 'brain-memory-file', 'converse', 'task-close', 'recall-gap').").optional(), "sessionId": z.string().min(1).describe("Session the claim was observed in. Omitted, never empty, when the source has no session.").optional(), "ref": z.string().min(1).describe("Opaque reference into the source (message uuid, file path, ...). Omitted, never empty, when the source has none.").optional(), "quote": z.string().describe("Verbatim quote backing the claim.").optional(), "at": z.string().datetime({ offset: true }).describe("When the claim was observed.").optional(), "extractorVersion": z.string().min(1).describe("Identifier and version of the extractor that produced the claim (e.g. 'lens@3'), so a reader can tell items from different extractor generations apart.").optional() }).strict().describe("Origin of the item's claims.").optional(), "evidence": z.object({ "sourceKind": z.string().min(1).describe("Kind of source the evidence came from (e.g. 'brain-transcript', 'brain-memory-file').").optional(), "trust": z.string().min(1).describe("Trust class of the source (e.g. 'jeremy', 'internal').").optional(), "explicitness": z.string().min(1).describe("Whether the claim was stated outright or inferred (e.g. 'implicit', 'explicit').").optional(), "corroborations": z.number().int().gte(0).describe("Number of independent corroborating observations.").optional(), "contradictsActive": z.boolean().describe("True when this item contradicts an active item.").optional(), "impact": z.string().min(1).describe("Impact class of the item (e.g. 'low', 'high').").optional(), "lifetime": z.object({ "kind": z.enum(["durable", "until", "while", "session"]).describe("Lifetime kind. 'durable' never expires; 'until' expires at `at`; 'while' lasts as long as `anchor` holds; 'session' lasts for the session."), "at": z.string().datetime({ offset: true }).describe("Expiry timestamp for kind 'until'.").optional(), "anchor": z.object({ "kind": z.enum(["global", "project", "task", "session", "pr", "contact", "agent"]).describe("Anchor kind. 'agent' extends the conversation-contract set for per-agent anchoring."), "slug": z.string().min(1).describe("Anchor target slug (project, contact, agent).").optional(), "id": z.string().min(1).describe("Anchor target id (task, session).").optional(), "repo": z.string().min(1).describe("Repository 'owner/name' for a pr anchor.").optional(), "number": z.number().int().gte(1).describe("Pull-request number for a pr anchor.").optional() }).strict().describe("Governing anchor for kind 'while'.").optional() }).strict().and(z.intersection(z.any(), z.any())).describe("Lifetime as computed for the evidence pass.").optional(), "anchorsResolved": z.array(z.string().min(1)).describe("Resolved anchor labels in their formatted form (e.g. 'global', 'project:fhcm', 'pr:agentproto/ts#1858', 'session:sess_\u2026').").default([]), "ageHours": z.number().describe("Age of the evidence in hours (may be fractional or negative relative to the write clock).").optional(), "flags": z.array(z.string().min(1)).describe("Machine-stable flags raised during the evidence pass.").default([]) }).strict().describe("Trust/impact accounting captured at write time.").optional(), "capture": z.object({ "mode": z.enum(["live", "batch"]).describe("How the item was captured: 'live' during the session it describes, 'batch' by a later pass over stored transcripts or files.").optional(), "producer": z.string().min(1).describe("Who or what captured the item (e.g. a clerk lens, a migration, an importer). 'unknown' when the producer did not identify itself.").optional(), "capturedAt": z.string().datetime({ offset: true }).describe("ISO 8601 timestamp at which the source was captured or extracted.").optional() }).strict().describe("Who, what and how captured the item, kept apart from `provenance` (where the claim was observed).").optional(), "body": z.string().describe("Unrestricted markdown body of the MEMORY.md file. Not constrained by this schema.").optional() }).strict().and(z.union([z.any(), z.any()])).describe("Validates the YAML frontmatter of an AIP-72 memory/v1 item: one small typed assertion (user, feedback, project, reference) about a principal or its work, with a lifecycle. A third doctype in the memory layer: it neither absorbs nor replaces AIP-10 knowledge/v1 entries (curated, shareable wiki pages over immutable sources) nor AIP-11 lesson/v1 (distilled lessons from runs). A memory/v1 item carries the AIP-10 base fields (schema, slug, kind, title, updated_at); the remaining fields are the memory item shape and are OPTIONAL, so a writer that emits only the AIP-10 base fields and a writer that emits the full item both validate against the same schema. The markdown body (the `body` property) is unrestricted text.");
33
+
34
+ // src/types.ts
35
+ var MEMORY_TYPES = ["user", "feedback", "project", "reference"];
36
+ var MEMORY_STATUSES = ["active", "superseded", "archived"];
37
+
38
+ // src/validate.ts
39
+ var CANONICAL_FIELDS = ["schema", "slug", "kind", "title", "updated_at"];
40
+ var LEGACY_FIELDS = ["id", "name", "type"];
41
+ var ROOT_KEYS = Object.keys(memoryFrontmatterSchema.def.left.shape);
42
+ var LIFETIME_KEYS = Object.keys(
43
+ memoryFrontmatterSchema.def.left.shape.lifetime.unwrap().def.left.shape
44
+ );
45
+ function validateMemory(doc, opts = {}) {
46
+ const errors = [];
47
+ const warnings = [];
48
+ if (!isPlainObject(doc)) {
49
+ errors.push({ path: "", message: "must be an object", rule: "schema" });
50
+ return { ok: false, errors, warnings };
51
+ }
52
+ const parsed = memoryFrontmatterSchema.safeParse(doc);
53
+ if (!parsed.success) {
54
+ for (const issue of parsed.error.issues) {
55
+ const base = issue.path.map(String);
56
+ if (issue.code === "unrecognized_keys") {
57
+ for (const key of issue.keys) {
58
+ errors.push({
59
+ path: [...base, key].join("."),
60
+ message: "unknown field",
61
+ rule: "schema"
62
+ });
63
+ }
64
+ } else {
65
+ errors.push({ path: base.join("."), message: issue.message, rule: "schema" });
66
+ }
67
+ }
68
+ }
69
+ checkUnknownKeys(doc, ROOT_KEYS, "", errors);
70
+ const missingCanonical = CANONICAL_FIELDS.filter((f) => doc[f] === void 0);
71
+ const hasLegacy = LEGACY_FIELDS.every((f) => doc[f] !== void 0);
72
+ if (missingCanonical.length > 0 && (opts.canonical || !hasLegacy)) {
73
+ for (const field of missingCanonical) {
74
+ errors.push({
75
+ path: field,
76
+ message: opts.canonical ? "required on an item a writer creates" : "required (or carry the legacy id, name, type trio)",
77
+ rule: opts.canonical ? "rule-1" : "schema"
78
+ });
79
+ }
80
+ }
81
+ checkLifetime(doc.lifetime, "lifetime", errors);
82
+ if (isPlainObject(doc.evidence)) {
83
+ checkLifetime(doc.evidence.lifetime, "evidence.lifetime", errors);
84
+ }
85
+ if (typeof doc.kind === "string" && !MEMORY_TYPES.includes(doc.kind)) {
86
+ errors.push({
87
+ path: "kind",
88
+ message: `must be one of ${MEMORY_TYPES.join(", ")}`,
89
+ rule: "section-2"
90
+ });
91
+ }
92
+ if (doc.kind !== void 0 && doc.type !== void 0 && doc.kind !== doc.type) {
93
+ errors.push({
94
+ path: "kind",
95
+ message: `must equal type ('${String(doc.type)}') when both are present`,
96
+ rule: "rule-2"
97
+ });
98
+ }
99
+ const { body: _body, ...frontmatter } = doc;
100
+ for (const path of emptyValuePaths(frontmatter, [])) {
101
+ if (errors.some((e) => e.path === path)) continue;
102
+ errors.push({ path, message: "empty value: omit the field instead", rule: "rule-4" });
103
+ }
104
+ const type = doc.type ?? doc.kind;
105
+ if (doc.polarity !== void 0 && type !== "feedback") {
106
+ errors.push({
107
+ path: "polarity",
108
+ message: "only allowed on an item whose type is 'feedback'",
109
+ rule: "rule-5"
110
+ });
111
+ }
112
+ const provenance = isPlainObject(doc.provenance) ? doc.provenance : {};
113
+ for (const field of ["source", "at"]) {
114
+ if (provenance[field] === void 0) {
115
+ warnings.push({
116
+ path: `provenance.${field}`,
117
+ message: "should be recorded",
118
+ rule: "rule-8"
119
+ });
120
+ }
121
+ }
122
+ return { ok: errors.length === 0, errors, warnings };
123
+ }
124
+ function checkLifetime(value, path, errors) {
125
+ if (!isPlainObject(value)) return;
126
+ checkUnknownKeys(value, LIFETIME_KEYS, path, errors);
127
+ if (value.kind === "until" && value.at === void 0) {
128
+ errors.push({ path: `${path}.at`, message: "required when kind is 'until'", rule: "schema" });
129
+ }
130
+ if (value.kind === "while" && value.anchor === void 0) {
131
+ errors.push({
132
+ path: `${path}.anchor`,
133
+ message: "required when kind is 'while'",
134
+ rule: "schema"
135
+ });
136
+ }
137
+ }
138
+ function checkUnknownKeys(value, known, path, errors) {
139
+ for (const key of Object.keys(value)) {
140
+ const keyPath = path ? `${path}.${key}` : key;
141
+ if (known.includes(key) || errors.some((e) => e.path === keyPath)) continue;
142
+ errors.push({ path: keyPath, message: "unknown field", rule: "schema" });
143
+ }
144
+ }
145
+ function emptyValuePaths(value, path) {
146
+ if (value === "") return [path.join(".")];
147
+ if (Array.isArray(value)) {
148
+ return value.flatMap((v, i) => emptyValuePaths(v, [...path, String(i)]));
149
+ }
150
+ if (!isPlainObject(value)) return [];
151
+ const entries = Object.entries(value).filter(([, v]) => v !== void 0);
152
+ if (entries.length === 0 && path.length > 0) return [path.join(".")];
153
+ return entries.flatMap(([k, v]) => emptyValuePaths(v, [...path, k]));
154
+ }
155
+ function isPlainObject(value) {
156
+ return typeof value === "object" && value !== null && !Array.isArray(value);
157
+ }
158
+
159
+ // src/define-memory.ts
160
+ var SLUG_PATTERN = /^[a-z][a-z0-9-]*[a-z0-9]$/;
161
+ var constructMemory = createDoctype({
162
+ aip: 72,
163
+ name: "memory",
164
+ readIdentity: (def) => def.slug,
165
+ idPattern: SLUG_PATTERN,
166
+ // `description` is optional and up to 4000 chars; the schema checks it.
167
+ readDescription: false,
168
+ build(def) {
169
+ return deepFreeze({
170
+ ...structuredClone(def),
171
+ body: def.body ?? "",
172
+ status: def.status ?? "active"
173
+ });
174
+ }
175
+ });
176
+ function defineMemory(input) {
177
+ const result = validateMemory(input, { canonical: true });
178
+ if (!result.ok) {
179
+ throw new MemoryValidationError("defineMemory (AIP-72)", result.errors);
180
+ }
181
+ return constructMemory(input);
182
+ }
183
+ function deepFreeze(value) {
184
+ if (typeof value === "object" && value !== null) {
185
+ for (const v of Object.values(value)) deepFreeze(v);
186
+ Object.freeze(value);
187
+ }
188
+ return value;
189
+ }
190
+ var FRONTMATTER_RE = /^---[ \t]*\r?\n(?:([\s\S]*?)\r?\n)?---[ \t]*(?:\r?\n|$)/;
191
+ function parseMemory(markdown) {
192
+ const text = markdown.startsWith("\uFEFF") ? markdown.slice(1) : markdown;
193
+ const match = FRONTMATTER_RE.exec(text);
194
+ if (!match) {
195
+ throw new MemoryParseError("missing frontmatter: the file must open with a '---' fence");
196
+ }
197
+ const yamlDoc = parseDocument(match[1] ?? "", { version: "1.2", schema: "core" });
198
+ if (yamlDoc.errors.length > 0) {
199
+ throw new MemoryParseError(
200
+ `invalid YAML frontmatter: ${yamlDoc.errors.map((e) => e.message).join("; ")}`
201
+ );
202
+ }
203
+ const data = yamlDoc.toJS();
204
+ if (typeof data !== "object" || data === null || Array.isArray(data)) {
205
+ throw new MemoryParseError("frontmatter must be a YAML mapping");
206
+ }
207
+ if ("body" in data) {
208
+ throw new MemoryParseError("'body' is the markdown after the fence, not a frontmatter key");
209
+ }
210
+ let body = text.slice(match[0].length);
211
+ if (body.startsWith("\r\n")) body = body.slice(2);
212
+ else if (body.startsWith("\n")) body = body.slice(1);
213
+ return { ...data, body };
214
+ }
215
+ function renderMemory(doc) {
216
+ const result = validateMemory(doc);
217
+ if (!result.ok) {
218
+ throw new MemoryValidationError("renderMemory (AIP-72)", result.errors);
219
+ }
220
+ const { body, ...frontmatter } = doc;
221
+ const yamlText = stringify(frontmatter, {
222
+ version: "1.2",
223
+ defaultStringType: "QUOTE_DOUBLE",
224
+ defaultKeyType: "PLAIN",
225
+ lineWidth: 0
226
+ });
227
+ return `---
228
+ ${yamlText}---
229
+ ${body ? `
230
+ ${body}` : ""}`;
231
+ }
232
+
233
+ // src/lifecycle.ts
234
+ function memoryStatus(doc) {
235
+ return doc.status ?? "active";
236
+ }
237
+ function isAllowedStatusTransition(from, to) {
238
+ const current = from ?? "active";
239
+ return current === to || current === "active";
240
+ }
241
+ function isInForce(doc, now = /* @__PURE__ */ new Date()) {
242
+ if (memoryStatus(doc) !== "active") return false;
243
+ const lifetime = doc.lifetime;
244
+ if (lifetime?.kind === "until" && lifetime.at !== void 0) {
245
+ return Date.parse(lifetime.at) > now.getTime();
246
+ }
247
+ return true;
248
+ }
249
+
250
+ // src/index.ts
251
+ var SPEC_NAME = "memory/v1";
252
+ var SPEC_VERSION = "1.0.0-alpha";
253
+
254
+ export { MEMORY_STATUSES, MEMORY_TYPES, MemoryParseError, MemoryValidationError, SPEC_NAME, SPEC_VERSION, defineMemory, isAllowedStatusTransition, isInForce, memoryFrontmatterSchema, memoryStatus, parseMemory, renderMemory, validateMemory };
255
+ //# sourceMappingURL=index.mjs.map
256
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/errors.ts","../src/schema.ts","../src/types.ts","../src/validate.ts","../src/define-memory.ts","../src/markdown.ts","../src/lifecycle.ts","../src/index.ts"],"names":[],"mappings":"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","file":"index.mjs","sourcesContent":["import type { MemoryIssue } from \"./types.js\"\n\n/** Thrown by `defineMemory` and `renderMemory` on an invalid item. */\nexport class MemoryValidationError extends Error {\n /** Every error found, each naming its field path. */\n readonly issues: readonly MemoryIssue[]\n /** Path of the first offending field (`\"\"` for the whole document). */\n readonly field: string\n\n constructor(prefix: string, issues: readonly MemoryIssue[]) {\n super(\n `${prefix}: ${issues\n .map((i) => `${i.path || \"(document)\"}: ${i.message}`)\n .join(\"; \")}`,\n )\n this.name = \"MemoryValidationError\"\n this.issues = issues\n this.field = issues[0]?.path ?? \"\"\n }\n}\n\n/** Thrown by `parseMemory` when the markdown has no readable frontmatter. */\nexport class MemoryParseError extends Error {\n constructor(message: string) {\n super(`parseMemory (AIP-72): ${message}`)\n this.name = \"MemoryParseError\"\n }\n}\n","/**\n * AIP-72 memory/v1 frontmatter zod schema.\n *\n * Codegen output of `scripts/scaffold-aip.mjs --aip 72 --slug memory\n * --doctype MEMORY --schema-only --inline-refs` over\n * `specs/resources/aip-72/draft/MEMORY.schema.json`. Do not edit the code\n * below by hand: change the JSON draft, then regenerate.\n * `scripts/check-spec-drift.mjs` fails CI when the code drifts from the\n * draft (it does not compare this comment).\n *\n * Only `validate.ts` imports this schema. `validateMemory` runs it, and\n * `defineMemory` (`define-memory.ts`) and `renderMemory` (`markdown.ts`)\n * validate through `validateMemory`. `index.ts` re-exports it.\n *\n * What the codegen cannot express also lives in `validate.ts`: the\n * root `anyOf` (canonical fields or the legacy id/name/type trio), the\n * `$defs.lifetime` if/then rules (`until` needs `at`, `while` needs\n * `anchor`), the unknown-key check on the root and `lifetime` objects\n * (zod 4 drops it inside the `.and(...)` intersections below), and the\n * AIP-72 conformance rules that apply to a single document.\n */\n\nimport { z } from \"zod\"\n\nexport const memoryFrontmatterSchema = z.object({ \"schema\": z.literal(\"memory/v1\").describe(\"Discriminator for a memory/v1 document. Optional only to tolerate legacy items written before this doctype existed; a canonical memory/v1 document MUST set it, and when present it MUST equal 'memory/v1'.\").optional(), \"slug\": z.string().regex(new RegExp(\"^[a-z][a-z0-9-]*[a-z0-9]$\")).min(2).max(96).describe(\"Stable kebab-case identifier (AIP-10 convention). Used as the item's path stem and cross-reference target.\").optional(), \"kind\": z.string().regex(new RegExp(\"^[a-z][a-z0-9-]*$\")).min(2).max(32).describe(\"Item kind (AIP-10 convention). Common values are the memory types: 'user', 'feedback', 'project', 'reference'.\").optional(), \"title\": z.string().min(1).max(200).describe(\"Human-readable title (AIP-10 convention).\").optional(), \"updated_at\": z.string().datetime({ offset: true }).describe(\"ISO 8601 timestamp of the last content write (AIP-10 convention). A writer MUST set it when it creates the item and when it changes any field other than `status`; a status-only lifecycle rewrite (to `superseded` or `archived`) MAY leave it unchanged.\").optional(), \"id\": z.string().min(1).describe(\"Item identifier (e.g. 'mem_qc8f407g'). Legacy alias for the AIP-10 path stem; when the base fields are omitted it takes part in identifying the item.\").optional(), \"name\": z.string().min(1).max(1000).describe(\"Slugified name, distinct from `title`. Kept as a separate field so a legacy item round-trips without loss.\").optional(), \"type\": z.enum([\"user\",\"feedback\",\"project\",\"reference\"]).describe(\"Memory type. The closed type set from the memory contract; `kind` is the AIP-10-aligned spelling of the same notion.\").optional(), \"description\": z.string().max(4000).describe(\"One-line human-readable summary, independent of the body.\").optional(), \"tags\": z.array(z.string().min(1)).describe(\"Free-form labels. Scope anchors are carried here in their formatted form (e.g. 'global', 'project:fhcm', 'agent:assistant', 'pr:agentproto/ts#1858').\").default([] as never), \"polarity\": z.enum([\"do\",\"dont\"]).describe(\"Feedback polarity. Present only on items of type 'feedback'.\").optional(), \"lifetime\": z.object({ \"kind\": z.enum([\"durable\",\"until\",\"while\",\"session\"]).describe(\"Lifetime kind. 'durable' never expires; 'until' expires at `at`; 'while' lasts as long as `anchor` holds; 'session' lasts for the session.\"), \"at\": z.string().datetime({ offset: true }).describe(\"Expiry timestamp for kind 'until'.\").optional(), \"anchor\": z.object({ \"kind\": z.enum([\"global\",\"project\",\"task\",\"session\",\"pr\",\"contact\",\"agent\"]).describe(\"Anchor kind. 'agent' extends the conversation-contract set for per-agent anchoring.\"), \"slug\": z.string().min(1).describe(\"Anchor target slug (project, contact, agent).\").optional(), \"id\": z.string().min(1).describe(\"Anchor target id (task, session).\").optional(), \"repo\": z.string().min(1).describe(\"Repository 'owner/name' for a pr anchor.\").optional(), \"number\": z.number().int().gte(1).describe(\"Pull-request number for a pr anchor.\").optional() }).strict().describe(\"Governing anchor for kind 'while'.\").optional() }).strict().and(z.intersection(z.any(), z.any())).describe(\"How long the item stays valid. Typical values are { kind: 'durable' } or { kind: 'while', anchor: { … } }.\").optional(), \"status\": z.enum([\"active\",\"superseded\",\"archived\"]).describe(\"Lifecycle status. A writer creates an item 'active'. 'superseded' means a newer item replaces it; 'archived' means it is retired without a replacement (decayed, or its anchor no longer resolves). Both are terminal: an item is marked, never erased. Absent means 'active'.\").optional(), \"created\": z.string().datetime({ offset: true }).describe(\"Creation timestamp.\").optional(), \"written\": z.string().datetime({ offset: true }).describe(\"Last-write timestamp. Mirrors the AIP-10 `updated_at`.\").optional(), \"decidedBy\": z.object({ \"rule\": z.string().min(1).describe(\"Classifier rule that produced the item (e.g. 'lens-confident', 'brain-memory-file').\").optional(), \"source\": z.string().min(1).describe(\"Where the deciding rule came from (e.g. 'builtin').\").optional() }).strict().describe(\"Which classifier/rule decided the item's shape and status.\").optional(), \"provenance\": z.object({ \"source\": z.string().min(1).describe(\"Provenance source class (e.g. 'brain-transcript', 'brain-memory-file', 'converse', 'task-close', 'recall-gap').\").optional(), \"sessionId\": z.string().min(1).describe(\"Session the claim was observed in. Omitted, never empty, when the source has no session.\").optional(), \"ref\": z.string().min(1).describe(\"Opaque reference into the source (message uuid, file path, ...). Omitted, never empty, when the source has none.\").optional(), \"quote\": z.string().describe(\"Verbatim quote backing the claim.\").optional(), \"at\": z.string().datetime({ offset: true }).describe(\"When the claim was observed.\").optional(), \"extractorVersion\": z.string().min(1).describe(\"Identifier and version of the extractor that produced the claim (e.g. 'lens@3'), so a reader can tell items from different extractor generations apart.\").optional() }).strict().describe(\"Origin of the item's claims.\").optional(), \"evidence\": z.object({ \"sourceKind\": z.string().min(1).describe(\"Kind of source the evidence came from (e.g. 'brain-transcript', 'brain-memory-file').\").optional(), \"trust\": z.string().min(1).describe(\"Trust class of the source (e.g. 'jeremy', 'internal').\").optional(), \"explicitness\": z.string().min(1).describe(\"Whether the claim was stated outright or inferred (e.g. 'implicit', 'explicit').\").optional(), \"corroborations\": z.number().int().gte(0).describe(\"Number of independent corroborating observations.\").optional(), \"contradictsActive\": z.boolean().describe(\"True when this item contradicts an active item.\").optional(), \"impact\": z.string().min(1).describe(\"Impact class of the item (e.g. 'low', 'high').\").optional(), \"lifetime\": z.object({ \"kind\": z.enum([\"durable\",\"until\",\"while\",\"session\"]).describe(\"Lifetime kind. 'durable' never expires; 'until' expires at `at`; 'while' lasts as long as `anchor` holds; 'session' lasts for the session.\"), \"at\": z.string().datetime({ offset: true }).describe(\"Expiry timestamp for kind 'until'.\").optional(), \"anchor\": z.object({ \"kind\": z.enum([\"global\",\"project\",\"task\",\"session\",\"pr\",\"contact\",\"agent\"]).describe(\"Anchor kind. 'agent' extends the conversation-contract set for per-agent anchoring.\"), \"slug\": z.string().min(1).describe(\"Anchor target slug (project, contact, agent).\").optional(), \"id\": z.string().min(1).describe(\"Anchor target id (task, session).\").optional(), \"repo\": z.string().min(1).describe(\"Repository 'owner/name' for a pr anchor.\").optional(), \"number\": z.number().int().gte(1).describe(\"Pull-request number for a pr anchor.\").optional() }).strict().describe(\"Governing anchor for kind 'while'.\").optional() }).strict().and(z.intersection(z.any(), z.any())).describe(\"Lifetime as computed for the evidence pass.\").optional(), \"anchorsResolved\": z.array(z.string().min(1)).describe(\"Resolved anchor labels in their formatted form (e.g. 'global', 'project:fhcm', 'pr:agentproto/ts#1858', 'session:sess_…').\").default([] as never), \"ageHours\": z.number().describe(\"Age of the evidence in hours (may be fractional or negative relative to the write clock).\").optional(), \"flags\": z.array(z.string().min(1)).describe(\"Machine-stable flags raised during the evidence pass.\").default([] as never) }).strict().describe(\"Trust/impact accounting captured at write time.\").optional(), \"capture\": z.object({ \"mode\": z.enum([\"live\",\"batch\"]).describe(\"How the item was captured: 'live' during the session it describes, 'batch' by a later pass over stored transcripts or files.\").optional(), \"producer\": z.string().min(1).describe(\"Who or what captured the item (e.g. a clerk lens, a migration, an importer). 'unknown' when the producer did not identify itself.\").optional(), \"capturedAt\": z.string().datetime({ offset: true }).describe(\"ISO 8601 timestamp at which the source was captured or extracted.\").optional() }).strict().describe(\"Who, what and how captured the item, kept apart from `provenance` (where the claim was observed).\").optional(), \"body\": z.string().describe(\"Unrestricted markdown body of the MEMORY.md file. Not constrained by this schema.\").optional() }).strict().and(z.union([z.any(), z.any()])).describe(\"Validates the YAML frontmatter of an AIP-72 memory/v1 item: one small typed assertion (user, feedback, project, reference) about a principal or its work, with a lifecycle. A third doctype in the memory layer: it neither absorbs nor replaces AIP-10 knowledge/v1 entries (curated, shareable wiki pages over immutable sources) nor AIP-11 lesson/v1 (distilled lessons from runs). A memory/v1 item carries the AIP-10 base fields (schema, slug, kind, title, updated_at); the remaining fields are the memory item shape and are OPTIONAL, so a writer that emits only the AIP-10 base fields and a writer that emits the full item both validate against the same schema. The markdown body (the `body` property) is unrestricted text.\")\n\nexport type MemoryFrontmatter = z.infer<typeof memoryFrontmatterSchema>\n","/**\n * AIP-72 memory/v1 document types.\n *\n * Hand-tuned on top of the shape `json-schema-to-typescript` generates\n * from `resources/aip-72/draft/MEMORY.schema.json`: the `$defs` get\n * `Memory*` names, and the document carries its markdown `body` as a\n * required string (the schema's `body` property stands for the text\n * after the closing frontmatter fence).\n */\n\nexport const MEMORY_TYPES = [\"user\", \"feedback\", \"project\", \"reference\"] as const\n/** The closed memory type set (`type`, and `kind` on a memory/v1 item). */\nexport type MemoryType = (typeof MEMORY_TYPES)[number]\n\nexport const MEMORY_STATUSES = [\"active\", \"superseded\", \"archived\"] as const\n/** Lifecycle status. Absent means `active` (AIP-72 rule 9). */\nexport type MemoryStatus = (typeof MEMORY_STATUSES)[number]\n\nexport interface MemoryAnchor {\n kind: \"global\" | \"project\" | \"task\" | \"session\" | \"pr\" | \"contact\" | \"agent\"\n /** Anchor target slug (project, contact, agent). */\n slug?: string\n /** Anchor target id (task, session). */\n id?: string\n /** Repository `owner/name` for a pr anchor. */\n repo?: string\n /** Pull-request number for a pr anchor. */\n number?: number\n}\n\nexport interface MemoryLifetime {\n kind: \"durable\" | \"until\" | \"while\" | \"session\"\n /** Expiry timestamp, REQUIRED for kind `until`. */\n at?: string\n /** Governing anchor, REQUIRED for kind `while`. */\n anchor?: MemoryAnchor\n}\n\nexport interface MemoryDecidedBy {\n rule?: string\n source?: string\n}\n\nexport interface MemoryProvenance {\n source?: string\n /** Omitted, never empty, when the source has no session. */\n sessionId?: string\n /** Omitted, never empty, when the source has none. */\n ref?: string\n quote?: string\n at?: string\n extractorVersion?: string\n}\n\nexport interface MemoryCapture {\n mode?: \"live\" | \"batch\"\n producer?: string\n capturedAt?: string\n}\n\nexport interface MemoryEvidence {\n sourceKind?: string\n trust?: string\n explicitness?: string\n corroborations?: number\n contradictsActive?: boolean\n impact?: string\n lifetime?: MemoryLifetime\n anchorsResolved?: string[]\n ageHours?: number\n flags?: string[]\n}\n\n/**\n * The YAML frontmatter of a memory/v1 item. Every field is optional at\n * the type level: the schema accepts either the canonical AIP-10 base\n * fields (`schema`, `slug`, `kind`, `title`, `updated_at`) or the legacy\n * trio (`id`, `name`, `type`), which `validateMemory` checks.\n */\nexport interface MemoryFrontmatter {\n schema?: \"memory/v1\"\n slug?: string\n kind?: string\n title?: string\n updated_at?: string\n id?: string\n name?: string\n type?: MemoryType\n description?: string\n tags?: string[]\n polarity?: \"do\" | \"dont\"\n lifetime?: MemoryLifetime\n status?: MemoryStatus\n created?: string\n written?: string\n decidedBy?: MemoryDecidedBy\n provenance?: MemoryProvenance\n evidence?: MemoryEvidence\n capture?: MemoryCapture\n}\n\n/** One memory/v1 item: frontmatter fields plus the markdown body. */\nexport interface MemoryDocument extends MemoryFrontmatter {\n body: string\n}\n\n/** `defineMemory` input: `body` may be omitted for an item with no prose. */\nexport type MemoryDefinition = MemoryFrontmatter & { body?: string }\n\n/** The frozen item `defineMemory` returns, with `status` made explicit. */\nexport type MemoryHandle = Readonly<MemoryDocument & { status: MemoryStatus }>\n\n/**\n * One validation finding. `path` is the dot path of the offending field\n * (`provenance.sessionId`, `lifetime.at`, `tags.0`), `\"\"` for the whole\n * document. `rule` is `schema` for a MEMORY.schema.json constraint,\n * `section-2` for the memory/v1 `kind` set, or `rule-<n>` for AIP-72\n * conformance rule n.\n */\nexport interface MemoryIssue {\n path: string\n message: string\n rule: string\n}\n\nexport interface MemoryValidationResult {\n ok: boolean\n errors: MemoryIssue[]\n /** SHOULD-level findings (rule 8). Never make `ok` false. */\n warnings: MemoryIssue[]\n}\n","import { memoryFrontmatterSchema } from \"./schema.js\"\nimport {\n MEMORY_TYPES,\n type MemoryIssue,\n type MemoryValidationResult,\n} from \"./types.js\"\n\nexport interface ValidateMemoryOptions {\n /**\n * Require the canonical fields (`schema`, `slug`, `kind`, `title`,\n * `updated_at`), as rule 1 asks of a writer creating an item. Off by\n * default: a reader MUST accept legacy items (rule 9).\n */\n canonical?: boolean\n}\n\nconst CANONICAL_FIELDS = [\"schema\", \"slug\", \"kind\", \"title\", \"updated_at\"] as const\nconst LEGACY_FIELDS = [\"id\", \"name\", \"type\"] as const\n\n// The codegen wraps the root and `lifetime` objects in `.strict().and(...)`\n// (for the `anyOf` / `allOf` branches), and zod 4 does not report a key\n// unknown to a strict object inside an intersection. Check those two\n// key sets here, read from the generated schema itself.\nconst ROOT_KEYS = Object.keys(memoryFrontmatterSchema.def.left.shape)\nconst LIFETIME_KEYS = Object.keys(\n memoryFrontmatterSchema.def.left.shape.lifetime.unwrap().def.left.shape,\n)\n\n/**\n * Validate one memory/v1 document (frontmatter fields plus `body`).\n *\n * Runs MEMORY.schema.json (through the generated zod, plus the\n * `anyOf` and lifetime `if/then` branches the codegen cannot express)\n * and the AIP-72 conformance rules checkable on a single document:\n * section 2 (`kind` set), rules 1, 2, 4 and 5 as errors, rule 8 as\n * warnings. Rules 6 and 7 compare two versions of an item and belong to\n * the store; `isAllowedStatusTransition` covers rule 7's table.\n */\nexport function validateMemory(\n doc: unknown,\n opts: ValidateMemoryOptions = {},\n): MemoryValidationResult {\n const errors: MemoryIssue[] = []\n const warnings: MemoryIssue[] = []\n if (!isPlainObject(doc)) {\n errors.push({ path: \"\", message: \"must be an object\", rule: \"schema\" })\n return { ok: false, errors, warnings }\n }\n\n const parsed = memoryFrontmatterSchema.safeParse(doc)\n if (!parsed.success) {\n for (const issue of parsed.error.issues) {\n const base = issue.path.map(String)\n if (issue.code === \"unrecognized_keys\") {\n for (const key of issue.keys) {\n errors.push({\n path: [...base, key].join(\".\"),\n message: \"unknown field\",\n rule: \"schema\",\n })\n }\n } else {\n errors.push({ path: base.join(\".\"), message: issue.message, rule: \"schema\" })\n }\n }\n }\n\n checkUnknownKeys(doc, ROOT_KEYS, \"\", errors)\n\n // anyOf: canonical base fields, or the legacy id/name/type trio.\n const missingCanonical = CANONICAL_FIELDS.filter((f) => doc[f] === undefined)\n const hasLegacy = LEGACY_FIELDS.every((f) => doc[f] !== undefined)\n if (missingCanonical.length > 0 && (opts.canonical || !hasLegacy)) {\n for (const field of missingCanonical) {\n errors.push({\n path: field,\n message: opts.canonical\n ? \"required on an item a writer creates\"\n : \"required (or carry the legacy id, name, type trio)\",\n rule: opts.canonical ? \"rule-1\" : \"schema\",\n })\n }\n }\n\n // $defs.lifetime allOf: `until` needs `at`, `while` needs `anchor`.\n checkLifetime(doc.lifetime, \"lifetime\", errors)\n if (isPlainObject(doc.evidence)) {\n checkLifetime(doc.evidence.lifetime, \"evidence.lifetime\", errors)\n }\n\n // Section 2: on a memory/v1 item `kind` is one of the memory types.\n if (\n typeof doc.kind === \"string\" &&\n !(MEMORY_TYPES as readonly string[]).includes(doc.kind)\n ) {\n errors.push({\n path: \"kind\",\n message: `must be one of ${MEMORY_TYPES.join(\", \")}`,\n rule: \"section-2\",\n })\n }\n\n // Rule 2: kind and type agree when both are present.\n if (doc.kind !== undefined && doc.type !== undefined && doc.kind !== doc.type) {\n errors.push({\n path: \"kind\",\n message: `must equal type ('${String(doc.type)}') when both are present`,\n rule: \"rule-2\",\n })\n }\n\n // Rule 4: an optional field with no value is omitted, never empty.\n const { body: _body, ...frontmatter } = doc\n for (const path of emptyValuePaths(frontmatter, [])) {\n if (errors.some((e) => e.path === path)) continue\n errors.push({ path, message: \"empty value: omit the field instead\", rule: \"rule-4\" })\n }\n\n // Rule 5: polarity only on feedback items.\n const type = doc.type ?? doc.kind\n if (doc.polarity !== undefined && type !== \"feedback\") {\n errors.push({\n path: \"polarity\",\n message: \"only allowed on an item whose type is 'feedback'\",\n rule: \"rule-5\",\n })\n }\n\n // Rule 8 (SHOULD): record where and when the claim was observed.\n const provenance = isPlainObject(doc.provenance) ? doc.provenance : {}\n for (const field of [\"source\", \"at\"] as const) {\n if (provenance[field] === undefined) {\n warnings.push({\n path: `provenance.${field}`,\n message: \"should be recorded\",\n rule: \"rule-8\",\n })\n }\n }\n\n return { ok: errors.length === 0, errors, warnings }\n}\n\nfunction checkLifetime(value: unknown, path: string, errors: MemoryIssue[]): void {\n if (!isPlainObject(value)) return\n checkUnknownKeys(value, LIFETIME_KEYS, path, errors)\n if (value.kind === \"until\" && value.at === undefined) {\n errors.push({ path: `${path}.at`, message: \"required when kind is 'until'\", rule: \"schema\" })\n }\n if (value.kind === \"while\" && value.anchor === undefined) {\n errors.push({\n path: `${path}.anchor`,\n message: \"required when kind is 'while'\",\n rule: \"schema\",\n })\n }\n}\n\nfunction checkUnknownKeys(\n value: Record<string, unknown>,\n known: readonly string[],\n path: string,\n errors: MemoryIssue[],\n): void {\n for (const key of Object.keys(value)) {\n const keyPath = path ? `${path}.${key}` : key\n if (known.includes(key) || errors.some((e) => e.path === keyPath)) continue\n errors.push({ path: keyPath, message: \"unknown field\", rule: \"schema\" })\n }\n}\n\n/** Paths of empty strings and empty objects below the document root. */\nfunction emptyValuePaths(value: unknown, path: string[]): string[] {\n if (value === \"\") return [path.join(\".\")]\n if (Array.isArray(value)) {\n return value.flatMap((v, i) => emptyValuePaths(v, [...path, String(i)]))\n }\n if (!isPlainObject(value)) return []\n const entries = Object.entries(value).filter(([, v]) => v !== undefined)\n if (entries.length === 0 && path.length > 0) return [path.join(\".\")]\n return entries.flatMap(([k, v]) => emptyValuePaths(v, [...path, k]))\n}\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value)\n}\n","import { createDoctype } from \"@agentproto/doctype\"\nimport { MemoryValidationError } from \"./errors.js\"\nimport type { MemoryDefinition, MemoryHandle } from \"./types.js\"\nimport { validateMemory } from \"./validate.js\"\n\nconst SLUG_PATTERN = /^[a-z][a-z0-9-]*[a-z0-9]$/\n\nconst constructMemory = createDoctype<MemoryDefinition, MemoryHandle>({\n aip: 72,\n name: \"memory\",\n readIdentity: (def) => def.slug as string,\n idPattern: SLUG_PATTERN,\n // `description` is optional and up to 4000 chars; the schema checks it.\n readDescription: false,\n build(def) {\n return deepFreeze({\n ...structuredClone(def),\n body: def.body ?? \"\",\n status: def.status ?? \"active\",\n }) as MemoryHandle\n },\n})\n\n/**\n * AIP-72 reference implementation of `defineMemory`: the writer path.\n *\n * Validates in canonical mode (rule 1: a writer sets `schema`, `slug`,\n * `kind`, `title`, `updated_at` on every item it creates) and throws a\n * `MemoryValidationError` whose `field` / `issues` name the offending\n * field paths. Only two values are filled in: `status` becomes `active`\n * when absent (section 3: a writer creates an item `active`), and an\n * absent `body` becomes `\"\"`. Nothing else is defaulted; in particular\n * `tags` is not set to `[]`, since rule 4 omits an empty optional field.\n *\n * Built on `createDoctype` for the shared slug check and top-level\n * freeze; the handle is frozen deeply and does not alias the input.\n */\nexport function defineMemory(input: MemoryDefinition): MemoryHandle {\n const result = validateMemory(input, { canonical: true })\n if (!result.ok) {\n throw new MemoryValidationError(\"defineMemory (AIP-72)\", result.errors)\n }\n return constructMemory(input)\n}\n\nfunction deepFreeze<T>(value: T): T {\n if (typeof value === \"object\" && value !== null) {\n for (const v of Object.values(value)) deepFreeze(v)\n Object.freeze(value)\n }\n return value\n}\n","import { parseDocument, stringify } from \"yaml\"\nimport { MemoryParseError, MemoryValidationError } from \"./errors.js\"\nimport type { MemoryDocument } from \"./types.js\"\nimport { validateMemory } from \"./validate.js\"\n\nconst FRONTMATTER_RE = /^---[ \\t]*\\r?\\n(?:([\\s\\S]*?)\\r?\\n)?---[ \\t]*(?:\\r?\\n|$)/\n\n/**\n * Split a MEMORY.md file into its frontmatter fields and `body`.\n *\n * The frontmatter is read as YAML 1.2 core schema, so a date-time stays\n * a string and an unquoted `true`, `null` or `2026` in a string field\n * reaches `validateMemory` as a non-string and fails there (rule 3).\n * This only parses: it does not validate, so a reader can still load an\n * item it will then inspect. The body is free (rule 12): one blank line\n * after the closing fence is the separator, everything after it is kept\n * byte for byte.\n */\nexport function parseMemory(markdown: string): MemoryDocument {\n const text = markdown.startsWith(\"\") ? markdown.slice(1) : markdown\n const match = FRONTMATTER_RE.exec(text)\n if (!match) {\n throw new MemoryParseError(\"missing frontmatter: the file must open with a '---' fence\")\n }\n const yamlDoc = parseDocument(match[1] ?? \"\", { version: \"1.2\", schema: \"core\" })\n if (yamlDoc.errors.length > 0) {\n throw new MemoryParseError(\n `invalid YAML frontmatter: ${yamlDoc.errors.map((e) => e.message).join(\"; \")}`,\n )\n }\n const data: unknown = yamlDoc.toJS()\n if (typeof data !== \"object\" || data === null || Array.isArray(data)) {\n throw new MemoryParseError(\"frontmatter must be a YAML mapping\")\n }\n if (\"body\" in data) {\n throw new MemoryParseError(\"'body' is the markdown after the fence, not a frontmatter key\")\n }\n let body = text.slice(match[0].length)\n if (body.startsWith(\"\\r\\n\")) body = body.slice(2)\n else if (body.startsWith(\"\\n\")) body = body.slice(1)\n return { ...(data as Omit<MemoryDocument, \"body\">), body }\n}\n\n/**\n * Render a memory/v1 document as MEMORY.md. Throws a\n * `MemoryValidationError` if the document does not validate.\n *\n * Every string value is double-quoted, so a YAML 1.1 or 1.2 reader\n * reads it back as a string (rule 3); undefined fields are omitted\n * (rule 4); key order is the document's own, so a status-only rewrite\n * changes only the `status` line (rule 6). `parseMemory(renderMemory(d))`\n * deep-equals `d`.\n */\nexport function renderMemory(doc: MemoryDocument): string {\n const result = validateMemory(doc)\n if (!result.ok) {\n throw new MemoryValidationError(\"renderMemory (AIP-72)\", result.errors)\n }\n const { body, ...frontmatter } = doc\n const yamlText = stringify(frontmatter, {\n version: \"1.2\",\n defaultStringType: \"QUOTE_DOUBLE\",\n defaultKeyType: \"PLAIN\",\n lineWidth: 0,\n })\n return `---\\n${yamlText}---\\n${body ? `\\n${body}` : \"\"}`\n}\n","import type { MemoryFrontmatter, MemoryStatus } from \"./types.js\"\n\n/** The item's status, reading an absent `status` as `active` (rule 9). */\nexport function memoryStatus(doc: Pick<MemoryFrontmatter, \"status\">): MemoryStatus {\n return doc.status ?? \"active\"\n}\n\n/**\n * Rule 7: a writer only moves `status` from `active` (or absent) to\n * `superseded` or `archived`, and never out of either. Rewriting the\n * same status is not a transition and is allowed.\n */\nexport function isAllowedStatusTransition(\n from: MemoryStatus | undefined,\n to: MemoryStatus,\n): boolean {\n const current = from ?? \"active\"\n return current === to || current === \"active\"\n}\n\n/**\n * Whether a reader may present the item as in force at `now`.\n *\n * Rule 10: a `superseded` or `archived` item is not. Rule 11: an\n * `until` lifetime past its `at` is not. A `while` anchor or a\n * `session` lifetime needs context outside the document (does the\n * anchor still hold, which session is current), so this check treats\n * them as holding; the store that has that context narrows further.\n */\nexport function isInForce(\n doc: Pick<MemoryFrontmatter, \"status\" | \"lifetime\">,\n now: Date = new Date(),\n): boolean {\n if (memoryStatus(doc) !== \"active\") return false\n const lifetime = doc.lifetime\n if (lifetime?.kind === \"until\" && lifetime.at !== undefined) {\n return Date.parse(lifetime.at) > now.getTime()\n }\n return true\n}\n","/**\n * @agentproto/memory: AIP-72 memory/v1 reference implementation.\n *\n * One small typed assertion (user, feedback, project, reference) about a\n * principal or its work, with a lifecycle, stored as a markdown file\n * with YAML frontmatter.\n *\n * Spec: https://agentproto.sh/docs/aip-72\n *\n * Authoring paths:\n * - TS: `defineMemory({...})` -> frozen `MemoryHandle` (writer, canonical)\n * - MD: `parseMemory(src)` -> `MemoryDocument`, then `validateMemory(doc)`\n * - out: `renderMemory(doc)` -> MEMORY.md text\n */\n\nexport const SPEC_NAME = \"memory/v1\" as const\nexport const SPEC_VERSION = \"1.0.0-alpha\" as const\n\nexport { defineMemory } from \"./define-memory.js\"\nexport { validateMemory, type ValidateMemoryOptions } from \"./validate.js\"\nexport { parseMemory, renderMemory } from \"./markdown.js\"\nexport { isAllowedStatusTransition, isInForce, memoryStatus } from \"./lifecycle.js\"\nexport { MemoryParseError, MemoryValidationError } from \"./errors.js\"\nexport { memoryFrontmatterSchema } from \"./schema.js\"\nexport {\n MEMORY_STATUSES,\n MEMORY_TYPES,\n type MemoryAnchor,\n type MemoryCapture,\n type MemoryDecidedBy,\n type MemoryDefinition,\n type MemoryDocument,\n type MemoryEvidence,\n type MemoryFrontmatter,\n type MemoryHandle,\n type MemoryIssue,\n type MemoryLifetime,\n type MemoryProvenance,\n type MemoryStatus,\n type MemoryType,\n type MemoryValidationResult,\n} from \"./types.js\"\n"]}
package/package.json CHANGED
@@ -1,6 +1,63 @@
1
1
  {
2
2
  "name": "@agentproto/memory",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
3
+ "version": "0.0.0",
4
+ "description": "@agentproto/memory: AIP-72 memory/v1 reference implementation. Define, validate, parse and render MEMORY.md items: one small typed assertion (user, feedback, project, reference) about a principal or its work, with a lifecycle.",
5
+ "keywords": [
6
+ "agentproto",
7
+ "aip-72",
8
+ "memory",
9
+ "defineMemory",
10
+ "open-standard",
11
+ "agentic"
12
+ ],
13
+ "homepage": "https://agentproto.sh/docs/aip-72",
14
+ "repository": {
15
+ "type": "git",
16
+ "url": "https://github.com/agentproto/ts",
17
+ "directory": "packages/memory"
18
+ },
19
+ "bugs": {
20
+ "url": "https://github.com/agentproto/ts/issues"
21
+ },
22
+ "license": "Apache-2.0",
23
+ "type": "module",
24
+ "main": "dist/index.mjs",
25
+ "module": "dist/index.mjs",
26
+ "types": "dist/index.d.ts",
27
+ "exports": {
28
+ ".": {
29
+ "types": "./dist/index.d.ts",
30
+ "import": "./dist/index.mjs",
31
+ "default": "./dist/index.mjs"
32
+ },
33
+ "./package.json": "./package.json"
34
+ },
35
+ "files": [
36
+ "dist",
37
+ "README.md",
38
+ "LICENSE"
39
+ ],
40
+ "publishConfig": {
41
+ "access": "public"
42
+ },
43
+ "dependencies": {
44
+ "yaml": "^2.9.1",
45
+ "zod": "^4.6.7",
46
+ "@agentproto/doctype": "0.2.0"
47
+ },
48
+ "devDependencies": {
49
+ "@types/node": "^25.9.9",
50
+ "tsup": "^8.5.1",
51
+ "typescript": "^5.9.3",
52
+ "vitest": "^3.2.7",
53
+ "@agentproto/tooling": "0.1.0"
54
+ },
55
+ "scripts": {
56
+ "dev": "tsup --watch",
57
+ "build": "tsup",
58
+ "clean": "rm -rf dist",
59
+ "check-types": "tsc --noEmit",
60
+ "test": "vitest run --passWithNoTests",
61
+ "test:watch": "vitest"
62
+ }
6
63
  }