@moxxy/plugin-memory 0.37.0 → 0.37.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/user-model.d.ts.map +1 -1
- package/dist/user-model.js +26 -14
- package/dist/user-model.js.map +1 -1
- package/package.json +4 -4
- package/src/user-model.test.ts +14 -2
- package/src/user-model.ts +22 -14
package/dist/user-model.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"user-model.d.ts","sourceRoot":"","sources":["../src/user-model.ts"],"names":[],"mappings":"AAEA,OAAO,
|
|
1
|
+
{"version":3,"file":"user-model.d.ts","sourceRoot":"","sources":["../src/user-model.ts"],"names":[],"mappings":"AAEA,OAAO,EAKL,KAAK,eAAe,EACpB,KAAK,OAAO,EACb,MAAM,YAAY,CAAC;AAGpB;;;;;;;;;;;;;GAaG;AAGH,eAAO,MAAM,eAAe,iBAAiB,CAAC;AAC9C,eAAO,MAAM,gBAAgB,kBAAkB,CAAC;AAEhD;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB,OAAO,CAAC;AAEvC,mDAAmD;AACnD,eAAO,MAAM,cAAc,8DAA+D,CAAC;AAE3F,gEAAgE;AAChE,eAAO,MAAM,cAAc,EAAE,MAAM,CAAC,mBAAmB,EAAE,MAAM,CAK9D,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG,UAAU,GAAG,aAAa,GAAG,WAAW,GAAG,SAAS,CAAC;AACvF,MAAM,MAAM,mBAAmB,GAAG,SAAS,GAAG,QAAQ,CAAC;AAEvD,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,SAAS;IACxB,wFAAwF;IACxF,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,gBAAgB,EAAE,CAAC;CAC9B;AAgBD;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,CAoBtD;AAUD,6EAA6E;AAC7E,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,SAAS,GAAG,MAAM,CAQ3D;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,SAAS,EAAE,GAAG,GAAE,MAA2B,GAAG,MAAM,CAkB9F;AAMD,oFAAoF;AACpF,wBAAgB,oBAAoB,IAAI,MAAM,CAE7C;AAED;;;;;;GAMG;AACH,qBAAa,cAAc;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAwB;IAC9C,OAAO,CAAC,MAAM,CAAsD;IAEpE,uEAAuE;gBAC3D,GAAG,CAAC,EAAE,MAAM;IAIxB;;;;OAIG;IACG,IAAI,IAAI,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC;IAsBvC,sEAAsE;IAChE,OAAO,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IASvC;;;;;OAKG;IACG,MAAM,CAAC,OAAO,EAAE,mBAAmB,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,mBAAmB,GAAG,OAAO,CAAC,SAAS,CAAC;IAkB1G,uDAAuD;YACzC,QAAQ;IAKtB;;;;;;OAMG;IACG,UAAU,CAAC,GAAG,EAAE,eAAe,GAAG,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC;CAgBxE;AAMD;;;GAGG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,cAAc,GAAG,OAAO,CA2B5D"}
|
package/dist/user-model.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { promises as fs } from 'node:fs';
|
|
2
2
|
import { join } from 'node:path';
|
|
3
|
-
import { z,
|
|
3
|
+
import { z, defineTool, createMutex, } from '@moxxy/sdk';
|
|
4
4
|
import { moxxyPath, writeFileAtomic } from '@moxxy/sdk/server';
|
|
5
5
|
/**
|
|
6
6
|
* The persistent USER MODEL — `~/.moxxy/memory/user-model.md`.
|
|
@@ -46,7 +46,6 @@ function templateModel() {
|
|
|
46
46
|
};
|
|
47
47
|
}
|
|
48
48
|
// ── parse / serialize (round-trips unknown sections) ─────────────────────────
|
|
49
|
-
const HEADING_RE = /^##\s+(.+?)\s*$/;
|
|
50
49
|
/**
|
|
51
50
|
* Parse the user-model Markdown into a preamble + ordered H2 sections. Any
|
|
52
51
|
* section title is preserved (not just the fixed four), so a user's hand-added
|
|
@@ -58,14 +57,12 @@ export function parseUserModel(text) {
|
|
|
58
57
|
let current = null;
|
|
59
58
|
let seenHeading = false;
|
|
60
59
|
for (const line of text.split('\n')) {
|
|
61
|
-
const
|
|
62
|
-
if (
|
|
60
|
+
const title = parseHeading(line);
|
|
61
|
+
if (title !== null) {
|
|
63
62
|
seenHeading = true;
|
|
64
63
|
if (current)
|
|
65
64
|
sections.push({ title: current.title, content: current.body.join('\n').trim() });
|
|
66
|
-
|
|
67
|
-
assertDefined(title, 'HEADING_RE capture group 1 is present on any match');
|
|
68
|
-
current = { title: title.trim(), body: [] };
|
|
65
|
+
current = { title, body: [] };
|
|
69
66
|
}
|
|
70
67
|
else if (!seenHeading) {
|
|
71
68
|
preambleLines.push(line);
|
|
@@ -78,6 +75,15 @@ export function parseUserModel(text) {
|
|
|
78
75
|
sections.push({ title: current.title, content: current.body.join('\n').trim() });
|
|
79
76
|
return { preamble: preambleLines.join('\n').trim(), sections };
|
|
80
77
|
}
|
|
78
|
+
function parseHeading(line) {
|
|
79
|
+
if (!line.startsWith('##') || !/\s/u.test(line[2] ?? ''))
|
|
80
|
+
return null;
|
|
81
|
+
let titleStart = 2;
|
|
82
|
+
while (titleStart < line.length && /\s/u.test(line[titleStart] ?? ''))
|
|
83
|
+
titleStart += 1;
|
|
84
|
+
const title = line.slice(titleStart).trim();
|
|
85
|
+
return title.length > 0 ? title : null;
|
|
86
|
+
}
|
|
81
87
|
/** Serialize back to Markdown, preserving the preamble and every section. */
|
|
82
88
|
export function serializeUserModel(model) {
|
|
83
89
|
const parts = [];
|
|
@@ -144,9 +150,9 @@ export class UserModelStore {
|
|
|
144
150
|
* provider request.
|
|
145
151
|
*/
|
|
146
152
|
async load() {
|
|
147
|
-
let
|
|
153
|
+
let handle;
|
|
148
154
|
try {
|
|
149
|
-
|
|
155
|
+
handle = await fs.open(this.path, 'r');
|
|
150
156
|
}
|
|
151
157
|
catch (err) {
|
|
152
158
|
if (isEnoent(err)) {
|
|
@@ -155,11 +161,17 @@ export class UserModelStore {
|
|
|
155
161
|
}
|
|
156
162
|
throw err;
|
|
157
163
|
}
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
164
|
+
try {
|
|
165
|
+
const mtimeMs = (await handle.stat()).mtimeMs;
|
|
166
|
+
if (this.cached && this.cached.mtimeMs === mtimeMs)
|
|
167
|
+
return this.cached.model;
|
|
168
|
+
const model = parseUserModel(await handle.readFile({ encoding: 'utf8' }));
|
|
169
|
+
this.cached = { mtimeMs, model };
|
|
170
|
+
return model;
|
|
171
|
+
}
|
|
172
|
+
finally {
|
|
173
|
+
await handle.close();
|
|
174
|
+
}
|
|
163
175
|
}
|
|
164
176
|
/** Raw file text (or `null` when absent) — used by the CLI viewer. */
|
|
165
177
|
async readRaw() {
|
package/dist/user-model.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"user-model.js","sourceRoot":"","sources":["../src/user-model.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,IAAI,EAAE,EAAE,MAAM,SAAS,CAAC;AACzC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EACL,CAAC,EACD,
|
|
1
|
+
{"version":3,"file":"user-model.js","sourceRoot":"","sources":["../src/user-model.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@moxxy/plugin-memory",
|
|
3
|
-
"version": "0.37.
|
|
3
|
+
"version": "0.37.2",
|
|
4
4
|
"description": "Journal-based long-term memory for moxxy. Markdown files at ~/.moxxy/memory/ with type/description frontmatter + an index. Plus STM selectors over the event log.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"moxxy",
|
|
@@ -43,13 +43,13 @@
|
|
|
43
43
|
}
|
|
44
44
|
},
|
|
45
45
|
"dependencies": {
|
|
46
|
-
"zod": "^3.
|
|
47
|
-
"@moxxy/sdk": "0.37.
|
|
46
|
+
"zod": "^3.25.76",
|
|
47
|
+
"@moxxy/sdk": "0.37.2"
|
|
48
48
|
},
|
|
49
49
|
"devDependencies": {
|
|
50
50
|
"@types/node": "^22.10.0",
|
|
51
51
|
"typescript": "^5.7.3",
|
|
52
|
-
"vitest": "^2.
|
|
52
|
+
"vitest": "^3.2.6",
|
|
53
53
|
"@moxxy/tsconfig": "0.0.0",
|
|
54
54
|
"@moxxy/vitest-preset": "0.0.0"
|
|
55
55
|
},
|
package/src/user-model.test.ts
CHANGED
|
@@ -19,6 +19,18 @@ let tmp: string;
|
|
|
19
19
|
const newStore = () => new UserModelStore(tmp);
|
|
20
20
|
const modelPath = () => path.join(tmp, 'user-model.md');
|
|
21
21
|
|
|
22
|
+
async function fileHandlePrototype(): Promise<
|
|
23
|
+
Pick<Awaited<ReturnType<typeof fs.open>>, 'readFile'>
|
|
24
|
+
> {
|
|
25
|
+
const handle = await fs.open(modelPath(), 'r');
|
|
26
|
+
const prototype = Object.getPrototypeOf(handle) as Pick<
|
|
27
|
+
Awaited<ReturnType<typeof fs.open>>,
|
|
28
|
+
'readFile'
|
|
29
|
+
>;
|
|
30
|
+
await handle.close();
|
|
31
|
+
return prototype;
|
|
32
|
+
}
|
|
33
|
+
|
|
22
34
|
beforeEach(async () => {
|
|
23
35
|
tmp = await fs.mkdtemp(path.join(os.tmpdir(), 'mox-um-'));
|
|
24
36
|
});
|
|
@@ -123,7 +135,7 @@ describe('UserModelStore.load (mtime cache)', () => {
|
|
|
123
135
|
it('reads the file once, then serves an unchanged file from cache (no re-read)', async () => {
|
|
124
136
|
const store = newStore();
|
|
125
137
|
await store.update('identity', 'first', 'replace');
|
|
126
|
-
const spy = vi.spyOn(
|
|
138
|
+
const spy = vi.spyOn(await fileHandlePrototype(), 'readFile');
|
|
127
139
|
await store.load();
|
|
128
140
|
await store.load();
|
|
129
141
|
await store.load();
|
|
@@ -264,7 +276,7 @@ describe('UserModelStore.injectInto', () => {
|
|
|
264
276
|
it('returns the request unchanged when parsing/reading throws', async () => {
|
|
265
277
|
const store = newStore();
|
|
266
278
|
await store.update('identity', 'A', 'replace');
|
|
267
|
-
vi.spyOn(
|
|
279
|
+
vi.spyOn(await fileHandlePrototype(), 'readFile').mockRejectedValueOnce(new Error('disk on fire'));
|
|
268
280
|
expect(await store.injectInto(req('BASE'))).toBeUndefined();
|
|
269
281
|
});
|
|
270
282
|
});
|
package/src/user-model.ts
CHANGED
|
@@ -2,7 +2,6 @@ import { promises as fs } from 'node:fs';
|
|
|
2
2
|
import { join } from 'node:path';
|
|
3
3
|
import {
|
|
4
4
|
z,
|
|
5
|
-
assertDefined,
|
|
6
5
|
defineTool,
|
|
7
6
|
createMutex,
|
|
8
7
|
type Mutex,
|
|
@@ -77,8 +76,6 @@ function templateModel(): UserModel {
|
|
|
77
76
|
|
|
78
77
|
// ── parse / serialize (round-trips unknown sections) ─────────────────────────
|
|
79
78
|
|
|
80
|
-
const HEADING_RE = /^##\s+(.+?)\s*$/;
|
|
81
|
-
|
|
82
79
|
/**
|
|
83
80
|
* Parse the user-model Markdown into a preamble + ordered H2 sections. Any
|
|
84
81
|
* section title is preserved (not just the fixed four), so a user's hand-added
|
|
@@ -91,13 +88,11 @@ export function parseUserModel(text: string): UserModel {
|
|
|
91
88
|
let seenHeading = false;
|
|
92
89
|
|
|
93
90
|
for (const line of text.split('\n')) {
|
|
94
|
-
const
|
|
95
|
-
if (
|
|
91
|
+
const title = parseHeading(line);
|
|
92
|
+
if (title !== null) {
|
|
96
93
|
seenHeading = true;
|
|
97
94
|
if (current) sections.push({ title: current.title, content: current.body.join('\n').trim() });
|
|
98
|
-
|
|
99
|
-
assertDefined(title, 'HEADING_RE capture group 1 is present on any match');
|
|
100
|
-
current = { title: title.trim(), body: [] };
|
|
95
|
+
current = { title, body: [] };
|
|
101
96
|
} else if (!seenHeading) {
|
|
102
97
|
preambleLines.push(line);
|
|
103
98
|
} else if (current) {
|
|
@@ -108,6 +103,14 @@ export function parseUserModel(text: string): UserModel {
|
|
|
108
103
|
return { preamble: preambleLines.join('\n').trim(), sections };
|
|
109
104
|
}
|
|
110
105
|
|
|
106
|
+
function parseHeading(line: string): string | null {
|
|
107
|
+
if (!line.startsWith('##') || !/\s/u.test(line[2] ?? '')) return null;
|
|
108
|
+
let titleStart = 2;
|
|
109
|
+
while (titleStart < line.length && /\s/u.test(line[titleStart] ?? '')) titleStart += 1;
|
|
110
|
+
const title = line.slice(titleStart).trim();
|
|
111
|
+
return title.length > 0 ? title : null;
|
|
112
|
+
}
|
|
113
|
+
|
|
111
114
|
/** Serialize back to Markdown, preserving the preamble and every section. */
|
|
112
115
|
export function serializeUserModel(model: UserModel): string {
|
|
113
116
|
const parts: string[] = [];
|
|
@@ -177,9 +180,9 @@ export class UserModelStore {
|
|
|
177
180
|
* provider request.
|
|
178
181
|
*/
|
|
179
182
|
async load(): Promise<UserModel | null> {
|
|
180
|
-
let
|
|
183
|
+
let handle: import('node:fs/promises').FileHandle;
|
|
181
184
|
try {
|
|
182
|
-
|
|
185
|
+
handle = await fs.open(this.path, 'r');
|
|
183
186
|
} catch (err) {
|
|
184
187
|
if (isEnoent(err)) {
|
|
185
188
|
this.cached = null;
|
|
@@ -187,10 +190,15 @@ export class UserModelStore {
|
|
|
187
190
|
}
|
|
188
191
|
throw err;
|
|
189
192
|
}
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
193
|
+
try {
|
|
194
|
+
const mtimeMs = (await handle.stat()).mtimeMs;
|
|
195
|
+
if (this.cached && this.cached.mtimeMs === mtimeMs) return this.cached.model;
|
|
196
|
+
const model = parseUserModel(await handle.readFile({ encoding: 'utf8' }));
|
|
197
|
+
this.cached = { mtimeMs, model };
|
|
198
|
+
return model;
|
|
199
|
+
} finally {
|
|
200
|
+
await handle.close();
|
|
201
|
+
}
|
|
194
202
|
}
|
|
195
203
|
|
|
196
204
|
/** Raw file text (or `null` when absent) — used by the CLI viewer. */
|