eklavya 1.18.3 → 1.19.1
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 +1 -1
- package/dist/assets/dashboard.html +570 -15
- package/dist/assets/tutor/SKILL.md +5 -5
- package/dist/assets/tutor/references/focus-and-level.md +34 -5
- package/dist/assets/tutor/references/grading.md +1 -1
- package/dist/cli.js +753 -41
- 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 +448 -31
- package/dist/config.js.map +1 -1
- package/dist/dashboard.js +357 -1
- 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 +2 -2
- package/dist/hooks/checkpoint-quiz.js.map +1 -1
- package/dist/hooks/lib.js +4 -30
- 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/pre-tool-gate.js +2 -2
- package/dist/hooks/pre-tool-gate.js.map +1 -1
- package/dist/hooks/prompt-submit-nudge.js +57 -20
- package/dist/hooks/prompt-submit-nudge.js.map +1 -1
- package/dist/hooks/session-start.js +110 -80
- package/dist/hooks/session-start.js.map +1 -1
- package/dist/hooks/stop-quiz-check.js +58 -38
- package/dist/hooks/stop-quiz-check.js.map +1 -1
- package/dist/hooks/subagent-start.js +2 -2
- package/dist/hooks/subagent-start.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/packs.js +26 -12
- package/dist/packs.js.map +1 -1
- package/dist/paths.js +60 -0
- package/dist/paths.js.map +1 -1
- package/dist/plugin/.claude-plugin/plugin.json +1 -1
- package/dist/plugin/agents/tutor.md +3 -1
- package/dist/plugin/cli/CLAUDE.md +32 -8
- package/dist/plugin/cli/eklavya-gate +80 -9
- package/dist/plugin/hooks/CLAUDE.md +44 -19
- package/dist/plugin/hooks/hooks.json +10 -0
- package/dist/plugin/scripts/install-git-hook.sh +2 -1
- package/dist/plugin/skills/CLAUDE.md +29 -17
- package/dist/plugin/skills/gate/SKILL.md +1 -1
- package/dist/plugin/skills/level/SKILL.md +3 -3
- package/dist/plugin/skills/memory/SKILL.md +88 -0
- package/dist/plugin/skills/mode/SKILL.md +24 -16
- package/dist/plugin/skills/pack/SKILL.md +8 -4
- package/dist/plugin/skills/progress/SKILL.md +1 -1
- package/dist/plugin/skills/quiz/SKILL.md +2 -2
- package/dist/plugin/skills/setup/SKILL.md +25 -11
- package/dist/plugin/skills/tutor/SKILL.md +5 -5
- package/dist/plugin/skills/tutor/references/focus-and-level.md +34 -5
- package/dist/plugin/skills/tutor/references/grading.md +1 -1
- package/dist/session.js +1 -1
- package/dist/statusline.js +20 -6
- package/dist/statusline.js.map +1 -1
- package/dist/store.js +2 -2
- package/dist/store.js.map +1 -1
- package/dist/surface.js +1 -1
- 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 +165 -32
- package/dist/tools/config_tools.js.map +1 -1
- package/dist/tools/get_learner_profile.js +2 -1
- package/dist/tools/get_learner_profile.js.map +1 -1
- package/dist/tools/get_session_quiz_plan.js +15 -9
- package/dist/tools/get_session_quiz_plan.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/tools/types.js +1 -1
- package/dist/tools/types.js.map +1 -1
- package/dist/user-skill/eklavya/SKILL.md +95 -25
- package/package.json +2 -1
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { nowIso } from '../time.js';
|
|
2
|
+
import { eventUid, relativeToProject } from './identity.js';
|
|
3
|
+
import { DEFAULT_PRIVACY, isOwnTraffic, pathExcluded, redact, toolExcluded } from './privacy.js';
|
|
4
|
+
import { appendEvent } from './store.js';
|
|
5
|
+
import { spoolEvent, takeSpooled } from './spool.js';
|
|
6
|
+
/** Per-event body cap. Evidence is a pointer to work, not a copy of the repo. */
|
|
7
|
+
const MAX_BODY = 4000;
|
|
8
|
+
/**
|
|
9
|
+
* `minimal` keeps the shape of a session — what was asked, what was decided,
|
|
10
|
+
* where it started and stopped — and drops the per-file traffic that makes up
|
|
11
|
+
* most of the volume.
|
|
12
|
+
*/
|
|
13
|
+
const MINIMAL_KINDS = ['prompt', 'assistant', 'lifecycle', 'note'];
|
|
14
|
+
export function policyFrom(config) {
|
|
15
|
+
return {
|
|
16
|
+
excludePaths: [...DEFAULT_PRIVACY.excludePaths, ...config.privacy.exclude_paths],
|
|
17
|
+
excludeTools: [...DEFAULT_PRIVACY.excludeTools, ...config.privacy.exclude_tools],
|
|
18
|
+
redactPatterns: config.privacy.redact_patterns,
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
/** Everything but the write: pure, so the exclusion rules are testable alone. */
|
|
22
|
+
export function prepare(config, identity, event) {
|
|
23
|
+
if (!config.memory.enabled || config.memory.capture === 'off')
|
|
24
|
+
return null;
|
|
25
|
+
if (config.memory.capture === 'minimal' && !MINIMAL_KINDS.includes(event.kind))
|
|
26
|
+
return null;
|
|
27
|
+
const policy = policyFrom(config);
|
|
28
|
+
if (toolExcluded(event.tool, policy))
|
|
29
|
+
return null;
|
|
30
|
+
if (isOwnTraffic(event.tool, event.body))
|
|
31
|
+
return null;
|
|
32
|
+
const files = (event.files ?? []).filter((f) => !pathExcluded(f, policy));
|
|
33
|
+
// A file event whose only file was excluded is the excluded file. Keeping the
|
|
34
|
+
// event for its body would re-admit through the back door what the path rule
|
|
35
|
+
// just shut out.
|
|
36
|
+
if ((event.kind === 'file_edit' || event.kind === 'file_read') && (event.files?.length ?? 0) > 0 && files.length === 0) {
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
const cleaned = redact(event.body.slice(0, MAX_BODY), policy);
|
|
40
|
+
const occurredAt = event.occurredAt ?? nowIso();
|
|
41
|
+
return {
|
|
42
|
+
eventUid: eventUid({
|
|
43
|
+
host: identity.host,
|
|
44
|
+
sessionId: identity.sessionId,
|
|
45
|
+
agentId: identity.agentId,
|
|
46
|
+
kind: event.kind,
|
|
47
|
+
tool: event.tool,
|
|
48
|
+
occurredAt,
|
|
49
|
+
body: cleaned.text,
|
|
50
|
+
}),
|
|
51
|
+
project: identity.project,
|
|
52
|
+
checkout: identity.checkout,
|
|
53
|
+
sessionId: identity.sessionId,
|
|
54
|
+
agentId: identity.agentId,
|
|
55
|
+
host: identity.host,
|
|
56
|
+
source: event.source ?? 'hook',
|
|
57
|
+
kind: event.kind,
|
|
58
|
+
tool: event.tool ?? null,
|
|
59
|
+
title: event.title ? redact(event.title, policy).text : null,
|
|
60
|
+
body: cleaned.text,
|
|
61
|
+
files: files.map((f) => relativeToProject(f, identity.project)),
|
|
62
|
+
occurredAt,
|
|
63
|
+
redacted: cleaned.redacted,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
export function capture(db, config, identity, event) {
|
|
67
|
+
const input = prepare(config, identity, event);
|
|
68
|
+
if (!input)
|
|
69
|
+
return 'excluded';
|
|
70
|
+
const { inserted } = appendEvent(db, input);
|
|
71
|
+
return inserted ? 'stored' : 'duplicate';
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Capture when the database may not be reachable. The sanitised record — never
|
|
75
|
+
* the raw one — is what reaches the spool, so a degraded path cannot write a
|
|
76
|
+
* secret that the healthy path would have removed.
|
|
77
|
+
*/
|
|
78
|
+
export function captureOrSpool(db, config, identity, event) {
|
|
79
|
+
const input = prepare(config, identity, event);
|
|
80
|
+
if (!input)
|
|
81
|
+
return 'excluded';
|
|
82
|
+
if (db) {
|
|
83
|
+
try {
|
|
84
|
+
const { inserted } = appendEvent(db, input);
|
|
85
|
+
return inserted ? 'stored' : 'duplicate';
|
|
86
|
+
}
|
|
87
|
+
catch {
|
|
88
|
+
// Fall through to the spool: a locked or unwritable database is exactly
|
|
89
|
+
// what it exists for.
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return spoolEvent(input);
|
|
93
|
+
}
|
|
94
|
+
/** Replays whatever the spool holds. Idempotent through `event_uid`. */
|
|
95
|
+
export function drainSpool(db) {
|
|
96
|
+
const records = takeSpooled();
|
|
97
|
+
let replayed = 0;
|
|
98
|
+
let skipped = 0;
|
|
99
|
+
for (const record of records) {
|
|
100
|
+
const input = record;
|
|
101
|
+
// A spool line is data this process wrote earlier, but a truncated write or
|
|
102
|
+
// a hand-edited file is still possible; anything without an identity cannot
|
|
103
|
+
// be replayed idempotently and is discarded rather than guessed at.
|
|
104
|
+
if (!input || typeof input !== 'object' || !input.eventUid || !input.project) {
|
|
105
|
+
skipped++;
|
|
106
|
+
continue;
|
|
107
|
+
}
|
|
108
|
+
try {
|
|
109
|
+
const { inserted } = appendEvent(db, input);
|
|
110
|
+
if (inserted)
|
|
111
|
+
replayed++;
|
|
112
|
+
else
|
|
113
|
+
skipped++;
|
|
114
|
+
}
|
|
115
|
+
catch {
|
|
116
|
+
// Put it back rather than lose it; the next drain tries again.
|
|
117
|
+
spoolEvent(input);
|
|
118
|
+
skipped++;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
return { replayed, skipped };
|
|
122
|
+
}
|
|
123
|
+
//# sourceMappingURL=capture.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"capture.js","sourceRoot":"","sources":["../../src/memory/capture.ts"],"names":[],"mappings":"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"}
|
|
@@ -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":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AA0C7B,MAAM,SAAS,GAAqC;IAClD,KAAK,EAAE,MAAM,CAAC,YAAY,CAAC;IAC3B,MAAM,EAAE,MAAM,CAAC,YAAY,CAAC;IAC5B,KAAK,EAAE,MAAM,CAAC,YAAY,CAAC;IAC3B,MAAM,EAAE,MAAM,CAAC,YAAY,CAAC;IAC5B,MAAM,EAAE,MAAM,CAAC,YAAY,CAAC;IAC5B,MAAM,EAAE,MAAM,CAAC,YAAY,CAAC;IAC5B,KAAK,EAAE;QACL,QAAQ,EAAE,QAAQ;QAClB,WAAW,EAAE,CAAC,GAAG,CAAC;QAClB,QAAQ,EAAE;YACR,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,EAAE,4BAA4B,EAAE;YACnD,EAAE,IAAI,EAAE,UAAU,EAAE,EAAE,EAAE,uCAAuC,EAAE;SAClE;KACF;IACD,KAAK,EAAE;QACL,QAAQ,EAAE,IAAI;QACd,WAAW,EAAE,CAAC,IAAI,CAAC;QACnB,QAAQ,EAAE;YACR,EAAE,IAAI,EAAE,UAAU,EAAE,EAAE,EAAE,4CAA4C,EAAE;YACtE,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,2BAA2B,EAAE;SAClD;KACF;IACD,KAAK,EAAE;QACL,QAAQ,EAAE,MAAM;QAChB,WAAW,EAAE,CAAC,IAAI,CAAC;QACnB,QAAQ,EAAE;YACR,EAAE,IAAI,EAAE,UAAU,EAAE,EAAE,EAAE,iDAAiD,EAAE;YAC3E,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,EAAE,wCAAwC,EAAE;YAChE,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,sCAAsC,EAAE;YAC5D,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,EAAE,uCAAuC,EAAE;YAC9D,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,4CAA4C,EAAE;SACnE;KACF;IACD,OAAO,EAAE;QACP,QAAQ,EAAE,MAAM;QAChB,WAAW,EAAE,CAAC,IAAI,CAAC;QACnB,QAAQ,EAAE;YACR,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,EAAE,iGAAiG,EAAE;YACxH,EAAE,IAAI,EAAE,WAAW,EAAE,EAAE,EAAE,8CAA8C,EAAE;YACzE,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,EAAE,wFAAwF,EAAE;SACjH;KACF;IACD,KAAK,EAAE;QACL,QAAQ,EAAE,MAAM;QAChB,WAAW,EAAE,CAAC,GAAG,CAAC;QAClB,QAAQ,EAAE;YACR,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,EAAE,4BAA4B,EAAE;YACnD,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,EAAE,6BAA6B,EAAE;YACrD,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,EAAE,kCAAkC,EAAE;SAC3D;KACF;IACD,MAAM,EAAE;QACN,QAAQ,EAAE,KAAK;QACf,WAAW,EAAE,CAAC,IAAI,CAAC;QACnB,QAAQ,EAAE;YACR,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,EAAE,8EAA8E,EAAE;YACrG,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,EAAE,6EAA6E,EAAE;YACpG,EAAE,IAAI,EAAE,SAAS,EAAE,EAAE,EAAE,iEAAiE,EAAE;SAC3F;KACF;CACF,CAAC;AAEF,SAAS,MAAM,CAAC,QAAgB;IAC9B,OAAO;QACL,QAAQ;QACR,WAAW,EAAE,CAAC,IAAI,EAAE,GAAG,EAAE,IAAI,CAAC;QAC9B,QAAQ,EAAE;YACR,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,EAAE,8DAA8D,EAAE;YACrF,EAAE,IAAI,EAAE,WAAW,EAAE,EAAE,EAAE,kDAAkD,EAAE;YAC7E,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,6CAA6C,EAAE;YACnE,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,0DAA0D,EAAE;YAChF,EAAE,IAAI,EAAE,UAAU,EAAE,EAAE,EAAE,mFAAmF,EAAE;YAC7G,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,EAAE,6DAA6D,EAAE;SACrF;KACF,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,IAAY;IACrC,OAAO,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,WAAW,EAAE,CAAC,EAAE,QAAQ,IAAI,IAAI,CAAC;AACvE,CAAC;AAED,MAAM,SAAS,GAAG,CAAC,GAAG,IAAI,GAAG,IAAI,CAAC;AAClC,MAAM,WAAW,GAAG,GAAG,CAAC;AAExB,MAAM,UAAU,OAAO,CAAC,IAAY;IAClC,MAAM,IAAI,GAAG,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC;IACzD,IAAI,CAAC,IAAI;QAAE,OAAO,IAAI,CAAC;IAEvB,IAAI,IAAY,CAAC;IACjB,IAAI,CAAC;QACH,IAAI,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,IAAI,GAAG,SAAS;YAAE,OAAO,IAAI,CAAC;QACpD,IAAI,GAAG,EAAE,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IACvC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC/B,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,IAAI,OAAO,CAAC,MAAM,GAAG,WAAW,EAAE,CAAC,EAAE,EAAE,CAAC;QACtE,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAE,CAAC;QACvB,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;QAC5B,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;YAAE,SAAS;QAC9E,KAAK,MAAM,EAAE,IAAI,EAAE,EAAE,EAAE,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YACzC,MAAM,KAAK,GAAG,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAC5B,IAAI,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;gBACf,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,GAAG,CAAC,EAAE,IAAI,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC;gBACjF,MAAM;YACR,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO;QACL,IAAI;QACJ,QAAQ,EAAE,IAAI,CAAC,QAAQ;QACvB,KAAK,EAAE,KAAK,CAAC,MAAM;QACnB,OAAO;QACP,SAAS,EAAE,OAAO,CAAC,MAAM,IAAI,WAAW;KACzC,CAAC;AACJ,CAAC;AASD,MAAM,SAAS,GAAG,IAAI,GAAG,CAAC,CAAC,cAAc,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,QAAQ,EAAE,aAAa,CAAC,CAAC,CAAC;AAEnH,QAAQ,CAAC,CAAC,IAAI,CAAC,IAAY,EAAE,KAAK,GAAG,CAAC;IACpC,IAAI,KAAK,GAAG,EAAE;QAAE,OAAO;IACvB,IAAI,OAAoB,CAAC;IACzB,IAAI,CAAC;QACH,OAAO,GAAG,EAAE,CAAC,WAAW,CAAC,IAAI,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;IAC1D,CAAC;IAAC,MAAM,CAAC;QACP,OAAO;IACT,CAAC;IACD,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC5B,IAAI,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,IAAI,KAAK,UAAU;YAAE,SAAS;QACtE,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;QACzC,IAAI,KAAK,CAAC,WAAW,EAAE,EAAE,CAAC;YACxB,IAAI,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC;gBAAE,SAAS;YACxC,KAAK,CAAC,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,GAAG,CAAC,CAAC,CAAC;QAC/B,CAAC;aAAM,IAAI,KAAK,CAAC,MAAM,EAAE,IAAI,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,WAAW,EAAE,CAAC,EAAE,CAAC;YAC/E,MAAM,IAAI,CAAC;QACb,CAAC;IACH,CAAC;AACH,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,UAAU,CAAC,IAAY,EAAE,IAAY,EAAE,KAAK,GAAG,EAAE;IAC/D,MAAM,MAAM,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC;IAClC,MAAM,IAAI,GAAc,EAAE,CAAC;IAC3B,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QAC9B,MAAM,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;QAC7B,IAAI,CAAC,MAAM;YAAE,SAAS;QACtB,KAAK,MAAM,MAAM,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;YACpC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC;gBAAE,SAAS;YAC1D,IAAI,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC;YAC1G,IAAI,IAAI,CAAC,MAAM,IAAI,KAAK;gBAAE,OAAO,IAAI,CAAC;QACxC,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,mFAAmF;AACnF,MAAM,UAAU,MAAM,CAAC,IAAY,EAAE,IAAY,EAAE,MAAM,GAAG,CAAC,EAAE,KAAK,GAAG,EAAE;IACvE,IAAI,CAAC;QACH,MAAM,KAAK,GAAG,EAAE,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QACxD,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,GAAG,CAAC,GAAG,MAAM,CAAC,CAAC;QAC7C,4EAA4E;QAC5E,4EAA4E;QAC5E,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,EAAE,IAAI,GAAG,KAAK,CAAC,CAAC;QACjD,OAAO,KAAK;aACT,KAAK,CAAC,KAAK,EAAE,GAAG,CAAC;aACjB,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,MAAM,CAAC,KAAK,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC;aACjE,IAAI,CAAC,IAAI,CAAC,CAAC;IAChB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC"}
|
|
@@ -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"}
|