@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 +202 -0
- package/README.md +49 -2
- package/dist/index.d.ts +365 -0
- package/dist/index.mjs +256 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +60 -3
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
|
-
#
|
|
1
|
+
# @agentproto/memory
|
|
2
2
|
|
|
3
|
-
|
|
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).
|
package/dist/index.d.ts
ADDED
|
@@ -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":";;;;;;;;;;;AAGO,IAAM,qBAAA,GAAN,cAAoC,KAAA,CAAM;AAAA;AAAA,EAEtC,MAAA;AAAA;AAAA,EAEA,KAAA;AAAA,EAET,WAAA,CAAY,QAAgB,MAAA,EAAgC;AAC1D,IAAA,KAAA;AAAA,MACE,GAAG,MAAM,CAAA,EAAA,EAAK,OACX,GAAA,CAAI,CAAC,MAAM,CAAA,EAAG,CAAA,CAAE,IAAA,IAAQ,YAAY,KAAK,CAAA,CAAE,OAAO,EAAE,CAAA,CACpD,IAAA,CAAK,IAAI,CAAC,CAAA;AAAA,KACf;AACA,IAAA,IAAA,CAAK,IAAA,GAAO,uBAAA;AACZ,IAAA,IAAA,CAAK,MAAA,GAAS,MAAA;AACd,IAAA,IAAA,CAAK,KAAA,GAAQ,MAAA,CAAO,CAAC,CAAA,EAAG,IAAA,IAAQ,EAAA;AAAA,EAClC;AACF;AAGO,IAAM,gBAAA,GAAN,cAA+B,KAAA,CAAM;AAAA,EAC1C,YAAY,OAAA,EAAiB;AAC3B,IAAA,KAAA,CAAM,CAAA,sBAAA,EAAyB,OAAO,CAAA,CAAE,CAAA;AACxC,IAAA,IAAA,CAAK,IAAA,GAAO,kBAAA;AAAA,EACd;AACF;ACHO,IAAM,uBAAA,GAA0B,EAAE,MAAA,CAAO,EAAE,UAAU,CAAA,CAAE,OAAA,CAAQ,WAAW,CAAA,CAAE,QAAA,CAAS,6MAA6M,CAAA,CAAE,QAAA,IAAY,MAAA,EAAQ,CAAA,CAAE,QAAO,CAAE,KAAA,CAAM,IAAI,MAAA,CAAO,2BAA2B,CAAC,EAAE,GAAA,CAAI,CAAC,EAAE,GAAA,CAAI,EAAE,EAAE,QAAA,CAAS,4GAA4G,EAAE,QAAA,EAAS,EAAG,QAAQ,CAAA,CAAE,MAAA,GAAS,KAAA,CAAM,IAAI,OAAO,mBAAmB,CAAC,CAAA,CAAE,GAAA,CAAI,CAAC,CAAA,CAAE,IAAI,EAAE,CAAA,CAAE,SAAS,gHAAgH,CAAA,CAAE,UAAS,EAAG,OAAA,EAAS,EAAE,MAAA,EAAO,CAAE,IAAI,CAAC,CAAA,CAAE,IAAI,GAAG,CAAA,CAAE,SAAS,2CAA2C,CAAA,CAAE,QAAA,EAAS,EAAG,YAAA,EAAc,CAAA,CAAE,QAAO,CAAE,QAAA,CAAS,EAAE,MAAA,EAAQ,IAAA,EAAM,CAAA,CAAE,QAAA,CAAS,4PAA4P,CAAA,CAAE,QAAA,EAAS,EAAG,MAAM,CAAA,CAAE,MAAA,GAAS,GAAA,CAAI,CAAC,EAAE,QAAA,CAAS,uJAAuJ,CAAA,CAAE,QAAA,EAAS,EAAG,MAAA,EAAQ,EAAE,MAAA,EAAO,CAAE,IAAI,CAAC,CAAA,CAAE,IAAI,GAAI,CAAA,CAAE,SAAS,4GAA4G,CAAA,CAAE,UAAS,EAAG,MAAA,EAAQ,EAAE,IAAA,CAAK,CAAC,QAAO,UAAA,EAAW,SAAA,EAAU,WAAW,CAAC,CAAA,CAAE,QAAA,CAAS,sHAAsH,CAAA,CAAE,QAAA,IAAY,aAAA,EAAe,CAAA,CAAE,QAAO,CAAE,GAAA,CAAI,GAAI,CAAA,CAAE,QAAA,CAAS,2DAA2D,CAAA,CAAE,QAAA,IAAY,MAAA,EAAQ,CAAA,CAAE,MAAM,CAAA,CAAE,MAAA,EAAO,CAAE,GAAA,CAAI,CAAC,CAAC,EAAE,QAAA,CAAS,uJAAuJ,EAAE,OAAA,CAAQ,EAAW,CAAA,EAAG,UAAA,EAAY,EAAE,IAAA,CAAK,CAAC,MAAK,MAAM,CAAC,EAAE,QAAA,CAAS,8DAA8D,EAAE,QAAA,EAAS,EAAG,UAAA,EAAY,CAAA,CAAE,MAAA,CAAO,EAAE,QAAQ,CAAA,CAAE,IAAA,CAAK,CAAC,SAAA,EAAU,OAAA,EAAQ,SAAQ,SAAS,CAAC,CAAA,CAAE,QAAA,CAAS,4IAA4I,CAAA,EAAG,MAAM,CAAA,CAAE,MAAA,GAAS,QAAA,CAAS,EAAE,QAAQ,IAAA,EAAM,CAAA,CAAE,QAAA,CAAS,oCAAoC,CAAA,CAAE,UAAS,EAAG,QAAA,EAAU,EAAE,MAAA,CAAO,EAAE,QAAQ,CAAA,CAAE,IAAA,CAAK,CAAC,QAAA,EAAS,SAAA,EAAU,QAAO,SAAA,EAAU,IAAA,EAAK,WAAU,OAAO,CAAC,EAAE,QAAA,CAAS,qFAAqF,CAAA,EAAG,MAAA,EAAQ,CAAA,CAAE,MAAA,GAAS,GAAA,CAAI,CAAC,EAAE,QAAA,CAAS,+CAA+C,EAAE,QAAA,EAAS,EAAG,MAAM,CAAA,CAAE,MAAA,GAAS,GAAA,CAAI,CAAC,EAAE,QAAA,CAAS,mCAAmC,EAAE,QAAA,EAAS,EAAG,MAAA,EAAQ,CAAA,CAAE,MAAA,EAAO,CAAE,IAAI,CAAC,CAAA,CAAE,SAAS,0CAA0C,CAAA,CAAE,UAAS,EAAG,QAAA,EAAU,EAAE,MAAA,EAAO,CAAE,KAAI,CAAE,GAAA,CAAI,CAAC,CAAA,CAAE,QAAA,CAAS,sCAAsC,CAAA,CAAE,QAAA,EAAS,EAAG,CAAA,CAAE,MAAA,GAAS,QAAA,CAAS,oCAAoC,EAAE,QAAA,EAAS,EAAG,CAAA,CAAE,MAAA,EAAO,CAAE,GAAA,CAAI,CAAA,CAAE,YAAA,CAAa,EAAE,GAAA,EAAI,EAAG,EAAE,GAAA,EAAK,CAAC,CAAA,CAAE,QAAA,CAAS,iHAA4G,CAAA,CAAE,QAAA,EAAS,EAAG,UAAU,CAAA,CAAE,IAAA,CAAK,CAAC,QAAA,EAAS,YAAA,EAAa,UAAU,CAAC,CAAA,CAAE,SAAS,gRAAgR,CAAA,CAAE,UAAS,EAAG,SAAA,EAAW,EAAE,MAAA,EAAO,CAAE,SAAS,EAAE,MAAA,EAAQ,IAAA,EAAM,CAAA,CAAE,QAAA,CAAS,qBAAqB,CAAA,CAAE,QAAA,IAAY,SAAA,EAAW,CAAA,CAAE,QAAO,CAAE,QAAA,CAAS,EAAE,MAAA,EAAQ,IAAA,EAAM,CAAA,CAAE,QAAA,CAAS,wDAAwD,CAAA,CAAE,QAAA,IAAY,WAAA,EAAa,CAAA,CAAE,MAAA,CAAO,EAAE,MAAA,EAAQ,CAAA,CAAE,QAAO,CAAE,GAAA,CAAI,CAAC,CAAA,CAAE,QAAA,CAAS,sFAAsF,CAAA,CAAE,QAAA,IAAY,QAAA,EAAU,CAAA,CAAE,QAAO,CAAE,GAAA,CAAI,CAAC,CAAA,CAAE,QAAA,CAAS,qDAAqD,CAAA,CAAE,QAAA,EAAS,EAAG,CAAA,CAAE,MAAA,GAAS,QAAA,CAAS,4DAA4D,EAAE,QAAA,EAAS,EAAG,cAAc,CAAA,CAAE,MAAA,CAAO,EAAE,QAAA,EAAU,CAAA,CAAE,MAAA,GAAS,GAAA,CAAI,CAAC,EAAE,QAAA,CAAS,iHAAiH,EAAE,QAAA,EAAS,EAAG,WAAA,EAAa,CAAA,CAAE,MAAA,EAAO,CAAE,IAAI,CAAC,CAAA,CAAE,SAAS,0FAA0F,CAAA,CAAE,UAAS,EAAG,KAAA,EAAO,EAAE,MAAA,EAAO,CAAE,IAAI,CAAC,CAAA,CAAE,SAAS,kHAAkH,CAAA,CAAE,UAAS,EAAG,OAAA,EAAS,CAAA,CAAE,MAAA,EAAO,CAAE,QAAA,CAAS,mCAAmC,CAAA,CAAE,QAAA,IAAY,IAAA,EAAM,CAAA,CAAE,QAAO,CAAE,QAAA,CAAS,EAAE,MAAA,EAAQ,IAAA,EAAM,CAAA,CAAE,QAAA,CAAS,8BAA8B,CAAA,CAAE,QAAA,IAAY,kBAAA,EAAoB,CAAA,CAAE,MAAA,EAAO,CAAE,GAAA,CAAI,CAAC,EAAE,QAAA,CAAS,yJAAyJ,EAAE,QAAA,EAAS,EAAG,CAAA,CAAE,MAAA,GAAS,QAAA,CAAS,8BAA8B,EAAE,QAAA,EAAS,EAAG,YAAY,CAAA,CAAE,MAAA,CAAO,EAAE,YAAA,EAAc,CAAA,CAAE,MAAA,EAAO,CAAE,GAAA,CAAI,CAAC,EAAE,QAAA,CAAS,uFAAuF,EAAE,QAAA,EAAS,EAAG,SAAS,CAAA,CAAE,MAAA,EAAO,CAAE,GAAA,CAAI,CAAC,CAAA,CAAE,SAAS,wDAAwD,CAAA,CAAE,UAAS,EAAG,cAAA,EAAgB,EAAE,MAAA,EAAO,CAAE,GAAA,CAAI,CAAC,CAAA,CAAE,QAAA,CAAS,kFAAkF,CAAA,CAAE,QAAA,IAAY,gBAAA,EAAkB,CAAA,CAAE,QAAO,CAAE,GAAA,GAAM,GAAA,CAAI,CAAC,EAAE,QAAA,CAAS,mDAAmD,EAAE,QAAA,EAAS,EAAG,qBAAqB,CAAA,CAAE,OAAA,EAAQ,CAAE,QAAA,CAAS,iDAAiD,CAAA,CAAE,UAAS,EAAG,QAAA,EAAU,EAAE,MAAA,EAAO,CAAE,IAAI,CAAC,CAAA,CAAE,SAAS,gDAAgD,CAAA,CAAE,UAAS,EAAG,UAAA,EAAY,EAAE,MAAA,CAAO,EAAE,QAAQ,CAAA,CAAE,IAAA,CAAK,CAAC,SAAA,EAAU,OAAA,EAAQ,OAAA,EAAQ,SAAS,CAAC,CAAA,CAAE,SAAS,4IAA4I,CAAA,EAAG,MAAM,CAAA,CAAE,MAAA,GAAS,QAAA,CAAS,EAAE,QAAQ,IAAA,EAAM,EAAE,QAAA,CAAS,oCAAoC,EAAE,QAAA,EAAS,EAAG,QAAA,EAAU,CAAA,CAAE,MAAA,CAAO,EAAE,QAAQ,CAAA,CAAE,IAAA,CAAK,CAAC,QAAA,EAAS,SAAA,EAAU,QAAO,SAAA,EAAU,IAAA,EAAK,SAAA,EAAU,OAAO,CAAC,CAAA,CAAE,SAAS,qFAAqF,CAAA,EAAG,QAAQ,CAAA,CAAE,MAAA,GAAS,GAAA,CAAI,CAAC,CAAA,CAAE,QAAA,CAAS,+CAA+C,CAAA,CAAE,UAAS,EAAG,IAAA,EAAM,EAAE,MAAA,EAAO,CAAE,IAAI,CAAC,CAAA,CAAE,SAAS,mCAAmC,CAAA,CAAE,UAAS,EAAG,MAAA,EAAQ,EAAE,MAAA,EAAO,CAAE,IAAI,CAAC,CAAA,CAAE,QAAA,CAAS,0CAA0C,CAAA,CAAE,QAAA,IAAY,QAAA,EAAU,CAAA,CAAE,QAAO,CAAE,GAAA,GAAM,GAAA,CAAI,CAAC,EAAE,QAAA,CAAS,sCAAsC,EAAE,QAAA,EAAS,EAAG,CAAA,CAAE,MAAA,GAAS,QAAA,CAAS,oCAAoC,CAAA,CAAE,QAAA,EAAS,EAAG,EAAE,MAAA,EAAO,CAAE,IAAI,CAAA,CAAE,YAAA,CAAa,EAAE,GAAA,EAAI,EAAG,EAAE,GAAA,EAAK,CAAC,CAAA,CAAE,QAAA,CAAS,6CAA6C,CAAA,CAAE,QAAA,IAAY,iBAAA,EAAmB,CAAA,CAAE,KAAA,CAAM,CAAA,CAAE,MAAA,EAAO,CAAE,IAAI,CAAC,CAAC,EAAE,QAAA,CAAS,iIAA4H,EAAE,OAAA,CAAQ,EAAW,CAAA,EAAG,UAAA,EAAY,CAAA,CAAE,QAAO,CAAE,QAAA,CAAS,2FAA2F,CAAA,CAAE,QAAA,IAAY,OAAA,EAAS,CAAA,CAAE,KAAA,CAAM,CAAA,CAAE,MAAA,EAAO,CAAE,IAAI,CAAC,CAAC,EAAE,QAAA,CAAS,uDAAuD,EAAE,OAAA,CAAQ,EAAW,CAAA,EAAG,EAAE,MAAA,EAAO,CAAE,SAAS,iDAAiD,CAAA,CAAE,UAAS,EAAG,SAAA,EAAW,CAAA,CAAE,MAAA,CAAO,EAAE,MAAA,EAAQ,EAAE,IAAA,CAAK,CAAC,QAAO,OAAO,CAAC,EAAE,QAAA,CAAS,8HAA8H,CAAA,CAAE,QAAA,EAAS,EAAG,UAAA,EAAY,EAAE,MAAA,EAAO,CAAE,IAAI,CAAC,CAAA,CAAE,SAAS,mIAAmI,CAAA,CAAE,QAAA,EAAS,EAAG,YAAA,EAAc,CAAA,CAAE,QAAO,CAAE,QAAA,CAAS,EAAE,MAAA,EAAQ,IAAA,EAAM,CAAA,CAAE,QAAA,CAAS,mEAAmE,CAAA,CAAE,QAAA,IAAY,CAAA,CAAE,QAAO,CAAE,QAAA,CAAS,mGAAmG,CAAA,CAAE,QAAA,EAAS,EAAG,MAAA,EAAQ,CAAA,CAAE,MAAA,GAAS,QAAA,CAAS,mFAAmF,EAAE,QAAA,EAAS,EAAG,CAAA,CAAE,MAAA,EAAO,CAAE,GAAA,CAAI,CAAA,CAAE,KAAA,CAAM,CAAC,CAAA,CAAE,GAAA,IAAO,CAAA,CAAE,GAAA,EAAK,CAAC,CAAC,CAAA,CAAE,QAAA,CAAS,itBAAitB;;;ACdj5R,IAAM,YAAA,GAAe,CAAC,MAAA,EAAQ,UAAA,EAAY,WAAW,WAAW;AAIhE,IAAM,eAAA,GAAkB,CAAC,QAAA,EAAU,YAAA,EAAc,UAAU;;;ACElE,IAAM,mBAAmB,CAAC,QAAA,EAAU,MAAA,EAAQ,MAAA,EAAQ,SAAS,YAAY,CAAA;AACzE,IAAM,aAAA,GAAgB,CAAC,IAAA,EAAM,MAAA,EAAQ,MAAM,CAAA;AAM3C,IAAM,YAAY,MAAA,CAAO,IAAA,CAAK,uBAAA,CAAwB,GAAA,CAAI,KAAK,KAAK,CAAA;AACpE,IAAM,gBAAgB,MAAA,CAAO,IAAA;AAAA,EAC3B,uBAAA,CAAwB,IAAI,IAAA,CAAK,KAAA,CAAM,SAAS,MAAA,EAAO,CAAE,IAAI,IAAA,CAAK;AACpE,CAAA;AAYO,SAAS,cAAA,CACd,GAAA,EACA,IAAA,GAA8B,EAAC,EACP;AACxB,EAAA,MAAM,SAAwB,EAAC;AAC/B,EAAA,MAAM,WAA0B,EAAC;AACjC,EAAA,IAAI,CAAC,aAAA,CAAc,GAAG,CAAA,EAAG;AACvB,IAAA,MAAA,CAAO,IAAA,CAAK,EAAE,IAAA,EAAM,EAAA,EAAI,SAAS,mBAAA,EAAqB,IAAA,EAAM,UAAU,CAAA;AACtE,IAAA,OAAO,EAAE,EAAA,EAAI,KAAA,EAAO,MAAA,EAAQ,QAAA,EAAS;AAAA,EACvC;AAEA,EAAA,MAAM,MAAA,GAAS,uBAAA,CAAwB,SAAA,CAAU,GAAG,CAAA;AACpD,EAAA,IAAI,CAAC,OAAO,OAAA,EAAS;AACnB,IAAA,KAAA,MAAW,KAAA,IAAS,MAAA,CAAO,KAAA,CAAM,MAAA,EAAQ;AACvC,MAAA,MAAM,IAAA,GAAO,KAAA,CAAM,IAAA,CAAK,GAAA,CAAI,MAAM,CAAA;AAClC,MAAA,IAAI,KAAA,CAAM,SAAS,mBAAA,EAAqB;AACtC,QAAA,KAAA,MAAW,GAAA,IAAO,MAAM,IAAA,EAAM;AAC5B,UAAA,MAAA,CAAO,IAAA,CAAK;AAAA,YACV,MAAM,CAAC,GAAG,MAAM,GAAG,CAAA,CAAE,KAAK,GAAG,CAAA;AAAA,YAC7B,OAAA,EAAS,eAAA;AAAA,YACT,IAAA,EAAM;AAAA,WACP,CAAA;AAAA,QACH;AAAA,MACF,CAAA,MAAO;AACL,QAAA,MAAA,CAAO,IAAA,CAAK,EAAE,IAAA,EAAM,IAAA,CAAK,IAAA,CAAK,GAAG,CAAA,EAAG,OAAA,EAAS,KAAA,CAAM,OAAA,EAAS,IAAA,EAAM,QAAA,EAAU,CAAA;AAAA,MAC9E;AAAA,IACF;AAAA,EACF;AAEA,EAAA,gBAAA,CAAiB,GAAA,EAAK,SAAA,EAAW,EAAA,EAAI,MAAM,CAAA;AAG3C,EAAA,MAAM,gBAAA,GAAmB,iBAAiB,MAAA,CAAO,CAAC,MAAM,GAAA,CAAI,CAAC,MAAM,MAAS,CAAA;AAC5E,EAAA,MAAM,SAAA,GAAY,cAAc,KAAA,CAAM,CAAC,MAAM,GAAA,CAAI,CAAC,MAAM,MAAS,CAAA;AACjE,EAAA,IAAI,iBAAiB,MAAA,GAAS,CAAA,KAAM,IAAA,CAAK,SAAA,IAAa,CAAC,SAAA,CAAA,EAAY;AACjE,IAAA,KAAA,MAAW,SAAS,gBAAA,EAAkB;AACpC,MAAA,MAAA,CAAO,IAAA,CAAK;AAAA,QACV,IAAA,EAAM,KAAA;AAAA,QACN,OAAA,EAAS,IAAA,CAAK,SAAA,GACV,sCAAA,GACA,oDAAA;AAAA,QACJ,IAAA,EAAM,IAAA,CAAK,SAAA,GAAY,QAAA,GAAW;AAAA,OACnC,CAAA;AAAA,IACH;AAAA,EACF;AAGA,EAAA,aAAA,CAAc,GAAA,CAAI,QAAA,EAAU,UAAA,EAAY,MAAM,CAAA;AAC9C,EAAA,IAAI,aAAA,CAAc,GAAA,CAAI,QAAQ,CAAA,EAAG;AAC/B,IAAA,aAAA,CAAc,GAAA,CAAI,QAAA,CAAS,QAAA,EAAU,mBAAA,EAAqB,MAAM,CAAA;AAAA,EAClE;AAGA,EAAA,IACE,OAAO,IAAI,IAAA,KAAS,QAAA,IACpB,CAAE,YAAA,CAAmC,QAAA,CAAS,GAAA,CAAI,IAAI,CAAA,EACtD;AACA,IAAA,MAAA,CAAO,IAAA,CAAK;AAAA,MACV,IAAA,EAAM,MAAA;AAAA,MACN,OAAA,EAAS,CAAA,eAAA,EAAkB,YAAA,CAAa,IAAA,CAAK,IAAI,CAAC,CAAA,CAAA;AAAA,MAClD,IAAA,EAAM;AAAA,KACP,CAAA;AAAA,EACH;AAGA,EAAA,IAAI,GAAA,CAAI,SAAS,MAAA,IAAa,GAAA,CAAI,SAAS,MAAA,IAAa,GAAA,CAAI,IAAA,KAAS,GAAA,CAAI,IAAA,EAAM;AAC7E,IAAA,MAAA,CAAO,IAAA,CAAK;AAAA,MACV,IAAA,EAAM,MAAA;AAAA,MACN,OAAA,EAAS,CAAA,kBAAA,EAAqB,MAAA,CAAO,GAAA,CAAI,IAAI,CAAC,CAAA,wBAAA,CAAA;AAAA,MAC9C,IAAA,EAAM;AAAA,KACP,CAAA;AAAA,EACH;AAGA,EAAA,MAAM,EAAE,IAAA,EAAM,KAAA,EAAO,GAAG,aAAY,GAAI,GAAA;AACxC,EAAA,KAAA,MAAW,IAAA,IAAQ,eAAA,CAAgB,WAAA,EAAa,EAAE,CAAA,EAAG;AACnD,IAAA,IAAI,OAAO,IAAA,CAAK,CAAC,MAAM,CAAA,CAAE,IAAA,KAAS,IAAI,CAAA,EAAG;AACzC,IAAA,MAAA,CAAO,KAAK,EAAE,IAAA,EAAM,SAAS,qCAAA,EAAuC,IAAA,EAAM,UAAU,CAAA;AAAA,EACtF;AAGA,EAAA,MAAM,IAAA,GAAO,GAAA,CAAI,IAAA,IAAQ,GAAA,CAAI,IAAA;AAC7B,EAAA,IAAI,GAAA,CAAI,QAAA,KAAa,MAAA,IAAa,IAAA,KAAS,UAAA,EAAY;AACrD,IAAA,MAAA,CAAO,IAAA,CAAK;AAAA,MACV,IAAA,EAAM,UAAA;AAAA,MACN,OAAA,EAAS,kDAAA;AAAA,MACT,IAAA,EAAM;AAAA,KACP,CAAA;AAAA,EACH;AAGA,EAAA,MAAM,aAAa,aAAA,CAAc,GAAA,CAAI,UAAU,CAAA,GAAI,GAAA,CAAI,aAAa,EAAC;AACrE,EAAA,KAAA,MAAW,KAAA,IAAS,CAAC,QAAA,EAAU,IAAI,CAAA,EAAY;AAC7C,IAAA,IAAI,UAAA,CAAW,KAAK,CAAA,KAAM,MAAA,EAAW;AACnC,MAAA,QAAA,CAAS,IAAA,CAAK;AAAA,QACZ,IAAA,EAAM,cAAc,KAAK,CAAA,CAAA;AAAA,QACzB,OAAA,EAAS,oBAAA;AAAA,QACT,IAAA,EAAM;AAAA,OACP,CAAA;AAAA,IACH;AAAA,EACF;AAEA,EAAA,OAAO,EAAE,EAAA,EAAI,MAAA,CAAO,MAAA,KAAW,CAAA,EAAG,QAAQ,QAAA,EAAS;AACrD;AAEA,SAAS,aAAA,CAAc,KAAA,EAAgB,IAAA,EAAc,MAAA,EAA6B;AAChF,EAAA,IAAI,CAAC,aAAA,CAAc,KAAK,CAAA,EAAG;AAC3B,EAAA,gBAAA,CAAiB,KAAA,EAAO,aAAA,EAAe,IAAA,EAAM,MAAM,CAAA;AACnD,EAAA,IAAI,KAAA,CAAM,IAAA,KAAS,OAAA,IAAW,KAAA,CAAM,OAAO,MAAA,EAAW;AACpD,IAAA,MAAA,CAAO,IAAA,CAAK,EAAE,IAAA,EAAM,CAAA,EAAG,IAAI,OAAO,OAAA,EAAS,+BAAA,EAAiC,IAAA,EAAM,QAAA,EAAU,CAAA;AAAA,EAC9F;AACA,EAAA,IAAI,KAAA,CAAM,IAAA,KAAS,OAAA,IAAW,KAAA,CAAM,WAAW,MAAA,EAAW;AACxD,IAAA,MAAA,CAAO,IAAA,CAAK;AAAA,MACV,IAAA,EAAM,GAAG,IAAI,CAAA,OAAA,CAAA;AAAA,MACb,OAAA,EAAS,+BAAA;AAAA,MACT,IAAA,EAAM;AAAA,KACP,CAAA;AAAA,EACH;AACF;AAEA,SAAS,gBAAA,CACP,KAAA,EACA,KAAA,EACA,IAAA,EACA,MAAA,EACM;AACN,EAAA,KAAA,MAAW,GAAA,IAAO,MAAA,CAAO,IAAA,CAAK,KAAK,CAAA,EAAG;AACpC,IAAA,MAAM,UAAU,IAAA,GAAO,CAAA,EAAG,IAAI,CAAA,CAAA,EAAI,GAAG,CAAA,CAAA,GAAK,GAAA;AAC1C,IAAA,IAAI,KAAA,CAAM,QAAA,CAAS,GAAG,CAAA,IAAK,MAAA,CAAO,IAAA,CAAK,CAAC,CAAA,KAAM,CAAA,CAAE,IAAA,KAAS,OAAO,CAAA,EAAG;AACnE,IAAA,MAAA,CAAO,IAAA,CAAK,EAAE,IAAA,EAAM,OAAA,EAAS,SAAS,eAAA,EAAiB,IAAA,EAAM,UAAU,CAAA;AAAA,EACzE;AACF;AAGA,SAAS,eAAA,CAAgB,OAAgB,IAAA,EAA0B;AACjE,EAAA,IAAI,UAAU,EAAA,EAAI,OAAO,CAAC,IAAA,CAAK,IAAA,CAAK,GAAG,CAAC,CAAA;AACxC,EAAA,IAAI,KAAA,CAAM,OAAA,CAAQ,KAAK,CAAA,EAAG;AACxB,IAAA,OAAO,KAAA,CAAM,OAAA,CAAQ,CAAC,CAAA,EAAG,MAAM,eAAA,CAAgB,CAAA,EAAG,CAAC,GAAG,IAAA,EAAM,MAAA,CAAO,CAAC,CAAC,CAAC,CAAC,CAAA;AAAA,EACzE;AACA,EAAA,IAAI,CAAC,aAAA,CAAc,KAAK,CAAA,SAAU,EAAC;AACnC,EAAA,MAAM,OAAA,GAAU,MAAA,CAAO,OAAA,CAAQ,KAAK,CAAA,CAAE,MAAA,CAAO,CAAC,GAAG,CAAC,CAAA,KAAM,CAAA,KAAM,MAAS,CAAA;AACvE,EAAA,IAAI,OAAA,CAAQ,MAAA,KAAW,CAAA,IAAK,IAAA,CAAK,MAAA,GAAS,CAAA,EAAG,OAAO,CAAC,IAAA,CAAK,IAAA,CAAK,GAAG,CAAC,CAAA;AACnE,EAAA,OAAO,OAAA,CAAQ,OAAA,CAAQ,CAAC,CAAC,GAAG,CAAC,CAAA,KAAM,eAAA,CAAgB,CAAA,EAAG,CAAC,GAAG,IAAA,EAAM,CAAC,CAAC,CAAC,CAAA;AACrE;AAEA,SAAS,cAAc,KAAA,EAAkD;AACvE,EAAA,OAAO,OAAO,UAAU,QAAA,IAAY,KAAA,KAAU,QAAQ,CAAC,KAAA,CAAM,QAAQ,KAAK,CAAA;AAC5E;;;ACpLA,IAAM,YAAA,GAAe,2BAAA;AAErB,IAAM,kBAAkB,aAAA,CAA8C;AAAA,EACpE,GAAA,EAAK,EAAA;AAAA,EACL,IAAA,EAAM,QAAA;AAAA,EACN,YAAA,EAAc,CAAC,GAAA,KAAQ,GAAA,CAAI,IAAA;AAAA,EAC3B,SAAA,EAAW,YAAA;AAAA;AAAA,EAEX,eAAA,EAAiB,KAAA;AAAA,EACjB,MAAM,GAAA,EAAK;AACT,IAAA,OAAO,UAAA,CAAW;AAAA,MAChB,GAAG,gBAAgB,GAAG,CAAA;AAAA,MACtB,IAAA,EAAM,IAAI,IAAA,IAAQ,EAAA;AAAA,MAClB,MAAA,EAAQ,IAAI,MAAA,IAAU;AAAA,KACvB,CAAA;AAAA,EACH;AACF,CAAC,CAAA;AAgBM,SAAS,aAAa,KAAA,EAAuC;AAClE,EAAA,MAAM,SAAS,cAAA,CAAe,KAAA,EAAO,EAAE,SAAA,EAAW,MAAM,CAAA;AACxD,EAAA,IAAI,CAAC,OAAO,EAAA,EAAI;AACd,IAAA,MAAM,IAAI,qBAAA,CAAsB,uBAAA,EAAyB,MAAA,CAAO,MAAM,CAAA;AAAA,EACxE;AACA,EAAA,OAAO,gBAAgB,KAAK,CAAA;AAC9B;AAEA,SAAS,WAAc,KAAA,EAAa;AAClC,EAAA,IAAI,OAAO,KAAA,KAAU,QAAA,IAAY,KAAA,KAAU,IAAA,EAAM;AAC/C,IAAA,KAAA,MAAW,KAAK,MAAA,CAAO,MAAA,CAAO,KAAK,CAAA,aAAc,CAAC,CAAA;AAClD,IAAA,MAAA,CAAO,OAAO,KAAK,CAAA;AAAA,EACrB;AACA,EAAA,OAAO,KAAA;AACT;AC9CA,IAAM,cAAA,GAAiB,yDAAA;AAahB,SAAS,YAAY,QAAA,EAAkC;AAC5D,EAAA,MAAM,IAAA,GAAO,SAAS,UAAA,CAAW,QAAG,IAAI,QAAA,CAAS,KAAA,CAAM,CAAC,CAAA,GAAI,QAAA;AAC5D,EAAA,MAAM,KAAA,GAAQ,cAAA,CAAe,IAAA,CAAK,IAAI,CAAA;AACtC,EAAA,IAAI,CAAC,KAAA,EAAO;AACV,IAAA,MAAM,IAAI,iBAAiB,4DAA4D,CAAA;AAAA,EACzF;AACA,EAAA,MAAM,OAAA,GAAU,aAAA,CAAc,KAAA,CAAM,CAAC,CAAA,IAAK,EAAA,EAAI,EAAE,OAAA,EAAS,KAAA,EAAO,MAAA,EAAQ,MAAA,EAAQ,CAAA;AAChF,EAAA,IAAI,OAAA,CAAQ,MAAA,CAAO,MAAA,GAAS,CAAA,EAAG;AAC7B,IAAA,MAAM,IAAI,gBAAA;AAAA,MACR,CAAA,0BAAA,EAA6B,OAAA,CAAQ,MAAA,CAAO,GAAA,CAAI,CAAC,CAAA,KAAM,CAAA,CAAE,OAAO,CAAA,CAAE,IAAA,CAAK,IAAI,CAAC,CAAA;AAAA,KAC9E;AAAA,EACF;AACA,EAAA,MAAM,IAAA,GAAgB,QAAQ,IAAA,EAAK;AACnC,EAAA,IAAI,OAAO,SAAS,QAAA,IAAY,IAAA,KAAS,QAAQ,KAAA,CAAM,OAAA,CAAQ,IAAI,CAAA,EAAG;AACpE,IAAA,MAAM,IAAI,iBAAiB,oCAAoC,CAAA;AAAA,EACjE;AACA,EAAA,IAAI,UAAU,IAAA,EAAM;AAClB,IAAA,MAAM,IAAI,iBAAiB,+DAA+D,CAAA;AAAA,EAC5F;AACA,EAAA,IAAI,OAAO,IAAA,CAAK,KAAA,CAAM,KAAA,CAAM,CAAC,EAAE,MAAM,CAAA;AACrC,EAAA,IAAI,KAAK,UAAA,CAAW,MAAM,GAAG,IAAA,GAAO,IAAA,CAAK,MAAM,CAAC,CAAA;AAAA,OAAA,IACvC,KAAK,UAAA,CAAW,IAAI,GAAG,IAAA,GAAO,IAAA,CAAK,MAAM,CAAC,CAAA;AACnD,EAAA,OAAO,EAAE,GAAI,IAAA,EAAuC,IAAA,EAAK;AAC3D;AAYO,SAAS,aAAa,GAAA,EAA6B;AACxD,EAAA,MAAM,MAAA,GAAS,eAAe,GAAG,CAAA;AACjC,EAAA,IAAI,CAAC,OAAO,EAAA,EAAI;AACd,IAAA,MAAM,IAAI,qBAAA,CAAsB,uBAAA,EAAyB,MAAA,CAAO,MAAM,CAAA;AAAA,EACxE;AACA,EAAA,MAAM,EAAE,IAAA,EAAM,GAAG,WAAA,EAAY,GAAI,GAAA;AACjC,EAAA,MAAM,QAAA,GAAW,UAAU,WAAA,EAAa;AAAA,IACtC,OAAA,EAAS,KAAA;AAAA,IACT,iBAAA,EAAmB,cAAA;AAAA,IACnB,cAAA,EAAgB,OAAA;AAAA,IAChB,SAAA,EAAW;AAAA,GACZ,CAAA;AACD,EAAA,OAAO,CAAA;AAAA,EAAQ,QAAQ,CAAA;AAAA,EAAQ,IAAA,GAAO;AAAA,EAAK,IAAI,KAAK,EAAE,CAAA,CAAA;AACxD;;;AC/DO,SAAS,aAAa,GAAA,EAAsD;AACjF,EAAA,OAAO,IAAI,MAAA,IAAU,QAAA;AACvB;AAOO,SAAS,yBAAA,CACd,MACA,EAAA,EACS;AACT,EAAA,MAAM,UAAU,IAAA,IAAQ,QAAA;AACxB,EAAA,OAAO,OAAA,KAAY,MAAM,OAAA,KAAY,QAAA;AACvC;AAWO,SAAS,SAAA,CACd,GAAA,EACA,GAAA,mBAAY,IAAI,MAAK,EACZ;AACT,EAAA,IAAI,YAAA,CAAa,GAAG,CAAA,KAAM,QAAA,EAAU,OAAO,KAAA;AAC3C,EAAA,MAAM,WAAW,GAAA,CAAI,QAAA;AACrB,EAAA,IAAI,QAAA,EAAU,IAAA,KAAS,OAAA,IAAW,QAAA,CAAS,OAAO,MAAA,EAAW;AAC3D,IAAA,OAAO,KAAK,KAAA,CAAM,QAAA,CAAS,EAAE,CAAA,GAAI,IAAI,OAAA,EAAQ;AAAA,EAC/C;AACA,EAAA,OAAO,IAAA;AACT;;;ACxBO,IAAM,SAAA,GAAY;AAClB,IAAM,YAAA,GAAe","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
|
|
4
|
-
"
|
|
5
|
-
"
|
|
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
|
}
|