@openagentsy/memory 0.0.0-stage → 0.2.1
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 +674 -0
- package/README.md +33 -2
- package/dist/acceptance.test.d.ts +2 -0
- package/dist/acceptance.test.d.ts.map +1 -0
- package/dist/acceptance.test.js +138 -0
- package/dist/acceptance.test.js.map +1 -0
- package/dist/fixtures.d.ts +30 -0
- package/dist/fixtures.d.ts.map +1 -0
- package/dist/fixtures.js +49 -0
- package/dist/fixtures.js.map +1 -0
- package/dist/inbox.d.ts +131 -0
- package/dist/inbox.d.ts.map +1 -0
- package/dist/inbox.js +161 -0
- package/dist/inbox.js.map +1 -0
- package/dist/inbox.test.d.ts +2 -0
- package/dist/inbox.test.d.ts.map +1 -0
- package/dist/inbox.test.js +144 -0
- package/dist/inbox.test.js.map +1 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +15 -0
- package/dist/index.js.map +1 -0
- package/dist/model.d.ts +85 -0
- package/dist/model.d.ts.map +1 -0
- package/dist/model.js +71 -0
- package/dist/model.js.map +1 -0
- package/dist/permissions.test.d.ts +2 -0
- package/dist/permissions.test.d.ts.map +1 -0
- package/dist/permissions.test.js +53 -0
- package/dist/permissions.test.js.map +1 -0
- package/dist/store.d.ts +142 -0
- package/dist/store.d.ts.map +1 -0
- package/dist/store.js +263 -0
- package/dist/store.js.map +1 -0
- package/dist/store.test.d.ts +2 -0
- package/dist/store.test.d.ts.map +1 -0
- package/dist/store.test.js +223 -0
- package/dist/store.test.js.map +1 -0
- package/dist/tools.d.ts +86 -0
- package/dist/tools.d.ts.map +1 -0
- package/dist/tools.js +108 -0
- package/dist/tools.js.map +1 -0
- package/dist/tools.test.d.ts +2 -0
- package/dist/tools.test.d.ts.map +1 -0
- package/dist/tools.test.js +142 -0
- package/dist/tools.test.js.map +1 -0
- package/package.json +47 -4
package/dist/store.js
ADDED
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
import { appendFile, mkdir, readFile, stat } from 'node:fs/promises';
|
|
2
|
+
import { dirname, join } from 'node:path';
|
|
3
|
+
import { etagOf, redact } from '@openagentsy/core';
|
|
4
|
+
import { createMemoryId, isMemoryEntry, isMemoryMarker, isValidCategory, MAX_TEXT_LENGTH, normalizeText, } from './model.js';
|
|
5
|
+
/**
|
|
6
|
+
* The store of what a person decided to keep.
|
|
7
|
+
*
|
|
8
|
+
* Append-only: an entry is written once and never changed. A person who
|
|
9
|
+
* forgets or verifies a memory appends a marker instead, and the view this
|
|
10
|
+
* class returns folds the markers into the entries. The file therefore only
|
|
11
|
+
* ever grows, and a line in it is never rewritten.
|
|
12
|
+
*/
|
|
13
|
+
export class MemoryStore {
|
|
14
|
+
root;
|
|
15
|
+
now;
|
|
16
|
+
onAudit;
|
|
17
|
+
cached;
|
|
18
|
+
constructor(options) {
|
|
19
|
+
this.root = options.root;
|
|
20
|
+
this.now = options.now ?? (() => new Date());
|
|
21
|
+
this.onAudit = options.onAudit;
|
|
22
|
+
}
|
|
23
|
+
/** The file the store lives in. */
|
|
24
|
+
get path() {
|
|
25
|
+
return join(this.root, '.openagentsy', 'memory.jsonl');
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Add a memory.
|
|
29
|
+
*
|
|
30
|
+
* The redaction runs first: nothing reaches the file that carries a
|
|
31
|
+
* secret, and the count comes back with the outcome so the caller can
|
|
32
|
+
* report it. A text that is already a duplicate is refused with the id of
|
|
33
|
+
* the entry that holds it, because a memory that is already known is not
|
|
34
|
+
* a new thing.
|
|
35
|
+
*/
|
|
36
|
+
async append(input) {
|
|
37
|
+
const redacted = redact(input.text);
|
|
38
|
+
const redactions = redacted.count;
|
|
39
|
+
const text = redacted.text.trim();
|
|
40
|
+
if (text.length === 0) {
|
|
41
|
+
return { ok: false, reason: 'invalid', detail: 'the text is empty', redactions };
|
|
42
|
+
}
|
|
43
|
+
if (text.length > MAX_TEXT_LENGTH) {
|
|
44
|
+
return {
|
|
45
|
+
ok: false,
|
|
46
|
+
reason: 'invalid',
|
|
47
|
+
detail: `the text is longer than ${MAX_TEXT_LENGTH} characters`,
|
|
48
|
+
redactions,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
if (!isValidCategory(input.category)) {
|
|
52
|
+
return {
|
|
53
|
+
ok: false,
|
|
54
|
+
reason: 'invalid',
|
|
55
|
+
detail: 'the category is not a tag: a lowercase word, words joined by single hyphens',
|
|
56
|
+
redactions,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
const { entries, tags } = await this.read();
|
|
60
|
+
const tag = etagOf(normalizeText(text));
|
|
61
|
+
for (const entry of entries) {
|
|
62
|
+
if (tags.get(entry.id) === tag) {
|
|
63
|
+
return { ok: false, reason: 'duplicate', id: entry.id, redactions };
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
const entry = {
|
|
67
|
+
id: createMemoryId(),
|
|
68
|
+
text,
|
|
69
|
+
category: input.category,
|
|
70
|
+
source: input.source,
|
|
71
|
+
confidence: input.confidence,
|
|
72
|
+
created_at: this.now().toISOString(),
|
|
73
|
+
// A memory is trusted on write (the human's decision of 2026-10-28):
|
|
74
|
+
// the archivist's judgement is the source, and a person marks a
|
|
75
|
+
// memory unverified when they disagree. The flag is what the search
|
|
76
|
+
// reads before anyone has said anything about the memory.
|
|
77
|
+
verified: input.verified ?? true,
|
|
78
|
+
};
|
|
79
|
+
await this.appendLine(entry);
|
|
80
|
+
return { ok: true, entry, redactions };
|
|
81
|
+
}
|
|
82
|
+
/** Every memory that is neither forgotten nor superseded. */
|
|
83
|
+
async entries() {
|
|
84
|
+
const { entries, markers } = await this.read();
|
|
85
|
+
const forgotten = new Set();
|
|
86
|
+
const verified = new Set();
|
|
87
|
+
for (const marker of markers) {
|
|
88
|
+
if (marker.kind === 'forget')
|
|
89
|
+
forgotten.add(marker.id);
|
|
90
|
+
else
|
|
91
|
+
verified.add(marker.id);
|
|
92
|
+
}
|
|
93
|
+
return entries
|
|
94
|
+
.filter((entry) => !forgotten.has(entry.id))
|
|
95
|
+
.map((entry) => (verified.has(entry.id) ? { ...entry, verified: true } : entry));
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Find the memories that mention the terms of a query.
|
|
99
|
+
*
|
|
100
|
+
* The store is small - a target of under ten thousand entries - so the
|
|
101
|
+
* scan is over the parsed lines, and the parsed lines are cached against
|
|
102
|
+
* the file's stat, so a search that follows a search reads the file once.
|
|
103
|
+
*/
|
|
104
|
+
async search(query) {
|
|
105
|
+
const entries = await this.entries();
|
|
106
|
+
const terms = termsOf(query.q);
|
|
107
|
+
const category = query.category?.toLowerCase();
|
|
108
|
+
const scored = entries
|
|
109
|
+
.filter((entry) => inCategory(entry, category))
|
|
110
|
+
.map((entry) => ({ entry, score: scoreOf(entry, terms) }))
|
|
111
|
+
.filter((result) => result.score > 0);
|
|
112
|
+
const ordering = query.ordering ?? 'lost-in-the-middle';
|
|
113
|
+
scored.sort(ordering === 'recency' ? byRecency : byRelevance);
|
|
114
|
+
const top = query.limit === undefined ? scored : scored.slice(0, query.limit);
|
|
115
|
+
return ordering === 'lost-in-the-middle' ? lostInTheMiddle(top) : top;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Forget a memory.
|
|
119
|
+
*
|
|
120
|
+
* The entry stays in the file and the store stops returning it. The
|
|
121
|
+
* removal command is a later phase; this is the path it will take, and
|
|
122
|
+
* the person who did it is on the marker.
|
|
123
|
+
*/
|
|
124
|
+
async forget(id, by = 'unknown') {
|
|
125
|
+
const { entries } = await this.read();
|
|
126
|
+
if (!entries.some((entry) => entry.id === id))
|
|
127
|
+
return false;
|
|
128
|
+
await this.appendLine({ kind: 'forget', id, by, at: this.now().toISOString() });
|
|
129
|
+
await this.onAudit?.('oa.memory.forget', { id, decidedBy: by });
|
|
130
|
+
return true;
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Mark a memory verified.
|
|
134
|
+
*
|
|
135
|
+
* A memory is trusted on write, and this is the other direction: a
|
|
136
|
+
* person says it is true. The archivist's label is not a verification:
|
|
137
|
+
* the archivist never looked at the memory.
|
|
138
|
+
*/
|
|
139
|
+
async verify(id, decidedBy) {
|
|
140
|
+
const { entries } = await this.read();
|
|
141
|
+
if (!entries.some((entry) => entry.id === id))
|
|
142
|
+
return false;
|
|
143
|
+
await this.appendLine({ kind: 'verify', id, by: decidedBy, at: this.now().toISOString() });
|
|
144
|
+
await this.onAudit?.('oa.memory.verify', { id, decidedBy });
|
|
145
|
+
return true;
|
|
146
|
+
}
|
|
147
|
+
/** Append one line to the file. */
|
|
148
|
+
async appendLine(line) {
|
|
149
|
+
const path = this.path;
|
|
150
|
+
await mkdir(dirname(path), { recursive: true });
|
|
151
|
+
await appendFile(path, `${JSON.stringify(line)}\n`, 'utf8');
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* The parsed file, from the cache when the file has not changed.
|
|
155
|
+
*
|
|
156
|
+
* The key is the file's size and modification time, so a file that was
|
|
157
|
+
* appended to misses the cache and a file that was not is read once.
|
|
158
|
+
*/
|
|
159
|
+
async read() {
|
|
160
|
+
const path = this.path;
|
|
161
|
+
let key;
|
|
162
|
+
try {
|
|
163
|
+
const info = await stat(path);
|
|
164
|
+
key = `${info.mtimeMs}:${info.size}`;
|
|
165
|
+
}
|
|
166
|
+
catch {
|
|
167
|
+
return { entries: [], markers: [], tags: new Map() };
|
|
168
|
+
}
|
|
169
|
+
if (this.cached?.key === key)
|
|
170
|
+
return this.cached.file;
|
|
171
|
+
const text = await readFile(path, 'utf8');
|
|
172
|
+
const entries = [];
|
|
173
|
+
const markers = [];
|
|
174
|
+
const tags = new Map();
|
|
175
|
+
for (const line of text.split('\n')) {
|
|
176
|
+
const trimmed = line.trim();
|
|
177
|
+
if (trimmed.length === 0)
|
|
178
|
+
continue;
|
|
179
|
+
let parsed;
|
|
180
|
+
try {
|
|
181
|
+
parsed = JSON.parse(trimmed);
|
|
182
|
+
}
|
|
183
|
+
catch {
|
|
184
|
+
continue;
|
|
185
|
+
}
|
|
186
|
+
if (isMemoryEntry(parsed)) {
|
|
187
|
+
entries.push(parsed);
|
|
188
|
+
tags.set(parsed.id, etagOf(normalizeText(parsed.text)));
|
|
189
|
+
}
|
|
190
|
+
else if (isMemoryMarker(parsed)) {
|
|
191
|
+
markers.push(parsed);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
const file = { entries, markers, tags };
|
|
195
|
+
this.cached = { key, file };
|
|
196
|
+
return file;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
/** The terms of a query, lowercased. */
|
|
200
|
+
function termsOf(query) {
|
|
201
|
+
return query
|
|
202
|
+
.toLowerCase()
|
|
203
|
+
.split(/\s+/)
|
|
204
|
+
.filter((term) => term.length > 0);
|
|
205
|
+
}
|
|
206
|
+
/** Whether an entry is in the category the query names. */
|
|
207
|
+
function inCategory(entry, category) {
|
|
208
|
+
return category === undefined || entry.category.toLowerCase() === category;
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* The score of one entry against the terms of a query.
|
|
212
|
+
*
|
|
213
|
+
* A term in the text is worth more than a term in the category: the text is
|
|
214
|
+
* what the memory says, and the category only says what it is about.
|
|
215
|
+
*/
|
|
216
|
+
function scoreOf(entry, terms) {
|
|
217
|
+
const text = entry.text.toLowerCase();
|
|
218
|
+
const category = entry.category.toLowerCase();
|
|
219
|
+
let score = 0;
|
|
220
|
+
for (const term of terms) {
|
|
221
|
+
if (text.includes(term))
|
|
222
|
+
score += 2;
|
|
223
|
+
if (category.includes(term))
|
|
224
|
+
score += 1;
|
|
225
|
+
}
|
|
226
|
+
return score;
|
|
227
|
+
}
|
|
228
|
+
/** The best match first, and the newest of two equals first. */
|
|
229
|
+
function byRelevance(a, b) {
|
|
230
|
+
if (b.score !== a.score)
|
|
231
|
+
return b.score - a.score;
|
|
232
|
+
if (a.entry.created_at !== b.entry.created_at) {
|
|
233
|
+
return a.entry.created_at < b.entry.created_at ? 1 : -1;
|
|
234
|
+
}
|
|
235
|
+
return a.entry.id.localeCompare(b.entry.id);
|
|
236
|
+
}
|
|
237
|
+
/** The newest first. */
|
|
238
|
+
function byRecency(a, b) {
|
|
239
|
+
if (a.entry.created_at !== b.entry.created_at) {
|
|
240
|
+
return a.entry.created_at < b.entry.created_at ? 1 : -1;
|
|
241
|
+
}
|
|
242
|
+
return a.entry.id.localeCompare(b.entry.id);
|
|
243
|
+
}
|
|
244
|
+
/**
|
|
245
|
+
* The order that keeps the best results where a model looks first.
|
|
246
|
+
*
|
|
247
|
+
* A model attends to the start and the end of a context more than to the
|
|
248
|
+
* middle, so the strongest result goes first and the second strongest goes
|
|
249
|
+
* last, and the rest fill in between. The ranking is unchanged: only the
|
|
250
|
+
* positions move.
|
|
251
|
+
*/
|
|
252
|
+
export function lostInTheMiddle(ranked) {
|
|
253
|
+
const front = [];
|
|
254
|
+
const back = [];
|
|
255
|
+
ranked.forEach((item, index) => {
|
|
256
|
+
if (index % 2 === 0)
|
|
257
|
+
front.push(item);
|
|
258
|
+
else
|
|
259
|
+
back.push(item);
|
|
260
|
+
});
|
|
261
|
+
return [...front, ...back.reverse()];
|
|
262
|
+
}
|
|
263
|
+
//# sourceMappingURL=store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store.js","sourceRoot":"","sources":["../src/store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AACrE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AACnD,OAAO,EACL,cAAc,EACd,aAAa,EACb,cAAc,EACd,eAAe,EACf,eAAe,EAIf,aAAa,GACd,MAAM,YAAY,CAAC;AA6EpB;;;;;;;GAOG;AACH,MAAM,OAAO,WAAW;IACL,IAAI,CAAS;IACb,GAAG,CAAa;IAChB,OAAO,CAA8B;IAC9C,MAAM,CAAgD;IAE9D,YAAY,OAA2B;QACrC,IAAI,CAAC,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;QACzB,IAAI,CAAC,GAAG,GAAG,OAAO,CAAC,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC;QAC7C,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC;IACjC,CAAC;IAED,mCAAmC;IACnC,IAAI,IAAI;QACN,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,cAAc,EAAE,cAAc,CAAC,CAAC;IACzD,CAAC;IAED;;;;;;;;OAQG;IACH,KAAK,CAAC,MAAM,CAAC,KAAkB;QAC7B,MAAM,QAAQ,GAAG,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QACpC,MAAM,UAAU,GAAG,QAAQ,CAAC,KAAK,CAAC;QAClC,MAAM,IAAI,GAAG,QAAQ,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;QAElC,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACtB,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,mBAAmB,EAAE,UAAU,EAAE,CAAC;QACnF,CAAC;QACD,IAAI,IAAI,CAAC,MAAM,GAAG,eAAe,EAAE,CAAC;YAClC,OAAO;gBACL,EAAE,EAAE,KAAK;gBACT,MAAM,EAAE,SAAS;gBACjB,MAAM,EAAE,2BAA2B,eAAe,aAAa;gBAC/D,UAAU;aACX,CAAC;QACJ,CAAC;QACD,IAAI,CAAC,eAAe,CAAC,KAAK,CAAC,QAAQ,CAAC,EAAE,CAAC;YACrC,OAAO;gBACL,EAAE,EAAE,KAAK;gBACT,MAAM,EAAE,SAAS;gBACjB,MAAM,EAAE,6EAA6E;gBACrF,UAAU;aACX,CAAC;QACJ,CAAC;QAED,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,GAAG,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC;QAC5C,MAAM,GAAG,GAAG,MAAM,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC;QACxC,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;YAC5B,IAAI,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,GAAG,EAAE,CAAC;gBAC/B,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,EAAE,EAAE,KAAK,CAAC,EAAE,EAAE,UAAU,EAAE,CAAC;YACtE,CAAC;QACH,CAAC;QAED,MAAM,KAAK,GAAgB;YACzB,EAAE,EAAE,cAAc,EAAE;YACpB,IAAI;YACJ,QAAQ,EAAE,KAAK,CAAC,QAAQ;YACxB,MAAM,EAAE,KAAK,CAAC,MAAM;YACpB,UAAU,EAAE,KAAK,CAAC,UAAU;YAC5B,UAAU,EAAE,IAAI,CAAC,GAAG,EAAE,CAAC,WAAW,EAAE;YACpC,qEAAqE;YACrE,gEAAgE;YAChE,oEAAoE;YACpE,0DAA0D;YAC1D,QAAQ,EAAE,KAAK,CAAC,QAAQ,IAAI,IAAI;SACjC,CAAC;QACF,MAAM,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;QAC7B,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,UAAU,EAAE,CAAC;IACzC,CAAC;IAED,6DAA6D;IAC7D,KAAK,CAAC,OAAO;QACX,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,GAAG,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC;QAC/C,MAAM,SAAS,GAAG,IAAI,GAAG,EAAU,CAAC;QACpC,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAU,CAAC;QACnC,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;YAC7B,IAAI,MAAM,CAAC,IAAI,KAAK,QAAQ;gBAAE,SAAS,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;;gBAClD,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QAC/B,CAAC;QACD,OAAO,OAAO;aACX,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;aAC3C,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,GAAG,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC;IACrF,CAAC;IAED;;;;;;OAMG;IACH,KAAK,CAAC,MAAM,CAAC,KAAkB;QAC7B,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,OAAO,EAAE,CAAC;QACrC,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;QAC/B,MAAM,QAAQ,GAAG,KAAK,CAAC,QAAQ,EAAE,WAAW,EAAE,CAAC;QAE/C,MAAM,MAAM,GAAG,OAAO;aACnB,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,UAAU,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;aAC9C,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC,EAAE,CAAC,CAAC;aACzD,MAAM,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC;QAExC,MAAM,QAAQ,GAAG,KAAK,CAAC,QAAQ,IAAI,oBAAoB,CAAC;QACxD,MAAM,CAAC,IAAI,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC;QAC9D,MAAM,GAAG,GAAG,KAAK,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC;QAC9E,OAAO,QAAQ,KAAK,oBAAoB,CAAC,CAAC,CAAC,eAAe,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC;IACxE,CAAC;IAED;;;;;;OAMG;IACH,KAAK,CAAC,MAAM,CAAC,EAAU,EAAE,KAAa,SAAS;QAC7C,MAAM,EAAE,OAAO,EAAE,GAAG,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC;QACtC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,CAAC;YAAE,OAAO,KAAK,CAAC;QAC5D,MAAM,IAAI,CAAC,UAAU,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,IAAI,CAAC,GAAG,EAAE,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC;QAChF,MAAM,IAAI,CAAC,OAAO,EAAE,CAAC,kBAAkB,EAAE,EAAE,EAAE,EAAE,SAAS,EAAE,EAAE,EAAE,CAAC,CAAC;QAChE,OAAO,IAAI,CAAC;IACd,CAAC;IAED;;;;;;OAMG;IACH,KAAK,CAAC,MAAM,CAAC,EAAU,EAAE,SAAiB;QACxC,MAAM,EAAE,OAAO,EAAE,GAAG,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC;QACtC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,CAAC;YAAE,OAAO,KAAK,CAAC;QAC5D,MAAM,IAAI,CAAC,UAAU,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,EAAE,EAAE,EAAE,SAAS,EAAE,EAAE,EAAE,IAAI,CAAC,GAAG,EAAE,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC;QAC3F,MAAM,IAAI,CAAC,OAAO,EAAE,CAAC,kBAAkB,EAAE,EAAE,EAAE,EAAE,SAAS,EAAE,CAAC,CAAC;QAC5D,OAAO,IAAI,CAAC;IACd,CAAC;IAED,mCAAmC;IAC3B,KAAK,CAAC,UAAU,CAAC,IAAgC;QACvD,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC;QACvB,MAAM,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAChD,MAAM,UAAU,CAAC,IAAI,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IAC9D,CAAC;IAED;;;;;OAKG;IACK,KAAK,CAAC,IAAI;QAChB,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC;QACvB,IAAI,GAAW,CAAC;QAChB,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,CAAC;YAC9B,GAAG,GAAG,GAAG,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;QACvC,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,EAAE,OAAO,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,GAAG,EAAE,EAAE,CAAC;QACvD,CAAC;QACD,IAAI,IAAI,CAAC,MAAM,EAAE,GAAG,KAAK,GAAG;YAAE,OAAO,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC;QAEtD,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;QAC1C,MAAM,OAAO,GAAkB,EAAE,CAAC;QAClC,MAAM,OAAO,GAAmB,EAAE,CAAC;QACnC,MAAM,IAAI,GAAG,IAAI,GAAG,EAAkB,CAAC;QACvC,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;YACpC,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;YAC5B,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;gBAAE,SAAS;YACnC,IAAI,MAAe,CAAC;YACpB,IAAI,CAAC;gBACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;YAC/B,CAAC;YAAC,MAAM,CAAC;gBACP,SAAS;YACX,CAAC;YACD,IAAI,aAAa,CAAC,MAAM,CAAC,EAAE,CAAC;gBAC1B,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;gBACrB,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,EAAE,MAAM,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YAC1D,CAAC;iBAAM,IAAI,cAAc,CAAC,MAAM,CAAC,EAAE,CAAC;gBAClC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YACvB,CAAC;QACH,CAAC;QACD,MAAM,IAAI,GAAe,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;QACpD,IAAI,CAAC,MAAM,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC;QAC5B,OAAO,IAAI,CAAC;IACd,CAAC;CACF;AAED,wCAAwC;AACxC,SAAS,OAAO,CAAC,KAAa;IAC5B,OAAO,KAAK;SACT,WAAW,EAAE;SACb,KAAK,CAAC,KAAK,CAAC;SACZ,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;AACvC,CAAC;AAED,2DAA2D;AAC3D,SAAS,UAAU,CAAC,KAAkB,EAAE,QAA4B;IAClE,OAAO,QAAQ,KAAK,SAAS,IAAI,KAAK,CAAC,QAAQ,CAAC,WAAW,EAAE,KAAK,QAAQ,CAAC;AAC7E,CAAC;AAED;;;;;GAKG;AACH,SAAS,OAAO,CAAC,KAAkB,EAAE,KAAe;IAClD,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;IACtC,MAAM,QAAQ,GAAG,KAAK,CAAC,QAAQ,CAAC,WAAW,EAAE,CAAC;IAC9C,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC;YAAE,KAAK,IAAI,CAAC,CAAC;QACpC,IAAI,QAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC;YAAE,KAAK,IAAI,CAAC,CAAC;IAC1C,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,gEAAgE;AAChE,SAAS,WAAW,CAAC,CAAe,EAAE,CAAe;IACnD,IAAI,CAAC,CAAC,KAAK,KAAK,CAAC,CAAC,KAAK;QAAE,OAAO,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC;IAClD,IAAI,CAAC,CAAC,KAAK,CAAC,UAAU,KAAK,CAAC,CAAC,KAAK,CAAC,UAAU,EAAE,CAAC;QAC9C,OAAO,CAAC,CAAC,KAAK,CAAC,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC1D,CAAC;IACD,OAAO,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,aAAa,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;AAC9C,CAAC;AAED,wBAAwB;AACxB,SAAS,SAAS,CAAC,CAAe,EAAE,CAAe;IACjD,IAAI,CAAC,CAAC,KAAK,CAAC,UAAU,KAAK,CAAC,CAAC,KAAK,CAAC,UAAU,EAAE,CAAC;QAC9C,OAAO,CAAC,CAAC,KAAK,CAAC,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC1D,CAAC;IACD,OAAO,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,aAAa,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;AAC9C,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,eAAe,CAAI,MAAoB;IACrD,MAAM,KAAK,GAAQ,EAAE,CAAC;IACtB,MAAM,IAAI,GAAQ,EAAE,CAAC;IACrB,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE;QAC7B,IAAI,KAAK,GAAG,CAAC,KAAK,CAAC;YAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;;YACjC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACvB,CAAC,CAAC,CAAC;IACH,OAAO,CAAC,GAAG,KAAK,EAAE,GAAG,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC;AACvC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store.test.d.ts","sourceRoot":"","sources":["../src/store.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
import { existsSync } from 'node:fs';
|
|
2
|
+
import { readFile } from 'node:fs/promises';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import { describe, expect, it } from 'vitest';
|
|
5
|
+
import { temporaryRoot } from './fixtures.js';
|
|
6
|
+
import { MemoryStore } from './store.js';
|
|
7
|
+
/** A key that looks like an AWS access key, so the redaction finds it. */
|
|
8
|
+
const SEEDED_KEY = 'AKIAIOSFODNN7EXAMPLE';
|
|
9
|
+
async function storeIn(root, onAudit) {
|
|
10
|
+
return new MemoryStore({
|
|
11
|
+
root,
|
|
12
|
+
now: () => new Date('2026-10-26T12:00:00.000Z'),
|
|
13
|
+
...(onAudit === undefined ? {} : { onAudit }),
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
describe('the memory store', () => {
|
|
17
|
+
it('writes one line per memory', async () => {
|
|
18
|
+
const { root, remove } = await temporaryRoot();
|
|
19
|
+
try {
|
|
20
|
+
const store = await storeIn(root);
|
|
21
|
+
const outcome = await store.append({
|
|
22
|
+
text: 'The lease guard refuses a write to a file no lease covers.',
|
|
23
|
+
category: 'build',
|
|
24
|
+
source: '7 · 2a-1',
|
|
25
|
+
confidence: 'inferred',
|
|
26
|
+
});
|
|
27
|
+
expect(outcome.ok).toBe(true);
|
|
28
|
+
const path = join(root, '.openagentsy', 'memory.jsonl');
|
|
29
|
+
const lines = (await readFile(path, 'utf8')).trim().split('\n');
|
|
30
|
+
expect(lines).toHaveLength(1);
|
|
31
|
+
const entry = JSON.parse(lines[0] ?? '{}');
|
|
32
|
+
expect(entry.id).toMatch(/^oa-mem-[A-Za-z0-9_-]{6}$/);
|
|
33
|
+
expect(entry.category).toBe('build');
|
|
34
|
+
expect(entry.source).toBe('7 · 2a-1');
|
|
35
|
+
expect(entry.confidence).toBe('inferred');
|
|
36
|
+
expect(entry.created_at).toBe('2026-10-26T12:00:00.000Z');
|
|
37
|
+
// A memory is trusted on write; a person marks it unverified when
|
|
38
|
+
// they disagree (the human's decision of 2026-10-28).
|
|
39
|
+
expect(entry.verified).toBe(true);
|
|
40
|
+
}
|
|
41
|
+
finally {
|
|
42
|
+
await remove();
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
it('redacts before it writes, and says how much it redacted', async () => {
|
|
46
|
+
const { root, remove } = await temporaryRoot();
|
|
47
|
+
try {
|
|
48
|
+
const store = await storeIn(root);
|
|
49
|
+
const outcome = await store.append({
|
|
50
|
+
text: `The key is ${SEEDED_KEY} and it must not be kept.`,
|
|
51
|
+
category: 'build',
|
|
52
|
+
source: '7 · 2a-1',
|
|
53
|
+
confidence: 'verified',
|
|
54
|
+
});
|
|
55
|
+
expect(outcome.ok).toBe(true);
|
|
56
|
+
expect(outcome.redactions).toBe(1);
|
|
57
|
+
const written = await readFile(join(root, '.openagentsy', 'memory.jsonl'), 'utf8');
|
|
58
|
+
expect(written).not.toContain(SEEDED_KEY);
|
|
59
|
+
expect(written).toContain('redacted:aws-access-key');
|
|
60
|
+
}
|
|
61
|
+
finally {
|
|
62
|
+
await remove();
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
it('refuses a duplicate with the id of the memory that holds it', async () => {
|
|
66
|
+
const { root, remove } = await temporaryRoot();
|
|
67
|
+
try {
|
|
68
|
+
const store = await storeIn(root);
|
|
69
|
+
const first = await store.append({
|
|
70
|
+
text: 'The lease guard refuses a write to a file no lease covers.',
|
|
71
|
+
category: 'build',
|
|
72
|
+
source: '7 · 2a-1',
|
|
73
|
+
confidence: 'inferred',
|
|
74
|
+
});
|
|
75
|
+
expect(first.ok).toBe(true);
|
|
76
|
+
if (!first.ok)
|
|
77
|
+
return;
|
|
78
|
+
const second = await store.append({
|
|
79
|
+
text: ' The LEASE guard refuses a write to a file no lease covers. ',
|
|
80
|
+
category: 'build',
|
|
81
|
+
source: '8 · 2a-2',
|
|
82
|
+
confidence: 'inferred',
|
|
83
|
+
});
|
|
84
|
+
expect(second.ok).toBe(false);
|
|
85
|
+
if (second.ok)
|
|
86
|
+
throw new Error('the duplicate was accepted');
|
|
87
|
+
expect(second.reason).toBe('duplicate');
|
|
88
|
+
if (second.reason !== 'duplicate')
|
|
89
|
+
throw new Error(`the outcome was ${second.reason}`);
|
|
90
|
+
expect(second.id).toBe(first.entry.id);
|
|
91
|
+
}
|
|
92
|
+
finally {
|
|
93
|
+
await remove();
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
it('refuses a text that is too long, and a category that is not a tag', async () => {
|
|
97
|
+
const { root, remove } = await temporaryRoot();
|
|
98
|
+
try {
|
|
99
|
+
const store = await storeIn(root);
|
|
100
|
+
const long = await store.append({
|
|
101
|
+
text: 'x'.repeat(281),
|
|
102
|
+
category: 'build',
|
|
103
|
+
source: '7 · 2a-1',
|
|
104
|
+
confidence: 'guess',
|
|
105
|
+
});
|
|
106
|
+
expect(long.ok).toBe(false);
|
|
107
|
+
if (!long.ok)
|
|
108
|
+
expect(long.reason).toBe('invalid');
|
|
109
|
+
const tagged = await store.append({
|
|
110
|
+
text: 'A short enough text.',
|
|
111
|
+
category: 'Not A Tag',
|
|
112
|
+
source: '7 · 2a-1',
|
|
113
|
+
confidence: 'guess',
|
|
114
|
+
});
|
|
115
|
+
expect(tagged.ok).toBe(false);
|
|
116
|
+
}
|
|
117
|
+
finally {
|
|
118
|
+
await remove();
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
it('folds a forget marker into the view and keeps the line in the file', async () => {
|
|
122
|
+
const { root, remove } = await temporaryRoot();
|
|
123
|
+
try {
|
|
124
|
+
const store = await storeIn(root);
|
|
125
|
+
const outcome = await store.append({
|
|
126
|
+
text: 'A memory that is about to be forgotten.',
|
|
127
|
+
category: 'build',
|
|
128
|
+
source: '7 · 2a-1',
|
|
129
|
+
confidence: 'guess',
|
|
130
|
+
});
|
|
131
|
+
expect(outcome.ok).toBe(true);
|
|
132
|
+
if (!outcome.ok)
|
|
133
|
+
return;
|
|
134
|
+
expect(await store.forget(outcome.entry.id, 'daniel')).toBe(true);
|
|
135
|
+
expect(await store.entries()).toEqual([]);
|
|
136
|
+
const lines = (await readFile(join(root, '.openagentsy', 'memory.jsonl'), 'utf8'))
|
|
137
|
+
.trim()
|
|
138
|
+
.split('\n');
|
|
139
|
+
expect(lines).toHaveLength(2);
|
|
140
|
+
expect(JSON.parse(lines[1] ?? '{}')).toMatchObject({
|
|
141
|
+
kind: 'forget',
|
|
142
|
+
id: outcome.entry.id,
|
|
143
|
+
by: 'daniel',
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
finally {
|
|
147
|
+
await remove();
|
|
148
|
+
}
|
|
149
|
+
});
|
|
150
|
+
it('marks a memory verified, and audits who did it', async () => {
|
|
151
|
+
const { root, remove } = await temporaryRoot();
|
|
152
|
+
try {
|
|
153
|
+
const audited = [];
|
|
154
|
+
const store = await storeIn(root, (policyId, detail) => {
|
|
155
|
+
audited.push({ policyId, detail });
|
|
156
|
+
});
|
|
157
|
+
const outcome = await store.append({
|
|
158
|
+
text: 'A memory that a person will verify.',
|
|
159
|
+
category: 'build',
|
|
160
|
+
source: '7 · 2a-1',
|
|
161
|
+
confidence: 'guess',
|
|
162
|
+
// A person marked it unverified when it was written; the call
|
|
163
|
+
// below marks it verified.
|
|
164
|
+
verified: false,
|
|
165
|
+
});
|
|
166
|
+
expect(outcome.ok).toBe(true);
|
|
167
|
+
if (!outcome.ok)
|
|
168
|
+
return;
|
|
169
|
+
expect(outcome.entry.verified).toBe(false);
|
|
170
|
+
expect(await store.verify(outcome.entry.id, 'daniel')).toBe(true);
|
|
171
|
+
const entries = await store.entries();
|
|
172
|
+
expect(entries[0]?.verified).toBe(true);
|
|
173
|
+
expect(audited).toEqual([
|
|
174
|
+
{ policyId: 'oa.memory.verify', detail: { id: outcome.entry.id, decidedBy: 'daniel' } },
|
|
175
|
+
]);
|
|
176
|
+
}
|
|
177
|
+
finally {
|
|
178
|
+
await remove();
|
|
179
|
+
}
|
|
180
|
+
});
|
|
181
|
+
it('reads the file once until it changes', async () => {
|
|
182
|
+
const { root, remove } = await temporaryRoot();
|
|
183
|
+
try {
|
|
184
|
+
const store = await storeIn(root);
|
|
185
|
+
await store.append({
|
|
186
|
+
text: 'The first memory.',
|
|
187
|
+
category: 'build',
|
|
188
|
+
source: '7 · 2a-1',
|
|
189
|
+
confidence: 'guess',
|
|
190
|
+
});
|
|
191
|
+
expect(await store.entries()).toHaveLength(1);
|
|
192
|
+
// The cache is keyed on the file's stat, so a line written behind the
|
|
193
|
+
// store's back is seen on the next read.
|
|
194
|
+
const path = join(root, '.openagentsy', 'memory.jsonl');
|
|
195
|
+
const { appendFile } = await import('node:fs/promises');
|
|
196
|
+
await appendFile(path, `${JSON.stringify({
|
|
197
|
+
id: 'oa-mem-external',
|
|
198
|
+
text: 'A memory written by someone else.',
|
|
199
|
+
category: 'build',
|
|
200
|
+
source: '9 · 2a-9',
|
|
201
|
+
confidence: 'guess',
|
|
202
|
+
created_at: '2026-10-26T12:00:00.000Z',
|
|
203
|
+
verified: false,
|
|
204
|
+
})}\n`, 'utf8');
|
|
205
|
+
expect(await store.entries()).toHaveLength(2);
|
|
206
|
+
}
|
|
207
|
+
finally {
|
|
208
|
+
await remove();
|
|
209
|
+
}
|
|
210
|
+
});
|
|
211
|
+
it('has no file before the first memory', async () => {
|
|
212
|
+
const { root, remove } = await temporaryRoot();
|
|
213
|
+
try {
|
|
214
|
+
const store = await storeIn(root);
|
|
215
|
+
expect(existsSync(join(root, '.openagentsy', 'memory.jsonl'))).toBe(false);
|
|
216
|
+
expect(await store.entries()).toEqual([]);
|
|
217
|
+
}
|
|
218
|
+
finally {
|
|
219
|
+
await remove();
|
|
220
|
+
}
|
|
221
|
+
});
|
|
222
|
+
});
|
|
223
|
+
//# sourceMappingURL=store.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store.test.js","sourceRoot":"","sources":["../src/store.test.ts"],"names":[],"mappings":"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"}
|
package/dist/tools.d.ts
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { memoryToolNames, type ToolDef } from '@openagentsy/core';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import type { MemoryInbox } from './inbox.js';
|
|
4
|
+
import type { MemoryStore } from './store.js';
|
|
5
|
+
/**
|
|
6
|
+
* The memory tools.
|
|
7
|
+
*
|
|
8
|
+
* Three tools: search, list, and the decision. The decision tool exists and
|
|
9
|
+
* works, and the permission map is what keeps an agent from calling it: the
|
|
10
|
+
* map denies `oa_memory_decide` to every role, so only a person, through
|
|
11
|
+
* the command that will call this same code, decides what is kept.
|
|
12
|
+
*/
|
|
13
|
+
/**
|
|
14
|
+
* The tool names, for the base-tool vocabulary.
|
|
15
|
+
*
|
|
16
|
+
* The list lives in core, with the other tool names, so that the vocabulary
|
|
17
|
+
* which resolves a tool name back to its base name and the tools the
|
|
18
|
+
* server offers are one list.
|
|
19
|
+
*/
|
|
20
|
+
export { memoryToolNames };
|
|
21
|
+
/** The most results a search may return. */
|
|
22
|
+
export declare const MAX_SEARCH_LIMIT = 8;
|
|
23
|
+
/** The results a search returns when the caller names no limit. */
|
|
24
|
+
export declare const DEFAULT_SEARCH_LIMIT = 5;
|
|
25
|
+
/** The most entries a list may return. */
|
|
26
|
+
export declare const MAX_LIST_LIMIT = 20;
|
|
27
|
+
/** The entries a list returns when the caller names no limit. */
|
|
28
|
+
export declare const DEFAULT_LIST_LIMIT = 10;
|
|
29
|
+
/** What the tools need. */
|
|
30
|
+
export type MemoryToolDeps = {
|
|
31
|
+
store: MemoryStore;
|
|
32
|
+
inbox: MemoryInbox;
|
|
33
|
+
};
|
|
34
|
+
/**
|
|
35
|
+
* A memory, as a tool returns it.
|
|
36
|
+
*
|
|
37
|
+
* An unverified memory is redacted: the text becomes a marker that names
|
|
38
|
+
* the id, so the caller can ask for it again and say so. The rest of the
|
|
39
|
+
* entry stays, because what a memory is about is not a secret and a caller
|
|
40
|
+
* that cannot see the category cannot ask for the category.
|
|
41
|
+
*/
|
|
42
|
+
export type MemoryHit = {
|
|
43
|
+
id: string;
|
|
44
|
+
text: string;
|
|
45
|
+
category: string;
|
|
46
|
+
source: string;
|
|
47
|
+
confidence: string;
|
|
48
|
+
created_at: string;
|
|
49
|
+
verified: boolean;
|
|
50
|
+
};
|
|
51
|
+
declare const searchArgs: z.ZodObject<{
|
|
52
|
+
q: z.ZodString;
|
|
53
|
+
category: z.ZodOptional<z.ZodString>;
|
|
54
|
+
limit: z.ZodOptional<z.ZodNumber>;
|
|
55
|
+
ordering: z.ZodOptional<z.ZodEnum<{
|
|
56
|
+
relevance: "relevance";
|
|
57
|
+
recency: "recency";
|
|
58
|
+
"lost-in-the-middle": "lost-in-the-middle";
|
|
59
|
+
}>>;
|
|
60
|
+
includeUnverified: z.ZodOptional<z.ZodBoolean>;
|
|
61
|
+
}, z.core.$strip>;
|
|
62
|
+
declare const listArgs: z.ZodObject<{
|
|
63
|
+
limit: z.ZodOptional<z.ZodNumber>;
|
|
64
|
+
}, z.core.$strip>;
|
|
65
|
+
declare const decideArgs: z.ZodObject<{
|
|
66
|
+
recordId: z.ZodNumber;
|
|
67
|
+
decision: z.ZodEnum<{
|
|
68
|
+
approve: "approve";
|
|
69
|
+
reject: "reject";
|
|
70
|
+
}>;
|
|
71
|
+
decidedBy: z.ZodString;
|
|
72
|
+
}, z.core.$strip>;
|
|
73
|
+
/** The three tools, each with the shape of its own arguments. */
|
|
74
|
+
export type MemoryTools = {
|
|
75
|
+
oa_memory_search: ToolDef<typeof searchArgs>;
|
|
76
|
+
oa_memory_list: ToolDef<typeof listArgs>;
|
|
77
|
+
oa_memory_decide: ToolDef<typeof decideArgs>;
|
|
78
|
+
};
|
|
79
|
+
/**
|
|
80
|
+
* The three tools, defined once.
|
|
81
|
+
*
|
|
82
|
+
* The definitions are handed to the two runtimes through the helper in
|
|
83
|
+
* core, so the shape is the same in both OpenCode versions.
|
|
84
|
+
*/
|
|
85
|
+
export declare function createMemoryTools(deps: MemoryToolDeps): MemoryTools;
|
|
86
|
+
//# sourceMappingURL=tools.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"AAAA,OAAO,EAAc,eAAe,EAAE,KAAK,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAC9E,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C;;;;;;;GAOG;AAEH;;;;;;GAMG;AACH,OAAO,EAAE,eAAe,EAAE,CAAC;AAE3B,4CAA4C;AAC5C,eAAO,MAAM,gBAAgB,IAAI,CAAC;AAElC,mEAAmE;AACnE,eAAO,MAAM,oBAAoB,IAAI,CAAC;AAEtC,0CAA0C;AAC1C,eAAO,MAAM,cAAc,KAAK,CAAC;AAEjC,iEAAiE;AACjE,eAAO,MAAM,kBAAkB,KAAK,CAAC;AAErC,2BAA2B;AAC3B,MAAM,MAAM,cAAc,GAAG;IAC3B,KAAK,EAAE,WAAW,CAAC;IACnB,KAAK,EAAE,WAAW,CAAC;CACpB,CAAC;AAEF;;;;;;;GAOG;AACH,MAAM,MAAM,SAAS,GAAG;IACtB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,OAAO,CAAC;CACnB,CAAC;AAEF,QAAA,MAAM,UAAU;;;;;;;;;;iBAMd,CAAC;AAEH,QAAA,MAAM,QAAQ;;iBAEZ,CAAC;AAEH,QAAA,MAAM,UAAU;;;;;;;iBAId,CAAC;AAEH,iEAAiE;AACjE,MAAM,MAAM,WAAW,GAAG;IACxB,gBAAgB,EAAE,OAAO,CAAC,OAAO,UAAU,CAAC,CAAC;IAC7C,cAAc,EAAE,OAAO,CAAC,OAAO,QAAQ,CAAC,CAAC;IACzC,gBAAgB,EAAE,OAAO,CAAC,OAAO,UAAU,CAAC,CAAC;CAC9C,CAAC;AAEF;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,cAAc,GAAG,WAAW,CA8CnE"}
|