eklavya 1.18.2 → 1.19.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/dist/assets/dashboard.html +559 -14
- package/dist/assets/tutor/references/focus-and-level.md +29 -0
- package/dist/cli.js +648 -20
- package/dist/cli.js.map +1 -1
- package/dist/config-path.js +156 -0
- package/dist/config-path.js.map +1 -0
- package/dist/config.js +231 -2
- package/dist/config.js.map +1 -1
- package/dist/dashboard.js +355 -0
- package/dist/dashboard.js.map +1 -1
- package/dist/eval/retrieval-score.js +77 -0
- package/dist/eval/retrieval-score.js.map +1 -0
- package/dist/hooks/capture-tool.js +107 -0
- package/dist/hooks/capture-tool.js.map +1 -0
- package/dist/hooks/checkpoint-quiz.js +5 -6
- package/dist/hooks/checkpoint-quiz.js.map +1 -1
- package/dist/hooks/lib.js +7 -33
- package/dist/hooks/lib.js.map +1 -1
- package/dist/hooks/memory-lib.js +181 -0
- package/dist/hooks/memory-lib.js.map +1 -0
- package/dist/hooks/prompt-submit-nudge.js +56 -20
- package/dist/hooks/prompt-submit-nudge.js.map +1 -1
- package/dist/hooks/session-start.js +73 -74
- package/dist/hooks/session-start.js.map +1 -1
- package/dist/hooks/stop-quiz-check.js +44 -7
- package/dist/hooks/stop-quiz-check.js.map +1 -1
- package/dist/install.js +37 -0
- package/dist/install.js.map +1 -1
- package/dist/memory/capture.js +123 -0
- package/dist/memory/capture.js.map +1 -0
- package/dist/memory/code.js +186 -0
- package/dist/memory/code.js.map +1 -0
- package/dist/memory/collections.js +87 -0
- package/dist/memory/collections.js.map +1 -0
- package/dist/memory/embed.js +81 -0
- package/dist/memory/embed.js.map +1 -0
- package/dist/memory/hosts.js +71 -0
- package/dist/memory/hosts.js.map +1 -0
- package/dist/memory/identity.js +74 -0
- package/dist/memory/identity.js.map +1 -0
- package/dist/memory/import.js +916 -0
- package/dist/memory/import.js.map +1 -0
- package/dist/memory/learning.js +148 -0
- package/dist/memory/learning.js.map +1 -0
- package/dist/memory/notify.js +220 -0
- package/dist/memory/notify.js.map +1 -0
- package/dist/memory/privacy.js +117 -0
- package/dist/memory/privacy.js.map +1 -0
- package/dist/memory/provider.js +155 -0
- package/dist/memory/provider.js.map +1 -0
- package/dist/memory/recall.js +327 -0
- package/dist/memory/recall.js.map +1 -0
- package/dist/memory/replay.js +174 -0
- package/dist/memory/replay.js.map +1 -0
- package/dist/memory/search.js +143 -0
- package/dist/memory/search.js.map +1 -0
- package/dist/memory/spool.js +93 -0
- package/dist/memory/spool.js.map +1 -0
- package/dist/memory/store.js +399 -0
- package/dist/memory/store.js.map +1 -0
- package/dist/memory/summarize.js +178 -0
- package/dist/memory/summarize.js.map +1 -0
- package/dist/memory/sync.js +595 -0
- package/dist/memory/sync.js.map +1 -0
- package/dist/memory/tokens.js +55 -0
- package/dist/memory/tokens.js.map +1 -0
- package/dist/memory/worker.js +207 -0
- package/dist/memory/worker.js.map +1 -0
- package/dist/migrations/009_memory.sql +226 -0
- package/dist/migrations/010_import.sql +26 -0
- package/dist/migrations/011_sync.sql +76 -0
- package/dist/migrations/012_job_backoff.sql +13 -0
- package/dist/migrations/013_batch_provenance.sql +18 -0
- package/dist/migrations/014_batch_events_index.sql +18 -0
- package/dist/plugin/.claude-plugin/plugin.json +1 -1
- package/dist/plugin/agents/tutor.md +3 -1
- package/dist/plugin/hooks/CLAUDE.md +30 -6
- package/dist/plugin/hooks/hooks.json +10 -0
- package/dist/plugin/skills/CLAUDE.md +17 -7
- package/dist/plugin/skills/memory/SKILL.md +88 -0
- package/dist/plugin/skills/setup/SKILL.md +16 -2
- package/dist/plugin/skills/tutor/references/focus-and-level.md +29 -0
- package/dist/time.js +41 -0
- package/dist/time.js.map +1 -0
- package/dist/tools/code_tools.js +73 -0
- package/dist/tools/code_tools.js.map +1 -0
- package/dist/tools/collection_tools.js +92 -0
- package/dist/tools/collection_tools.js.map +1 -0
- package/dist/tools/config_tools.js +104 -1
- package/dist/tools/config_tools.js.map +1 -1
- package/dist/tools/index.js +15 -0
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/memory_read_tools.js +277 -0
- package/dist/tools/memory_read_tools.js.map +1 -0
- package/dist/tools/memory_write_tools.js +115 -0
- package/dist/tools/memory_write_tools.js.map +1 -0
- package/dist/user-skill/eklavya/SKILL.md +64 -8
- package/package.json +2 -1
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
const LANGUAGES = {
|
|
4
|
+
'.ts': tsLike('typescript'),
|
|
5
|
+
'.tsx': tsLike('typescript'),
|
|
6
|
+
'.js': tsLike('javascript'),
|
|
7
|
+
'.jsx': tsLike('javascript'),
|
|
8
|
+
'.mjs': tsLike('javascript'),
|
|
9
|
+
'.cjs': tsLike('javascript'),
|
|
10
|
+
'.py': {
|
|
11
|
+
language: 'python',
|
|
12
|
+
lineComment: ['#'],
|
|
13
|
+
patterns: [
|
|
14
|
+
{ kind: 'class', re: /^\s*class\s+([A-Za-z_]\w*)/ },
|
|
15
|
+
{ kind: 'function', re: /^\s*(?:async\s+)?def\s+([A-Za-z_]\w*)/ },
|
|
16
|
+
],
|
|
17
|
+
},
|
|
18
|
+
'.go': {
|
|
19
|
+
language: 'go',
|
|
20
|
+
lineComment: ['//'],
|
|
21
|
+
patterns: [
|
|
22
|
+
{ kind: 'function', re: /^\s*func\s+(?:\([^)]*\)\s*)?([A-Za-z_]\w*)/ },
|
|
23
|
+
{ kind: 'type', re: /^\s*type\s+([A-Za-z_]\w*)/ },
|
|
24
|
+
],
|
|
25
|
+
},
|
|
26
|
+
'.rs': {
|
|
27
|
+
language: 'rust',
|
|
28
|
+
lineComment: ['//'],
|
|
29
|
+
patterns: [
|
|
30
|
+
{ kind: 'function', re: /^\s*(?:pub\s+)?(?:async\s+)?fn\s+([A-Za-z_]\w*)/ },
|
|
31
|
+
{ kind: 'struct', re: /^\s*(?:pub\s+)?struct\s+([A-Za-z_]\w*)/ },
|
|
32
|
+
{ kind: 'enum', re: /^\s*(?:pub\s+)?enum\s+([A-Za-z_]\w*)/ },
|
|
33
|
+
{ kind: 'trait', re: /^\s*(?:pub\s+)?trait\s+([A-Za-z_]\w*)/ },
|
|
34
|
+
{ kind: 'impl', re: /^\s*impl(?:<[^>]*>)?\s+([A-Za-z_][\w:<>]*)/ },
|
|
35
|
+
],
|
|
36
|
+
},
|
|
37
|
+
'.java': {
|
|
38
|
+
language: 'java',
|
|
39
|
+
lineComment: ['//'],
|
|
40
|
+
patterns: [
|
|
41
|
+
{ kind: 'class', re: /^\s*(?:public\s+|private\s+|protected\s+|abstract\s+|final\s+|static\s+)*class\s+([A-Za-z_]\w*)/ },
|
|
42
|
+
{ kind: 'interface', re: /^\s*(?:public\s+)?interface\s+([A-Za-z_]\w*)/ },
|
|
43
|
+
{ kind: 'method', re: /^\s*(?:public|private|protected)\s+(?:static\s+)?[\w<>\[\],.\s]+\s+([A-Za-z_]\w*)\s*\(/ },
|
|
44
|
+
],
|
|
45
|
+
},
|
|
46
|
+
'.rb': {
|
|
47
|
+
language: 'ruby',
|
|
48
|
+
lineComment: ['#'],
|
|
49
|
+
patterns: [
|
|
50
|
+
{ kind: 'class', re: /^\s*class\s+([A-Za-z_]\w*)/ },
|
|
51
|
+
{ kind: 'module', re: /^\s*module\s+([A-Za-z_]\w*)/ },
|
|
52
|
+
{ kind: 'method', re: /^\s*def\s+([A-Za-z_?!.]\w*[?!]?)/ },
|
|
53
|
+
],
|
|
54
|
+
},
|
|
55
|
+
'.sql': {
|
|
56
|
+
language: 'sql',
|
|
57
|
+
lineComment: ['--'],
|
|
58
|
+
patterns: [
|
|
59
|
+
{ kind: 'table', re: /^\s*CREATE\s+(?:VIRTUAL\s+)?TABLE(?:\s+IF\s+NOT\s+EXISTS)?\s+([A-Za-z_]\w*)/i },
|
|
60
|
+
{ kind: 'index', re: /^\s*CREATE\s+(?:UNIQUE\s+)?INDEX(?:\s+IF\s+NOT\s+EXISTS)?\s+([A-Za-z_]\w*)/i },
|
|
61
|
+
{ kind: 'trigger', re: /^\s*CREATE\s+TRIGGER(?:\s+IF\s+NOT\s+EXISTS)?\s+([A-Za-z_]\w*)/i },
|
|
62
|
+
],
|
|
63
|
+
},
|
|
64
|
+
};
|
|
65
|
+
function tsLike(language) {
|
|
66
|
+
return {
|
|
67
|
+
language,
|
|
68
|
+
lineComment: ['//', '*', '/*'],
|
|
69
|
+
patterns: [
|
|
70
|
+
{ kind: 'class', re: /^\s*(?:export\s+)?(?:abstract\s+)?class\s+([A-Za-z_$][\w$]*)/ },
|
|
71
|
+
{ kind: 'interface', re: /^\s*(?:export\s+)?interface\s+([A-Za-z_$][\w$]*)/ },
|
|
72
|
+
{ kind: 'type', re: /^\s*(?:export\s+)?type\s+([A-Za-z_$][\w$]*)/ },
|
|
73
|
+
{ kind: 'enum', re: /^\s*(?:export\s+)?(?:const\s+)?enum\s+([A-Za-z_$][\w$]*)/ },
|
|
74
|
+
{ kind: 'function', re: /^\s*(?:export\s+)?(?:default\s+)?(?:async\s+)?function\s*\*?\s+([A-Za-z_$][\w$]*)/ },
|
|
75
|
+
{ kind: 'const', re: /^\s*(?:export\s+)?(?:const|let)\s+([A-Za-z_$][\w$]*)\s*[:=]/ },
|
|
76
|
+
],
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
export function languageOf(file) {
|
|
80
|
+
return LANGUAGES[path.extname(file).toLowerCase()]?.language ?? null;
|
|
81
|
+
}
|
|
82
|
+
const MAX_BYTES = 2 * 1024 * 1024;
|
|
83
|
+
const MAX_SYMBOLS = 400;
|
|
84
|
+
export function outline(file) {
|
|
85
|
+
const spec = LANGUAGES[path.extname(file).toLowerCase()];
|
|
86
|
+
if (!spec)
|
|
87
|
+
return null;
|
|
88
|
+
let text;
|
|
89
|
+
try {
|
|
90
|
+
if (fs.statSync(file).size > MAX_BYTES)
|
|
91
|
+
return null;
|
|
92
|
+
text = fs.readFileSync(file, 'utf8');
|
|
93
|
+
}
|
|
94
|
+
catch {
|
|
95
|
+
return null;
|
|
96
|
+
}
|
|
97
|
+
const lines = text.split('\n');
|
|
98
|
+
const symbols = [];
|
|
99
|
+
for (let i = 0; i < lines.length && symbols.length < MAX_SYMBOLS; i++) {
|
|
100
|
+
const line = lines[i];
|
|
101
|
+
const trimmed = line.trim();
|
|
102
|
+
if (!trimmed || spec.lineComment.some((c) => trimmed.startsWith(c)))
|
|
103
|
+
continue;
|
|
104
|
+
for (const { kind, re } of spec.patterns) {
|
|
105
|
+
const match = re.exec(line);
|
|
106
|
+
if (match?.[1]) {
|
|
107
|
+
symbols.push({ name: match[1], kind, line: i + 1, text: trimmed.slice(0, 200) });
|
|
108
|
+
break;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return {
|
|
113
|
+
file,
|
|
114
|
+
language: spec.language,
|
|
115
|
+
lines: lines.length,
|
|
116
|
+
symbols,
|
|
117
|
+
truncated: symbols.length >= MAX_SYMBOLS,
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
const SKIP_DIRS = new Set(['node_modules', '.git', 'dist', 'build', 'coverage', '.next', 'vendor', '__pycache__']);
|
|
121
|
+
function* walk(root, depth = 0) {
|
|
122
|
+
if (depth > 12)
|
|
123
|
+
return;
|
|
124
|
+
let entries;
|
|
125
|
+
try {
|
|
126
|
+
entries = fs.readdirSync(root, { withFileTypes: true });
|
|
127
|
+
}
|
|
128
|
+
catch {
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
for (const entry of entries) {
|
|
132
|
+
if (entry.name.startsWith('.') && entry.name !== '.eklavya')
|
|
133
|
+
continue;
|
|
134
|
+
const full = path.join(root, entry.name);
|
|
135
|
+
if (entry.isDirectory()) {
|
|
136
|
+
if (SKIP_DIRS.has(entry.name))
|
|
137
|
+
continue;
|
|
138
|
+
yield* walk(full, depth + 1);
|
|
139
|
+
}
|
|
140
|
+
else if (entry.isFile() && LANGUAGES[path.extname(entry.name).toLowerCase()]) {
|
|
141
|
+
yield full;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Finds a declaration by name across a tree, with the containing symbol.
|
|
147
|
+
*
|
|
148
|
+
* Declaration-first rather than every mention: an agent asking "where is
|
|
149
|
+
* `recordReceipt`" wants the one line that defines it, and a list of its 40
|
|
150
|
+
* call sites is the answer to a different question.
|
|
151
|
+
*/
|
|
152
|
+
export function findSymbol(root, name, limit = 20) {
|
|
153
|
+
const needle = name.toLowerCase();
|
|
154
|
+
const hits = [];
|
|
155
|
+
for (const file of walk(root)) {
|
|
156
|
+
const parsed = outline(file);
|
|
157
|
+
if (!parsed)
|
|
158
|
+
continue;
|
|
159
|
+
for (const symbol of parsed.symbols) {
|
|
160
|
+
if (!symbol.name.toLowerCase().includes(needle))
|
|
161
|
+
continue;
|
|
162
|
+
hits.push({ file: path.relative(root, file), line: symbol.line, text: symbol.text, symbol: symbol.name });
|
|
163
|
+
if (hits.length >= limit)
|
|
164
|
+
return hits;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
return hits;
|
|
168
|
+
}
|
|
169
|
+
/** The lines around a symbol's declaration: "show me this, not the whole file". */
|
|
170
|
+
export function expand(file, line, before = 5, after = 25) {
|
|
171
|
+
try {
|
|
172
|
+
const lines = fs.readFileSync(file, 'utf8').split('\n');
|
|
173
|
+
const start = Math.max(0, line - 1 - before);
|
|
174
|
+
// `after` counts lines AFTER the declaration, so the declaration's own line
|
|
175
|
+
// does not eat one of them -- `before: 2, after: 2` is five lines, centred.
|
|
176
|
+
const end = Math.min(lines.length, line + after);
|
|
177
|
+
return lines
|
|
178
|
+
.slice(start, end)
|
|
179
|
+
.map((text, i) => `${String(start + i + 1).padStart(5)} ${text}`)
|
|
180
|
+
.join('\n');
|
|
181
|
+
}
|
|
182
|
+
catch {
|
|
183
|
+
return null;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
//# sourceMappingURL=code.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"code.js","sourceRoot":"","sources":["../../src/memory/code.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { nowIso } from '../time.js';
|
|
2
|
+
import { search } from './search.js';
|
|
3
|
+
import { timeline } from './store.js';
|
|
4
|
+
export function createCollection(db, opts) {
|
|
5
|
+
return Number(db
|
|
6
|
+
.prepare(`INSERT INTO memory_collections (name, description, filter, project, status, created_at)
|
|
7
|
+
VALUES (?, ?, ?, ?, 'ready', ?)
|
|
8
|
+
ON CONFLICT(name) DO UPDATE SET
|
|
9
|
+
description = excluded.description,
|
|
10
|
+
filter = excluded.filter,
|
|
11
|
+
project = excluded.project`)
|
|
12
|
+
.run(opts.name, opts.description ?? null, JSON.stringify(opts.filter), opts.project ?? null, nowIso())
|
|
13
|
+
.lastInsertRowid) || collectionByName(db, opts.name).id;
|
|
14
|
+
}
|
|
15
|
+
export function collectionByName(db, name) {
|
|
16
|
+
return db.prepare('SELECT * FROM memory_collections WHERE name = ?').get(name);
|
|
17
|
+
}
|
|
18
|
+
export function listCollections(db) {
|
|
19
|
+
return db.prepare('SELECT * FROM memory_collections ORDER BY name').all();
|
|
20
|
+
}
|
|
21
|
+
export function deleteCollection(db, name) {
|
|
22
|
+
return db.prepare('DELETE FROM memory_collections WHERE name = ?').run(name).changes > 0;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Re-runs a collection's query and swaps in the result.
|
|
26
|
+
*
|
|
27
|
+
* The swap is conditional and that is the point: a rebuild that finds nothing
|
|
28
|
+
* where there used to be something is far more likely to be a broken index or
|
|
29
|
+
* a mistyped filter than a genuine emptying, and overwriting on that guess
|
|
30
|
+
* destroys the collection. It keeps the last good set, marks the collection
|
|
31
|
+
* `failed`, and says why. `force` is for the developer who really did mean it.
|
|
32
|
+
*/
|
|
33
|
+
export function rebuildCollection(db, config, name, opts = {}) {
|
|
34
|
+
const collection = collectionByName(db, name);
|
|
35
|
+
if (!collection)
|
|
36
|
+
return null;
|
|
37
|
+
let filter;
|
|
38
|
+
try {
|
|
39
|
+
filter = JSON.parse(collection.filter);
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
return { name, members: 0, previous: memberCount(db, collection.id), kept: true, reason: 'unreadable_filter' };
|
|
43
|
+
}
|
|
44
|
+
const previous = memberCount(db, collection.id);
|
|
45
|
+
const searchFilter = {
|
|
46
|
+
project: filter.project ?? collection.project ?? null,
|
|
47
|
+
allProjects: filter.allProjects ?? false,
|
|
48
|
+
type: filter.type ?? null,
|
|
49
|
+
tag: filter.tag ?? null,
|
|
50
|
+
since: filter.since ?? null,
|
|
51
|
+
limit: filter.limit ?? 100,
|
|
52
|
+
};
|
|
53
|
+
const found = filter.query
|
|
54
|
+
? search(db, filter.query, filter.mode ?? config.retrieval.mode, searchFilter).map((h) => h.entry)
|
|
55
|
+
: timeline(db, {
|
|
56
|
+
project: searchFilter.allProjects ? null : searchFilter.project,
|
|
57
|
+
type: searchFilter.type,
|
|
58
|
+
since: searchFilter.since,
|
|
59
|
+
limit: searchFilter.limit,
|
|
60
|
+
});
|
|
61
|
+
if (found.length === 0 && previous > 0 && !opts.force) {
|
|
62
|
+
db.prepare("UPDATE memory_collections SET status = 'failed' WHERE id = ?").run(collection.id);
|
|
63
|
+
return { name, members: previous, previous, kept: true, reason: 'empty_rebuild_refused' };
|
|
64
|
+
}
|
|
65
|
+
db.transaction(() => {
|
|
66
|
+
db.prepare('DELETE FROM memory_collection_items WHERE collection_id = ?').run(collection.id);
|
|
67
|
+
const insert = db.prepare('INSERT OR REPLACE INTO memory_collection_items (collection_id, entry_id, rank) VALUES (?, ?, ?)');
|
|
68
|
+
found.forEach((entry, index) => insert.run(collection.id, entry.id, found.length - index));
|
|
69
|
+
db.prepare("UPDATE memory_collections SET built_at = ?, status = 'ready' WHERE id = ?").run(nowIso(), collection.id);
|
|
70
|
+
})();
|
|
71
|
+
return { name, members: found.length, previous, kept: false };
|
|
72
|
+
}
|
|
73
|
+
export function collectionEntries(db, name, limit = 50) {
|
|
74
|
+
const collection = collectionByName(db, name);
|
|
75
|
+
if (!collection)
|
|
76
|
+
return [];
|
|
77
|
+
return db
|
|
78
|
+
.prepare(`SELECT e.* FROM memory_entries e
|
|
79
|
+
JOIN memory_collection_items i ON i.entry_id = e.id
|
|
80
|
+
WHERE i.collection_id = ? AND e.deleted_at IS NULL
|
|
81
|
+
ORDER BY i.rank DESC LIMIT ?`)
|
|
82
|
+
.all(collection.id, limit);
|
|
83
|
+
}
|
|
84
|
+
function memberCount(db, collectionId) {
|
|
85
|
+
return db.prepare('SELECT COUNT(*) AS n FROM memory_collection_items WHERE collection_id = ?').get(collectionId).n;
|
|
86
|
+
}
|
|
87
|
+
//# sourceMappingURL=collections.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"collections.js","sourceRoot":"","sources":["../../src/memory/collections.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Local semantic vectors (ADR-03).
|
|
3
|
+
*
|
|
4
|
+
* `local-hash-v1` is a dependency-free hashed bag of tokens and character
|
|
5
|
+
* 4-grams, L2-normalised, compared by cosine. The n-grams are what make it more
|
|
6
|
+
* than keyword matching: `migrations`, `migration` and `migrating` land in
|
|
7
|
+
* overlapping buckets, and so do typos. It does *not* know that `cookie` and
|
|
8
|
+
* `session token` are related — that needs a trained model, and the manual says
|
|
9
|
+
* so rather than calling this neural parity.
|
|
10
|
+
*
|
|
11
|
+
* Every vector stores the embedder that produced it, because comparing a
|
|
12
|
+
* hashed vector with a provider embedding is meaningless arithmetic.
|
|
13
|
+
*/
|
|
14
|
+
export const LOCAL_EMBEDDER = 'local-hash-v1';
|
|
15
|
+
export const LOCAL_DIM = 256;
|
|
16
|
+
function fnv1a(s) {
|
|
17
|
+
let h = 0x811c9dc5;
|
|
18
|
+
for (let i = 0; i < s.length; i++) {
|
|
19
|
+
h ^= s.charCodeAt(i);
|
|
20
|
+
h = Math.imul(h, 0x01000193) >>> 0;
|
|
21
|
+
}
|
|
22
|
+
return h >>> 0;
|
|
23
|
+
}
|
|
24
|
+
function tokenize(text) {
|
|
25
|
+
return text
|
|
26
|
+
.toLowerCase()
|
|
27
|
+
.split(/[^\p{L}\p{N}_]+/u)
|
|
28
|
+
.filter((t) => t.length > 1);
|
|
29
|
+
}
|
|
30
|
+
export function embedLocal(text, dim = LOCAL_DIM) {
|
|
31
|
+
const vec = new Float32Array(dim);
|
|
32
|
+
const tokens = tokenize(text);
|
|
33
|
+
for (const token of tokens) {
|
|
34
|
+
vec[fnv1a(token) % dim] += 1;
|
|
35
|
+
// Character n-grams, weighted lower: they generalise over morphology but
|
|
36
|
+
// are noisier than the whole token, so they should not outvote it.
|
|
37
|
+
const padded = `^${token}$`;
|
|
38
|
+
for (let i = 0; i + 4 <= padded.length; i++) {
|
|
39
|
+
vec[fnv1a(padded.slice(i, i + 4)) % dim] += 0.35;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
// Sublinear damping, as TF-IDF does: a word repeated twenty times is not
|
|
43
|
+
// twenty times more about the document than a word used once. `log1p` rather
|
|
44
|
+
// than `1 + log`, which goes negative below 1 -- a single n-gram hit weighs
|
|
45
|
+
// 0.35, and a negative weight makes two related documents score as opposites.
|
|
46
|
+
for (let i = 0; i < dim; i++) {
|
|
47
|
+
const v = vec[i];
|
|
48
|
+
if (v > 0)
|
|
49
|
+
vec[i] = Math.log1p(v);
|
|
50
|
+
}
|
|
51
|
+
return normalise(vec);
|
|
52
|
+
}
|
|
53
|
+
export function normalise(vec) {
|
|
54
|
+
let sum = 0;
|
|
55
|
+
for (const v of vec)
|
|
56
|
+
sum += v * v;
|
|
57
|
+
const norm = Math.sqrt(sum);
|
|
58
|
+
if (norm === 0)
|
|
59
|
+
return vec;
|
|
60
|
+
for (let i = 0; i < vec.length; i++)
|
|
61
|
+
vec[i] = vec[i] / norm;
|
|
62
|
+
return vec;
|
|
63
|
+
}
|
|
64
|
+
/** Both arguments must already be normalised, which `embedLocal` guarantees. */
|
|
65
|
+
export function cosine(a, b) {
|
|
66
|
+
const n = Math.min(a.length, b.length);
|
|
67
|
+
let dot = 0;
|
|
68
|
+
for (let i = 0; i < n; i++)
|
|
69
|
+
dot += a[i] * b[i];
|
|
70
|
+
return dot;
|
|
71
|
+
}
|
|
72
|
+
export function toBlob(vec) {
|
|
73
|
+
return Buffer.from(vec.buffer, vec.byteOffset, vec.byteLength);
|
|
74
|
+
}
|
|
75
|
+
export function fromBlob(blob) {
|
|
76
|
+
// Copy rather than view: better-sqlite3's buffer is not guaranteed aligned
|
|
77
|
+
// for Float32Array, and an unaligned view throws.
|
|
78
|
+
const copy = Buffer.from(blob);
|
|
79
|
+
return new Float32Array(copy.buffer, copy.byteOffset, copy.byteLength / 4);
|
|
80
|
+
}
|
|
81
|
+
//# sourceMappingURL=embed.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"embed.js","sourceRoot":"","sources":["../../src/memory/embed.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Host capability descriptors (PRD CAP-01, EXT-01; ADR-06).
|
|
3
|
+
*
|
|
4
|
+
* The rule this file enforces is "record capabilities rather than fabricate
|
|
5
|
+
* missing events". A host that cannot tell us a tool failed must not have a
|
|
6
|
+
* memory that quietly implies every tool succeeded, and a host nobody has run
|
|
7
|
+
* a fixture through must not be advertised as supported.
|
|
8
|
+
*
|
|
9
|
+
* `status` is the honest axis. `proven` means there is a transcript fixture and
|
|
10
|
+
* a passing test in this repository. `unverified` means the adapter is written
|
|
11
|
+
* from documentation and nobody has run it. Nothing is listed here that has no
|
|
12
|
+
* adapter at all — the parity ledger tracks those.
|
|
13
|
+
*/
|
|
14
|
+
export const HOSTS = {
|
|
15
|
+
'claude-code': {
|
|
16
|
+
id: 'claude-code',
|
|
17
|
+
label: 'Claude Code',
|
|
18
|
+
status: 'proven',
|
|
19
|
+
hooks: true,
|
|
20
|
+
transcripts: true,
|
|
21
|
+
toolOutcomes: true,
|
|
22
|
+
agentIdentity: true,
|
|
23
|
+
humanDisplay: true,
|
|
24
|
+
modelContext: true,
|
|
25
|
+
},
|
|
26
|
+
cursor: {
|
|
27
|
+
id: 'cursor',
|
|
28
|
+
label: 'Cursor',
|
|
29
|
+
status: 'unverified',
|
|
30
|
+
hooks: false,
|
|
31
|
+
transcripts: false,
|
|
32
|
+
toolOutcomes: false,
|
|
33
|
+
agentIdentity: false,
|
|
34
|
+
humanDisplay: false,
|
|
35
|
+
// The MCP server works, so explicit tool calls are captured. Nothing else
|
|
36
|
+
// is: with no hooks there is no seam to capture a prompt or an edit at.
|
|
37
|
+
modelContext: false,
|
|
38
|
+
notes: 'MCP tools only. No hooks, so nothing is captured that the model does not explicitly log.',
|
|
39
|
+
},
|
|
40
|
+
cowork: {
|
|
41
|
+
id: 'cowork',
|
|
42
|
+
label: 'Claude Cowork',
|
|
43
|
+
status: 'unverified',
|
|
44
|
+
hooks: true,
|
|
45
|
+
transcripts: false,
|
|
46
|
+
toolOutcomes: true,
|
|
47
|
+
agentIdentity: true,
|
|
48
|
+
humanDisplay: true,
|
|
49
|
+
modelContext: true,
|
|
50
|
+
notes: 'No git commits, so the commit gate never fires there; `surface.ts` already says so at session start.',
|
|
51
|
+
},
|
|
52
|
+
};
|
|
53
|
+
export function capabilitiesOf(host) {
|
|
54
|
+
return (HOSTS[host ?? 'claude-code'] ?? {
|
|
55
|
+
id: host ?? 'unknown',
|
|
56
|
+
label: host ?? 'unknown host',
|
|
57
|
+
status: 'unverified',
|
|
58
|
+
hooks: false,
|
|
59
|
+
transcripts: false,
|
|
60
|
+
toolOutcomes: false,
|
|
61
|
+
agentIdentity: false,
|
|
62
|
+
humanDisplay: false,
|
|
63
|
+
modelContext: false,
|
|
64
|
+
notes: 'Unrecognised host: assume nothing is delivered that was not explicitly logged.',
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
/** The hosts an install may honestly claim to support today. */
|
|
68
|
+
export function provenHosts() {
|
|
69
|
+
return Object.values(HOSTS).filter((h) => h.status === 'proven');
|
|
70
|
+
}
|
|
71
|
+
//# sourceMappingURL=hosts.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hosts.js","sourceRoot":"","sources":["../../src/memory/hosts.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAuBH,MAAM,CAAC,MAAM,KAAK,GAAqC;IACrD,aAAa,EAAE;QACb,EAAE,EAAE,aAAa;QACjB,KAAK,EAAE,aAAa;QACpB,MAAM,EAAE,QAAQ;QAChB,KAAK,EAAE,IAAI;QACX,WAAW,EAAE,IAAI;QACjB,YAAY,EAAE,IAAI;QAClB,aAAa,EAAE,IAAI;QACnB,YAAY,EAAE,IAAI;QAClB,YAAY,EAAE,IAAI;KACnB;IACD,MAAM,EAAE;QACN,EAAE,EAAE,QAAQ;QACZ,KAAK,EAAE,QAAQ;QACf,MAAM,EAAE,YAAY;QACpB,KAAK,EAAE,KAAK;QACZ,WAAW,EAAE,KAAK;QAClB,YAAY,EAAE,KAAK;QACnB,aAAa,EAAE,KAAK;QACpB,YAAY,EAAE,KAAK;QACnB,0EAA0E;QAC1E,wEAAwE;QACxE,YAAY,EAAE,KAAK;QACnB,KAAK,EAAE,0FAA0F;KAClG;IACD,MAAM,EAAE;QACN,EAAE,EAAE,QAAQ;QACZ,KAAK,EAAE,eAAe;QACtB,MAAM,EAAE,YAAY;QACpB,KAAK,EAAE,IAAI;QACX,WAAW,EAAE,KAAK;QAClB,YAAY,EAAE,IAAI;QAClB,aAAa,EAAE,IAAI;QACnB,YAAY,EAAE,IAAI;QAClB,YAAY,EAAE,IAAI;QAClB,KAAK,EAAE,sGAAsG;KAC9G;CACF,CAAC;AAEF,MAAM,UAAU,cAAc,CAAC,IAA+B;IAC5D,OAAO,CACL,KAAK,CAAC,IAAI,IAAI,aAAa,CAAC,IAAI;QAC9B,EAAE,EAAE,IAAI,IAAI,SAAS;QACrB,KAAK,EAAE,IAAI,IAAI,cAAc;QAC7B,MAAM,EAAE,YAAY;QACpB,KAAK,EAAE,KAAK;QACZ,WAAW,EAAE,KAAK;QAClB,YAAY,EAAE,KAAK;QACnB,aAAa,EAAE,KAAK;QACpB,YAAY,EAAE,KAAK;QACnB,YAAY,EAAE,KAAK;QACnB,KAAK,EAAE,gFAAgF;KACxF,CACF,CAAC;AACJ,CAAC;AAED,gEAAgE;AAChE,MAAM,UAAU,WAAW;IACzB,OAAO,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,QAAQ,CAAC,CAAC;AACnE,CAAC"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import crypto from 'node:crypto';
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { findRepoConfig, mainRepoRoot } from '../config.js';
|
|
5
|
+
import { GLOBAL_PROJECT } from '../store.js';
|
|
6
|
+
// Re-exported rather than redeclared: the learning half already has a spelling
|
|
7
|
+
// for "no repository" (`*`), and a second one here would silently split the
|
|
8
|
+
// counts -- work outside a checkout would be filed under a project nothing else
|
|
9
|
+
// ever reads.
|
|
10
|
+
export { GLOBAL_PROJECT };
|
|
11
|
+
export function identityFor(opts) {
|
|
12
|
+
const { repoRoot } = findRepoConfig(opts.cwd ?? process.cwd());
|
|
13
|
+
return {
|
|
14
|
+
project: repoRoot ? mainRepoRoot(repoRoot) : GLOBAL_PROJECT,
|
|
15
|
+
checkout: repoRoot,
|
|
16
|
+
sessionId: opts.sessionId,
|
|
17
|
+
agentId: opts.agentId?.trim() || null,
|
|
18
|
+
host: opts.host?.trim() || 'claude-code',
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* The idempotency key for one host event.
|
|
23
|
+
*
|
|
24
|
+
* Hook capture and transcript replay see the same turn from two directions and
|
|
25
|
+
* must converge on one row (CAP-02). Hashing the identity plus the event's own
|
|
26
|
+
* content and time does that without either side knowing about the other. It is
|
|
27
|
+
* deliberately not a short title hash over a time window: two legitimate
|
|
28
|
+
* identical edits a minute apart are two events, and collapsing them loses work.
|
|
29
|
+
*/
|
|
30
|
+
export function eventUid(parts) {
|
|
31
|
+
const h = crypto.createHash('sha256');
|
|
32
|
+
h.update([
|
|
33
|
+
parts.host,
|
|
34
|
+
parts.sessionId,
|
|
35
|
+
parts.agentId ?? '',
|
|
36
|
+
parts.kind,
|
|
37
|
+
parts.tool ?? '',
|
|
38
|
+
parts.occurredAt,
|
|
39
|
+
parts.body,
|
|
40
|
+
].join('\u0000'));
|
|
41
|
+
return h.digest('hex').slice(0, 32);
|
|
42
|
+
}
|
|
43
|
+
/** Stable id for a generated or imported memory entry. */
|
|
44
|
+
export function entryUid(parts) {
|
|
45
|
+
const h = crypto.createHash('sha256');
|
|
46
|
+
h.update([parts.project, parts.title, parts.occurredAt, parts.salt ?? ''].join('\u0000'));
|
|
47
|
+
return h.digest('hex').slice(0, 32);
|
|
48
|
+
}
|
|
49
|
+
export function receiptUid() {
|
|
50
|
+
return crypto.randomUUID().replace(/-/g, '').slice(0, 32);
|
|
51
|
+
}
|
|
52
|
+
/** Repo-relative when the file is inside the checkout, absolute otherwise. */
|
|
53
|
+
export function relativeToProject(file, project) {
|
|
54
|
+
if (!path.isAbsolute(file) || project === GLOBAL_PROJECT)
|
|
55
|
+
return file;
|
|
56
|
+
const rel = path.relative(project, file);
|
|
57
|
+
if (rel && !rel.startsWith('..'))
|
|
58
|
+
return rel;
|
|
59
|
+
// The project key is a realpath (`findRepoConfig` resolves symlinks so the
|
|
60
|
+
// git hook can match on it), and a host may report the unresolved path --
|
|
61
|
+
// on macOS every repo under /tmp is really under /private/tmp. Only tried
|
|
62
|
+
// when the cheap comparison already failed, so it costs nothing in the
|
|
63
|
+
// ordinary case.
|
|
64
|
+
try {
|
|
65
|
+
const resolved = path.relative(project, fs.realpathSync(path.dirname(file)));
|
|
66
|
+
if (resolved && !resolved.startsWith('..'))
|
|
67
|
+
return path.join(resolved, path.basename(file));
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
/* A path that no longer exists keeps the spelling the host gave. */
|
|
71
|
+
}
|
|
72
|
+
return file;
|
|
73
|
+
}
|
|
74
|
+
//# sourceMappingURL=identity.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"identity.js","sourceRoot":"","sources":["../../src/memory/identity.ts"],"names":[],"mappings":"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"}
|