@shahmilsaari/memory-core 1.0.13 → 1.0.18
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 +80 -36
- package/dist/{chunk-PRRVI3YM.js → chunk-UNGXRKD2.js} +75 -21
- package/dist/cli.js +744 -147
- package/dist/dashboard/assets/index-Bz-Tzypa.css +1 -0
- package/dist/dashboard/assets/index-CaevtejN.js +2 -0
- package/dist/dashboard/index.html +6 -3
- package/dist/{dashboard-server-53HVL7LF.js → dashboard-server-ZBGR4CO7.js} +6 -4
- package/package.json +1 -1
- package/dist/dashboard/assets/index-CJyZEmIe.css +0 -1
- package/dist/dashboard/assets/index-DM82nOf5.js +0 -2
package/README.md
CHANGED
|
@@ -292,7 +292,7 @@ npx @shahmilsaari/memory-core remember "Use DTOs for all API responses" \
|
|
|
292
292
|
|---|---|---|
|
|
293
293
|
| `--type` | `decision` `rule` `pattern` `note` | `decision` |
|
|
294
294
|
| `--scope` | `global` `project` | `project` |
|
|
295
|
-
| `--reason` | any text | asked interactively |
|
|
295
|
+
| `--reason` | any text | asked interactively; fallback is stored if blank |
|
|
296
296
|
| `--applies-to` | comma-separated use cases | none |
|
|
297
297
|
| `--avoid-when` | comma-separated exceptions | none |
|
|
298
298
|
| `--example` | comma-separated examples | none |
|
|
@@ -393,58 +393,56 @@ When `--path` is provided, it must point to the project root (the directory cont
|
|
|
393
393
|
### `check` — Manual check (for CI)
|
|
394
394
|
|
|
395
395
|
```bash
|
|
396
|
-
npx @shahmilsaari/memory-core check --staged
|
|
397
|
-
npx @shahmilsaari/memory-core check --staged --
|
|
398
|
-
npx @shahmilsaari/memory-core check --staged --
|
|
399
|
-
npx @shahmilsaari/memory-core check --
|
|
400
|
-
npx @shahmilsaari/memory-core check --
|
|
401
|
-
npx @shahmilsaari/memory-core check --
|
|
396
|
+
npx @shahmilsaari/memory-core check --staged # check staged files
|
|
397
|
+
npx @shahmilsaari/memory-core check --staged --fast # deterministic-only staged check
|
|
398
|
+
npx @shahmilsaari/memory-core check --staged --verbose # with extra detail
|
|
399
|
+
npx @shahmilsaari/memory-core check --staged --debug # show prompt, diff, and raw model output
|
|
400
|
+
npx @shahmilsaari/memory-core check --commit-msg # check .git/COMMIT_EDITMSG
|
|
401
|
+
npx @shahmilsaari/memory-core check --commit-msg <file> # check a specific message file
|
|
402
|
+
npx @shahmilsaari/memory-core check --ci # CI mode using memories.json
|
|
403
|
+
npx @shahmilsaari/memory-core check --all # scan all tracked source files
|
|
404
|
+
npx @shahmilsaari/memory-core check --all --path src/ # scan only tracked files under src/
|
|
402
405
|
```
|
|
403
406
|
|
|
404
|
-
`--staged` is the same path used by the pre-commit hook. `--
|
|
405
|
-
`--all` runs a full tracked-file snapshot check and exits non-zero if violations are found.
|
|
406
|
-
`--all` and `--ci` are mutually exclusive in the same command.
|
|
407
|
+
`--staged` is the same path used by the pre-commit hook. Add `--fast` to skip AI and memory retrieval for a low-latency deterministic check. `--commit-msg` validates a commit message file against `commitRules` — this is called automatically by the `commit-msg` hook. `--ci` reads `memories.json` with no database or Ollama required. `--all` and `--ci` are mutually exclusive.
|
|
407
408
|
|
|
408
409
|
---
|
|
409
410
|
|
|
410
|
-
### `hook install` — Install
|
|
411
|
+
### `hook install` — Install hooks
|
|
411
412
|
|
|
412
413
|
```bash
|
|
413
414
|
npx @shahmilsaari/memory-core hook install # advisory mode (default)
|
|
414
415
|
npx @shahmilsaari/memory-core hook install --advisory # logs violations, never blocks
|
|
415
416
|
npx @shahmilsaari/memory-core hook install --strict # blocks commits on violations
|
|
417
|
+
npx @shahmilsaari/memory-core hook install --fast # deterministic-only hook
|
|
416
418
|
```
|
|
417
419
|
|
|
418
|
-
Installs
|
|
420
|
+
Installs **two** git hooks:
|
|
421
|
+
- `.git/hooks/pre-commit` — checks staged code against architecture rules.
|
|
422
|
+
- `.git/hooks/commit-msg` — checks commit messages against `commitRules` (see `commit-rules` command).
|
|
419
423
|
|
|
420
|
-
**Advisory mode (default):** violations are logged
|
|
424
|
+
**Advisory mode (default):** violations are logged but the commit always goes through.
|
|
421
425
|
|
|
422
|
-
**Strict mode:** violations block the commit
|
|
426
|
+
**Strict mode:** violations block the commit. You see exactly what's wrong:
|
|
423
427
|
|
|
424
428
|
```
|
|
425
429
|
✗ 2 rule violations found — commit blocked
|
|
426
430
|
|
|
427
|
-
[1]
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
— it gets siloed and duplicated across handlers
|
|
431
|
-
Issue: Password validation logic inside route handler
|
|
432
|
-
Fix: Move to UserService.validateCredentials()
|
|
431
|
+
[1] Thin controllers ×2
|
|
432
|
+
src/controllers/user.ts:32 Password validation in route handler
|
|
433
|
+
src/controllers/auth.ts:18 DB query inside controller
|
|
433
434
|
|
|
434
435
|
[2] src/domain/user.entity.ts:5
|
|
435
436
|
Rule: Domain has zero external imports
|
|
436
|
-
Why: Framework imports tie business logic to infrastructure
|
|
437
437
|
Issue: Imports 'typeorm' directly
|
|
438
438
|
Fix: Define IUserRepository interface in domain/
|
|
439
439
|
|
|
440
|
-
To bypass: git commit
|
|
441
|
-
|
|
440
|
+
To bypass: MEMORY_CORE_SKIP_HOOK=1 git commit
|
|
441
|
+
Manage rules: memory-core commit-rules --list
|
|
442
442
|
```
|
|
443
443
|
|
|
444
|
-
The hook mode is chosen during `init` — no separate step needed unless you want to change it later.
|
|
445
|
-
|
|
446
444
|
```bash
|
|
447
|
-
npx @shahmilsaari/memory-core hook uninstall # remove
|
|
445
|
+
npx @shahmilsaari/memory-core hook uninstall # remove both hooks
|
|
448
446
|
```
|
|
449
447
|
|
|
450
448
|
---
|
|
@@ -578,6 +576,28 @@ Stores lightweight per-project allowlist strings in `.memory-core.json`. Hook an
|
|
|
578
576
|
|
|
579
577
|
---
|
|
580
578
|
|
|
579
|
+
### `commit-rules` — Enforce commit message format
|
|
580
|
+
|
|
581
|
+
```bash
|
|
582
|
+
npx @shahmilsaari/memory-core commit-rules "^(feat|fix|chore)" --message "Use conventional commits"
|
|
583
|
+
npx @shahmilsaari/memory-core commit-rules "^WIP" --message "Don't commit WIP" --negate
|
|
584
|
+
npx @shahmilsaari/memory-core commit-rules "PROJ-\d+" --message "Reference a JIRA ticket" --advisory
|
|
585
|
+
npx @shahmilsaari/memory-core commit-rules --list
|
|
586
|
+
npx @shahmilsaari/memory-core commit-rules --remove "^WIP"
|
|
587
|
+
```
|
|
588
|
+
|
|
589
|
+
Stores regex-based rules in `.memory-core.json` under `commitRules`. The `commit-msg` git hook (installed by `hook install`) validates every commit message against these rules before the commit is accepted.
|
|
590
|
+
|
|
591
|
+
| Flag | What it does |
|
|
592
|
+
|---|---|
|
|
593
|
+
| `--message <msg>` | Required. Error message shown on violation. |
|
|
594
|
+
| `--negate` | Pattern must NOT match (default: must match) |
|
|
595
|
+
| `--advisory` | Warn only — do not block the commit |
|
|
596
|
+
| `--list` | Show all saved commit rules |
|
|
597
|
+
| `--remove <pattern>` | Remove a rule by pattern |
|
|
598
|
+
|
|
599
|
+
---
|
|
600
|
+
|
|
581
601
|
### `ci-setup` — GitHub Actions integration
|
|
582
602
|
|
|
583
603
|
```bash
|
|
@@ -592,13 +612,33 @@ Generates `.github/workflows/memory-core.yml`. Adds a PR check that runs `npx @s
|
|
|
592
612
|
|
|
593
613
|
```bash
|
|
594
614
|
npx @shahmilsaari/memory-core stats
|
|
615
|
+
npx @shahmilsaari/memory-core stats --tune
|
|
595
616
|
npx @shahmilsaari/memory-core stats --reset
|
|
596
617
|
```
|
|
597
618
|
|
|
598
|
-
Shows which rules fire most often and which files have the most violations.
|
|
599
|
-
|
|
600
|
-
-
|
|
601
|
-
-
|
|
619
|
+
Shows which rules fire most often and which files have the most violations. Each rule shows hit count and false-positive rate where available.
|
|
620
|
+
|
|
621
|
+
- `--tune` shows only noisy rules (>40% false-positive rate) with their exact disable commands.
|
|
622
|
+
- `--reset` clears all counters and recent violation history.
|
|
623
|
+
- Live watch-state counters are shown when available.
|
|
624
|
+
|
|
625
|
+
---
|
|
626
|
+
|
|
627
|
+
### `tune` — Silence noisy rules
|
|
628
|
+
|
|
629
|
+
```bash
|
|
630
|
+
npx @shahmilsaari/memory-core tune
|
|
631
|
+
npx @shahmilsaari/memory-core tune --threshold 30
|
|
632
|
+
npx @shahmilsaari/memory-core tune --yes
|
|
633
|
+
```
|
|
634
|
+
|
|
635
|
+
Interactively reviews rules with a high false-positive rate and adds them to `allowPatterns` in `.memory-core.json`.
|
|
636
|
+
|
|
637
|
+
| Flag | What it does |
|
|
638
|
+
|---|---|
|
|
639
|
+
| `--threshold <n>` | False-positive % cutoff (default `40`) |
|
|
640
|
+
| `--min-count <n>` | Minimum hits required (default `5`) |
|
|
641
|
+
| `--yes` | Silence all qualifying rules without prompting |
|
|
602
642
|
|
|
603
643
|
---
|
|
604
644
|
|
|
@@ -852,20 +892,24 @@ npm run smoke:npx
|
|
|
852
892
|
| ✓ | NestJS profile and 39 rules |
|
|
853
893
|
| ✓ | Hook auto-prompt — hook mode offered during init, no separate step needed |
|
|
854
894
|
| ✓ | CI/CD — `ci-setup` generates GitHub Actions workflow for PR enforcement |
|
|
855
|
-
| ✓ | Violation stats — see which rules fire most and which files break most |
|
|
856
895
|
| ✓ | Agent selection — choose which agents to generate files for during init |
|
|
857
896
|
| ✓ | Auto-sync — memory-changing commands refresh selected agent files by default |
|
|
858
897
|
| ✓ | Export / import — portable memories.json for version control and team sharing |
|
|
859
898
|
| ✓ | List / remove / edit — full CRUD for stored memories |
|
|
860
|
-
| ✓ | False positive tagging — `ignore`
|
|
861
|
-
| ✓ | Cleanup commands — `reset` regenerates/reinitializes files; `uninstall` removes memory-core
|
|
862
|
-
| ✓ | Test suite —
|
|
899
|
+
| ✓ | False positive tagging — `ignore` and `allow` save exceptions for hook and watcher |
|
|
900
|
+
| ✓ | Cleanup commands — `reset` regenerates/reinitializes files; `uninstall` removes memory-core |
|
|
901
|
+
| ✓ | Test suite — 94 tests using Node.js built-in `node:test` |
|
|
863
902
|
| ✓ | Multi-provider code checking — Ollama, OpenAI, Anthropic, MiniMax |
|
|
864
903
|
| ✓ | Context-aware retrieval — surface the most relevant rules for the file being edited |
|
|
865
904
|
| ✓ | Setup management — `status`, `provider set`, `model set`, `model doctor` |
|
|
866
905
|
| ✓ | `--debug` flag — verbose output for diagnosing hook and watcher issues |
|
|
867
|
-
| ✓ | Local Svelte dashboard — WebSocket live feed, runtime status, stats, and
|
|
868
|
-
| ✓ |
|
|
906
|
+
| ✓ | Local Svelte dashboard — WebSocket live feed, runtime status, stats, and rules browser |
|
|
907
|
+
| ✓ | Rule cache — 5-min TTL cache, invalidated by config or DB version change |
|
|
908
|
+
| ✓ | Batch suppression — same rule ≥3× on same file auto-suppressed with a notice |
|
|
909
|
+
| ✓ | False-positive tracking — `{ count, falsePositives }` stored per rule in stats |
|
|
910
|
+
| ✓ | Violation clustering — violations grouped by rule, compact multi-location display |
|
|
911
|
+
| ✓ | Auto-tuning — `memory-core tune` silences noisy rules interactively |
|
|
912
|
+
| ✓ | Commit message linting — `commit-rules` + `commit-msg` git hook |
|
|
869
913
|
| | Model guidance during init — recommend a model based on machine specs |
|
|
870
914
|
| | Violation → rule pipeline — auto-suggest a new rule when the same violation repeats |
|
|
871
915
|
| | Team sync — shared database so the whole team works from the same rule set |
|
|
@@ -90,12 +90,17 @@ var Config = {
|
|
|
90
90
|
};
|
|
91
91
|
|
|
92
92
|
// src/embedding.ts
|
|
93
|
+
function getEmbeddingTimeoutMs() {
|
|
94
|
+
const raw = Number(process.env.EMBEDDING_TIMEOUT_MS ?? process.env.MEMORY_CORE_RETRIEVAL_TIMEOUT_MS ?? 5e3);
|
|
95
|
+
return Number.isFinite(raw) && raw > 0 ? Math.floor(raw) : 5e3;
|
|
96
|
+
}
|
|
93
97
|
async function embed(text) {
|
|
94
98
|
let response;
|
|
95
99
|
try {
|
|
96
100
|
response = await fetch(`${Config.ollamaUrl}/api/embeddings`, {
|
|
97
101
|
method: "POST",
|
|
98
102
|
headers: { "Content-Type": "application/json" },
|
|
103
|
+
signal: AbortSignal.timeout(getEmbeddingTimeoutMs()),
|
|
99
104
|
body: JSON.stringify({ model: Config.ollamaModel, prompt: text })
|
|
100
105
|
});
|
|
101
106
|
} catch {
|
|
@@ -119,13 +124,29 @@ function getChatConfig() {
|
|
|
119
124
|
provider,
|
|
120
125
|
model,
|
|
121
126
|
ollamaUrl: process.env.OLLAMA_URL ?? "http://localhost:11434",
|
|
122
|
-
apiKey: process.env.CHAT_API_KEY ?? ""
|
|
127
|
+
apiKey: process.env.CHAT_API_KEY ?? "",
|
|
128
|
+
baseUrl: process.env.CHAT_BASE_URL ?? ""
|
|
123
129
|
};
|
|
124
130
|
}
|
|
125
|
-
|
|
131
|
+
function getDefaultTimeoutMs() {
|
|
132
|
+
const raw = Number(process.env.CHAT_TIMEOUT_MS ?? 2e4);
|
|
133
|
+
return Number.isFinite(raw) && raw > 0 ? Math.floor(raw) : 2e4;
|
|
134
|
+
}
|
|
135
|
+
function timeoutSignal(timeoutMs) {
|
|
136
|
+
return AbortSignal.timeout(timeoutMs ?? getDefaultTimeoutMs());
|
|
137
|
+
}
|
|
138
|
+
function normalizeChatError(err, timeoutMs) {
|
|
139
|
+
const ms = timeoutMs ?? getDefaultTimeoutMs();
|
|
140
|
+
if (err instanceof Error && err.name === "AbortError") {
|
|
141
|
+
return new Error(`TIMEOUT:${ms}`);
|
|
142
|
+
}
|
|
143
|
+
return err instanceof Error ? err : new Error(String(err));
|
|
144
|
+
}
|
|
145
|
+
async function callOllama(cfg, messages, options = {}) {
|
|
126
146
|
const res = await fetch(`${cfg.ollamaUrl}/api/chat`, {
|
|
127
147
|
method: "POST",
|
|
128
148
|
headers: { "Content-Type": "application/json" },
|
|
149
|
+
signal: timeoutSignal(options.timeoutMs),
|
|
129
150
|
body: JSON.stringify({ model: cfg.model, messages, stream: false, format: "json" })
|
|
130
151
|
});
|
|
131
152
|
if (!res.ok) {
|
|
@@ -138,13 +159,15 @@ async function callOllama(cfg, messages) {
|
|
|
138
159
|
const data = await res.json();
|
|
139
160
|
return data.message.content.trim();
|
|
140
161
|
}
|
|
141
|
-
async function
|
|
142
|
-
const
|
|
162
|
+
async function callOpenAICompat(cfg, messages, options = {}) {
|
|
163
|
+
const base = (cfg.baseUrl ?? "").replace(/\/$/, "") || "https://api.openai.com/v1";
|
|
164
|
+
const res = await fetch(`${base}/chat/completions`, {
|
|
143
165
|
method: "POST",
|
|
144
166
|
headers: {
|
|
145
167
|
"Content-Type": "application/json",
|
|
146
168
|
"Authorization": `Bearer ${cfg.apiKey}`
|
|
147
169
|
},
|
|
170
|
+
signal: timeoutSignal(options.timeoutMs),
|
|
148
171
|
body: JSON.stringify({
|
|
149
172
|
model: cfg.model,
|
|
150
173
|
messages,
|
|
@@ -155,7 +178,7 @@ async function callOpenAI(cfg, messages) {
|
|
|
155
178
|
const data = await res.json();
|
|
156
179
|
return data.choices[0].message.content.trim();
|
|
157
180
|
}
|
|
158
|
-
async function callAnthropic(cfg, messages) {
|
|
181
|
+
async function callAnthropic(cfg, messages, options = {}) {
|
|
159
182
|
const system = messages.find((m) => m.role === "system")?.content ?? "";
|
|
160
183
|
const userMessages = messages.filter((m) => m.role !== "system");
|
|
161
184
|
const res = await fetch("https://api.anthropic.com/v1/messages", {
|
|
@@ -165,6 +188,7 @@ async function callAnthropic(cfg, messages) {
|
|
|
165
188
|
"x-api-key": cfg.apiKey,
|
|
166
189
|
"anthropic-version": "2023-06-01"
|
|
167
190
|
},
|
|
191
|
+
signal: timeoutSignal(options.timeoutMs),
|
|
168
192
|
body: JSON.stringify({
|
|
169
193
|
model: cfg.model,
|
|
170
194
|
max_tokens: 4096,
|
|
@@ -176,13 +200,14 @@ async function callAnthropic(cfg, messages) {
|
|
|
176
200
|
const data = await res.json();
|
|
177
201
|
return data.content[0].text.trim();
|
|
178
202
|
}
|
|
179
|
-
async function callMiniMax(cfg, messages) {
|
|
203
|
+
async function callMiniMax(cfg, messages, options = {}) {
|
|
180
204
|
const res = await fetch("https://api.minimax.io/v1/chat/completions", {
|
|
181
205
|
method: "POST",
|
|
182
206
|
headers: {
|
|
183
207
|
"Content-Type": "application/json",
|
|
184
208
|
"Authorization": `Bearer ${cfg.apiKey}`
|
|
185
209
|
},
|
|
210
|
+
signal: timeoutSignal(options.timeoutMs),
|
|
186
211
|
body: JSON.stringify({
|
|
187
212
|
model: cfg.model,
|
|
188
213
|
messages,
|
|
@@ -193,22 +218,31 @@ async function callMiniMax(cfg, messages) {
|
|
|
193
218
|
const data = await res.json();
|
|
194
219
|
return data.choices[0].message.content.trim();
|
|
195
220
|
}
|
|
196
|
-
async function callChatModel(messages) {
|
|
221
|
+
async function callChatModel(messages, options = {}) {
|
|
197
222
|
const cfg = getChatConfig();
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
223
|
+
try {
|
|
224
|
+
switch (cfg.provider) {
|
|
225
|
+
case "openai":
|
|
226
|
+
case "openai-compatible":
|
|
227
|
+
return await callOpenAICompat(cfg, messages, options);
|
|
228
|
+
case "anthropic":
|
|
229
|
+
return await callAnthropic(cfg, messages, options);
|
|
230
|
+
case "minimax":
|
|
231
|
+
return await callMiniMax(cfg, messages, options);
|
|
232
|
+
default:
|
|
233
|
+
return await callOllama(cfg, messages, options);
|
|
234
|
+
}
|
|
235
|
+
} catch (err) {
|
|
236
|
+
throw normalizeChatError(err, options.timeoutMs);
|
|
207
237
|
}
|
|
208
238
|
}
|
|
209
239
|
function getChatProviderLabel() {
|
|
210
240
|
const cfg = getChatConfig();
|
|
211
241
|
if (cfg.provider === "ollama") return `ollama (${cfg.model})`;
|
|
242
|
+
if (cfg.provider === "openai-compatible") {
|
|
243
|
+
const host = cfg.baseUrl ? new URL(cfg.baseUrl).hostname : "custom";
|
|
244
|
+
return `openai-compat/${host} (${cfg.model})`;
|
|
245
|
+
}
|
|
212
246
|
return `${cfg.provider} (${cfg.model})`;
|
|
213
247
|
}
|
|
214
248
|
|
|
@@ -218,6 +252,10 @@ import { createHash } from "crypto";
|
|
|
218
252
|
var { Pool } = pg;
|
|
219
253
|
var pool = null;
|
|
220
254
|
var migrationsRun = false;
|
|
255
|
+
function readPositiveIntEnv(name, fallback) {
|
|
256
|
+
const raw = Number(process.env[name]);
|
|
257
|
+
return Number.isFinite(raw) && raw > 0 ? Math.floor(raw) : fallback;
|
|
258
|
+
}
|
|
221
259
|
function hashMemoryContent(content) {
|
|
222
260
|
return createHash("md5").update(content.trim()).digest("hex");
|
|
223
261
|
}
|
|
@@ -226,7 +264,13 @@ function getPool() {
|
|
|
226
264
|
if (!Config.databaseUrl) {
|
|
227
265
|
throw new Error("DATABASE_URL is not set. Add it to your .env or .memory-core.env file.");
|
|
228
266
|
}
|
|
229
|
-
|
|
267
|
+
const timeoutMs = readPositiveIntEnv("DATABASE_TIMEOUT_MS", 5e3);
|
|
268
|
+
pool = new Pool({
|
|
269
|
+
connectionString: Config.databaseUrl,
|
|
270
|
+
connectionTimeoutMillis: timeoutMs,
|
|
271
|
+
query_timeout: timeoutMs,
|
|
272
|
+
statement_timeout: timeoutMs
|
|
273
|
+
});
|
|
230
274
|
}
|
|
231
275
|
return pool;
|
|
232
276
|
}
|
|
@@ -235,6 +279,7 @@ async function runMigrations() {
|
|
|
235
279
|
const client = await getPool().connect();
|
|
236
280
|
try {
|
|
237
281
|
await client.query("BEGIN");
|
|
282
|
+
await client.query(`ALTER TABLE memories ALTER COLUMN scope SET DEFAULT 'project'`);
|
|
238
283
|
await client.query(`ALTER TABLE memories ADD COLUMN IF NOT EXISTS reason TEXT`);
|
|
239
284
|
await client.query(`ALTER TABLE memories ADD COLUMN IF NOT EXISTS content_hash TEXT`);
|
|
240
285
|
await client.query(`ALTER TABLE memories ADD COLUMN IF NOT EXISTS context JSONB NOT NULL DEFAULT '{}'::jsonb`);
|
|
@@ -1243,13 +1288,22 @@ var MemoryEngineService = class {
|
|
|
1243
1288
|
}
|
|
1244
1289
|
memoryRepository;
|
|
1245
1290
|
embeddingProvider;
|
|
1291
|
+
withReason(input) {
|
|
1292
|
+
const reason = input.reason?.trim();
|
|
1293
|
+
return {
|
|
1294
|
+
...input,
|
|
1295
|
+
reason: reason || `Captured as a ${input.type} memory because it should be remembered: ${input.content}`
|
|
1296
|
+
};
|
|
1297
|
+
}
|
|
1246
1298
|
async remember(input) {
|
|
1247
|
-
const
|
|
1248
|
-
|
|
1299
|
+
const normalized = this.withReason(input);
|
|
1300
|
+
const embedding = await this.embeddingProvider.embed(normalized.content);
|
|
1301
|
+
return this.memoryRepository.upsert({ ...normalized, embedding });
|
|
1249
1302
|
}
|
|
1250
1303
|
async rememberForce(input) {
|
|
1251
|
-
const
|
|
1252
|
-
await this.
|
|
1304
|
+
const normalized = this.withReason(input);
|
|
1305
|
+
const embedding = await this.embeddingProvider.embed(normalized.content);
|
|
1306
|
+
await this.memoryRepository.save({ ...normalized, embedding });
|
|
1253
1307
|
}
|
|
1254
1308
|
async list(filters = {}) {
|
|
1255
1309
|
return this.memoryRepository.list(filters);
|