@chiligpt/memory 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.
Files changed (50) hide show
  1. package/README.md +30 -0
  2. package/dist/core/backfill.d.ts +78 -0
  3. package/dist/core/backfill.d.ts.map +1 -0
  4. package/dist/core/backfill.js +155 -0
  5. package/dist/core/context-graph.d.ts +73 -0
  6. package/dist/core/context-graph.d.ts.map +1 -0
  7. package/dist/core/context-graph.js +363 -0
  8. package/dist/core/entity-index.d.ts +33 -0
  9. package/dist/core/entity-index.d.ts.map +1 -0
  10. package/dist/core/entity-index.js +59 -0
  11. package/dist/core/entity.d.ts +56 -0
  12. package/dist/core/entity.d.ts.map +1 -0
  13. package/dist/core/entity.js +65 -0
  14. package/dist/core/narrative.d.ts +110 -0
  15. package/dist/core/narrative.d.ts.map +1 -0
  16. package/dist/core/narrative.js +765 -0
  17. package/dist/core/read-path.d.ts +53 -0
  18. package/dist/core/read-path.d.ts.map +1 -0
  19. package/dist/core/read-path.js +592 -0
  20. package/dist/core/session-tree-index.d.ts +69 -0
  21. package/dist/core/session-tree-index.d.ts.map +1 -0
  22. package/dist/core/session-tree-index.js +131 -0
  23. package/dist/index.d.ts +16 -0
  24. package/dist/index.d.ts.map +1 -0
  25. package/dist/index.js +15 -0
  26. package/dist/service.d.ts +105 -0
  27. package/dist/service.d.ts.map +1 -0
  28. package/dist/service.js +465 -0
  29. package/dist/storage/adapter.d.ts +61 -0
  30. package/dist/storage/adapter.d.ts.map +1 -0
  31. package/dist/storage/adapter.js +14 -0
  32. package/dist/storage/file-utils.d.ts +7 -0
  33. package/dist/storage/file-utils.d.ts.map +1 -0
  34. package/dist/storage/file-utils.js +37 -0
  35. package/dist/storage/in-memory-entry.d.ts +5 -0
  36. package/dist/storage/in-memory-entry.d.ts.map +1 -0
  37. package/dist/storage/in-memory-entry.js +5 -0
  38. package/dist/storage/in-memory.d.ts +37 -0
  39. package/dist/storage/in-memory.d.ts.map +1 -0
  40. package/dist/storage/in-memory.js +54 -0
  41. package/dist/storage/jsonl-entry.d.ts +6 -0
  42. package/dist/storage/jsonl-entry.d.ts.map +1 -0
  43. package/dist/storage/jsonl-entry.js +6 -0
  44. package/dist/storage/jsonl.d.ts +32 -0
  45. package/dist/storage/jsonl.d.ts.map +1 -0
  46. package/dist/storage/jsonl.js +171 -0
  47. package/dist/types.d.ts +66 -0
  48. package/dist/types.d.ts.map +1 -0
  49. package/dist/types.js +9 -0
  50. package/package.json +46 -0
@@ -0,0 +1,54 @@
1
+ /**
2
+ * InMemoryStorage — IMemoryStorage implementation backed by in-memory Maps.
3
+ *
4
+ * No disk I/O. No locking. All methods return immediately resolved promises.
5
+ * Used for:
6
+ * - Unit tests (replacing temp-dir JSONL setup with fast in-memory fixtures)
7
+ * - Phase 1 web validation (confirms MemoryService lifecycle works without Postgres)
8
+ *
9
+ * Records are stored in insertion order per collection. readAll() returns
10
+ * records in insertion order, matching the JSONL file behaviour.
11
+ */
12
+ export class InMemoryStorage {
13
+ store = new Map();
14
+ async init() {
15
+ // Nothing to initialise for in-memory storage.
16
+ }
17
+ async append(collection, record) {
18
+ const existing = this.store.get(collection) ?? [];
19
+ existing.push(record);
20
+ this.store.set(collection, existing);
21
+ }
22
+ async readAll(collection) {
23
+ return (this.store.get(collection) ?? []);
24
+ }
25
+ async update(collection, id, patch) {
26
+ const records = this.store.get(collection);
27
+ if (!records)
28
+ return;
29
+ const idx = records.findIndex((r) => r.id === id);
30
+ if (idx === -1)
31
+ return;
32
+ records[idx] = { ...records[idx], ...patch };
33
+ }
34
+ async archive(collection, record) {
35
+ const existing = this.store.get(collection) ?? [];
36
+ existing.push(record);
37
+ this.store.set(collection, existing);
38
+ }
39
+ /**
40
+ * Reset all collections — useful between tests.
41
+ * Not part of IMemoryStorage interface; test-only utility.
42
+ */
43
+ clear() {
44
+ this.store.clear();
45
+ }
46
+ /**
47
+ * Read from archive collections — not part of IMemoryStorage but useful for
48
+ * asserting archive operations in tests.
49
+ */
50
+ readArchive(collection) {
51
+ return (this.store.get(collection) ?? []);
52
+ }
53
+ }
54
+ //# sourceMappingURL=in-memory.js.map
@@ -0,0 +1,6 @@
1
+ /**
2
+ * @chili/memory/storage/jsonl — direct sub-entry point for JsonlMemoryStorage.
3
+ * Consumers who only need the JSONL adapter can import this path directly.
4
+ */
5
+ export { JsonlMemoryStorage } from "./jsonl.js";
6
+ //# sourceMappingURL=jsonl-entry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jsonl-entry.d.ts","sourceRoot":"","sources":["../../src/storage/jsonl-entry.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC","sourcesContent":["/**\n * @chili/memory/storage/jsonl — direct sub-entry point for JsonlMemoryStorage.\n * Consumers who only need the JSONL adapter can import this path directly.\n */\nexport { JsonlMemoryStorage } from \"./jsonl.js\";\n"]}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * @chili/memory/storage/jsonl — direct sub-entry point for JsonlMemoryStorage.
3
+ * Consumers who only need the JSONL adapter can import this path directly.
4
+ */
5
+ export { JsonlMemoryStorage } from "./jsonl.js";
6
+ //# sourceMappingURL=jsonl-entry.js.map
@@ -0,0 +1,32 @@
1
+ /**
2
+ * JsonlMemoryStorage — IMemoryStorage implementation backed by JSONL files on disk.
3
+ *
4
+ * Intra-process write safety: per-file async queue (one write at a time per path).
5
+ * Inter-process write safety: proper-lockfile (stale lock detection, retry backoff).
6
+ *
7
+ * readAll() wraps readFileSync in Promise.resolve() — synchronous under the hood,
8
+ * zero async overhead, satisfies the IMemoryStorage async interface.
9
+ *
10
+ * Archive paths:
11
+ * entities-archive → <storageDir>/archive/entities/<id>.jsonl
12
+ * narratives-archive → <storageDir>/archive/narratives/<id>.jsonl
13
+ */
14
+ import type { IMemoryStorage, MemoryCollection, MemoryArchiveCollection } from "./adapter.js";
15
+ export declare class JsonlMemoryStorage implements IMemoryStorage {
16
+ private readonly storageDir;
17
+ constructor(storageDir: string);
18
+ private filePath;
19
+ private archivePath;
20
+ init(): Promise<void>;
21
+ append<T extends {
22
+ id: string;
23
+ }>(collection: MemoryCollection, record: T): Promise<void>;
24
+ readAll<T>(collection: MemoryCollection): Promise<T[]>;
25
+ update<T extends {
26
+ id: string;
27
+ }>(collection: MemoryCollection, id: string, patch: Partial<T>): Promise<void>;
28
+ archive<T extends {
29
+ id: string;
30
+ }>(collection: MemoryArchiveCollection, record: T): Promise<void>;
31
+ }
32
+ //# sourceMappingURL=jsonl.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jsonl.d.ts","sourceRoot":"","sources":["../../src/storage/jsonl.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAKH,OAAO,KAAK,EAAE,cAAc,EAAE,gBAAgB,EAAE,uBAAuB,EAAE,MAAM,cAAc,CAAC;AAyE9F,qBAAa,kBAAmB,YAAW,cAAc;IACxD,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IAEpC,YAAY,UAAU,EAAE,MAAM,EAE7B;IAED,OAAO,CAAC,QAAQ;IAIhB,OAAO,CAAC,WAAW;IAKb,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAQ1B;IAEK,MAAM,CAAC,CAAC,SAAS;QAAE,EAAE,EAAE,MAAM,CAAA;KAAE,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,EAAE,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAW7F;IAEK,OAAO,CAAC,CAAC,EAAE,UAAU,EAAE,gBAAgB,GAAG,OAAO,CAAC,CAAC,EAAE,CAAC,CAG3D;IAEK,MAAM,CAAC,CAAC,SAAS;QAAE,EAAE,EAAE,MAAM,CAAA;KAAE,EACpC,UAAU,EAAE,gBAAgB,EAC5B,EAAE,EAAE,MAAM,EACV,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC,GACf,OAAO,CAAC,IAAI,CAAC,CAqCf;IAEK,OAAO,CAAC,CAAC,SAAS;QAAE,EAAE,EAAE,MAAM,CAAA;KAAE,EACrC,UAAU,EAAE,uBAAuB,EACnC,MAAM,EAAE,CAAC,GACP,OAAO,CAAC,IAAI,CAAC,CAWf;CACD","sourcesContent":["/**\n * JsonlMemoryStorage — IMemoryStorage implementation backed by JSONL files on disk.\n *\n * Intra-process write safety: per-file async queue (one write at a time per path).\n * Inter-process write safety: proper-lockfile (stale lock detection, retry backoff).\n *\n * readAll() wraps readFileSync in Promise.resolve() — synchronous under the hood,\n * zero async overhead, satisfies the IMemoryStorage async interface.\n *\n * Archive paths:\n * entities-archive → <storageDir>/archive/entities/<id>.jsonl\n * narratives-archive → <storageDir>/archive/narratives/<id>.jsonl\n */\n\nimport { closeSync, mkdirSync, openSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport lockfile from \"proper-lockfile\";\nimport type { IMemoryStorage, MemoryCollection, MemoryArchiveCollection } from \"./adapter.js\";\n\n// ---------------------------------------------------------------------------\n// Collection → file path mapping\n// ---------------------------------------------------------------------------\n\nconst COLLECTION_FILES: Record<MemoryCollection, string> = {\n\t\"context\": \"layer2-context.jsonl\",\n\t\"entities\": \"layer2-entity.jsonl\",\n\t\"narratives\": \"layer3-narrative.jsonl\",\n\t\"session-tree\": \"layer0-session-tree.jsonl\",\n};\n\n// ---------------------------------------------------------------------------\n// Per-file write queue (intra-process serialisation)\n// ---------------------------------------------------------------------------\n\nconst _writeQueues = new Map<string, Promise<void>>();\n\nfunction enqueue(filePath: string, fn: () => Promise<void>): Promise<void> {\n\tconst prev = _writeQueues.get(filePath) ?? Promise.resolve();\n\tconst next = prev.then(fn, fn); // run even if previous failed\n\t_writeQueues.set(filePath, next);\n\tnext.then(() => {\n\t\tif (_writeQueues.get(filePath) === next) _writeQueues.delete(filePath);\n\t});\n\treturn next;\n}\n\n// ---------------------------------------------------------------------------\n// File helpers\n// ---------------------------------------------------------------------------\n\nfunction ensureDir(filePath: string): void {\n\tmkdirSync(dirname(filePath), { recursive: true });\n}\n\nfunction ensureFile(filePath: string): void {\n\tensureDir(filePath);\n\tconst fd = openSync(filePath, \"a\"); // creates if absent, no-op otherwise\n\tcloseSync(fd);\n}\n\nconst LOCK_OPTIONS = {\n\tretries: { retries: 8, minTimeout: 30, maxTimeout: 300 },\n\tstale: 10_000,\n} as const;\n\nfunction readAllFromFile<T>(filePath: string): T[] {\n\tlet raw: string;\n\ttry {\n\t\traw = readFileSync(filePath, \"utf-8\");\n\t} catch {\n\t\treturn [];\n\t}\n\n\tconst records: T[] = [];\n\tfor (const line of raw.split(\"\\n\")) {\n\t\tconst trimmed = line.trim();\n\t\tif (!trimmed) continue;\n\t\ttry {\n\t\t\trecords.push(JSON.parse(trimmed) as T);\n\t\t} catch {\n\t\t\t// Skip malformed lines\n\t\t}\n\t}\n\treturn records;\n}\n\n// ---------------------------------------------------------------------------\n// JsonlMemoryStorage\n// ---------------------------------------------------------------------------\n\nexport class JsonlMemoryStorage implements IMemoryStorage {\n\tprivate readonly storageDir: string;\n\n\tconstructor(storageDir: string) {\n\t\tthis.storageDir = storageDir;\n\t}\n\n\tprivate filePath(collection: MemoryCollection): string {\n\t\treturn join(this.storageDir, COLLECTION_FILES[collection]);\n\t}\n\n\tprivate archivePath(collection: MemoryArchiveCollection, id: string): string {\n\t\tconst subDir = collection === \"entities-archive\" ? \"entities\" : \"narratives\";\n\t\treturn join(this.storageDir, \"archive\", subDir, `${id}.jsonl`);\n\t}\n\n\tasync init(): Promise<void> {\n\t\t// Create archive subdirs and touch all collection JSONL files\n\t\tmkdirSync(join(this.storageDir, \"archive\", \"entities\"), { recursive: true });\n\t\tmkdirSync(join(this.storageDir, \"archive\", \"narratives\"), { recursive: true });\n\n\t\tfor (const collection of Object.keys(COLLECTION_FILES) as MemoryCollection[]) {\n\t\t\tensureFile(this.filePath(collection));\n\t\t}\n\t}\n\n\tasync append<T extends { id: string }>(collection: MemoryCollection, record: T): Promise<void> {\n\t\tconst filePath = this.filePath(collection);\n\t\treturn enqueue(filePath, async () => {\n\t\t\tensureFile(filePath);\n\t\t\tconst release = await lockfile.lock(filePath, LOCK_OPTIONS);\n\t\t\ttry {\n\t\t\t\twriteFileSync(filePath, `${JSON.stringify(record)}\\n`, { flag: \"a\", encoding: \"utf-8\" });\n\t\t\t} finally {\n\t\t\t\tawait release();\n\t\t\t}\n\t\t});\n\t}\n\n\tasync readAll<T>(collection: MemoryCollection): Promise<T[]> {\n\t\t// Synchronous file read wrapped in a resolved promise — zero async overhead.\n\t\treturn Promise.resolve(readAllFromFile<T>(this.filePath(collection)));\n\t}\n\n\tasync update<T extends { id: string }>(\n\t\tcollection: MemoryCollection,\n\t\tid: string,\n\t\tpatch: Partial<T>,\n\t): Promise<void> {\n\t\tconst filePath = this.filePath(collection);\n\t\treturn enqueue(filePath, async () => {\n\t\t\tensureFile(filePath);\n\t\t\tconst release = await lockfile.lock(filePath, LOCK_OPTIONS);\n\t\t\ttry {\n\t\t\t\tlet raw: string;\n\t\t\t\ttry {\n\t\t\t\t\traw = readFileSync(filePath, \"utf-8\");\n\t\t\t\t} catch {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tlet matched = false;\n\t\t\t\tconst updated = raw.split(\"\\n\").map((line) => {\n\t\t\t\t\tconst trimmed = line.trim();\n\t\t\t\t\tif (!trimmed) return line;\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst record = JSON.parse(trimmed) as T;\n\t\t\t\t\t\tif ((record as { id?: string }).id === id) {\n\t\t\t\t\t\t\tmatched = true;\n\t\t\t\t\t\t\treturn JSON.stringify({ ...record, ...patch });\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch {\n\t\t\t\t\t\t// Leave malformed lines untouched\n\t\t\t\t\t}\n\t\t\t\t\treturn line;\n\t\t\t\t});\n\n\t\t\t\tif (!matched) return;\n\n\t\t\t\tconst content = `${updated.join(\"\\n\").replace(/\\n+$/, \"\")}\\n`;\n\t\t\t\twriteFileSync(filePath, content, { encoding: \"utf-8\" });\n\t\t\t} finally {\n\t\t\t\tawait release();\n\t\t\t}\n\t\t});\n\t}\n\n\tasync archive<T extends { id: string }>(\n\t\tcollection: MemoryArchiveCollection,\n\t\trecord: T,\n\t): Promise<void> {\n\t\tconst archivePath = this.archivePath(collection, record.id);\n\t\treturn enqueue(archivePath, async () => {\n\t\t\tensureFile(archivePath);\n\t\t\tconst release = await lockfile.lock(archivePath, LOCK_OPTIONS);\n\t\t\ttry {\n\t\t\t\twriteFileSync(archivePath, `${JSON.stringify(record)}\\n`, { flag: \"a\", encoding: \"utf-8\" });\n\t\t\t} finally {\n\t\t\t\tawait release();\n\t\t\t}\n\t\t});\n\t}\n}\n"]}
@@ -0,0 +1,171 @@
1
+ /**
2
+ * JsonlMemoryStorage — IMemoryStorage implementation backed by JSONL files on disk.
3
+ *
4
+ * Intra-process write safety: per-file async queue (one write at a time per path).
5
+ * Inter-process write safety: proper-lockfile (stale lock detection, retry backoff).
6
+ *
7
+ * readAll() wraps readFileSync in Promise.resolve() — synchronous under the hood,
8
+ * zero async overhead, satisfies the IMemoryStorage async interface.
9
+ *
10
+ * Archive paths:
11
+ * entities-archive → <storageDir>/archive/entities/<id>.jsonl
12
+ * narratives-archive → <storageDir>/archive/narratives/<id>.jsonl
13
+ */
14
+ import { closeSync, mkdirSync, openSync, readFileSync, writeFileSync } from "node:fs";
15
+ import { dirname, join } from "node:path";
16
+ import lockfile from "proper-lockfile";
17
+ // ---------------------------------------------------------------------------
18
+ // Collection → file path mapping
19
+ // ---------------------------------------------------------------------------
20
+ const COLLECTION_FILES = {
21
+ "context": "layer2-context.jsonl",
22
+ "entities": "layer2-entity.jsonl",
23
+ "narratives": "layer3-narrative.jsonl",
24
+ "session-tree": "layer0-session-tree.jsonl",
25
+ };
26
+ // ---------------------------------------------------------------------------
27
+ // Per-file write queue (intra-process serialisation)
28
+ // ---------------------------------------------------------------------------
29
+ const _writeQueues = new Map();
30
+ function enqueue(filePath, fn) {
31
+ const prev = _writeQueues.get(filePath) ?? Promise.resolve();
32
+ const next = prev.then(fn, fn); // run even if previous failed
33
+ _writeQueues.set(filePath, next);
34
+ next.then(() => {
35
+ if (_writeQueues.get(filePath) === next)
36
+ _writeQueues.delete(filePath);
37
+ });
38
+ return next;
39
+ }
40
+ // ---------------------------------------------------------------------------
41
+ // File helpers
42
+ // ---------------------------------------------------------------------------
43
+ function ensureDir(filePath) {
44
+ mkdirSync(dirname(filePath), { recursive: true });
45
+ }
46
+ function ensureFile(filePath) {
47
+ ensureDir(filePath);
48
+ const fd = openSync(filePath, "a"); // creates if absent, no-op otherwise
49
+ closeSync(fd);
50
+ }
51
+ const LOCK_OPTIONS = {
52
+ retries: { retries: 8, minTimeout: 30, maxTimeout: 300 },
53
+ stale: 10_000,
54
+ };
55
+ function readAllFromFile(filePath) {
56
+ let raw;
57
+ try {
58
+ raw = readFileSync(filePath, "utf-8");
59
+ }
60
+ catch {
61
+ return [];
62
+ }
63
+ const records = [];
64
+ for (const line of raw.split("\n")) {
65
+ const trimmed = line.trim();
66
+ if (!trimmed)
67
+ continue;
68
+ try {
69
+ records.push(JSON.parse(trimmed));
70
+ }
71
+ catch {
72
+ // Skip malformed lines
73
+ }
74
+ }
75
+ return records;
76
+ }
77
+ // ---------------------------------------------------------------------------
78
+ // JsonlMemoryStorage
79
+ // ---------------------------------------------------------------------------
80
+ export class JsonlMemoryStorage {
81
+ storageDir;
82
+ constructor(storageDir) {
83
+ this.storageDir = storageDir;
84
+ }
85
+ filePath(collection) {
86
+ return join(this.storageDir, COLLECTION_FILES[collection]);
87
+ }
88
+ archivePath(collection, id) {
89
+ const subDir = collection === "entities-archive" ? "entities" : "narratives";
90
+ return join(this.storageDir, "archive", subDir, `${id}.jsonl`);
91
+ }
92
+ async init() {
93
+ // Create archive subdirs and touch all collection JSONL files
94
+ mkdirSync(join(this.storageDir, "archive", "entities"), { recursive: true });
95
+ mkdirSync(join(this.storageDir, "archive", "narratives"), { recursive: true });
96
+ for (const collection of Object.keys(COLLECTION_FILES)) {
97
+ ensureFile(this.filePath(collection));
98
+ }
99
+ }
100
+ async append(collection, record) {
101
+ const filePath = this.filePath(collection);
102
+ return enqueue(filePath, async () => {
103
+ ensureFile(filePath);
104
+ const release = await lockfile.lock(filePath, LOCK_OPTIONS);
105
+ try {
106
+ writeFileSync(filePath, `${JSON.stringify(record)}\n`, { flag: "a", encoding: "utf-8" });
107
+ }
108
+ finally {
109
+ await release();
110
+ }
111
+ });
112
+ }
113
+ async readAll(collection) {
114
+ // Synchronous file read wrapped in a resolved promise — zero async overhead.
115
+ return Promise.resolve(readAllFromFile(this.filePath(collection)));
116
+ }
117
+ async update(collection, id, patch) {
118
+ const filePath = this.filePath(collection);
119
+ return enqueue(filePath, async () => {
120
+ ensureFile(filePath);
121
+ const release = await lockfile.lock(filePath, LOCK_OPTIONS);
122
+ try {
123
+ let raw;
124
+ try {
125
+ raw = readFileSync(filePath, "utf-8");
126
+ }
127
+ catch {
128
+ return;
129
+ }
130
+ let matched = false;
131
+ const updated = raw.split("\n").map((line) => {
132
+ const trimmed = line.trim();
133
+ if (!trimmed)
134
+ return line;
135
+ try {
136
+ const record = JSON.parse(trimmed);
137
+ if (record.id === id) {
138
+ matched = true;
139
+ return JSON.stringify({ ...record, ...patch });
140
+ }
141
+ }
142
+ catch {
143
+ // Leave malformed lines untouched
144
+ }
145
+ return line;
146
+ });
147
+ if (!matched)
148
+ return;
149
+ const content = `${updated.join("\n").replace(/\n+$/, "")}\n`;
150
+ writeFileSync(filePath, content, { encoding: "utf-8" });
151
+ }
152
+ finally {
153
+ await release();
154
+ }
155
+ });
156
+ }
157
+ async archive(collection, record) {
158
+ const archivePath = this.archivePath(collection, record.id);
159
+ return enqueue(archivePath, async () => {
160
+ ensureFile(archivePath);
161
+ const release = await lockfile.lock(archivePath, LOCK_OPTIONS);
162
+ try {
163
+ writeFileSync(archivePath, `${JSON.stringify(record)}\n`, { flag: "a", encoding: "utf-8" });
164
+ }
165
+ finally {
166
+ await release();
167
+ }
168
+ });
169
+ }
170
+ }
171
+ //# sourceMappingURL=jsonl.js.map
@@ -0,0 +1,66 @@
1
+ /**
2
+ * Portable boundary types for @chili/memory.
3
+ *
4
+ * These replace chili-internal types so the package has zero dependency on
5
+ * @chili/ai or @chili/app internals. All types are structurally compatible
6
+ * with their chili counterparts — adapters pass chili objects directly.
7
+ */
8
+ /**
9
+ * Portable representation of a session entry passed into memory operations.
10
+ * The chili adapter maps SessionEntry[] → MemoryBranchEntry[] before handing off.
11
+ * Only the fields the memory system actually reads are required here.
12
+ */
13
+ export interface MemoryBranchEntry {
14
+ /** Entry type discriminant — memory processing only acts on "message" entries. */
15
+ type: string;
16
+ /** Present when type === "message". */
17
+ message?: {
18
+ role: "user" | "assistant";
19
+ /**
20
+ * Content as a plain string or array of typed blocks.
21
+ * Memory system extracts text blocks for transcript formatting.
22
+ */
23
+ content: string | Array<{
24
+ type: string;
25
+ text?: string;
26
+ [key: string]: unknown;
27
+ }>;
28
+ };
29
+ }
30
+ /**
31
+ * Minimal LLM message shape. Structurally compatible with @chili/ai's
32
+ * UserMessage and AssistantMessage — the chili adapter passes ctx.complete
33
+ * directly without wrapping.
34
+ */
35
+ export interface LLMMessage {
36
+ role: "user" | "assistant";
37
+ content: string | Array<{
38
+ type: string;
39
+ text?: string;
40
+ [key: string]: unknown;
41
+ }>;
42
+ timestamp?: number;
43
+ }
44
+ /**
45
+ * Minimal LLM response shape. The memory system only reads text blocks
46
+ * from content — extra fields (usage, stopReason, etc.) are ignored.
47
+ * Structurally compatible with @chili/ai's AssistantMessage.
48
+ */
49
+ export interface LLMResponse {
50
+ content: Array<{
51
+ type: string;
52
+ text?: string;
53
+ [key: string]: unknown;
54
+ }>;
55
+ }
56
+ /**
57
+ * LLM completion function. Any LLM provider that accepts a message array and
58
+ * returns a response with content blocks satisfies this contract.
59
+ *
60
+ * The chili adapter passes ctx.complete (which uses @chili/ai under the hood).
61
+ * A web consumer passes whatever client it uses.
62
+ */
63
+ export type LLMCompleteFn = (messages: LLMMessage[], options?: {
64
+ systemPrompt?: string;
65
+ }) => Promise<LLMResponse>;
66
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAMH;;;;GAIG;AACH,MAAM,WAAW,iBAAiB;IACjC,oFAAkF;IAClF,IAAI,EAAE,MAAM,CAAC;IACb,uCAAuC;IACvC,OAAO,CAAC,EAAE;QACT,IAAI,EAAE,MAAM,GAAG,WAAW,CAAC;QAC3B;;;WAGG;QACH,OAAO,EAAE,MAAM,GAAG,KAAK,CAAC;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,IAAI,CAAC,EAAE,MAAM,CAAC;YAAC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;SAAE,CAAC,CAAC;KACjF,CAAC;CACF;AAMD;;;;GAIG;AACH,MAAM,WAAW,UAAU;IAC1B,IAAI,EAAE,MAAM,GAAG,WAAW,CAAC;IAC3B,OAAO,EAAE,MAAM,GAAG,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;KAAE,CAAC,CAAC;IACjF,SAAS,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;;GAIG;AACH,MAAM,WAAW,WAAW;IAC3B,OAAO,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;KAAE,CAAC,CAAC;CACxE;AAED;;;;;;GAMG;AACH,MAAM,MAAM,aAAa,GAAG,CAC3B,QAAQ,EAAE,UAAU,EAAE,EACtB,OAAO,CAAC,EAAE;IAAE,YAAY,CAAC,EAAE,MAAM,CAAA;CAAE,KAC/B,OAAO,CAAC,WAAW,CAAC,CAAC","sourcesContent":["/**\n * Portable boundary types for @chili/memory.\n *\n * These replace chili-internal types so the package has zero dependency on\n * @chili/ai or @chili/app internals. All types are structurally compatible\n * with their chili counterparts — adapters pass chili objects directly.\n */\n\n// ---------------------------------------------------------------------------\n// Session entry — replaces chili-internal SessionEntry\n// ---------------------------------------------------------------------------\n\n/**\n * Portable representation of a session entry passed into memory operations.\n * The chili adapter maps SessionEntry[] → MemoryBranchEntry[] before handing off.\n * Only the fields the memory system actually reads are required here.\n */\nexport interface MemoryBranchEntry {\n\t/** Entry type discriminant — memory processing only acts on \"message\" entries. */\n\ttype: string;\n\t/** Present when type === \"message\". */\n\tmessage?: {\n\t\trole: \"user\" | \"assistant\";\n\t\t/**\n\t\t * Content as a plain string or array of typed blocks.\n\t\t * Memory system extracts text blocks for transcript formatting.\n\t\t */\n\t\tcontent: string | Array<{ type: string; text?: string; [key: string]: unknown }>;\n\t};\n}\n\n// ---------------------------------------------------------------------------\n// LLM completion — replaces @chili/ai Message / AssistantMessage dependency\n// ---------------------------------------------------------------------------\n\n/**\n * Minimal LLM message shape. Structurally compatible with @chili/ai's\n * UserMessage and AssistantMessage — the chili adapter passes ctx.complete\n * directly without wrapping.\n */\nexport interface LLMMessage {\n\trole: \"user\" | \"assistant\";\n\tcontent: string | Array<{ type: string; text?: string; [key: string]: unknown }>;\n\ttimestamp?: number;\n}\n\n/**\n * Minimal LLM response shape. The memory system only reads text blocks\n * from content — extra fields (usage, stopReason, etc.) are ignored.\n * Structurally compatible with @chili/ai's AssistantMessage.\n */\nexport interface LLMResponse {\n\tcontent: Array<{ type: string; text?: string; [key: string]: unknown }>;\n}\n\n/**\n * LLM completion function. Any LLM provider that accepts a message array and\n * returns a response with content blocks satisfies this contract.\n *\n * The chili adapter passes ctx.complete (which uses @chili/ai under the hood).\n * A web consumer passes whatever client it uses.\n */\nexport type LLMCompleteFn = (\n\tmessages: LLMMessage[],\n\toptions?: { systemPrompt?: string },\n) => Promise<LLMResponse>;\n"]}
package/dist/types.js ADDED
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Portable boundary types for @chili/memory.
3
+ *
4
+ * These replace chili-internal types so the package has zero dependency on
5
+ * @chili/ai or @chili/app internals. All types are structurally compatible
6
+ * with their chili counterparts — adapters pass chili objects directly.
7
+ */
8
+ export {};
9
+ //# sourceMappingURL=types.js.map
package/package.json ADDED
@@ -0,0 +1,46 @@
1
+ {
2
+ "name": "@chiligpt/memory",
3
+ "version": "0.1.0",
4
+ "description": "Persistent agent memory system — storage-adapter-agnostic, LLM-provider-agnostic",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.js"
12
+ },
13
+ "./storage/jsonl": {
14
+ "types": "./dist/storage/jsonl-entry.d.ts",
15
+ "import": "./dist/storage/jsonl-entry.js"
16
+ },
17
+ "./storage/in-memory": {
18
+ "types": "./dist/storage/in-memory-entry.d.ts",
19
+ "import": "./dist/storage/in-memory-entry.js"
20
+ }
21
+ },
22
+ "files": [
23
+ "dist/**/*.js",
24
+ "dist/**/*.d.ts",
25
+ "dist/**/*.d.ts.map",
26
+ "README.md"
27
+ ],
28
+ "engines": {
29
+ "node": ">=19.0.0"
30
+ },
31
+ "scripts": {
32
+ "build": "tsgo -p tsconfig.build.json",
33
+ "dev": "tsgo -p tsconfig.build.json --watch --preserveWatchOutput",
34
+ "clean": "shx rm -rf dist"
35
+ },
36
+ "devDependencies": {
37
+ "@types/node": "^22.10.5",
38
+ "shx": "^0.4.0"
39
+ },
40
+ "dependencies": {
41
+ "proper-lockfile": "^4.1.2"
42
+ },
43
+ "publishConfig": {
44
+ "access": "public"
45
+ }
46
+ }