Skip to content

memory-core: native context-service search seam (retire dist-patch) - #3

Merged
pavel-satalkin merged 1 commit into
doctorina/base-v2026.6.6from
doctorina/memory-core-context-service-seam
Sep 8, 2026
Merged

pavel-satalkin merged 1 commit into
doctorina/base-v2026.6.6from
doctorina/memory-core-context-service-seam

Conversation

@fred-doctorina

Copy link
Copy Markdown
Collaborator

What

Port the context-service search seam natively into fork source, replacing the runtime dist-patch docker/patches/patch-memory-context-service.py (in doctorina-fred-bot). This is a step toward a zero-dist-patch production image.

Why

On this deployment the native OpenClaw memory search is dead:

  • vector provider = none (sqlite-vec disabled / degraded), and
  • fts5 is stripped from the official Node build (no such module: fts5).

So MemoryIndexManager.search() returns no memory-corpus hits. A separate context-service (Postgres full-text now, vector later) is live at http://context-service:8090 and holds the ingested memory corpus (MEMORY.md + memory/*.md). It exposes POST /getContext {query, k, mode} -> {hits:[{text, source, line, score, ...}]}.

Until now that routing was injected at image build time by a dist-patch. This PR moves it into fork source.

Change

extensions/memory-core/src/memory/manager.ts:

  • New method MemoryIndexManager.__fredContextServiceSearch(query, opts) placed immediately before search().
  • A short-circuit at the very top of search() (right after opts?.onDebug?.({ backend: "builtin" })): try the seam; if it returns an array, return it; otherwise fall through to the unchanged native body.

Seam behavior (matches the dist-patch semantics exactly, now typed against MemorySearchResult[]):

  • returns undefined if global fetch is not a function;
  • returns undefined if opts.sources is a non-empty array that does not include "memory" (sessions-only / other filters fall through to native);
  • base URL = process.env.FRED_CONTEXT_SERVICE_URL || "http://context-service:8090" (trailing slashes stripped);
  • k = opts.maxResults > 0 ? opts.maxResults : 8;
  • POST ${base}/getContext with {query, k, mode: "hybrid"}, content-type: application/json, AbortSignal.timeout(800);
  • returns undefined on no response / non-2xx;
  • maps each hit into a MemorySearchResult (id, source: "memory", path, startLine, endLine, score, textScore, snippet, text) with safe defaults, then slices to the limit;
  • wrapped in try/catch returning undefined -> never throws.

Compatibility

  • Additive and backward-compatible. On any error, missing fetch, non-memory source filter, or non-2xx response the seam returns undefined and execution falls through to the existing native body, so behavior is never worse than today.
  • The retiring dist-patch is idempotent and keys off the marker method name __fredContextServiceSearch; once this method exists in the built dist the patch is a no-op.

Tests

New hermetic vitest suite extensions/memory-core/src/memory/manager.context-service-seam.test.ts (mocks global fetch, no network) covering:

  • fetch success -> hits mapped into MemorySearchResult[], request shape verified, and search() returns the seam result (native path bypassed);
  • non-memory source filter -> undefined (fetch not called);
  • non-2xx and thrown fetch -> undefined (never throws);
  • missing global fetch -> undefined;
  • default FRED_CONTEXT_SERVICE_URL + trailing-slash strip.

Verified locally:

  • pnpm tsgo:extensions (source typecheck): pass
  • pnpm tsgo:extensions:test (test typecheck): pass
  • vitest run (memory extension config), seam suite: 10/10 pass
  • oxlint on both changed files: 0 errors

Follow-up (separate doctorina-fred-bot PR, not in this PR)

  • Remove the 4 dead sqlite-vec patches + the context-service patch RUN steps from the Dockerfile.
  • Bump the fork pin to include this commit.

Add MemoryIndexManager.__fredContextServiceSearch and a short-circuit at
the top of search() that routes memory-corpus searches to the external
context-service HTTP API. Ports docker/patches/patch-memory-context-service.py
into fork source. Additive and backward-compatible; the seam returns
undefined (falls through to native) on any error, non-memory source
filter, missing fetch, or non-2xx response, and never throws.

Includes a hermetic vitest suite covering hit mapping, source filtering,
and fall-through/no-throw behavior.
@pavel-satalkin
pavel-satalkin merged commit a6124a3 into doctorina/base-v2026.6.6 Sep 8, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants