@persql/context 0.1.0 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,15 +1,13 @@
1
1
  # @persql/context
2
2
 
3
- Shared, structured agent context on the [PerSQL](https://persql.com) substrate.
3
+ Shared, structured agent memory on the [PerSQL](https://persql.com) substrate.
4
4
 
5
- One store, readable and writable from every agent surface PerSQL speaks — SDK,
6
- MCP, published endpoints, CLI — so a fleet of agents (local, cloud, or
7
- third-party) shares the same durable facts, entities, and relationships.
5
+ One store, readable and writable from every agent surface — SDK, MCP, published
6
+ endpoints, CLI — so a fleet of agents (local, cloud, or third-party) shares the
7
+ same durable facts across sessions.
8
8
 
9
- Retrieval is **lexical**: FTS5 with the porter stemmer, BM25-ranked. The way a
10
- coding agent already retrieves grep over exact tokens but over durable,
11
- shared, structured memory. No vector database. If you need semantic similarity,
12
- layer one over these rows and keep the source of truth here.
9
+ Retrieval is **lexical**: FTS5 with the porter stemmer, BM25-ranked. No vector
10
+ database. Layer one over these rows if you need semantic similarity.
13
11
 
14
12
  ## Install
15
13
 
@@ -28,62 +26,111 @@ const ctx = context(persql.database("acme/team-context"), { source: "my-agent" }
28
26
 
29
27
  await ctx.init(); // create the schema once (idempotent)
30
28
 
31
- // Write structured facts.
29
+ // Save a structured memory. Same name → update; new name → insert.
32
30
  await ctx.remember({
33
- topic: "billing",
34
- body: "Acme prefers net-30 invoicing",
35
- tags: ["billing", "invoice"],
31
+ name: "billing-preference",
32
+ description: "Acme prefers net-30 invoicing",
33
+ type: "project",
34
+ body: "Acme Corp prefers net-30 invoicing terms. Invoices are sent at month-end.",
36
35
  });
37
36
 
38
37
  // Recall by keyword — BM25-ranked, most relevant first.
39
38
  const hits = await ctx.recall("invoice OR payment");
39
+ console.log(hits[0].body); // "Acme Corp prefers net-30 invoicing terms…"
40
+
41
+ // Load the full index (name + description, no body) for prompt injection.
42
+ const index = await ctx.index();
43
+
44
+ // Fetch one entry's full body by name.
45
+ const row = await ctx.get("billing-preference");
40
46
 
41
47
  // Relate entities; walk the neighborhood.
42
48
  await ctx.link("api worker", "depends_on", "billing meter");
43
49
  const graph = await ctx.neighbors("api worker", { depth: 1 });
44
50
  ```
45
51
 
46
- ## Write-time intelligence (optional)
52
+ ## OpenAI Agents SDK
47
53
 
48
- The expensive part turning a raw dump into clean rows runs on the **write**
49
- path, not on every read. Bring your own LLM:
54
+ `memoryTools(store)` returns three ready-to-use tools`remember_memory`,
55
+ `recall_memory`, `forget_memory` in the `{ type, name, description, parameters, invoke }`
56
+ shape the OpenAI Agents SDK expects.
50
57
 
51
58
  ```ts
52
- const ctx = context(db, {
53
- extract: async (raw) => myLlm.extract(raw), // -> { facts, entities, edges }
59
+ import OpenAI from "openai";
60
+ import { Agent, run } from "@openai/agents";
61
+ import { PerSQL } from "@persql/sdk";
62
+ import { context, memoryTools } from "@persql/context";
63
+
64
+ const persql = new PerSQL({ token: process.env.PERSQL_TOKEN! });
65
+ const ctx = context(persql.database("acme/agent-memory"), { source: "my-agent" });
66
+ await ctx.init();
67
+
68
+ // Load existing memories and inject into the system prompt.
69
+ const memories = await ctx.index();
70
+ const memSection = memories.length
71
+ ? memories.map((m) => `[${m.type}] ${m.name}: ${m.description}`).join("\n")
72
+ : "No memories saved yet.";
73
+
74
+ const agent = new Agent({
75
+ name: "assistant",
76
+ model: "gpt-4o",
77
+ instructions: `You are a helpful assistant.\n\nMemory index:\n${memSection}\n\nUse recall_memory to read the full body of any entry, remember_memory to save new facts, and forget_memory to remove stale ones.`,
78
+ tools: memoryTools(ctx),
54
79
  });
55
80
 
56
- await ctx.rememberRaw("long conversation or doc…");
81
+ const result = await run(agent, "What invoicing terms does Acme prefer?");
82
+ console.log(result.finalOutput);
57
83
  ```
58
84
 
59
- `@persql/context/core` exports the extraction contract (`EXTRACTION_SYSTEM_PROMPT`,
60
- `EXTRACTION_JSON_SCHEMA`) so your extractor returns exactly the shape `write()`
61
- persists. The hosted PerSQL Context MCP runs this server-side and meters it; the
62
- SDK keeps reads cheap and AI-free.
85
+ ### Full bodies in the system prompt (no recall tool needed)
86
+
87
+ Load full bodies up front so the model answers without a tool call:
88
+
89
+ ```ts
90
+ const memories = await ctx.recall(""); // or ctx.index() + ctx.get() per entry
91
+ // Or use a raw query: SELECT * FROM ctx_memory ORDER BY updated_at DESC LIMIT 50
92
+
93
+ // Format one block per memory:
94
+ const memSection = memories.length
95
+ ? memories.map((m) => `[${m.type}] ${m.name}\n${m.description}\n---\n${m.body}`).join("\n\n")
96
+ : "No memories saved yet.";
63
97
 
64
- ## Surface
98
+ const agent = new Agent({
99
+ instructions: `You are a helpful assistant.\n\nMEMORIES:\n${memSection}\n\nAnswer questions covered above directly. Use remember_memory to save new facts.`,
100
+ tools: memoryTools(ctx),
101
+ });
102
+ ```
103
+
104
+ ## API surface
65
105
 
66
106
  | | |
67
107
  |---|---|
68
108
  | `init()` | create schema (idempotent) |
69
- | `remember(input)` | store a structured fact/episode/artifactid |
70
- | `rememberRaw(raw, { extract })` | extract durable context from text, then store |
71
- | `write(extracted)` | persist an already-extracted `{ facts, entities, edges }` bundle |
109
+ | `remember(input)` | UPSERT a named memory (same name update) |
110
+ | `write(extracted)` | persist a `{ memories, entities, edges }` bundle |
111
+ | `forget(name)` | delete a memory by name |
112
+ | `index(opts)` | name + description + type for all memories, newest first |
113
+ | `get(name)` | full body of one memory by name |
72
114
  | `recall(query, opts)` | keyword recall, BM25-ranked |
73
- | `recent(opts)` / `byTag(tag, opts)` | list by recency / tag |
74
115
  | `link(src, rel, dst)` | relate two entities |
75
- | `neighbors(name, { depth })` | the subgraph around an entity |
76
- | `supersede(oldIds, newId)` / `forget(id)` | replace / delete |
77
- | `stats()` | counts |
116
+ | `neighbors(name, { depth })` | subgraph around an entity |
117
+ | `stats()` | counts: memories, entities, edges |
118
+ | `memoryTools(store)` | OpenAI Agents SDK tool definitions |
119
+
120
+ ### Memory types
121
+
122
+ `"user"` — facts about the person (role, preferences)
123
+ `"feedback"` — guidance on how to behave
124
+ `"project"` — decisions, constraints, ongoing work
125
+ `"reference"` — pointers to external resources
78
126
 
79
127
  ## Cost
80
128
 
81
- Memory is rows like any other — metered on rows read, rows written, and storage,
82
- with no per-table or per-memory fee. Reads are plain SQL (cheap); only
83
- `rememberRaw`/extraction spends AI tokens, and only on write.
129
+ Memory is rows like any other — metered on rows read, rows written, and
130
+ storage, with no per-memory fee.
84
131
 
85
132
  ## Schema
86
133
 
87
- `@persql/context/core` is zero-dependency and exports the DDL, SQL builders, and
88
- types — imported by both this SDK and the hosted worker so client-side and
89
- server-side recall can never drift.
134
+ `@persql/context/core` is zero-dependency and exports the DDL, SQL builders,
135
+ and types — shared by this SDK and the hosted worker so client and server
136
+ recall never drift.
@@ -1,41 +1,39 @@
1
1
  // src/core.ts
2
- var SCHEMA_VERSION = 1;
2
+ var SCHEMA_VERSION = 2;
3
3
  var TABLE_PREFIX = "ctx_";
4
4
  var T = TABLE_PREFIX;
5
5
  var SCHEMA_SQL = [
6
6
  `CREATE TABLE IF NOT EXISTS ${T}memory (
7
7
  id TEXT PRIMARY KEY,
8
- kind TEXT NOT NULL DEFAULT 'fact',
9
- topic TEXT,
8
+ name TEXT NOT NULL,
9
+ description TEXT NOT NULL,
10
+ type TEXT NOT NULL DEFAULT 'project',
10
11
  body TEXT NOT NULL,
11
- tags TEXT,
12
12
  source TEXT,
13
13
  created_at INTEGER NOT NULL,
14
- superseded_by TEXT
14
+ updated_at INTEGER NOT NULL
15
15
  )`,
16
- `CREATE INDEX IF NOT EXISTS ${T}memory_kind ON ${T}memory(kind)`,
17
- `CREATE INDEX IF NOT EXISTS ${T}memory_created ON ${T}memory(created_at)`,
18
- `CREATE INDEX IF NOT EXISTS ${T}memory_superseded ON ${T}memory(superseded_by)`,
19
- // External-content FTS index: body text is not duplicated, only indexed.
20
- // Porter stemmer so "invoice" recalls "invoicing" without embeddings.
16
+ `CREATE UNIQUE INDEX IF NOT EXISTS ${T}memory_name ON ${T}memory(name)`,
17
+ // External-content FTS on name+description+body. Porter stemmer so
18
+ // "invoice" recalls "invoicing" without embeddings.
21
19
  `CREATE VIRTUAL TABLE IF NOT EXISTS ${T}memory_fts USING fts5(
22
- topic, body, tags,
20
+ name, description, body,
23
21
  content='${T}memory', content_rowid='rowid',
24
22
  tokenize='porter unicode61'
25
23
  )`,
26
24
  `CREATE TRIGGER IF NOT EXISTS ${T}memory_ai AFTER INSERT ON ${T}memory BEGIN
27
- INSERT INTO ${T}memory_fts(rowid, topic, body, tags)
28
- VALUES (new.rowid, new.topic, new.body, new.tags);
25
+ INSERT INTO ${T}memory_fts(rowid, name, description, body)
26
+ VALUES (new.rowid, new.name, new.description, new.body);
29
27
  END`,
30
28
  `CREATE TRIGGER IF NOT EXISTS ${T}memory_ad AFTER DELETE ON ${T}memory BEGIN
31
- INSERT INTO ${T}memory_fts(${T}memory_fts, rowid, topic, body, tags)
32
- VALUES ('delete', old.rowid, old.topic, old.body, old.tags);
29
+ INSERT INTO ${T}memory_fts(${T}memory_fts, rowid, name, description, body)
30
+ VALUES ('delete', old.rowid, old.name, old.description, old.body);
33
31
  END`,
34
32
  `CREATE TRIGGER IF NOT EXISTS ${T}memory_au AFTER UPDATE ON ${T}memory BEGIN
35
- INSERT INTO ${T}memory_fts(${T}memory_fts, rowid, topic, body, tags)
36
- VALUES ('delete', old.rowid, old.topic, old.body, old.tags);
37
- INSERT INTO ${T}memory_fts(rowid, topic, body, tags)
38
- VALUES (new.rowid, new.topic, new.body, new.tags);
33
+ INSERT INTO ${T}memory_fts(${T}memory_fts, rowid, name, description, body)
34
+ VALUES ('delete', old.rowid, old.name, old.description, old.body);
35
+ INSERT INTO ${T}memory_fts(rowid, name, description, body)
36
+ VALUES (new.rowid, new.name, new.description, new.body);
39
37
  END`,
40
38
  `CREATE TABLE IF NOT EXISTS ${T}entity (
41
39
  id TEXT PRIMARY KEY,
@@ -58,15 +56,21 @@ var SCHEMA_SQL = [
58
56
  function buildInit() {
59
57
  return SCHEMA_SQL.map((sql) => ({ sql, params: [] }));
60
58
  }
61
- var FTS_OPERATORS = /* @__PURE__ */ new Set(["and", "or", "not", "near"]);
62
- function normalizeTags(tags) {
63
- if (!tags) return null;
64
- const arr = Array.isArray(tags) ? tags : String(tags).split(/[,\s]+/);
65
- const norm = Array.from(
66
- new Set(arr.map((t) => t.trim().toLowerCase()).filter(Boolean))
67
- );
68
- return norm.length ? norm.join(" ") : null;
59
+ function buildDetectLegacySchema() {
60
+ return {
61
+ sql: `SELECT count(*) AS n FROM pragma_table_info('${T}memory') WHERE name='topic'`,
62
+ params: []
63
+ };
69
64
  }
65
+ function buildDropLegacy() {
66
+ return [
67
+ { sql: `DROP TABLE IF EXISTS ${T}memory_fts`, params: [] },
68
+ { sql: `DROP TABLE IF EXISTS ${T}memory`, params: [] },
69
+ { sql: `DROP TABLE IF EXISTS ${T}entity`, params: [] },
70
+ { sql: `DROP TABLE IF EXISTS ${T}edge`, params: [] }
71
+ ];
72
+ }
73
+ var FTS_OPERATORS = /* @__PURE__ */ new Set(["and", "or", "not", "near"]);
70
74
  function toFtsQuery(input, opts = {}) {
71
75
  if (opts.mode === "raw") return input.trim() || null;
72
76
  const tokens = (input.match(/[\p{L}\p{N}_]+/gu) ?? []).filter(
@@ -89,103 +93,71 @@ function resolveEntityRef(ref) {
89
93
  function edgeId(srcId, rel, dstId) {
90
94
  return slugId("x_", `${srcId}|${rel}|${dstId}`);
91
95
  }
92
- var MEMORY_COLS = "id, kind, topic, body, tags, source, created_at AS createdAt, superseded_by AS supersededBy";
96
+ var MEMORY_COLS = "id, name, description, type, body, source, created_at AS createdAt, updated_at AS updatedAt";
93
97
  function buildRemember(input, now, id) {
94
98
  return {
95
- sql: `INSERT INTO ${T}memory (id, kind, topic, body, tags, source, created_at, superseded_by)
96
- VALUES (?, ?, ?, ?, ?, ?, ?, NULL)`,
99
+ sql: `INSERT INTO ${T}memory (id, name, description, type, body, source, created_at, updated_at)
100
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?)
101
+ ON CONFLICT(name) DO UPDATE SET
102
+ description = excluded.description,
103
+ body = excluded.body,
104
+ type = excluded.type,
105
+ source = excluded.source,
106
+ updated_at = excluded.updated_at`,
97
107
  params: [
98
108
  id,
99
- input.kind ?? "fact",
100
- input.topic ?? null,
109
+ input.name,
110
+ input.description,
111
+ input.type ?? "project",
101
112
  input.body,
102
- normalizeTags(input.tags),
103
113
  input.source ?? null,
114
+ now,
104
115
  now
105
116
  ]
106
117
  };
107
118
  }
108
- function buildSupersede(oldId, newId) {
109
- return {
110
- sql: `UPDATE ${T}memory SET superseded_by = ? WHERE id = ? AND superseded_by IS NULL`,
111
- params: [newId, oldId]
112
- };
113
- }
114
- function buildForget(id) {
115
- return { sql: `DELETE FROM ${T}memory WHERE id = ?`, params: [id] };
119
+ function buildForget(name) {
120
+ return { sql: `DELETE FROM ${T}memory WHERE name = ?`, params: [name] };
116
121
  }
117
- function buildGet(id) {
118
- return {
119
- sql: `SELECT ${MEMORY_COLS} FROM ${T}memory WHERE id = ?`,
120
- params: [id]
121
- };
122
- }
123
- function buildRecall(query, opts = {}) {
124
- const match = toFtsQuery(query, { operator: opts.operator, mode: opts.mode });
125
- if (!match) return null;
126
- const where = [`${T}memory_fts MATCH ?`];
127
- const params = [match];
128
- if (!opts.includeSuperseded) where.push("m.superseded_by IS NULL");
129
- if (opts.kind) {
130
- where.push("m.kind = ?");
131
- params.push(opts.kind);
132
- }
133
- if (opts.sinceMs != null) {
134
- where.push("m.created_at >= ?");
135
- params.push(opts.sinceMs);
136
- }
137
- params.push(opts.limit ?? 8);
122
+ function buildGet(name) {
138
123
  return {
139
- sql: `SELECT m.id, m.kind, m.topic, m.body, m.tags, m.source,
140
- m.created_at AS createdAt, m.superseded_by AS supersededBy
141
- FROM ${T}memory_fts
142
- JOIN ${T}memory m ON m.rowid = ${T}memory_fts.rowid
143
- WHERE ${where.join(" AND ")}
144
- ORDER BY ${T}memory_fts.rank
145
- LIMIT ?`,
146
- params
124
+ sql: `SELECT ${MEMORY_COLS} FROM ${T}memory WHERE name = ?`,
125
+ params: [name]
147
126
  };
148
127
  }
149
- function buildRecent(opts = {}) {
128
+ function buildIndex(opts = {}) {
150
129
  const where = [];
151
130
  const params = [];
152
- if (!opts.includeSuperseded) where.push("superseded_by IS NULL");
153
- if (opts.kind) {
154
- where.push("kind = ?");
155
- params.push(opts.kind);
131
+ if (opts.type) {
132
+ where.push("type = ?");
133
+ params.push(opts.type);
156
134
  }
157
- params.push(opts.limit ?? 20);
135
+ params.push(opts.limit ?? 50);
158
136
  return {
159
- sql: `SELECT ${MEMORY_COLS} FROM ${T}memory
137
+ sql: `SELECT id, name, description, type, updated_at AS updatedAt FROM ${T}memory
160
138
  ${where.length ? "WHERE " + where.join(" AND ") : ""}
161
- ORDER BY created_at DESC
162
- LIMIT ?`,
139
+ ORDER BY updated_at DESC LIMIT ?`,
163
140
  params
164
141
  };
165
142
  }
166
- function buildByTag(tag, opts = {}) {
167
- const t = tag.trim().toLowerCase();
168
- const where = ["(' ' || COALESCE(tags, '') || ' ') LIKE ?"];
169
- const params = [`% ${t} %`];
170
- if (!opts.includeSuperseded) where.push("superseded_by IS NULL");
171
- if (opts.kind) {
172
- where.push("kind = ?");
173
- params.push(opts.kind);
174
- }
175
- params.push(opts.limit ?? 20);
143
+ function buildRecall(query, opts = {}) {
144
+ const match = toFtsQuery(query, { operator: opts.operator, mode: opts.mode });
145
+ if (!match) return null;
176
146
  return {
177
- sql: `SELECT ${MEMORY_COLS} FROM ${T}memory
178
- WHERE ${where.join(" AND ")}
179
- ORDER BY created_at DESC
147
+ sql: `SELECT m.id, m.name, m.description, m.type, m.body, m.source,
148
+ m.created_at AS createdAt, m.updated_at AS updatedAt
149
+ FROM ${T}memory_fts
150
+ JOIN ${T}memory m ON m.rowid = ${T}memory_fts.rowid
151
+ WHERE ${T}memory_fts MATCH ?
152
+ ORDER BY ${T}memory_fts.rank
180
153
  LIMIT ?`,
181
- params
154
+ params: [match, opts.limit ?? 8]
182
155
  };
183
156
  }
184
157
  function buildStats() {
185
158
  return {
186
159
  sql: `SELECT
187
- (SELECT count(*) FROM ${T}memory WHERE superseded_by IS NULL) AS facts,
188
- (SELECT count(*) FROM ${T}memory) AS facts_total,
160
+ (SELECT count(*) FROM ${T}memory) AS memories,
189
161
  (SELECT count(*) FROM ${T}entity) AS entities,
190
162
  (SELECT count(*) FROM ${T}edge) AS edges`,
191
163
  params: []
@@ -234,8 +206,7 @@ function buildNeighborEntities(seedId, depth, limit) {
234
206
  GROUP BY en.id
235
207
  ORDER BY depth, en.name
236
208
  LIMIT ?`,
237
- // The seed reappears at depth >0 via a round-trip on undirected edges;
238
- // exclude it explicitly so it isn't listed as its own neighbor.
209
+ // The seed appears at depth >0 on undirected round-trips exclude it.
239
210
  params: [seedId, depth, seedId, limit]
240
211
  };
241
212
  }
@@ -251,14 +222,21 @@ function buildEdgesAmong(ids) {
251
222
  params: [...ids, ...ids]
252
223
  };
253
224
  }
254
- var EXTRACTION_SYSTEM_PROMPT = `You extract durable, shareable context from raw text for a team of AI agents that will read it later by keyword.
225
+ var EXTRACTION_SYSTEM_PROMPT = `You extract durable, structured memories from a conversation or document for an AI agent that will recall them across sessions.
255
226
 
256
- Return JSON: { "facts": [...], "entities": [...], "edges": [...] }.
257
- - facts: atomic, self-contained statements worth remembering across sessions \u2014 decisions, conventions, constraints, identifiers, preferences. Each: { "body": string, "topic"?: string, "tags"?: string[] }. Prefer specific and stable over transient chatter.
258
- - entities: named things \u2014 services, people, tables, repos, concepts. Each: { "name": string, "kind"?: string, "body"?: short description }.
259
- - edges: relationships between entities, referencing entity names. Each: { "src": name, "rel": string, "dst": name }.
227
+ Return JSON: { "memories": [...], "entities": [...], "edges": [...] }.
228
+ - memories: facts worth remembering across sessions \u2014 decisions, conventions, constraints, identifiers, preferences, schema details. Each:
229
+ {
230
+ "name": string (kebab-case slug, unique key \u2014 e.g. "auth-provider-choice", "todos-table-schema", "user-prefers-metrics"),
231
+ "description": string (one-liner shown in the always-loaded index),
232
+ "type": "user"|"feedback"|"project"|"reference",
233
+ "body": string (full markdown content with all relevant detail)
234
+ }
235
+ Types: user=who they are/preferences; feedback=guidance for agents; project=ongoing work/decisions; reference=where to find things.
236
+ - entities: named things \u2014 services, people, tables, repos. Each: { "name": string, "kind"?: string, "body"?: string }
237
+ - edges: relationships between entities. Each: { "src": name, "rel": string, "dst": name }
260
238
 
261
- Be conservative: omit anything ephemeral or low-value. Use lowercase tags. If nothing is worth keeping, return empty arrays.`;
239
+ Be conservative. Use unique, meaningful names. If nothing durable is found, return empty arrays.`;
262
240
  function buildExtractionMessages(raw, opts = {}) {
263
241
  return [
264
242
  { role: "system", content: EXTRACTION_SYSTEM_PROMPT },
@@ -273,16 +251,17 @@ function buildExtractionMessages(raw, opts = {}) {
273
251
  var EXTRACTION_JSON_SCHEMA = {
274
252
  type: "object",
275
253
  properties: {
276
- facts: {
254
+ memories: {
277
255
  type: "array",
278
256
  items: {
279
257
  type: "object",
280
258
  properties: {
281
- body: { type: "string" },
282
- topic: { type: "string" },
283
- tags: { type: "array", items: { type: "string" } }
259
+ name: { type: "string" },
260
+ description: { type: "string" },
261
+ type: { type: "string", enum: ["user", "feedback", "project", "reference"] },
262
+ body: { type: "string" }
284
263
  },
285
- required: ["body"]
264
+ required: ["name", "description", "body"]
286
265
  }
287
266
  },
288
267
  entities: {
@@ -317,16 +296,15 @@ export {
317
296
  TABLE_PREFIX,
318
297
  SCHEMA_SQL,
319
298
  buildInit,
320
- normalizeTags,
299
+ buildDetectLegacySchema,
300
+ buildDropLegacy,
321
301
  toFtsQuery,
322
302
  entityId,
323
303
  buildRemember,
324
- buildSupersede,
325
304
  buildForget,
326
305
  buildGet,
306
+ buildIndex,
327
307
  buildRecall,
328
- buildRecent,
329
- buildByTag,
330
308
  buildStats,
331
309
  buildUpsertEntity,
332
310
  buildUpsertEdge,
@@ -336,4 +314,4 @@ export {
336
314
  buildExtractionMessages,
337
315
  EXTRACTION_JSON_SCHEMA
338
316
  };
339
- //# sourceMappingURL=chunk-RMG66FDI.js.map
317
+ //# sourceMappingURL=chunk-HRHBXOPL.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/core.ts"],"sourcesContent":["/**\n * @persql/context/core — zero-runtime-dependency engine.\n *\n * Schema DDL, SQL builders, and the extraction contract shared by the\n * `@persql/context` SDK (client-side, over `@persql/sdk`) and the hosted\n * PerSQL Context worker (server-side, over `@persql/ai`). Structured memories\n * with a name-keyed UPSERT + always-loaded index replace the old episode model.\n *\n * Retrieval is lexical: FTS5 with the porter stemmer, BM25-ranked. No vectors.\n */\n\nexport const SCHEMA_VERSION = 2;\nexport const TABLE_PREFIX = \"ctx_\";\nconst T = TABLE_PREFIX;\n\nexport type MemoryType = \"user\" | \"feedback\" | \"project\" | \"reference\";\n\nexport interface MemoryRow {\n id: string;\n name: string;\n description: string;\n type: MemoryType;\n body: string;\n source: string | null;\n createdAt: number;\n updatedAt: number;\n}\n\nexport interface IndexRow {\n id: string;\n name: string;\n description: string;\n type: MemoryType;\n updatedAt: number;\n}\n\nexport interface Entity {\n id: string;\n name: string;\n kind: string | null;\n body: string | null;\n createdAt: number;\n}\n\nexport interface Edge {\n id: string;\n src: string;\n rel: string;\n dst: string;\n source: string | null;\n createdAt: number;\n}\n\nexport interface RememberInput {\n name: string;\n description: string;\n type?: MemoryType;\n body: string;\n source?: string;\n}\n\nexport interface EntityInput {\n name: string;\n kind?: string;\n body?: string;\n id?: string;\n}\n\nexport interface EdgeInput {\n src: string;\n rel: string;\n dst: string;\n source?: string;\n}\n\nexport interface RecallOptions {\n limit?: number;\n operator?: \"or\" | \"and\";\n mode?: \"terms\" | \"raw\";\n}\n\nexport interface ListOptions {\n limit?: number;\n type?: MemoryType;\n}\n\n/** Structured memories extracted by an LLM from raw text. */\nexport interface ExtractedContext {\n memories?: Array<{\n name: string;\n description: string;\n type?: MemoryType;\n body: string;\n }>;\n entities?: EntityInput[];\n edges?: EdgeInput[];\n}\n\nexport interface SqlStatement {\n sql: string;\n params: unknown[];\n}\n\n// ---------------------------------------------------------------------------\n// Schema\n// ---------------------------------------------------------------------------\n\nexport const SCHEMA_SQL: string[] = [\n `CREATE TABLE IF NOT EXISTS ${T}memory (\n id TEXT PRIMARY KEY,\n name TEXT NOT NULL,\n description TEXT NOT NULL,\n type TEXT NOT NULL DEFAULT 'project',\n body TEXT NOT NULL,\n source TEXT,\n created_at INTEGER NOT NULL,\n updated_at INTEGER NOT NULL\n )`,\n `CREATE UNIQUE INDEX IF NOT EXISTS ${T}memory_name ON ${T}memory(name)`,\n // External-content FTS on name+description+body. Porter stemmer so\n // \"invoice\" recalls \"invoicing\" without embeddings.\n `CREATE VIRTUAL TABLE IF NOT EXISTS ${T}memory_fts USING fts5(\n name, description, body,\n content='${T}memory', content_rowid='rowid',\n tokenize='porter unicode61'\n )`,\n `CREATE TRIGGER IF NOT EXISTS ${T}memory_ai AFTER INSERT ON ${T}memory BEGIN\n INSERT INTO ${T}memory_fts(rowid, name, description, body)\n VALUES (new.rowid, new.name, new.description, new.body);\n END`,\n `CREATE TRIGGER IF NOT EXISTS ${T}memory_ad AFTER DELETE ON ${T}memory BEGIN\n INSERT INTO ${T}memory_fts(${T}memory_fts, rowid, name, description, body)\n VALUES ('delete', old.rowid, old.name, old.description, old.body);\n END`,\n `CREATE TRIGGER IF NOT EXISTS ${T}memory_au AFTER UPDATE ON ${T}memory BEGIN\n INSERT INTO ${T}memory_fts(${T}memory_fts, rowid, name, description, body)\n VALUES ('delete', old.rowid, old.name, old.description, old.body);\n INSERT INTO ${T}memory_fts(rowid, name, description, body)\n VALUES (new.rowid, new.name, new.description, new.body);\n END`,\n `CREATE TABLE IF NOT EXISTS ${T}entity (\n id TEXT PRIMARY KEY,\n name TEXT NOT NULL,\n kind TEXT,\n body TEXT,\n created_at INTEGER NOT NULL\n )`,\n `CREATE TABLE IF NOT EXISTS ${T}edge (\n id TEXT PRIMARY KEY,\n src TEXT NOT NULL,\n rel TEXT NOT NULL,\n dst TEXT NOT NULL,\n source TEXT,\n created_at INTEGER NOT NULL\n )`,\n `CREATE INDEX IF NOT EXISTS ${T}edge_src ON ${T}edge(src)`,\n `CREATE INDEX IF NOT EXISTS ${T}edge_dst ON ${T}edge(dst)`,\n];\n\nexport function buildInit(): SqlStatement[] {\n return SCHEMA_SQL.map((sql) => ({ sql, params: [] }));\n}\n\n// ---------------------------------------------------------------------------\n// Legacy migration helpers\n// ---------------------------------------------------------------------------\n\n/** Returns a non-zero count when the old (topic-based) schema is present. */\nexport function buildDetectLegacySchema(): SqlStatement {\n return {\n sql: `SELECT count(*) AS n FROM pragma_table_info('${T}memory') WHERE name='topic'`,\n params: [],\n };\n}\n\n/**\n * Drop all ctx_ tables + triggers so buildInit() can recreate them cleanly.\n * Run in order — FTS must go first (it references the content table).\n */\nexport function buildDropLegacy(): SqlStatement[] {\n return [\n { sql: `DROP TABLE IF EXISTS ${T}memory_fts`, params: [] },\n { sql: `DROP TABLE IF EXISTS ${T}memory`, params: [] },\n { sql: `DROP TABLE IF EXISTS ${T}entity`, params: [] },\n { sql: `DROP TABLE IF EXISTS ${T}edge`, params: [] },\n ];\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nconst FTS_OPERATORS = new Set([\"and\", \"or\", \"not\", \"near\"]);\n\n/**\n * Turn arbitrary user text into a safe FTS5 MATCH expression. `terms` mode\n * (default) extracts word tokens, drops bare boolean operators, and ORs the\n * rest as quoted terms. `raw` mode passes a hand-written FTS expression through.\n */\nexport function toFtsQuery(\n input: string,\n opts: { operator?: \"or\" | \"and\"; mode?: \"terms\" | \"raw\" } = {}\n): string | null {\n if (opts.mode === \"raw\") return input.trim() || null;\n const tokens = (input.match(/[\\p{L}\\p{N}_]+/gu) ?? []).filter(\n (t) => !FTS_OPERATORS.has(t.toLowerCase())\n );\n if (!tokens.length) return null;\n const joiner = opts.operator === \"and\" ? \" AND \" : \" OR \";\n return tokens.map((t) => `\"${t}\"`).join(joiner);\n}\n\nfunction slugId(prefix: string, s: string): string {\n const base = s\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, \"-\")\n .replace(/^-+|-+$/g, \"\")\n .slice(0, 96);\n return prefix + (base || \"x\");\n}\n\nexport function entityId(name: string): string {\n return slugId(\"e_\", name);\n}\n\nfunction resolveEntityRef(ref: string): string {\n return ref.startsWith(\"e_\") ? ref : entityId(ref);\n}\n\nfunction edgeId(srcId: string, rel: string, dstId: string): string {\n return slugId(\"x_\", `${srcId}|${rel}|${dstId}`);\n}\n\nconst MEMORY_COLS =\n \"id, name, description, type, body, source, created_at AS createdAt, updated_at AS updatedAt\";\n\n// ---------------------------------------------------------------------------\n// Memory builders\n// ---------------------------------------------------------------------------\n\n/** UPSERT by name — same name overwrites description/body/type/source. */\nexport function buildRemember(\n input: RememberInput,\n now: number,\n id: string\n): SqlStatement {\n return {\n sql: `INSERT INTO ${T}memory (id, name, description, type, body, source, created_at, updated_at)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?)\n ON CONFLICT(name) DO UPDATE SET\n description = excluded.description,\n body = excluded.body,\n type = excluded.type,\n source = excluded.source,\n updated_at = excluded.updated_at`,\n params: [\n id,\n input.name,\n input.description,\n input.type ?? \"project\",\n input.body,\n input.source ?? null,\n now,\n now,\n ],\n };\n}\n\n/** Delete a memory by its name slug. */\nexport function buildForget(name: string): SqlStatement {\n return { sql: `DELETE FROM ${T}memory WHERE name = ?`, params: [name] };\n}\n\n/** Fetch one memory row by name, or null if absent. */\nexport function buildGet(name: string): SqlStatement {\n return {\n sql: `SELECT ${MEMORY_COLS} FROM ${T}memory WHERE name = ?`,\n params: [name],\n };\n}\n\n/** Always-loaded index: name+description+type, newest first. */\nexport function buildIndex(opts: ListOptions = {}): SqlStatement {\n const where: string[] = [];\n const params: unknown[] = [];\n if (opts.type) {\n where.push(\"type = ?\");\n params.push(opts.type);\n }\n params.push(opts.limit ?? 50);\n return {\n sql: `SELECT id, name, description, type, updated_at AS updatedAt FROM ${T}memory\n ${where.length ? \"WHERE \" + where.join(\" AND \") : \"\"}\n ORDER BY updated_at DESC LIMIT ?`,\n params,\n };\n}\n\n/** BM25 recall across name+description+body. */\nexport function buildRecall(\n query: string,\n opts: RecallOptions = {}\n): SqlStatement | null {\n const match = toFtsQuery(query, { operator: opts.operator, mode: opts.mode });\n if (!match) return null;\n return {\n sql: `SELECT m.id, m.name, m.description, m.type, m.body, m.source,\n m.created_at AS createdAt, m.updated_at AS updatedAt\n FROM ${T}memory_fts\n JOIN ${T}memory m ON m.rowid = ${T}memory_fts.rowid\n WHERE ${T}memory_fts MATCH ?\n ORDER BY ${T}memory_fts.rank\n LIMIT ?`,\n params: [match, opts.limit ?? 8],\n };\n}\n\nexport function buildStats(): SqlStatement {\n return {\n sql: `SELECT\n (SELECT count(*) FROM ${T}memory) AS memories,\n (SELECT count(*) FROM ${T}entity) AS entities,\n (SELECT count(*) FROM ${T}edge) AS edges`,\n params: [],\n };\n}\n\n// ---------------------------------------------------------------------------\n// Graph builders (unchanged from v1)\n// ---------------------------------------------------------------------------\n\nexport function buildUpsertEntity(e: EntityInput, now: number): SqlStatement {\n const id = e.id ?? entityId(e.name);\n return {\n sql: `INSERT INTO ${T}entity (id, name, kind, body, created_at)\n VALUES (?, ?, ?, ?, ?)\n ON CONFLICT(id) DO UPDATE SET\n name = excluded.name,\n kind = COALESCE(excluded.kind, ${T}entity.kind),\n body = COALESCE(excluded.body, ${T}entity.body)`,\n params: [id, e.name, e.kind ?? null, e.body ?? null, now],\n };\n}\n\nexport function buildUpsertEdge(e: EdgeInput, now: number): SqlStatement {\n const srcId = resolveEntityRef(e.src);\n const dstId = resolveEntityRef(e.dst);\n const id = edgeId(srcId, e.rel, dstId);\n return {\n sql: `INSERT INTO ${T}edge (id, src, rel, dst, source, created_at)\n VALUES (?, ?, ?, ?, ?, ?)\n ON CONFLICT(id) DO UPDATE SET\n source = COALESCE(excluded.source, ${T}edge.source)`,\n params: [id, srcId, e.rel, dstId, e.source ?? null, now],\n };\n}\n\nexport function buildNeighborEntities(\n seedId: string,\n depth: number,\n limit: number\n): SqlStatement {\n return {\n sql: `WITH RECURSIVE reach(id, depth) AS (\n SELECT ?, 0\n UNION\n SELECT CASE WHEN e.src = reach.id THEN e.dst ELSE e.src END,\n reach.depth + 1\n FROM reach\n JOIN ${T}edge e ON (e.src = reach.id OR e.dst = reach.id)\n WHERE reach.depth < ?\n )\n SELECT en.id, en.name, en.kind, en.body,\n en.created_at AS createdAt, MIN(reach.depth) AS depth\n FROM reach\n JOIN ${T}entity en ON en.id = reach.id\n WHERE reach.depth > 0 AND en.id <> ?\n GROUP BY en.id\n ORDER BY depth, en.name\n LIMIT ?`,\n // The seed appears at depth >0 on undirected round-trips — exclude it.\n params: [seedId, depth, seedId, limit],\n };\n}\n\nexport function buildEdgesAmong(ids: string[]): SqlStatement {\n if (!ids.length) {\n return { sql: `SELECT id, src, rel, dst, source, created_at AS createdAt FROM ${T}edge WHERE 0`, params: [] };\n }\n const ph = ids.map(() => \"?\").join(\", \");\n return {\n sql: `SELECT id, src, rel, dst, source, created_at AS createdAt\n FROM ${T}edge\n WHERE src IN (${ph}) AND dst IN (${ph})`,\n params: [...ids, ...ids],\n };\n}\n\n// ---------------------------------------------------------------------------\n// Extraction contract (versioned here so client and server stay in sync)\n// ---------------------------------------------------------------------------\n\nexport const EXTRACTION_SYSTEM_PROMPT = `You extract durable, structured memories from a conversation or document for an AI agent that will recall them across sessions.\n\nReturn JSON: { \"memories\": [...], \"entities\": [...], \"edges\": [...] }.\n- memories: facts worth remembering across sessions — decisions, conventions, constraints, identifiers, preferences, schema details. Each:\n {\n \"name\": string (kebab-case slug, unique key — e.g. \"auth-provider-choice\", \"todos-table-schema\", \"user-prefers-metrics\"),\n \"description\": string (one-liner shown in the always-loaded index),\n \"type\": \"user\"|\"feedback\"|\"project\"|\"reference\",\n \"body\": string (full markdown content with all relevant detail)\n }\n Types: user=who they are/preferences; feedback=guidance for agents; project=ongoing work/decisions; reference=where to find things.\n- entities: named things — services, people, tables, repos. Each: { \"name\": string, \"kind\"?: string, \"body\"?: string }\n- edges: relationships between entities. Each: { \"src\": name, \"rel\": string, \"dst\": name }\n\nBe conservative. Use unique, meaningful names. If nothing durable is found, return empty arrays.`;\n\nexport function buildExtractionMessages(\n raw: string,\n opts: { hint?: string } = {}\n): Array<{ role: \"system\" | \"user\"; content: string }> {\n return [\n { role: \"system\", content: EXTRACTION_SYSTEM_PROMPT },\n {\n role: \"user\",\n content: (opts.hint ? `Context: ${opts.hint}\\n\\n` : \"\") + raw,\n },\n ];\n}\n\nexport const EXTRACTION_JSON_SCHEMA = {\n type: \"object\",\n properties: {\n memories: {\n type: \"array\",\n items: {\n type: \"object\",\n properties: {\n name: { type: \"string\" },\n description: { type: \"string\" },\n type: { type: \"string\", enum: [\"user\", \"feedback\", \"project\", \"reference\"] },\n body: { type: \"string\" },\n },\n required: [\"name\", \"description\", \"body\"],\n },\n },\n entities: {\n type: \"array\",\n items: {\n type: \"object\",\n properties: {\n name: { type: \"string\" },\n kind: { type: \"string\" },\n body: { type: \"string\" },\n },\n required: [\"name\"],\n },\n },\n edges: {\n type: \"array\",\n items: {\n type: \"object\",\n properties: {\n src: { type: \"string\" },\n rel: { type: \"string\" },\n dst: { type: \"string\" },\n },\n required: [\"src\", \"rel\", \"dst\"],\n },\n },\n },\n} as const;\n"],"mappings":"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","names":[]}