91 Commits

Author SHA1 Message Date
Sunil Srivatsa 49415712f2 fix(memory): separate extraction instructions from user input
The memory-extraction prompt concatenated its instructions, few-shot
examples, and the user message into a single user turn, so a small local
model could not distinguish instructions from input and frequently echoed
the Globex/weather examples instead of extracting facts.

Send the instructions as a real system turn and the raw text as the user
turn. The tiny worker protocol gains a systemPrompt field, and Mnemopi
completion input carries task metadata so the backend selects the right
prompt per call.

Drop the code-built MEMORY_EXTRACTION_TEMPLATE rather than porting it:
prompt text belongs in .md files, and resolveMemoryCompletionInput already
overrides that template for every extraction call, so Mnemopi rendered it
only for the result to be discarded.

Measured on ONNX q4 CPU, LFM2.5-1.2B memory extraction improved from 1/8
to 5/8 once the roles were separated.
2026-08-17 15:06:37 -07:00
roboomp fc9d40ca30 fix(mnemopi): keep global rows recallable under channelId filter
buildWhere() appended a redundant hard `channel_id = ?` clause on top of
the `(session_id = ? OR scope = 'global' OR channel_id = ?)` visibility
clause. The hard AND nullified the `scope='global'` branch, so any global
row whose channel_id differed from the recall channelId — including rows
imported via importFromDict() with channel_id NULL — was silently dropped.

Channel isolation is fully preserved by the visibility OR-clause alone
(other-channel, non-global, cross-session rows still fail all three
disjuncts), so removing the hard clause restores global visibility without
leaking other channels.

Fixes #8525
2026-08-14 06:55:59 +00:00
can1357 b279db1790 test: refactored test suites to eliminate time-based sleeps and polling loops
- Replaced time-based sleeps and polling loops with event-driven promise resolvers and fake timers across agent and tool tests.
- Migrated test suites to share in-memory auth storage and fixtures using lifecycle hooks.
- Updated catalog model definitions, metadata, and configurations.
2026-08-13 19:32:22 +02:00
can1357 8fed93632a refactor(ai): rebranded openrouter identifiers and user agent to omp
- Updated user agent and openrouter titles in packages/ai from Oh-My-Pi to omp.
- Integrated getOpenRouterHeaders into image generation tool requests in packages/coding-agent.
- Updated provider documentation and test assertions to reflect the rebrand.
2026-08-11 15:28:28 +02:00
roboomp 336975c46c fix(mnemopi): recover from partially-extracted embedding model cache
An interrupted fastembed model download leaves <cacheDir>/<model>/ with
sidecars and a truncated model.onnx_data but no model.onnx. Upstream
retrieveModel short-circuits on the existing dir (and reuses a leftover
partial <model>.tar.gz), so FlagEmbedding.init throws "Model file not
found at .../model.onnx" every session: semantic recall silently dies
machine-wide and reconcileEmbeddingModel re-enqueues the same
never-embeddable rows on every store open.

quarantineCorruptModelFile only matched "Protobuf parsing failed", never
this partial-extraction variant. Add clearIncompleteModelCache: a
"Model file not found" init failure now removes the incomplete model dir
and the leftover partial archive (containment-guarded to a direct child
of the fastembed cache root) and retries init exactly once, so the next
attempt re-downloads cleanly and recall self-heals.

Fixes #7916
2026-08-07 23:38:25 +02:00
can1357 3a8591a8af test: hardened two ci-flaky timing tests
- mnemopi provider parity 'diagnose, validate, graph' does ~6.7s of real
  work under bun --parallel=8 on loaded runners; raised its per-test
  timeout to 30s (default 5s flaked twice in three CI runs).
- utils LRUCache updateAgeOnGet drove a 30ms TTL with real 20ms sleeps
  (10ms margin); now drives performance.now() via a mocked clock, so the
  contract is asserted deterministically with no wall-clock wait.
2026-08-06 14:48:57 +02:00
metaphorics 3802d2dd80 chore(ts): enforce noImplicitOverride 2026-08-05 02:49:01 +09:00
Brad McCormack 2163ee38a6 fix(mnemopi): validate explicit SQLite page sizes
Reject invalid page-size values before interpolating the PRAGMA and cover new, existing, and in-memory database behavior.
2026-08-02 11:12:35 +10:00
Brad McCormack 68555bd677 fix(mnemopi): make SQLite page-size alignment opt-in
Keep SQLite default behavior unless a page size is explicitly configured, and route file-backed Mnemopi databases through the shared opener.
2026-08-02 11:12:35 +10:00
Brad McCormack e541ee2ef6 fix(mnemopi): align SQLite page size with OS page size
Add the initial page-size detection and regression coverage for file-backed Mnemopi databases.
2026-08-02 11:12:35 +10:00
roboomp c95d182bf2 fix(mnemopi): anchored think stripping to leading blocks
- Restricted the reasoning-wrapper removal to leading <think> blocks so a
  literal think tag after real content is preserved instead of deleted.
- Added coverage asserting mid-content think tags survive cleanOutput.

Fixes #7231
2026-08-01 05:18:25 +00:00
roboomp ebd7ea3578 fix(mnemopi): stripped reasoning blocks from remote llm output
- Removed MiniMax-style think wrappers in cleanOutput so reasoning-model
  responses no longer leak into consolidation summaries or corrupt fact
  extraction (the wrapper previously survived parsing and every stored fact
  became reasoning prose).
- Added remote consolidation and remote extraction regression coverage.

Fixes #7231
2026-08-01 05:13:19 +00:00
can1357 e9dda1f867 chore: applied biome formatting to merged pull request sources 2026-07-31 19:32:42 +02:00
can1357 8cd634350f test(mnemopi): make statement lifetime checks portable 2026-07-31 19:28:41 +02:00
Cyrus 63ca18bd53 test(mnemopi): release prepared statements in file-backed suites
Six suites build a real DB under a temp dir and then `rmSync` it in
`afterEach`. Each leaked its own one-shot statement the same way the source
did, so the teardown hit EBUSY on Windows and failed tests whose assertions
had already passed.

Only the suites that touch a file-backed DB are changed. The remaining
`prepare()` calls in the package tests run against `:memory:`, where there
is nothing to unlink and no cleanup to fail.
2026-07-30 19:53:57 +08:00
Cyrus 0c9cc54561 fix(mnemopi): release one-shot prepared statements
Every `db.prepare(...)` in the package was a one-shot: prepared, stepped
once via run/get/all, then dropped without `finalize()`. There were 65 such
sites and no `finalize()` call anywhere. An unfinalized statement keeps the
SQLite connection alive, so `BeamMemory.close()` -> `closeQuietly(db)` ->
`db.close()` never released the file. `closeQuietly` swallows the error;
calling `db.close(true)` instead surfaces "database is locked".

One-shot writes now go through `db.run(sql, params)`, which prepares and
finalizes internally. Statements that are read from, or reused across a
loop, are bound with `using` so they are finalized when they leave scope.

On Windows the effect is user-visible beyond tests: the memory DB and its
`-wal`/`-shm` sidecars stay locked after mnemopi closes them, so the file
cannot be deleted, moved, or rotated. On POSIX the leak is silent because
open files can be unlinked.

The new suite pins both halves: that `db.close(true)` does not throw after
the store paths run, and that a closed bank leaves no `-wal`/`-shm` behind
and can be deleted. All three cases fail on the unfixed sources.

Same class as 14252e71c and #6762, neither of which covered this package.
2026-07-30 19:53:57 +08:00
Wolfgang Schoenberger 4ffe1822d4 refactor(mnemopi): keep only production-backed native kernels after wrapper-level benchmarking 2026-07-22 04:45:13 -07:00
Wolfgang Schoenberger 3c84e2c410 fix(mnemopi): clamp native limits, preserve JS lowercase semantics, wrapper-level bench 2026-07-22 04:10:17 -07:00
Wolfgang Schoenberger 85fae69281 style(mnemopi): format native vector parity test 2026-07-22 01:58:23 -07:00
Wolfgang Schoenberger 22a5fb3d9f perf(mnemopi): use native batch vector kernels in recall hot loops
Swap the batch-shaped scoring loops to the pi-natives kernels behind the
existing TS function signatures, so every caller keeps its guards and
observable behavior: mmrRerank (default Jaccard path), searchExactVectorIndex,
clusterBySimilarity, BinaryVectorStore.search, and FastBinarySearch.search.
cosine_similarity_pairs now takes Float64Array so f64 vectors round-trip
without f32 narrowing. Scalar one-off call sites stay in TS (query-cache
cosine probe, shmr centroid/confidence loops, recall per-row cosine map,
custom-similarity mmrRerank). Adds a seeded parity suite: 1e-9 relative
tolerance for floats, exact equality for Hamming distances and pair lists,
identical MMR index sequences across lambda/topK grids.
2026-07-22 01:24:37 -07:00
DarkPhilosophy 7ca0235616 fix(mnemopi): normalize the default model cache root 2026-07-18 02:55:00 +03:00
DarkPhilosophy eed5da008e test(mnemopi): pin the corruption retry contract end-to-end
defaultLocalModelInitializer (exported @internal for tests; production
seam stays setLocalModelInitializer) now provably retries
FlagEmbedding.init EXACTLY once after a Protobuf-corruption failure,
quarantining the cached file in between, and surfaces the error without
looping when the retry fails too. The heal callsite now passes
options.cacheDir so containment checks the caller's cache root, not
only the global default.
2026-07-18 02:25:05 +03:00
DarkPhilosophy 6cc0c71d1d fix(mnemopi): self-heal a corrupt cached embedding model on init
A truncated model_optimized.onnx (observed live: 5.7MB file from June,
'Protobuf parsing failed' on every load) blocks local embeddings
forever: the downloader treats the existing file as complete, so every
init re-parses the same broken bytes and local recall/retain loses its
embedder. defaultLocalModelInitializer now quarantines the exact file
named by the loader (atomic rename to *.corrupt-<ts>) and retries init
ONCE so the model re-downloads.

The extracted path is error-message content: it is honored only when
it resolves inside the fastembed cache directory, so a dependency
emitting an unexpected message can never rename an arbitrary file.

Contract tests: protobuf failure quarantines exactly the named cache
file and reports retry-safe; unrelated errors touch nothing; a missing
file (concurrent heal) stays retry-safe; a path outside the cache root
is refused untouched.
2026-07-18 02:21:17 +03:00
can1357 358879afca merged PR #5432: fix(mnemopi): pin Windows ORT DLL path 2026-07-16 03:32:02 +02:00
can1357 5be1af81d8 merged PR #5434: fix(mnemopi): protect durable working memory from trim and cascade linked artifacts 2026-07-16 03:32:02 +02:00
roboomp d866eb8589 fix(mnemopi): protect durable working memory from trim and cascade linked artifacts
Working-memory TTL trim treated every consolidated_at IS NULL row as
scratch, so restored or imported durable rows disappeared on the next
write, and the trim delete left annotations, embeddings, facts, and
memoria projections orphaned.

- Exclude IMPORTED-tier rows from the trim eligibility query so restored
  banks survive a later remember()/rememberBatch().
- Stamp imported working-memory rows as consolidated in importFromDict so
  restored backups are durable regardless of trust tier.
- Add purgeWorkingMemoryArtifacts() and route trim, forgetWorking, and
  force-import overwrite through it to cascade annotations, embeddings,
  facts (source_msg_id), memoria_* (source_memory_id), gists, and the
  graph edges tied to those memory/gist/fact node ids.

Fixes #4819
2026-07-14 16:54:33 +00:00
roboomp 2997037ffa fix(mnemopi): pinned Windows ORT DLL path
- Resolved ONNX Runtime from fastembed's own dependency graph.
- Prepended the cached DLL directory before loading the native binding.
- Added regression coverage for inherited stale runtime paths.

Fixes #4849
2026-07-14 16:52:41 +00:00
can1357 ca68daa81c fix(mnemopi): made recall fact ids resolvable via memory reads
recall (includeFacts) surfaces facts.fact_id as a result id, but
store.get only searched working_memory + episodic_memory, so every
surfaced fact id was a dead end for 'read memory://<id>' and
memory_edit ('not found in any scoped bank').

- store.get now falls back to the facts table (visibility mirrors
  factRecall: same-session or scope='global'), returning a read-only
  row with memory_store 'fact' and the full triple as content.
- coding-agent labels the store honestly ('fact') in memory:// reads
  and reports not_editable (instead of not_found) for memory_edit ops
  on fact ids; the facts table stays immutable.

Fixes #4725
2026-07-09 18:27:23 +02:00
roboomp 3882256c7c fix(mnemopi): handled category-specific fact keys
- Accepted natural wrapper fields for instruction and preference objects.

- Preserved timeline descriptions with their dates when object-shaped timeline items are parsed.

- Extended the parseFacts regression to cover category-specific fields.

Fixes #4649
2026-07-06 01:11:59 +00:00
roboomp 4086aab83e fix(mnemopi): dropped object-shaped fact coercion
- Unwrapped extractor fact items from known text fields instead of coercing arbitrary objects with String().

- Dropped unrecognized object entries so derived fact indexes cannot persist literal [object Object] rows.

- Added a parseFacts regression covering mixed strings, wrapped objects, and junk timeline objects.

Fixes #4649
2026-07-06 00:56:44 +00:00
can1357 29340cf3cb Merge PR #4445: fix(mnemopi): let agents read stored memories in full (@roboomp)
# Conflicts:
#	packages/mnemopi/src/core/beam/recall.ts
2026-07-05 13:12:31 +02:00
can1357 66f1c689b6 fix(mnemopi): preserve small fact recall buckets 2026-07-05 13:12:04 +02:00
can1357 aa081c472b Merge PR #4405: fix(mnemopi): surface extracted facts in recall (@roboomp) 2026-07-05 13:12:04 +02:00
can1357 e9ec3211da Merge PR #4392: fix(mnemopi): preserve extraction categories (@roboomp)
# Conflicts:
#	packages/mnemopi/src/core/extraction.ts
2026-07-05 13:11:52 +02:00
roboomp 536ecc725a fix(mnemopi): let agents read stored memories in full
Recall silently clipped every result to 500 chars mid-word with no marker,
and memory_edit update replaces content wholesale by id. There was no way
for an agent to inspect the full row before overwriting it: Mnemopi.get
existed but nothing surfaced it, and the advertised URI only served the
file-backed memory summary. The natural recall/inspect/update loop had no
inspect step.

Three fixes across mnemopi and coding-agent:
- recall now appends an ellipsis marker when it clips content and reports
  truncated=true plus full_length. The cap is exposed as
  RecallOptions.contentPreviewChars (default 500, 0 disables). The factLine
  200-char clip used by the enhanced-context sandwich gets the same marker.
- Under the mnemopi backend the read-tool URI scheme now routes an id host
  to Mnemopi.get() across every session's scoped banks, returning the row
  as text/markdown with a YAML-frontmatter header (bank, store, source,
  timestamp, importance, veracity). The root namespace remains for the
  file-backed summary. Miss errors now name the backend explicitly.
- Updated the recall and memory_edit tool prompts to document the
  truncation marker and require reading the full memory before any
  wholesale content update.

Fixes #4443
2026-07-03 15:05:43 +00:00
roboomp 4ec8538d18 fix(mnemopi): surfaced extracted facts in recall
Scaled enhanced fact recall with the requested topK, bucketed formatted context without duplicate rows, and ignored flat fact storage fields during scoring.

Added regression coverage for extracted flat fact recall and storage-only fact/entity noise.

Fixes #4402
2026-07-03 05:55:42 +00:00
roboomp c8121d3db2 fix(mnemopi): preserved embed projections during sleep
Carried working_memory.embed_text into sleep consolidation summaries so episodic content, FTS, and embeddings do not reintroduce retention protocol markers after working rows age out.

Fixes #4395
2026-07-03 05:16:10 +00:00
roboomp f021ee577c fix(mnemopi): routed embed projections through recall
Scored working-memory recall candidates against embed_text when present so FTS matches from the projection survive the lexical gate even without dense embeddings.

Fixes #4395
2026-07-03 05:07:13 +00:00
roboomp ed715eda7b fix(mnemopi): stripped retention markers from embeddings
Added an embedText projection for remember() so stored transcripts can remain readable while embeddings, FTS indexing, and embedding-model rebuilds use marker-free text. Updated coding-agent retention to pass the marker-free projection and strip retained protocol markers from recall display.

Fixes #4395
2026-07-03 04:59:02 +00:00
roboomp 3249d2c5b1 fix(mnemopi): preserved extraction categories
- Parsed LLM extraction output into MEMORIA categories without flattening kg triples away.
- Routed background extraction into memoria_* tables and triples while keeping legacy fact rows stable.
- Covered structured category routing through remember(..., { extract: true }).

Fixes #4389
2026-07-03 03:50:50 +00:00
roboomp b24052b895 fix(mnemopi): preserve empty structured extraction
Short-circuit successfully parsed structured extractor output even when all extraction arrays are empty, so the JSON response body is not stored as a fallback memory.

Added coverage for plain and fenced empty extraction JSON.

Fixes #4390
2026-07-03 03:41:50 +00:00
roboomp f97e05a1c4 fix(memory): scope mnemopi extraction to user turns
- Added an extractText override to pi-mnemopi remember paths so stored content and mined facts can use different text.
- Routed coding-agent mnemopi retention to store the full transcript while extracting only user-authored turns.
- Tightened deterministic Instruction extraction to require an explicit I/you subject.

Fixes #3372
2026-06-24 14:21:26 +00:00
can1357 829d6c66dc Merge PR #2903: fix(mnemopi): make proactive linking configurable from host settings (@wolfiesch) 2026-06-21 00:09:20 +02:00
roboomp 4fcebcea0b fix(mnemopi): kept transcript tail when clipping oversized embedding inputs
`MnemopiSessionState.retainMessages` hands `embed()` the chronological
multi-turn transcript (oldest -> newest). A naive `slice(0, max)` cap landed
on the oldest turns and dropped the most recent content, so every retained
episode past the cap collapsed onto essentially the same prefix vector and
dense recall could not match topics introduced after the first 8192 chars.

`capInputs` now routes oversized inputs through `clipToWindow`, which keeps
roughly half the cap from the head and half from the tail with a small
`[...]` elision marker between them. Short inputs and array reference pass-
through are unchanged. Falls back to a tail-only clip when `max` is too small
to fit a useful split. Locked in by a new `embedding-input-cap.test.ts` case
that pins markers at both ends of a 50k transcript and asserts both survive
the clip.

Fixes #3126
2026-06-20 22:12:48 +02:00
roboomp f2ad18692f fix(mnemopi): lowered default embedding input cap to 8192 chars
Defaulted MNEMOPI_EMBEDDING_MAX_INPUT_CHARS to 8192 so the automatic guard matches bge-m3 and OpenAI text-embedding context limits by default. Larger local embedding servers such as Qwen3-Embedding with 32k ctx can still raise the cap, and 0 still disables truncation.

Fixes #3126
2026-06-20 22:12:48 +02:00
roboomp 6666dec657 fix(mnemopi): capped embed() input to keep retention transcripts under the embedding context
`MnemopiSessionState.retainMessages` (`packages/coding-agent/src/mnemopi/state.ts:352`)
always calls `prepareRetentionTranscript(messages, true)` and hands the whole
multi-turn transcript to `embed([transcript])`. Long sessions (especially CJK
content) routinely outgrow the embedding model's context window, and llama.cpp's
`/embeddings` server rejects oversized requests with
`request (N tokens) exceeds the available context size` — every retain after
that point silently lost its vector row, leaving recall on FTS-only.

Capped per-input length inside `embed()` (the single chokepoint every retain /
query / consolidate flow funnels through) at `MNEMOPI_EMBEDDING_MAX_INPUT_CHARS`
(default 32000 chars ≈ 8k English tokens / 16–32k CJK tokens, override via env
or `embeddings.maxInputChars` runtime option; `0` disables). The new array
is allocated only when at least one input is oversized, so the typical short-
query path through `embedQuery` still passes the original array through; the
truncation also emits a debug-or-warn log so the resize is no longer silent.

Fixes #3126
2026-06-20 22:12:48 +02:00
can1357 9478e3cc5c refactor: replaced ReturnType<typeof setTimeout> with Timer type
- Replaced usage of `ReturnType<typeof setTimeout>` and `ReturnType<typeof setInterval>` with the explicit `Timer` type across the codebase.
- Updated several type definitions and function signatures to use concrete types instead of inferred return types for improved clarity and maintainability.
2026-06-19 17:38:07 +02:00
can1357 ff3a1d8863 Merge remote-tracking branch 'origin/farm/f56ccf5e/mnemopi-local-embeddings-macos' 2026-06-19 17:24:56 +02:00
roboomp b4e1a9e235 fix(mnemopi): repaired stale fastembed configs
- Downloaded config.json with tokenizer sidecars when repairing stale fastembed model caches.\n- Treated fastembed Config file missing errors as repairable initializer failures.\n- Extended cache repair coverage for the missing-config state.\n\nFixes #3054
2026-06-19 15:21:09 +00:00
roboomp 156ec3c363 fix(mnemopi): restored local fastembed runtime
- Preserved fastembed's transitive ONNX Runtime instead of forcing an ABI-mismatched runtime cache install.\n- Repaired stale fastembed model caches by downloading missing tokenizer sidecars from the matching Hugging Face model repo.\n- Added regression coverage for the runtime install plan and tokenizer sidecar repair.\n\nFixes #3054
2026-06-19 15:14:38 +00:00