@nxgt/ai-memory 0.0.0-stage → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +173 -2
- package/dist/actions.d.ts +9 -0
- package/dist/actions.d.ts.map +1 -0
- package/dist/chunks/index-082dnty9.js +188 -0
- package/dist/chunks/index-082dnty9.js.map +14 -0
- package/dist/chunks/index-vj24rcgc.js +119 -0
- package/dist/chunks/index-vj24rcgc.js.map +13 -0
- package/dist/compact.d.ts +38 -0
- package/dist/compact.d.ts.map +1 -0
- package/dist/contract-actions.d.ts +3 -0
- package/dist/contract-actions.d.ts.map +1 -0
- package/dist/contract-messages.d.ts +3 -0
- package/dist/contract-messages.d.ts.map +1 -0
- package/dist/contract-owner.d.ts +3 -0
- package/dist/contract-owner.d.ts.map +1 -0
- package/dist/contract-samples.d.ts +6 -0
- package/dist/contract-samples.d.ts.map +1 -0
- package/dist/contract-threads.d.ts +3 -0
- package/dist/contract-threads.d.ts.map +1 -0
- package/dist/contract.d.ts +9 -0
- package/dist/contract.d.ts.map +1 -0
- package/dist/errors.d.ts +11 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/guards.d.ts +5 -0
- package/dist/guards.d.ts.map +1 -0
- package/dist/in-process.d.ts +8 -0
- package/dist/in-process.d.ts.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +167 -0
- package/dist/index.js.map +13 -0
- package/dist/messages.d.ts +7 -0
- package/dist/messages.d.ts.map +1 -0
- package/dist/mongo-actions.d.ts +8 -0
- package/dist/mongo-actions.d.ts.map +1 -0
- package/dist/mongo-collection.d.ts +32 -0
- package/dist/mongo-collection.d.ts.map +1 -0
- package/dist/mongo-ctx.d.ts +12 -0
- package/dist/mongo-ctx.d.ts.map +1 -0
- package/dist/mongo-fields.d.ts +41 -0
- package/dist/mongo-fields.d.ts.map +1 -0
- package/dist/mongo-map.d.ts +8 -0
- package/dist/mongo-map.d.ts.map +1 -0
- package/dist/mongo-messages.d.ts +7 -0
- package/dist/mongo-messages.d.ts.map +1 -0
- package/dist/mongo-store.d.ts +16 -0
- package/dist/mongo-store.d.ts.map +1 -0
- package/dist/mongo-threads.d.ts +13 -0
- package/dist/mongo-threads.d.ts.map +1 -0
- package/dist/mongo.d.ts +4 -0
- package/dist/mongo.d.ts.map +1 -0
- package/dist/mongo.js +271 -0
- package/dist/mongo.js.map +16 -0
- package/dist/paging.d.ts +8 -0
- package/dist/paging.d.ts.map +1 -0
- package/dist/pairs.d.ts +14 -0
- package/dist/pairs.d.ts.map +1 -0
- package/dist/redis-actions.d.ts +8 -0
- package/dist/redis-actions.d.ts.map +1 -0
- package/dist/redis-ctx.d.ts +37 -0
- package/dist/redis-ctx.d.ts.map +1 -0
- package/dist/redis-map.d.ts +13 -0
- package/dist/redis-map.d.ts.map +1 -0
- package/dist/redis-messages.d.ts +7 -0
- package/dist/redis-messages.d.ts.map +1 -0
- package/dist/redis-scripts.d.ts +40 -0
- package/dist/redis-scripts.d.ts.map +1 -0
- package/dist/redis-store.d.ts +21 -0
- package/dist/redis-store.d.ts.map +1 -0
- package/dist/redis-threads.d.ts +16 -0
- package/dist/redis-threads.d.ts.map +1 -0
- package/dist/redis.d.ts +3 -0
- package/dist/redis.d.ts.map +1 -0
- package/dist/redis.js +373 -0
- package/dist/redis.js.map +16 -0
- package/dist/schemas.d.ts +124 -0
- package/dist/schemas.d.ts.map +1 -0
- package/dist/state.d.ts +28 -0
- package/dist/state.d.ts.map +1 -0
- package/dist/summarize.d.ts +11 -0
- package/dist/summarize.d.ts.map +1 -0
- package/dist/testing.d.ts +3 -0
- package/dist/testing.d.ts.map +1 -0
- package/dist/testing.js +264 -0
- package/dist/testing.js.map +15 -0
- package/dist/threads.d.ts +7 -0
- package/dist/threads.d.ts.map +1 -0
- package/dist/truncate.d.ts +17 -0
- package/dist/truncate.d.ts.map +1 -0
- package/dist/types.d.ts +92 -0
- package/dist/types.d.ts.map +1 -0
- package/docs/README.md +9 -0
- package/docs/guide/memory.md +327 -0
- package/docs/roadmap.md +31 -0
- package/docs/troubleshooting.md +171 -0
- package/package.json +85 -5
package/dist/testing.js
ADDED
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
import {
|
|
2
|
+
inProcessMemory2
|
|
3
|
+
} from "./chunks/index-082dnty9.js";
|
|
4
|
+
import {
|
|
5
|
+
MemoryError2
|
|
6
|
+
} from "./chunks/index-vj24rcgc.js";
|
|
7
|
+
|
|
8
|
+
// src/contract.ts
|
|
9
|
+
import { describe } from "bun:test";
|
|
10
|
+
|
|
11
|
+
// src/contract-actions.ts
|
|
12
|
+
import { expect, it } from "bun:test";
|
|
13
|
+
|
|
14
|
+
// src/contract-samples.ts
|
|
15
|
+
var text = (t, role = "user") => ({
|
|
16
|
+
role,
|
|
17
|
+
parts: [{ type: "text", text: t }]
|
|
18
|
+
});
|
|
19
|
+
var page = (cursor) => ({
|
|
20
|
+
limit: 2,
|
|
21
|
+
...cursor === undefined ? {} : { cursor }
|
|
22
|
+
});
|
|
23
|
+
var proposal = (id) => ({
|
|
24
|
+
role: "assistant",
|
|
25
|
+
parts: [
|
|
26
|
+
{
|
|
27
|
+
type: "proposed-action",
|
|
28
|
+
id,
|
|
29
|
+
tool: "archive",
|
|
30
|
+
input: {},
|
|
31
|
+
expiresAt: 9000000000000000
|
|
32
|
+
}
|
|
33
|
+
]
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
// src/contract-actions.ts
|
|
37
|
+
function contractActions(make) {
|
|
38
|
+
it("finds an action from the proposed-action part", async () => {
|
|
39
|
+
const store = await make();
|
|
40
|
+
const t = await store.createThread({ ownerId: "a" });
|
|
41
|
+
await store.append(t.id, "a", [proposal("act-1")]);
|
|
42
|
+
expect(await store.findAction("act-1", "a")).toMatchObject({
|
|
43
|
+
id: "act-1",
|
|
44
|
+
tool: "archive",
|
|
45
|
+
status: "pending"
|
|
46
|
+
});
|
|
47
|
+
expect(await store.findAction("other", "a")).toBeUndefined();
|
|
48
|
+
});
|
|
49
|
+
it("lets one of two concurrent confirmations win", async () => {
|
|
50
|
+
const store = await make();
|
|
51
|
+
const t = await store.createThread({ ownerId: "a" });
|
|
52
|
+
await store.append(t.id, "a", [proposal("act-1")]);
|
|
53
|
+
const results = await Promise.all([
|
|
54
|
+
store.updateAction("act-1", "a", { status: "confirmed" }),
|
|
55
|
+
store.updateAction("act-1", "a", { status: "confirmed" })
|
|
56
|
+
]);
|
|
57
|
+
expect(results.filter(Boolean)).toHaveLength(1);
|
|
58
|
+
expect((await store.findAction("act-1", "a"))?.status).toBe("confirmed");
|
|
59
|
+
expect(await store.updateAction("act-1", "a", { status: "rejected" })).toBeUndefined();
|
|
60
|
+
});
|
|
61
|
+
it("removes a thread together with its actions", async () => {
|
|
62
|
+
const store = await make();
|
|
63
|
+
const t = await store.createThread({ ownerId: "a" });
|
|
64
|
+
await store.append(t.id, "a", [proposal("act-1")]);
|
|
65
|
+
await store.deleteThread(t.id, "a");
|
|
66
|
+
expect(await store.findAction("act-1", "a")).toBeUndefined();
|
|
67
|
+
});
|
|
68
|
+
it("keeps the same action id apart for two owners", async () => {
|
|
69
|
+
const store = await make();
|
|
70
|
+
const a = await store.createThread({ ownerId: "a" });
|
|
71
|
+
const b = await store.createThread({ ownerId: "b" });
|
|
72
|
+
await store.append(b.id, "b", [proposal("act-1")]);
|
|
73
|
+
await store.append(a.id, "a", [proposal("act-1")]);
|
|
74
|
+
expect((await store.findAction("act-1", "a"))?.status).toBe("pending");
|
|
75
|
+
await store.updateAction("act-1", "b", { status: "rejected" });
|
|
76
|
+
expect((await store.findAction("act-1", "a"))?.status).toBe("pending");
|
|
77
|
+
});
|
|
78
|
+
it("records the result of a settled action", async () => {
|
|
79
|
+
const store = await make();
|
|
80
|
+
const t = await store.createThread({ ownerId: "a" });
|
|
81
|
+
await store.append(t.id, "a", [proposal("act-1")]);
|
|
82
|
+
const result = {
|
|
83
|
+
type: "tool-result",
|
|
84
|
+
callId: "c1",
|
|
85
|
+
name: "archive",
|
|
86
|
+
isError: false,
|
|
87
|
+
output: { ok: true }
|
|
88
|
+
};
|
|
89
|
+
const done = await store.updateAction("act-1", "a", {
|
|
90
|
+
status: "confirmed",
|
|
91
|
+
result
|
|
92
|
+
});
|
|
93
|
+
expect(done?.result).toEqual(result);
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// src/contract-messages.ts
|
|
98
|
+
import { expect as expect2, it as it2 } from "bun:test";
|
|
99
|
+
function contractMessages(make) {
|
|
100
|
+
it2("appends messages in order and loads them", async () => {
|
|
101
|
+
const store = await make();
|
|
102
|
+
const t = await store.createThread({ ownerId: "a" });
|
|
103
|
+
await store.append(t.id, "a", [text("one"), text("two", "assistant")]);
|
|
104
|
+
await store.append(t.id, "a", [text("three")]);
|
|
105
|
+
const loaded = await store.load(t.id, "a", { sinceSummary: false });
|
|
106
|
+
expect2(loaded?.messages.map((m) => m.seq)).toEqual([1, 2, 3]);
|
|
107
|
+
expect2(loaded?.messages.map((m) => m.message.parts[0])).toEqual([
|
|
108
|
+
{ type: "text", text: "one" },
|
|
109
|
+
{ type: "text", text: "two" },
|
|
110
|
+
{ type: "text", text: "three" }
|
|
111
|
+
]);
|
|
112
|
+
expect2(loaded?.thread.messageCount).toBe(3);
|
|
113
|
+
});
|
|
114
|
+
it2("rejects a message that is not valid", async () => {
|
|
115
|
+
const store = await make();
|
|
116
|
+
const t = await store.createThread({ ownerId: "a" });
|
|
117
|
+
const bad = { role: "nobody", parts: [] };
|
|
118
|
+
await expect2(store.append(t.id, "a", [bad])).rejects.toThrow();
|
|
119
|
+
expect2((await store.getThread(t.id, "a"))?.messageCount).toBe(0);
|
|
120
|
+
});
|
|
121
|
+
it2("loads only what follows the summary when asked", async () => {
|
|
122
|
+
const store = await make();
|
|
123
|
+
const t = await store.createThread({ ownerId: "a" });
|
|
124
|
+
const stored = await store.append(t.id, "a", [
|
|
125
|
+
text("1"),
|
|
126
|
+
text("2"),
|
|
127
|
+
text("3")
|
|
128
|
+
]);
|
|
129
|
+
const through = stored[1]?.id ?? "";
|
|
130
|
+
await store.setSummary(t.id, "a", {
|
|
131
|
+
text: "sum",
|
|
132
|
+
throughMessageId: through,
|
|
133
|
+
tokens: 7
|
|
134
|
+
});
|
|
135
|
+
const since = await store.load(t.id, "a", { sinceSummary: true });
|
|
136
|
+
expect2(since?.summary).toMatchObject({
|
|
137
|
+
text: "sum",
|
|
138
|
+
throughMessageId: through,
|
|
139
|
+
tokens: 7
|
|
140
|
+
});
|
|
141
|
+
expect2(since?.messages.map((m) => m.seq)).toEqual([3]);
|
|
142
|
+
const all = await store.load(t.id, "a", { sinceSummary: false });
|
|
143
|
+
expect2(all?.messages).toHaveLength(3);
|
|
144
|
+
await expect2(store.setSummary(t.id, "a", {
|
|
145
|
+
text: "s",
|
|
146
|
+
throughMessageId: "missing",
|
|
147
|
+
tokens: 1
|
|
148
|
+
})).rejects.toMatchObject({ code: "MESSAGE_NOT_FOUND" });
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// src/contract-owner.ts
|
|
153
|
+
import { expect as expect3, it as it3 } from "bun:test";
|
|
154
|
+
function contractOwner(make) {
|
|
155
|
+
it3("hides another owner's thread on every call", async () => {
|
|
156
|
+
const store = await make();
|
|
157
|
+
const t = await store.createThread({ ownerId: "a" });
|
|
158
|
+
const [m] = await store.append(t.id, "a", [proposal("act-1")]);
|
|
159
|
+
expect3(await store.getThread(t.id, "b")).toBeUndefined();
|
|
160
|
+
expect3((await store.listThreads("b")).items).toEqual([]);
|
|
161
|
+
expect3(await store.load(t.id, "b", { sinceSummary: false })).toBeUndefined();
|
|
162
|
+
expect3(await store.findAction("act-1", "b")).toBeUndefined();
|
|
163
|
+
expect3(await store.updateAction("act-1", "b", { status: "confirmed" })).toBeUndefined();
|
|
164
|
+
expect3(await store.deleteThread(t.id, "b")).toBe(false);
|
|
165
|
+
const denied = [
|
|
166
|
+
store.append(t.id, "b", [text("x")]),
|
|
167
|
+
store.setSummary(t.id, "b", {
|
|
168
|
+
text: "s",
|
|
169
|
+
throughMessageId: m?.id ?? "",
|
|
170
|
+
tokens: 1
|
|
171
|
+
})
|
|
172
|
+
];
|
|
173
|
+
const settled = await Promise.allSettled(denied);
|
|
174
|
+
for (const outcome of settled) {
|
|
175
|
+
expect3(outcome.status).toBe("rejected");
|
|
176
|
+
expect3(outcome.reason).toMatchObject({
|
|
177
|
+
code: "THREAD_NOT_FOUND"
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
expect3((await store.findAction("act-1", "a"))?.status).toBe("pending");
|
|
181
|
+
expect3((await store.load(t.id, "a", { sinceSummary: false }))?.messages).toHaveLength(1);
|
|
182
|
+
});
|
|
183
|
+
it3("refuses an empty owner on every call", async () => {
|
|
184
|
+
const store = await make();
|
|
185
|
+
const calls = [
|
|
186
|
+
store.createThread({ ownerId: "" }),
|
|
187
|
+
store.getThread("x", ""),
|
|
188
|
+
store.listThreads(""),
|
|
189
|
+
store.findAction("x", "")
|
|
190
|
+
];
|
|
191
|
+
for (const call of calls) {
|
|
192
|
+
await expect3(call).rejects.toMatchObject({ code: "INVALID_OWNER" });
|
|
193
|
+
}
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// src/contract-threads.ts
|
|
198
|
+
import { expect as expect4, it as it4 } from "bun:test";
|
|
199
|
+
function contractThreads(make) {
|
|
200
|
+
it4("creates, gets and deletes a thread", async () => {
|
|
201
|
+
const store = await make();
|
|
202
|
+
const t = await store.createThread({
|
|
203
|
+
ownerId: "a",
|
|
204
|
+
title: "Hi",
|
|
205
|
+
meta: { k: 1 }
|
|
206
|
+
});
|
|
207
|
+
expect4(await store.getThread(t.id, "a")).toMatchObject({
|
|
208
|
+
id: t.id,
|
|
209
|
+
ownerId: "a",
|
|
210
|
+
title: "Hi",
|
|
211
|
+
meta: { k: 1 },
|
|
212
|
+
messageCount: 0
|
|
213
|
+
});
|
|
214
|
+
expect4(await store.deleteThread(t.id, "a")).toBe(true);
|
|
215
|
+
expect4(await store.getThread(t.id, "a")).toBeUndefined();
|
|
216
|
+
expect4(await store.deleteThread(t.id, "a")).toBe(false);
|
|
217
|
+
});
|
|
218
|
+
it4("pages threads newest first with a cursor", async () => {
|
|
219
|
+
const store = await make();
|
|
220
|
+
const ids = [];
|
|
221
|
+
for (let i = 0;i < 5; i++)
|
|
222
|
+
ids.push((await store.createThread({ ownerId: "a" })).id);
|
|
223
|
+
await store.createThread({ ownerId: "b" });
|
|
224
|
+
const id = (n) => ids[n];
|
|
225
|
+
const first = await store.listThreads("a", { limit: 2 });
|
|
226
|
+
expect4(first.items.map((t) => t.id)).toEqual([id(4), id(3)]);
|
|
227
|
+
expect4(first.nextCursor).toBeDefined();
|
|
228
|
+
const second = await store.listThreads("a", page(first.nextCursor));
|
|
229
|
+
expect4(second.items.map((t) => t.id)).toEqual([id(2), id(1)]);
|
|
230
|
+
const third = await store.listThreads("a", page(second.nextCursor));
|
|
231
|
+
expect4(third.items.map((t) => t.id)).toEqual([id(0)]);
|
|
232
|
+
expect4(third.nextCursor).toBeUndefined();
|
|
233
|
+
});
|
|
234
|
+
it4("rejects a cursor it did not give", async () => {
|
|
235
|
+
const store = await make();
|
|
236
|
+
await expect4(store.listThreads("a", { cursor: "nope" })).rejects.toBeInstanceOf(MemoryError2);
|
|
237
|
+
});
|
|
238
|
+
it4("clamps the page size to 1..100", async () => {
|
|
239
|
+
const store = await make();
|
|
240
|
+
for (let i = 0;i < 101; i++)
|
|
241
|
+
await store.createThread({ ownerId: "a" });
|
|
242
|
+
const big = await store.listThreads("a", { limit: 1000 });
|
|
243
|
+
expect4(big.items).toHaveLength(100);
|
|
244
|
+
expect4(big.nextCursor).toBeDefined();
|
|
245
|
+
expect4((await store.listThreads("a", { limit: 0 })).items).toHaveLength(1);
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// src/contract.ts
|
|
250
|
+
function memoryStoreContract(make) {
|
|
251
|
+
describe("MemoryStore contract", () => {
|
|
252
|
+
contractOwner(make);
|
|
253
|
+
contractThreads(make);
|
|
254
|
+
contractMessages(make);
|
|
255
|
+
contractActions(make);
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
export {
|
|
259
|
+
inProcessMemory2 as inProcessMemory,
|
|
260
|
+
memoryStoreContract
|
|
261
|
+
};
|
|
262
|
+
|
|
263
|
+
//# debugId=E202FEA64A1A3C1564756E2164756E21
|
|
264
|
+
//# sourceMappingURL=testing.js.map
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/contract.ts", "../src/contract-actions.ts", "../src/contract-samples.ts", "../src/contract-messages.ts", "../src/contract-owner.ts", "../src/contract-threads.ts"],
|
|
4
|
+
"sourcesContent": [
|
|
5
|
+
"import { describe } from 'bun:test';\nimport { contractActions } from './contract-actions';\nimport { contractMessages } from './contract-messages';\nimport { contractOwner } from './contract-owner';\nimport { contractThreads } from './contract-threads';\nimport type { MemoryStore } from './types';\n\n/**\n * The behaviour every `MemoryStore` must have. Call it inside a test file with\n * a factory that returns a fresh, empty store each time:\n *\n * memoryStoreContract(() => myStore());\n */\nexport function memoryStoreContract(\n\tmake: () => MemoryStore | Promise<MemoryStore>,\n): void {\n\tdescribe('MemoryStore contract', () => {\n\t\tcontractOwner(make);\n\t\tcontractThreads(make);\n\t\tcontractMessages(make);\n\t\tcontractActions(make);\n\t});\n}\n",
|
|
6
|
+
"import { expect, it } from 'bun:test';\nimport { proposal } from './contract-samples';\nimport type { MemoryStore } from './types';\n\nexport function contractActions(\n\tmake: () => MemoryStore | Promise<MemoryStore>,\n): void {\n\tit('finds an action from the proposed-action part', async () => {\n\t\tconst store = await make();\n\t\tconst t = await store.createThread({ ownerId: 'a' });\n\t\tawait store.append(t.id, 'a', [proposal('act-1')]);\n\t\texpect(await store.findAction('act-1', 'a')).toMatchObject({\n\t\t\tid: 'act-1',\n\t\t\ttool: 'archive',\n\t\t\tstatus: 'pending',\n\t\t});\n\t\texpect(await store.findAction('other', 'a')).toBeUndefined();\n\t});\n\n\tit('lets one of two concurrent confirmations win', async () => {\n\t\tconst store = await make();\n\t\tconst t = await store.createThread({ ownerId: 'a' });\n\t\tawait store.append(t.id, 'a', [proposal('act-1')]);\n\t\tconst results = await Promise.all([\n\t\t\tstore.updateAction('act-1', 'a', { status: 'confirmed' }),\n\t\t\tstore.updateAction('act-1', 'a', { status: 'confirmed' }),\n\t\t]);\n\t\texpect(results.filter(Boolean)).toHaveLength(1);\n\t\texpect((await store.findAction('act-1', 'a'))?.status).toBe('confirmed');\n\t\texpect(\n\t\t\tawait store.updateAction('act-1', 'a', { status: 'rejected' }),\n\t\t).toBeUndefined();\n\t});\n\n\tit('removes a thread together with its actions', async () => {\n\t\tconst store = await make();\n\t\tconst t = await store.createThread({ ownerId: 'a' });\n\t\tawait store.append(t.id, 'a', [proposal('act-1')]);\n\t\tawait store.deleteThread(t.id, 'a');\n\t\texpect(await store.findAction('act-1', 'a')).toBeUndefined();\n\t});\n\n\tit('keeps the same action id apart for two owners', async () => {\n\t\tconst store = await make();\n\t\tconst a = await store.createThread({ ownerId: 'a' });\n\t\tconst b = await store.createThread({ ownerId: 'b' });\n\t\tawait store.append(b.id, 'b', [proposal('act-1')]);\n\t\tawait store.append(a.id, 'a', [proposal('act-1')]);\n\t\texpect((await store.findAction('act-1', 'a'))?.status).toBe('pending');\n\t\tawait store.updateAction('act-1', 'b', { status: 'rejected' });\n\t\texpect((await store.findAction('act-1', 'a'))?.status).toBe('pending');\n\t});\n\n\tit('records the result of a settled action', async () => {\n\t\tconst store = await make();\n\t\tconst t = await store.createThread({ ownerId: 'a' });\n\t\tawait store.append(t.id, 'a', [proposal('act-1')]);\n\t\tconst result = {\n\t\t\ttype: 'tool-result',\n\t\t\tcallId: 'c1',\n\t\t\tname: 'archive',\n\t\t\tisError: false,\n\t\t\toutput: { ok: true },\n\t\t} as const;\n\t\tconst done = await store.updateAction('act-1', 'a', {\n\t\t\tstatus: 'confirmed',\n\t\t\tresult,\n\t\t});\n\t\texpect(done?.result).toEqual(result);\n\t});\n}\n",
|
|
7
|
+
"import type { Message } from '@nxgt/ai-core';\nimport type { Page } from './types';\n\nexport const text = (t: string, role: Message['role'] = 'user'): Message => ({\n\trole,\n\tparts: [{ type: 'text', text: t }],\n});\n\nexport const page = (cursor?: string): Page => ({\n\tlimit: 2,\n\t...(cursor === undefined ? {} : { cursor }),\n});\n\nexport const proposal = (id: string): Message => ({\n\trole: 'assistant',\n\tparts: [\n\t\t{\n\t\t\ttype: 'proposed-action',\n\t\t\tid,\n\t\t\ttool: 'archive',\n\t\t\tinput: {},\n\t\t\texpiresAt: 9e15,\n\t\t},\n\t],\n});\n",
|
|
8
|
+
"import { expect, it } from 'bun:test';\nimport type { Message } from '@nxgt/ai-core';\nimport { text } from './contract-samples';\nimport type { MemoryStore } from './types';\n\nexport function contractMessages(\n\tmake: () => MemoryStore | Promise<MemoryStore>,\n): void {\n\tit('appends messages in order and loads them', async () => {\n\t\tconst store = await make();\n\t\tconst t = await store.createThread({ ownerId: 'a' });\n\t\tawait store.append(t.id, 'a', [text('one'), text('two', 'assistant')]);\n\t\tawait store.append(t.id, 'a', [text('three')]);\n\t\tconst loaded = await store.load(t.id, 'a', { sinceSummary: false });\n\t\texpect(loaded?.messages.map((m) => m.seq)).toEqual([1, 2, 3]);\n\t\texpect(loaded?.messages.map((m) => m.message.parts[0])).toEqual([\n\t\t\t{ type: 'text', text: 'one' },\n\t\t\t{ type: 'text', text: 'two' },\n\t\t\t{ type: 'text', text: 'three' },\n\t\t]);\n\t\texpect(loaded?.thread.messageCount).toBe(3);\n\t});\n\n\tit('rejects a message that is not valid', async () => {\n\t\tconst store = await make();\n\t\tconst t = await store.createThread({ ownerId: 'a' });\n\t\tconst bad = { role: 'nobody', parts: [] } as unknown as Message;\n\t\tawait expect(store.append(t.id, 'a', [bad])).rejects.toThrow();\n\t\texpect((await store.getThread(t.id, 'a'))?.messageCount).toBe(0);\n\t});\n\n\tit('loads only what follows the summary when asked', async () => {\n\t\tconst store = await make();\n\t\tconst t = await store.createThread({ ownerId: 'a' });\n\t\tconst stored = await store.append(t.id, 'a', [\n\t\t\ttext('1'),\n\t\t\ttext('2'),\n\t\t\ttext('3'),\n\t\t]);\n\t\tconst through = stored[1]?.id ?? '';\n\t\tawait store.setSummary(t.id, 'a', {\n\t\t\ttext: 'sum',\n\t\t\tthroughMessageId: through,\n\t\t\ttokens: 7,\n\t\t});\n\t\tconst since = await store.load(t.id, 'a', { sinceSummary: true });\n\t\texpect(since?.summary).toMatchObject({\n\t\t\ttext: 'sum',\n\t\t\tthroughMessageId: through,\n\t\t\ttokens: 7,\n\t\t});\n\t\texpect(since?.messages.map((m) => m.seq)).toEqual([3]);\n\t\tconst all = await store.load(t.id, 'a', { sinceSummary: false });\n\t\texpect(all?.messages).toHaveLength(3);\n\t\tawait expect(\n\t\t\tstore.setSummary(t.id, 'a', {\n\t\t\t\ttext: 's',\n\t\t\t\tthroughMessageId: 'missing',\n\t\t\t\ttokens: 1,\n\t\t\t}),\n\t\t).rejects.toMatchObject({ code: 'MESSAGE_NOT_FOUND' });\n\t});\n}\n",
|
|
9
|
+
"import { expect, it } from 'bun:test';\nimport { proposal, text } from './contract-samples';\nimport type { MemoryStore } from './types';\n\nexport function contractOwner(\n\tmake: () => MemoryStore | Promise<MemoryStore>,\n): void {\n\tit(\"hides another owner's thread on every call\", async () => {\n\t\tconst store = await make();\n\t\tconst t = await store.createThread({ ownerId: 'a' });\n\t\tconst [m] = await store.append(t.id, 'a', [proposal('act-1')]);\n\t\texpect(await store.getThread(t.id, 'b')).toBeUndefined();\n\t\texpect((await store.listThreads('b')).items).toEqual([]);\n\t\texpect(\n\t\t\tawait store.load(t.id, 'b', { sinceSummary: false }),\n\t\t).toBeUndefined();\n\t\texpect(await store.findAction('act-1', 'b')).toBeUndefined();\n\t\texpect(\n\t\t\tawait store.updateAction('act-1', 'b', { status: 'confirmed' }),\n\t\t).toBeUndefined();\n\t\texpect(await store.deleteThread(t.id, 'b')).toBe(false);\n\t\tconst denied = [\n\t\t\tstore.append(t.id, 'b', [text('x')]),\n\t\t\tstore.setSummary(t.id, 'b', {\n\t\t\t\ttext: 's',\n\t\t\t\tthroughMessageId: m?.id ?? '',\n\t\t\t\ttokens: 1,\n\t\t\t}),\n\t\t];\n\t\t// Settled together: a store may answer the second call before the\n\t\t// first, and a rejection nobody awaits yet is reported as unhandled.\n\t\tconst settled = await Promise.allSettled(denied);\n\t\tfor (const outcome of settled) {\n\t\t\texpect(outcome.status).toBe('rejected');\n\t\t\texpect((outcome as PromiseRejectedResult).reason).toMatchObject({\n\t\t\t\tcode: 'THREAD_NOT_FOUND',\n\t\t\t});\n\t\t}\n\t\texpect((await store.findAction('act-1', 'a'))?.status).toBe('pending');\n\t\texpect(\n\t\t\t(await store.load(t.id, 'a', { sinceSummary: false }))?.messages,\n\t\t).toHaveLength(1);\n\t});\n\n\tit('refuses an empty owner on every call', async () => {\n\t\tconst store = await make();\n\t\tconst calls = [\n\t\t\tstore.createThread({ ownerId: '' }),\n\t\t\tstore.getThread('x', ''),\n\t\t\tstore.listThreads(''),\n\t\t\tstore.findAction('x', ''),\n\t\t];\n\t\tfor (const call of calls) {\n\t\t\tawait expect(call).rejects.toMatchObject({ code: 'INVALID_OWNER' });\n\t\t}\n\t});\n}\n",
|
|
10
|
+
"import { expect, it } from 'bun:test';\nimport { page } from './contract-samples';\nimport { MemoryError } from './errors';\nimport type { MemoryStore } from './types';\n\nexport function contractThreads(\n\tmake: () => MemoryStore | Promise<MemoryStore>,\n): void {\n\tit('creates, gets and deletes a thread', async () => {\n\t\tconst store = await make();\n\t\tconst t = await store.createThread({\n\t\t\townerId: 'a',\n\t\t\ttitle: 'Hi',\n\t\t\tmeta: { k: 1 },\n\t\t});\n\t\texpect(await store.getThread(t.id, 'a')).toMatchObject({\n\t\t\tid: t.id,\n\t\t\townerId: 'a',\n\t\t\ttitle: 'Hi',\n\t\t\tmeta: { k: 1 },\n\t\t\tmessageCount: 0,\n\t\t});\n\t\texpect(await store.deleteThread(t.id, 'a')).toBe(true);\n\t\texpect(await store.getThread(t.id, 'a')).toBeUndefined();\n\t\texpect(await store.deleteThread(t.id, 'a')).toBe(false);\n\t});\n\n\tit('pages threads newest first with a cursor', async () => {\n\t\tconst store = await make();\n\t\tconst ids: string[] = [];\n\t\tfor (let i = 0; i < 5; i++)\n\t\t\tids.push((await store.createThread({ ownerId: 'a' })).id);\n\t\tawait store.createThread({ ownerId: 'b' });\n\t\tconst id = (n: number) => ids[n] as string;\n\t\tconst first = await store.listThreads('a', { limit: 2 });\n\t\texpect(first.items.map((t) => t.id)).toEqual([id(4), id(3)]);\n\t\texpect(first.nextCursor).toBeDefined();\n\t\tconst second = await store.listThreads('a', page(first.nextCursor));\n\t\texpect(second.items.map((t) => t.id)).toEqual([id(2), id(1)]);\n\t\tconst third = await store.listThreads('a', page(second.nextCursor));\n\t\texpect(third.items.map((t) => t.id)).toEqual([id(0)]);\n\t\texpect(third.nextCursor).toBeUndefined();\n\t});\n\n\tit('rejects a cursor it did not give', async () => {\n\t\tconst store = await make();\n\t\tawait expect(\n\t\t\tstore.listThreads('a', { cursor: 'nope' }),\n\t\t).rejects.toBeInstanceOf(MemoryError);\n\t});\n\n\tit('clamps the page size to 1..100', async () => {\n\t\tconst store = await make();\n\t\tfor (let i = 0; i < 101; i++) await store.createThread({ ownerId: 'a' });\n\t\tconst big = await store.listThreads('a', { limit: 1000 });\n\t\texpect(big.items).toHaveLength(100);\n\t\texpect(big.nextCursor).toBeDefined();\n\t\texpect((await store.listThreads('a', { limit: 0 })).items).toHaveLength(1);\n\t});\n}\n"
|
|
11
|
+
],
|
|
12
|
+
"mappings": "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",
|
|
13
|
+
"debugId": "E202FEA64A1A3C1564756E2164756E21",
|
|
14
|
+
"names": []
|
|
15
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { type State } from './state.js';
|
|
2
|
+
import type { CreateThreadInput, Page, Thread, ThreadPage } from './types.js';
|
|
3
|
+
export declare function createThread(s: State, input: CreateThreadInput): Thread;
|
|
4
|
+
export declare function getThread(s: State, id: string, ownerId: string): Thread | undefined;
|
|
5
|
+
export declare function listThreads(s: State, ownerId: string, page: Page): ThreadPage;
|
|
6
|
+
export declare function deleteThread(s: State, id: string, ownerId: string): boolean;
|
|
7
|
+
//# sourceMappingURL=threads.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"threads.d.ts","sourceRoot":"","sources":["../src/threads.ts"],"names":[],"mappings":"AAEA,OAAO,EAAoB,KAAK,KAAK,EAAE,MAAM,SAAS,CAAC;AACvD,OAAO,KAAK,EAAE,iBAAiB,EAAE,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAU3E,wBAAgB,YAAY,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,iBAAiB,GAAG,MAAM,CAcvE;AAED,wBAAgB,SAAS,CAAC,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,sBAG9D;AAED,wBAAgB,WAAW,CAAC,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,GAAG,UAAU,CAc7E;AAED,wBAAgB,YAAY,CAAC,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAK3E"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { Message, Part } from '@nxgt/ai-core';
|
|
2
|
+
import type { StoredMessage } from './types.js';
|
|
3
|
+
/** What replaces the output of a tool result that was cut. */
|
|
4
|
+
export interface TruncatedOutput {
|
|
5
|
+
truncated: true;
|
|
6
|
+
/** The size of the original output, serialised, in characters. */
|
|
7
|
+
originalChars: number;
|
|
8
|
+
/** The start of the original output, serialised. */
|
|
9
|
+
preview: string;
|
|
10
|
+
}
|
|
11
|
+
export declare function isTruncated(output: unknown): output is TruncatedOutput;
|
|
12
|
+
/** The part with its output cut to `max` characters, or undefined when it fits. */
|
|
13
|
+
export declare function truncatePart(part: Part, max: number): Part | undefined;
|
|
14
|
+
/** The message with the tool result at `index` cut, or undefined when it fits. */
|
|
15
|
+
export declare function truncateAt(m: StoredMessage, index: number, max: number): StoredMessage | undefined;
|
|
16
|
+
export declare const plain: (messages: readonly StoredMessage[]) => Message[];
|
|
17
|
+
//# sourceMappingURL=truncate.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"truncate.d.ts","sourceRoot":"","sources":["../src/truncate.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AACnD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAE7C,8DAA8D;AAC9D,MAAM,WAAW,eAAe;IAC/B,SAAS,EAAE,IAAI,CAAC;IAChB,kEAAkE;IAClE,aAAa,EAAE,MAAM,CAAC;IACtB,oDAAoD;IACpD,OAAO,EAAE,MAAM,CAAC;CAChB;AAED,wBAAgB,WAAW,CAAC,MAAM,EAAE,OAAO,GAAG,MAAM,IAAI,eAAe,CAMtE;AAED,mFAAmF;AACnF,wBAAgB,YAAY,CAAC,IAAI,EAAE,IAAI,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAYtE;AAED,kFAAkF;AAClF,wBAAgB,UAAU,CACzB,CAAC,EAAE,aAAa,EAChB,KAAK,EAAE,MAAM,EACb,GAAG,EAAE,MAAM,GACT,aAAa,GAAG,SAAS,CAM3B;AAED,eAAO,MAAM,KAAK,GAAI,UAAU,SAAS,aAAa,EAAE,KAAG,OAAO,EACnC,CAAC"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import type { Message, ProposedAction, ProposedActionStatus, ToolResultPart } from '@nxgt/ai-core';
|
|
2
|
+
/** A rolling summary of the start of a thread. */
|
|
3
|
+
export interface ThreadSummary {
|
|
4
|
+
text: string;
|
|
5
|
+
/** The last message the summary covers. */
|
|
6
|
+
throughMessageId: string;
|
|
7
|
+
/** Estimated tokens of `text`. */
|
|
8
|
+
tokens: number;
|
|
9
|
+
/** Epoch milliseconds. */
|
|
10
|
+
createdAt: number;
|
|
11
|
+
}
|
|
12
|
+
export interface Thread {
|
|
13
|
+
id: string;
|
|
14
|
+
ownerId: string;
|
|
15
|
+
title?: string;
|
|
16
|
+
meta?: Record<string, unknown>;
|
|
17
|
+
messageCount: number;
|
|
18
|
+
summary?: ThreadSummary;
|
|
19
|
+
/** Epoch milliseconds. */
|
|
20
|
+
createdAt: number;
|
|
21
|
+
updatedAt: number;
|
|
22
|
+
}
|
|
23
|
+
export interface StoredMessage {
|
|
24
|
+
id: string;
|
|
25
|
+
threadId: string;
|
|
26
|
+
/** 1-based position in the thread. */
|
|
27
|
+
seq: number;
|
|
28
|
+
message: Message;
|
|
29
|
+
createdAt: number;
|
|
30
|
+
}
|
|
31
|
+
export interface CreateThreadInput {
|
|
32
|
+
ownerId: string;
|
|
33
|
+
title?: string;
|
|
34
|
+
meta?: Record<string, unknown>;
|
|
35
|
+
}
|
|
36
|
+
export interface Page {
|
|
37
|
+
/** At most this many threads; default 20, capped at 100. */
|
|
38
|
+
limit?: number;
|
|
39
|
+
/** The `nextCursor` of the previous page. */
|
|
40
|
+
cursor?: string;
|
|
41
|
+
}
|
|
42
|
+
export interface ThreadPage {
|
|
43
|
+
/** Newest thread first. */
|
|
44
|
+
items: Thread[];
|
|
45
|
+
/** Absent on the last page. */
|
|
46
|
+
nextCursor?: string;
|
|
47
|
+
}
|
|
48
|
+
export interface LoadOptions {
|
|
49
|
+
/** Only the messages after the summary's `throughMessageId`, when there is one. */
|
|
50
|
+
sinceSummary: boolean;
|
|
51
|
+
}
|
|
52
|
+
export interface LoadedThread {
|
|
53
|
+
thread: Thread;
|
|
54
|
+
summary?: ThreadSummary;
|
|
55
|
+
messages: StoredMessage[];
|
|
56
|
+
}
|
|
57
|
+
export interface SummaryInput {
|
|
58
|
+
text: string;
|
|
59
|
+
throughMessageId: string;
|
|
60
|
+
tokens: number;
|
|
61
|
+
}
|
|
62
|
+
/** What may change on a pending action; `status` is where it goes next. */
|
|
63
|
+
export interface ActionPatch {
|
|
64
|
+
status: Exclude<ProposedActionStatus, 'pending'>;
|
|
65
|
+
result?: ToolResultPart;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Where conversations live. Every call carries the `ownerId` and sees only
|
|
69
|
+
* that owner's data: another owner's thread reads as absent, never as
|
|
70
|
+
* forbidden. Reads of an absent thread return `undefined`; writes to one raise
|
|
71
|
+
* `MemoryError('THREAD_NOT_FOUND')`.
|
|
72
|
+
*/
|
|
73
|
+
export interface MemoryStore {
|
|
74
|
+
createThread(input: CreateThreadInput): Promise<Thread>;
|
|
75
|
+
getThread(id: string, ownerId: string): Promise<Thread | undefined>;
|
|
76
|
+
listThreads(ownerId: string, page?: Page): Promise<ThreadPage>;
|
|
77
|
+
/** Adds messages at the end, validated; returns them stored. */
|
|
78
|
+
append(threadId: string, ownerId: string, messages: readonly Message[]): Promise<StoredMessage[]>;
|
|
79
|
+
load(threadId: string, ownerId: string, options: LoadOptions): Promise<LoadedThread | undefined>;
|
|
80
|
+
setSummary(threadId: string, ownerId: string, summary: SummaryInput): Promise<ThreadSummary>;
|
|
81
|
+
/** A proposed action recorded by an appended `proposed-action` part. */
|
|
82
|
+
findAction(actionId: string, ownerId: string): Promise<ProposedAction | undefined>;
|
|
83
|
+
/**
|
|
84
|
+
* Compare-and-set: applies the patch only while the action is `pending`
|
|
85
|
+
* and returns it; returns `undefined` when it is absent or no longer
|
|
86
|
+
* pending, so of two concurrent confirmations only one wins.
|
|
87
|
+
*/
|
|
88
|
+
updateAction(actionId: string, ownerId: string, patch: ActionPatch): Promise<ProposedAction | undefined>;
|
|
89
|
+
/** Removes the thread, its messages and its actions; false when absent. */
|
|
90
|
+
deleteThread(id: string, ownerId: string): Promise<boolean>;
|
|
91
|
+
}
|
|
92
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/docs/README.md
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# `@nxgt/ai-memory` documentation
|
|
2
|
+
|
|
3
|
+
| Page | Read it when |
|
|
4
|
+
| -------------------------------------- | -------------------------------------------------------------------------------------- |
|
|
5
|
+
| [Conversation memory](guide/memory.md) | you store threads, page them, keep a summary, confirm actions, or write your own store |
|
|
6
|
+
| [Troubleshooting](troubleshooting.md) | you meet an error and want its cause and fix |
|
|
7
|
+
| [Roadmap](roadmap.md) | you want to know what is coming |
|
|
8
|
+
|
|
9
|
+
The [README](../README.md) is the short version.
|