Commit Graph

39 Commits

Author SHA1 Message Date
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 6b4823181b test: cleaned test suites and documented filtering guidelines
- Remove redundant definedness, null, and type checks across test suites in multiple packages.
- Clean up unused assertions, metadata tests, and obsolete test cases.
- Add good versus bad test filter guidelines and requirements to project documentation.
2026-08-13 08:28:42 +02:00
roboomp eacde4852e fix(mnemopi): contained lifecycle hook failures
Caught recall and retention failures at their fire-and-forget event boundaries, with bank and operation diagnostics.

Added agent-start and agent-end regression coverage for unavailable session data and bank access.

Fixes #8351
2026-08-12 18:01:55 +00:00
can1357 45e12e5bb7 test(mnemopi): awaited detached shared-bank flush in lock-wait test
dispose({ timeoutMs }) legitimately detaches the consolidate pass when
the shutdown budget expires mid-flight (#3641), so asserting the shared
bank flush synchronously after dispose raced the detached pass on slow
CI runners (0 calls observed on run 31287979000). Signal the flush call
through a deferred and await it after releasing the lock; the bounded
<500ms return assertion is unchanged.
2026-08-09 03:39:03 +02:00
can1357 2b4c33569b Merge PR #7367: fix(mnemopi): surface scoped recall failures (@roboomp) 2026-08-02 20:53:21 +02:00
can1357 51b71c098b test(mnemopi): exercise locked shared-bank flush 2026-08-02 20:53:20 +02:00
roboomp e82aa25590 fix(mnemopi): contained auto-recall failures
- Caught all-target recall failures inside the fire-and-forget auto-recall path.
- Kept first-turn recall eligible for retry after an engine failure.
- Added regression coverage for the background failure contract.
2026-08-02 09:18:09 +00:00
roboomp d99adc9325 fix(mnemopi): surfaced scoped recall failures
- Warned when a scoped recall target fails while preserving partial results from healthy targets.
- Re-threw the underlying failure when every target fails so recall cannot masquerade as an empty search.
- Added tool-level regression coverage for total and partial scoped failures.

Fixes #7364
2026-08-02 09:11:36 +00:00
roboomp b9bdbcec03 fix(mnemopi): bound busy timeout on every owned bank
Extended the shutdown busy-timeout clamp from the retain bank to all owned banks so a locked shared bank (per-project-tagged) cannot stall teardown for the default 5s SQLite timeout.

Fixes #7351
2026-08-02 05:34:58 +00:00
roboomp 82095f0c12 fix(mnemopi): bounded locked retention during teardown
Capped synchronous SQLite busy waits before starting bounded final consolidation, then spent only the remaining shutdown deadline on asynchronous drains.

Added regression coverage for a locked Mnemopi writer in the headless teardown path.

Fixes #7351
2026-08-02 05:27:11 +00:00
roboomp 5a1f227a6b fix(memory): synchronized live backend lifecycle
- Serialized backend transitions across runtime state, tools, and prompts.
- Rehydrated Mnemopi listeners after clear and enqueue maintenance.
- Made memory.backend the sole post-migration local runtime gate.

Fixes #5638
2026-07-23 19:52:09 +00:00
can1357 b6e9d387d7 fix(mnemopi): derive legacy retention cursor conservatively
Pre-fix resumed sessions wrote cumulative transcript rows under the
incremental ${sessionId}-<ts> source_id shape, so summing every prefixed
legacy row could overshoot the real retained prefix and permanently skip
unseen turns. Use per-row max instead: it can only under-count, which at
worst re-stores one suffix before an explicit cursor row takes over.
Adds a regression seeding a legacy bank with two incremental rows plus a
cumulative resumed row and asserting turns 7-8 still get retained.
2026-07-22 21:13:16 +02:00
can1357 576ee28434 Merge PR #6202: fix(mnemopi): retain only unretained turns on consolidation (@roboomp) 2026-07-22 21:13:16 +02:00
roboomp 050aec7632 fix(coding-agent): make hindsight request timeouts per-op and configurable
The single 30s hang-guard from #4290 (sized for metadata fetches per #4229)
wrapped every Hindsight op including reflect, an agentic retrieve+synthesize
call whose healthy latency routinely exceeds 30s, so ordinary successful
reflects were aborted client-side.

- Give HindsightApi per-op deadlines with sensible defaults (reflect 120s,
  retain 60s, recall/request 30s) plus a HindsightTimeouts option bag.
- Report the effective seconds in the timeout error instead of a hard-coded
  "after 30s".
- Add hindsight.requestTimeoutMs / reflectTimeoutMs / recallTimeoutMs /
  retainTimeoutMs settings and matching HINDSIGHT_*_TIMEOUT_MS env vars.
- Preserve caller-abort merging via withTimeoutSignal.

Fixes #6125
2026-07-21 21:26:11 +00:00
roboomp a480e99205 fix(mnemopi): retained only unretained session turns
Persisted the retained user-turn cursor in transcript metadata and restored it when sessions resume, including legacy rows from before the cursor existed.

Made forced lifecycle retention slice from that cursor so enqueue and shutdown no longer add cumulative snapshots beside incremental windows.

Fixes #6058
2026-07-21 20:34:48 +00:00
iacore 831b94fee9 fix(mnemopi): skip sleep on dispose so /quit avoids cross-session bank consolidation 2026-07-08 20:49:12 +08:00
iacore d698580d67 fix(mnemopi): lighter dispose consolidation so /quit returns quickly
Interactive shutdown (AgentSession.dispose) now runs a bounded
consolidation on the current session only and skips fresh LLM fact
extraction. The heavier cross-session consolidation with extraction is
still performed by the /memory enqueue command and the backend enqueue
path.

This avoids blocking /quit and /exit on a fresh LLM round-trip
and a full sleepAllSessions scan.

- Add full and extract options to consolidate() and
  forceRetainCurrentSession()
- Route dispose() through consolidate({ full: false, extract: false })
- Route /memory enqueue and backend enqueue through
  consolidate({ full: true })

Fixes #3641
2026-07-08 19:54:18 +08:00
can1357 45f1d63861 Merge remote-tracking branch 'origin/farm/035627c4/slice-mnemopi-retain-window' 2026-07-04 05:10:38 +02: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 5b6385df19 fix(mnemopi): retained only new auto-retain turns
Auto-retain now slices the transcript after the last retained user turn before storing an episode, preventing cumulative duplicate session transcripts in mnemopi banks.

Fixes #4396
2026-07-03 04:50:44 +00:00
roboomp efaad3ffb5 fix(coding-agent): bounded mnemopi consolidate-on-dispose so /quit returns within ~2 s
/quit and /exit hung for many seconds because AgentSession.dispose()
awaited MnemopiSessionState.dispose() unconditionally, and that path
runs consolidate() (state.ts:421) which fires a fresh LLM fact
extraction for the just-retained transcript and then awaits
flushExtractions() per owned bank. One LLM round-trip per shutdown,
no upper bound, no visible status.

- Add a timeoutMs option to MnemopiSessionState.dispose. When the cap
  fires the in-flight consolidate is detached to the background and the
  SQLite handles close once it settles, so writes never race a closed
  handle.
- AgentSession.dispose passes SHUTDOWN_CONSOLIDATE_BUDGET_MS = 1_500 on
  the user-visible shutdown path. Per-turn maybeRetainOnAgentEnd has
  already retained earlier turns, so the worst case is losing episodic
  promotion for the last few turns. State-replacement disposes
  (mnemopiBackend.start) stay unbounded.
- InteractiveMode.shutdown surfaces a 'Closing session…' status before
  dispose runs so the brief pause is explained rather than mysterious.

Two regression tests in memory-tools.test.ts cover (1) dispose returns
within the budget when flushExtractions stalls and the deferred close
still runs once consolidate settles, and (2) unbounded dispose still
runs the full #2320 consolidate-then-close pipeline.

Fixes #3641
2026-07-01 22:18:44 +02: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
oldschoola 14252e71cb fix: Windows test failures — path handling, EBUSY, SQLite handle leaks
Fix all Windows-specific test failures caused by path handling problems
and EBUSY errors from unclosed SQLite database handles.

Root causes fixed:
1. POSIX path assumptions: replaced hard-coded file:///tmp, /repo, etc.
   with pathToFileURL/path.resolve/path.join computed expectations
2. shortenPath() now normalizes backslashes to forward slashes after ~
   and respects home directory boundaries
3. HistoryStorage.resetInstance() leaked its Database — added #close()
   that finalizes all prepared statements and closes the DB
4. AgentStorage gained the same resetInstance()/#close() pattern
5. SqliteAuthCredentialStore.close() leaked one-off prepared statements
   from inline this.#db.prepare() calls — wrapped each in try/finally
6. model-cache.ts used a process-global DB even for custom dbPath —
   now opens/closes per-call via withModelCacheDb
7. createAgentSession leaked AuthStorage on construction failure —
   added ownsAuthStorage cleanup in catch block
8. MnemopiBackend.removeDbFiles() now truly best-effort (catches errors)
9. TempDir retry window expanded from 4x10ms to 40x25ms
10. TempDir prefix convention: non-@ prefixes created dirs relative to
    cwd instead of os.tmpdir() — all test temp dirs now use @ prefix
11. Shell-escaped interpolated paths in bash tool tests
12. git core.autocrlf false in autoresearch test repo init

All 522 previously-failing Windows tests now pass.
2026-06-18 21:32:38 -07:00
Wolfgang Schoenberger 8eb8ec4b74 fix(mnemopi): make proactive linking configurable from host settings
Proactive linking (ingesting new memories into the episodic graph as they
are stored) could only be toggled through the MNEMOPI_PROACTIVE_LINKING
environment variable, unlike its sibling recall features polyphonicRecall
and enhancedRecall, which hosts drive through configureRecallFeatures() and
the coding-agent mnemopi.polyphonicRecall / mnemopi.enhancedRecall
config.yml settings. The write-path gate in store.ts read process.env
directly and the existing env-only proactiveLinkingEnabled() helper was
dead code, so host configuration never reached it.

- Add proactiveLinking to RecallFeatureFlags / configureRecallFeatures()
  and rewrite proactiveLinkingEnabled() to fall back to the configured
  default, matching the polyphonic/enhanced resolvers. The
  MNEMOPI_PROACTIVE_LINKING env var still takes precedence when set.
- Route the store.ts proactiveLinkIfEnabled gate through
  proactiveLinkingEnabled() instead of reading process.env directly.
- Add the mnemopi.proactiveLinking coding-agent config.yml setting (off by
  default, /settings -> Memory -> Mnemopi) and wire it through
  loadMnemopiConfig and createScopedResources.

Closes #2440

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-18 13:29:03 -07:00
can1357 12bbefd48b feat(mnemopi): exposed polyphonic and enhanced recall via config.yml
- Added `mnemopi.polyphonicRecall` and `mnemopi.enhancedRecall` boolean settings (default off) to `settings-schema.ts` and `loadMnemopiConfig`, applied via the new `configureRecallFeatures()` in `createScopedResources`.
- Made `polyphonicRecallEnabled()`, `enhancedRecallEnabled()`, and `isEnhancedRecallEnabled()` fall back to the configured defaults while `MNEMOPI_POLYPHONIC_RECALL` / `MNEMOPI_ENHANCED_RECALL` env vars still win when set.
- Exported `configureRecallFeatures`/`RecallFeatureFlags` from the package root and `core` barrels; documented the settings in `docs/mnemosyne-memory-backend.md`.
- Added `recall-feature-flags.test.ts` covering defaults, config enablement, and env precedence; the mnemopi changelog hunk also carries the adjacent #2322 entry (same contiguous run).

Fixes #2323: Mnemopi: MNEMOPI_POLYPHONIC_RECALL and MNEMOPI_ENHANCED_RECALL not configurable via config.yml
2026-06-12 04:01:36 +02:00
roboomp 9d4fcdb536 fix(mnemopi): keep consolidate() reachable from aliased subagent enqueue
The previous refactor dropped `if (this.aliasOf) return` into
consolidate(), which made `/memory enqueue` invoked from inside a
subagent a no-op. The old enqueue() body short-circuited only
forceRetainCurrentSession (via its own guard) but still flushed
extractions and slept on state.memory, which for aliased states points
at the parent's shared retain bank.

Moved the alias guard back out of consolidate(): forceRetainCurrentSession
keeps its own early-return (the subagent's transcript is the parent's
concern), so consolidate() runs the SQL-level flush+sleep on every
owned bank for both primary and aliased states. The lifecycle guard
stays in dispose() so disposing a subagent still does not flush, sleep,
or close the parent's memory.

Added a regression test: a /memory enqueue routed through the aliased
child state calls flushExtractions + sleepAllSessions(false) on the
parent's owned memory exactly once, and forceRetainCurrentSession runs
on the child (where its own guard returns early) but not on the parent.

Addresses #2327 review.
2026-06-11 18:34:53 +00:00
roboomp 6abe6401bc fix(mnemopi): skip consolidation when clearing memory
`/memory clear` (mnemopiBackend.clear) calls dispose() right before
removing the SQLite files. Running the consolidation pass first would
make a destructive clear spend tokens/time creating memories that get
deleted on the very next line, and a slow extraction could block the
clear path under llmMode: remote.

Added a `{ consolidate?: boolean }` option to
MnemopiSessionState.dispose; defaults to true so normal shutdown still
runs the SHMR/beam pipeline. mnemopiBackend.clear passes
`{ consolidate: false }` so the close path stays unsubscribe + close
only. Added a regression test that spies on forceRetainCurrentSession,
consolidate, flushExtractions, and sleepAllSessions to pin the new
behavior, while close still runs once per owned bank.

Addresses #2327 review.
2026-06-11 18:29:23 +00:00
roboomp aba01289d0 fix(mnemopi): consolidate working memory on session shutdown
MnemopiSessionState.dispose() now drains pending fact extractions and
runs sleepAllSessions on every owned bank before closing handles, and
AgentSession.dispose() awaits the result. This matches the manual
`/memory enqueue` slash command which was the only caller of the
consolidation pipeline.

Without the shutdown hook, episodic_memory, gists, consolidation_log,
graph_edges, and triples stayed empty for every deployment that never
typed `/memory enqueue|rebuild` — working memory accumulated forever
and long-term recall never formed.

Factored the consolidate step out of `mnemopiBackend.enqueue` into a
shared MnemopiSessionState#consolidate so the slash command and the
shutdown path share one implementation. Added two regression tests
verifying owned-bank consolidation order (flush -> sleep -> close, per
bank) and that aliased subagent dispose stays a no-op against the
parent.

Fixes #2320
2026-06-11 18:24:11 +00:00
DarkPhilosophy b98aea5043 fix(memory): tighten runtime and backend coverage 2026-06-10 08:31:32 +02:00
can1357 8583421362 perf(coding-agent): deferred heavy imports until first feature use
- Added lazy async module loaders for @babel/parser, linkedom, puppeteer/browsers, @mozilla/readability, @xterm/headless, and mnemopi to avoid loading them during cold startup.
- Added an interactive startup splash before session construction and skipped it for resume/fork/continue, quiet mode, timing mode, or non-TTY runs.
- Updated JS import-rewrite and memory tests to match async parser loading and preloaded mnemopi modules for sync state helpers.
2026-06-10 03:57:32 +02:00
roboomp 34005e6656 fix(coding-agent/hindsight): reacted to live bank scope changes and flushed before dispose
Mid-session edits to hindsight.bankId / bankIdPrefix / scoping kept the
active HindsightSessionState pinned to the bank selected at session
start, so retain/recall/reflect calls landed in the stale bank. Settings
hooks now fire onHindsightScopeChanged; the backend rebuilds the
primary state against the recomputed scope, disposing the previous one
after flushing its queue so queued tool-initiated retains still land in
the bank they were enqueued for.

Also:
- Renamed ensureBankMission to ensureBankExists. The old version
  skipped creation entirely when bankMission was blank, so the first
  mental-model POST (auto-seed) could land against a never-PUT bank.
  Bank creation is now idempotent and unconditional, and runs before
  mental-model bootstrap.
- Fixed AgentSession.dispose to flush the retain queue BEFORE clearing
  the session state pointer. Reversed, HindsightRetainQueue.#doFlush's
  identity guard would see the cleared pointer and drop the spliced
  batch with a 'session vanished' warning.
- Snapshotted hindsightScopeCallbacks before iterating because each
  rebuild subscribes a fresh callback inside the same fire; iterating
  the live Set would spin.

Fixes #1902
2026-06-05 05:25:45 +00:00
roboomp 66b6597829 fix(mnemopi): populated memory_embeddings on remember and auto-derived queryEmbedding on recall
The beam backend never invoked the embedding pipeline during normal
operation: `remember()`/`rememberBatch()`/`updateWorking()` skipped `embed()`
entirely and `recall()`/`recallEnhanced()` never called `embedQuery()` on
the query text. As a result `memory_embeddings` stayed empty in every
deployment and recall silently degraded to FTS-only regardless of the
configured provider (fastembed, OpenAI-compatible API, custom).

- Added `scheduleEmbedding` on `beam.pendingExtractions` (mirroring
  `scheduleFactExtraction`) and wired it from `remember`, `rememberBatch`,
  `updateWorking`, and `consolidateToEpisodic`. Writes
  `INSERT OR REPLACE INTO memory_embeddings(memory_id, embedding_json, model)`
  with the active runtime-options model, captured before the AsyncLocalStorage
  scope exits and re-entered inside the task.
- Auto-derived `queryEmbedding` inside `recall()` via `embedQuery(query)` when
  the caller did not pass one. `queryEmbedding: null` is preserved as the
  explicit FTS-only opt-out; `undefined` triggers auto-derive.
- Propagated `queryEmbedding` through `Mnemopi`'s `toRecallOptions` so the
  facade no longer strips the override on the way to the beam layer.
- Made `Mnemopi.recall`/`recallEnhanced`/`search`/`query`, the module-level
  exports, `BeamMemory.recall`/`recallEnhanced`, the free `recall`/`recallEnhanced`,
  and `orchestrateRecall` async. MCP `handleToolCall`/`callToolJson`/`handleJsonRpc`
  follow suit so the recall handler can await.
- Fixed `withBeam`/`withSharedBeam` to defer `beam.close()` until the async
  handler resolves; otherwise the new async recall hit
  `RangeError: Cannot use a closed database`.
- Updated CLI, MCP entrypoints, coding-agent `MnemopiSessionState`, and every
  affected test to await the new shapes.

Verified with a new regression suite (`test/issue-1832-embedding-population.test.ts`)
exercising both ends of the bug: empty `memory_embeddings` and zero
`dense_score`.

Fixes #1832
2026-06-04 09:22:07 +00:00
can1357 68430dee5c chore: renamed mnemosyne package to mnemopi
- Updated package name, directory, and binary from mnemosyne to mnemopi.
- Updated all lockfile references and workspace paths accordingly.
2026-05-31 08:45:12 +02:00
can1357 6d5aba8397 feat: implemented Mnemosyne memory state typing for prompt generation
- Resolved memory backend before instruction assembly and used it to build developer instructions.
- Added a `memoryRootEnabled` prompt option when the memory backend id is `local`.
- Typed Mnemosyne memory state types and updated `registerMnemosyneState()` to call `setMnemosyneSessionState()`.
- Documented that `@oh-my-pi/pi-mnemosyne/diagnose` export was added in the mnemosyne changelog.
2026-05-30 16:55:53 +02:00
can1357 6b4accd05e feat(memory): added memory_edit tool and stats/diagnose commands
- Added `memory_edit` tool for update, forget, and invalidate operations on Mnemosyne memories by id.
- Added `stats` and `diagnose` methods to `MemoryBackend` interface with Mnemosyne implementations.
- Exposed `/memory stats` and `/memory diagnose` slash commands in TUI and ACP modes.
- Refactored recall output to include memory ids via `formatScopedRecallWithIds`.
2026-05-30 16:48:02 +02:00
can1357 515887fc31 test(testing): added Mnemosyne lifecycle and render-call suppression tests
- Added tests for `MnemosyneSessionState` lifecycle, including turn-based auto-retain.
- Added tests covering scoped Mnemosyne database clearing and per-project bank derivation.
- Added a task renderer test to suppress per-task previews when `renderContext.hasResult` is true.
2026-05-30 16:22:12 +02:00
can1357 395dddbc69 feat(mnemosyne): added fallback recall query for shared bank in per-project-tagged mode
- Strips project-bank tokens from the query before a second recall pass on the global bank.
- Prevents broad user-preference memories from being missed when the query is packed with project-specific tokens.
- Added helper functions for bank name tokenization, phrase stripping, and query normalization.
2026-05-30 14:47:54 +02:00
can1357 6618bfb4f4 refactor(tools): renamed Hindsight tools to Memory and fixed per-bank db paths
- Renamed HindsightRecall/Reflect/RetainTool classes and files to Memory* for backend-neutral naming.
- Fixed Mnemosyne state to resolve separate db paths per bank instead of sharing one file.
- Updated test file and all imports to reflect the new names.
2026-05-30 14:47:54 +02:00