@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 +83 -36
- package/dist/{chunk-RMG66FDI.js → chunk-HRHBXOPL.js} +91 -113
- package/dist/chunk-HRHBXOPL.js.map +1 -0
- package/dist/core.cjs +93 -116
- package/dist/core.cjs.map +1 -1
- package/dist/core.d.cts +54 -48
- package/dist/core.d.ts +54 -48
- package/dist/core.js +7 -9
- package/dist/index.cjs +211 -178
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +34 -40
- package/dist/index.d.ts +34 -40
- package/dist/index.js +124 -71
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
- package/dist/chunk-RMG66FDI.js.map +0 -1
package/README.md
CHANGED
|
@@ -1,15 +1,13 @@
|
|
|
1
1
|
# @persql/context
|
|
2
2
|
|
|
3
|
-
Shared, structured agent
|
|
3
|
+
Shared, structured agent memory on the [PerSQL](https://persql.com) substrate.
|
|
4
4
|
|
|
5
|
-
One store, readable and writable from every agent surface
|
|
6
|
-
|
|
7
|
-
|
|
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.
|
|
10
|
-
|
|
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
|
-
//
|
|
29
|
+
// Save a structured memory. Same name → update; new name → insert.
|
|
32
30
|
await ctx.remember({
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
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
|
-
##
|
|
52
|
+
## OpenAI Agents SDK
|
|
47
53
|
|
|
48
|
-
|
|
49
|
-
|
|
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
|
-
|
|
53
|
-
|
|
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
|
|
81
|
+
const result = await run(agent, "What invoicing terms does Acme prefer?");
|
|
82
|
+
console.log(result.finalOutput);
|
|
57
83
|
```
|
|
58
84
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
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
|
-
|
|
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)` |
|
|
70
|
-
| `
|
|
71
|
-
| `
|
|
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 })` |
|
|
76
|
-
| `
|
|
77
|
-
| `
|
|
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
|
|
82
|
-
with no per-
|
|
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,
|
|
88
|
-
types —
|
|
89
|
-
|
|
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 =
|
|
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
|
-
|
|
9
|
-
|
|
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
|
-
|
|
14
|
+
updated_at INTEGER NOT NULL
|
|
15
15
|
)`,
|
|
16
|
-
`CREATE INDEX IF NOT EXISTS ${T}
|
|
17
|
-
|
|
18
|
-
|
|
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
|
-
|
|
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,
|
|
28
|
-
VALUES (new.rowid, new.
|
|
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,
|
|
32
|
-
VALUES ('delete', old.rowid, old.
|
|
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,
|
|
36
|
-
VALUES ('delete', old.rowid, old.
|
|
37
|
-
INSERT INTO ${T}memory_fts(rowid,
|
|
38
|
-
VALUES (new.rowid, new.
|
|
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
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
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,
|
|
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,
|
|
96
|
-
VALUES (?, ?, ?, ?, ?, ?, ?,
|
|
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.
|
|
100
|
-
input.
|
|
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
|
|
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(
|
|
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
|
|
140
|
-
|
|
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
|
|
128
|
+
function buildIndex(opts = {}) {
|
|
150
129
|
const where = [];
|
|
151
130
|
const params = [];
|
|
152
|
-
if (
|
|
153
|
-
|
|
154
|
-
|
|
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 ??
|
|
135
|
+
params.push(opts.limit ?? 50);
|
|
158
136
|
return {
|
|
159
|
-
sql: `SELECT
|
|
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
|
|
162
|
-
LIMIT ?`,
|
|
139
|
+
ORDER BY updated_at DESC LIMIT ?`,
|
|
163
140
|
params
|
|
164
141
|
};
|
|
165
142
|
}
|
|
166
|
-
function
|
|
167
|
-
const
|
|
168
|
-
|
|
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
|
|
178
|
-
|
|
179
|
-
|
|
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
|
|
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
|
|
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,
|
|
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: { "
|
|
257
|
-
-
|
|
258
|
-
|
|
259
|
-
|
|
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
|
|
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
|
-
|
|
254
|
+
memories: {
|
|
277
255
|
type: "array",
|
|
278
256
|
items: {
|
|
279
257
|
type: "object",
|
|
280
258
|
properties: {
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
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
|
-
|
|
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-
|
|
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":[]}
|