feat(memory): validate official clients on current stack - #87
Conversation
tangletools
left a comment
There was a problem hiding this comment.
✅ Auto-approved drewstone PR — 528816e7
This PR was opened by the trusted drewstone account.
The full PR reviewer audit still runs separately and will publish findings if it detects issues.
tangletools · auto-approval · reason: drewstone_author · 2026-07-27T05:24:37Z
tangletools
left a comment
There was a problem hiding this comment.
🟡 Value Audit — sound-with-nits
| Verdict | sound-with-nits |
| Concerns | 1 (1 weak-concern) |
| Heuristic | 0.0s |
| Duplication | 0.0s |
| Interrogation | 126.9s (2 bridge agents) |
| Total | 126.9s |
💰 Value — sound-with-nits
Splits the loose Mem0 client type into mode-specific structural interfaces backed by a CI-compiled contract check and a real SQLite lifecycle test — a genuine, in-grain upgrade to type safety and validation, with one minor test-fixture duplication.
- What it does: Replaces the single loose Mem0ClientLike (Record<string,unknown> options) with two hand-mirrored structural interfaces — Mem0HostedClient and Mem0OssClient (src/memory/mem0.ts:36-72) — and turns Mem0MemoryAdapterOptions into a discriminated union on
mode(mem0.ts:86-100), so appId/latestOnly are compile-time forbidden in OSS mode (appId?: never) and the OSS-onlyfilterson add is reflected in - Goals it achieves: (1) Guarantee the adapter's structural client types cannot silently drift from the official Mem0/Neo4j clients on dependency bumps — the prior 'is type-compatible' test (tests/memory/mem0.test.ts, removed) was a
null as unknown as MemoryClientcast inside tests/ (excluded from tsc.json), so it verified nothing underpnpm typecheckand vitest's esbuild doesn't type-check; the new contract file - Assessment: Good change, firmly in the grain of the codebase. The runtime has always branched on mode; the discriminated union lifts those branches to the type system. The structural-mirror approach (not importing mem0ai types into public src) is correct because mem0ai is a devDependency/optional peer — importing MemoryClient into the public types would either force mem0ai into dependencies or yield unresolva
- Better / existing approach: Considered importing MemoryClient/Memory directly from mem0ai instead of hand-mirroring — rejected: mem0ai is a devDep/optional peer, so importing its types into the published src would force a hard runtime dependency or unresolvable consumer types; structural interface + separate contract compile is the standard optional-peer pattern and the right call. Checked for an existing equivalent: the Neo
- Model: opencode/zai-coding-plan/glm-5.2
- Bridge attempts: 2
- Bridge warning: opencode/kimi-for-coding/k2p7: bridge stream ended without value-audit content
🎯 Usefulness — sound
Tightens the Mem0 adapter's type contract into a mode-discriminated union that matches the adapter's existing runtime branching, guards it against upstream type drift via compile-only contract checks, and proves the OSS path with a real local-SQLite lifecycle test.
- Integration: The adapter is the public memory surface (src/memory/index.ts:17) and is consumed through the AgentMemoryAdapter interface by the eval improvement loops. The new contract file is reachable via the
typecheck:contractsscript (package.json); it is a compile-only type guard by design (no vitest.test.tsname, no runtime caller) — exercised by tsc, not dead. The integration test is a normal vitest - Fit with existing patterns: Strong fit. The adapter already branched on
options.modeeverywhere (mem0.ts:117-124, 139, 189, 194); the discriminated union (Mem0HostedMemoryAdapterOptions | Mem0OssMemoryAdapterOptions, mem0.ts:86-100) makes the type system match that existing runtime logic, andappId?: never/latestOnly?: neveron OSS forbid mode-inappropriate options at the type level. The compile-only contract-test - Real-world viability: The new integration test stands up a real
mem0ai/ossMemoryinstance backed by local SQLite for vectors+history and a stub OpenAI-compatible embedding server, exercising write→search→history→clear and asserting files are created then cleared — so the OSS path is proven end-to-end without external API calls. The hosted path keeps its existing mock-based tests plus the type contract. The union n - Model: opencode/zai-coding-plan/glm-5.2
- Bridge attempts: 1
💰 Value Audit
🟡 Local HTTP-server bind/close fixture duplicated across two integration tests [duplication] ``
tests/memory/mem0-oss.integration.test.ts:88-135 (startEmbeddingServer) and tests/official-optimization.integration.test.ts:283-319 (startModelServer) share the same skeleton: createServer(handler) → listen(0,'127.0.0.1') → guard server.address() → return
http://127.0.0.1:${port}/v1(+ close path). Only the response payload differs (embeddings vs chat-completions). A small shared helper like bindLocalOpenAiServer(handler) in tests/support/ would factor out ~25 duplicated lines and keep the bin
What this audit checks
It judges the change on its merits — not whether it was tasked out in an issue. Unticketed, fast-moving work is fine; the question is whether the change is good and whether a better or existing approach should be used instead.
| Pass | What it asks |
|---|---|
| Heuristic | Vague title? Whitespace-only or cruft-bearing diff? (content signals only) |
| Duplication | Do added function/class names already exist elsewhere in the repo? |
| Value Audit | What does it do? What goal does it achieve? Is it good? Better architecture or already-exists? |
| Usefulness Audit | Does it integrate and fit? Will it hold up in real use and actually get used? |
Findings are concerns, not blocks — the human reviewer decides what to do with them.
tangletools
left a comment
There was a problem hiding this comment.
✅ Auto-approved drewstone PR — 9f821e0e
This PR was opened by the trusted drewstone account.
The full PR reviewer audit still runs separately and will publish findings if it detects issues.
tangletools · auto-approval · reason: drewstone_author · 2026-07-27T05:48:03Z
tangletools
left a comment
There was a problem hiding this comment.
🟢 Value Audit — sound
| Verdict | sound |
| Concerns | 0 (none) |
| Heuristic | 0.0s |
| Duplication | 0.0s |
| Interrogation | 112.1s (2 bridge agents) |
| Total | 112.1s |
💰 Value — sound
Splits the loose Mem0 client type into strict hosted/OSS contracts and adds a compile-time compatibility check plus a real SQLite lifecycle test against the official Mem0 OSS package — a coherent hardening release that extends the repo's existing release-gate pattern.
- What it does: Replaces the single permissive Mem0ClientLike (Record<string,unknown>-options blob) with two mode-specific interfaces — Mem0HostedClient and Mem0OssClient — and turns the adapter options into a discriminated union on
mode, so hosted-only fields (appId, latestOnly, page/pageSize) are structurally excluded from OSS (src/memory/mem0.ts:36-100). Adds tests/contracts/official-memory-clients.ts:1-24, - Goals it achieves: (1) Prove the adapter's hand-rolled client interfaces actually match the real upstream Mem0/Neo4j types — the old Record<string,unknown> options would silently accept any upstream shape change. (2) Exercise the real Mem0 OSS package end-to-end through the adapter boundary so regressions in add/search/history/clear surface in CI instead of in user code. (3) Move to the current toolchain (TypeScript
- Assessment: Good change on its merits. The hosted/OSS split is correct, not cosmetic: the two Mem0 clients genuinely differ (hosted has appId/latestOnly/page/pageSize; OSS passes filters on add and topK on getAll), and the adapter already branches on options.mode in search/write/clear (mem0.ts:139-141, 189, 263-275) — the discriminated union lifts those runtime branches into the type system. The contract file
- Better / existing approach: none — this is the right approach. Checked: no prior contract-test or publint/attw pattern exists in the repo to extend (grep'd tests/ and scripts/); this is the first and is well-formed. No existing strict Mem0 interface to reuse — the old Mem0ClientLike was strictly looser, so this is a refinement. The tests/contracts/ pattern (separate tsconfig, compile-only, not run by vitest) is the correct i
- Model: opencode/zai-coding-plan/glm-5.2
- Bridge attempts: 2
- Bridge warning: opencode/kimi-for-coding/k2p7: bridge stream ended without value-audit content
🎯 Usefulness — sound
Tightens the Mem0 adapter to mode-specific typed client contracts validated against the real SDK shapes, plus a real SQLite OSS lifecycle test and build-tooling modernization — all reachable and in the grain of the existing adapter pattern.
- Integration: The new typed interfaces (
Mem0HostedClient/Mem0OssClient) are exported from src/memory/index.ts:17 and re-exported through the package's./memorysubpath (package.json:32-36). The contract test at tests/contracts/official-memory-clients.ts:11-23 is reached viatypecheck:contracts(package.json:70), whichpnpm typecheckruns — so any drift in the officialmem0ai/mem0ai/oss/`@neo4j-lab - Fit with existing patterns: Matches the established provider-adapter pattern exactly: a peer to
createNeo4jAgentMemoryAdapter(src/memory/neo4j.ts:27) and the Graphiti adapter, all returning the sharedAgentMemoryAdaptershape. The discriminated union (Mem0HostedMemoryAdapterOptions | Mem0OssMemoryAdapterOptions, src/memory/mem0.ts:100) withappId?: never/latestOnly?: neveron OSS is the grain of the codebase — f - Real-world viability: The integration test covers the write→search→history→clear lifecycle on real SQLite files and asserts the embedding server only received
/v1/embeddings(no hidden network fan-out). The adapter's runtime already handles the hard paths the typed contract exposes: delayed-delete visibility polling (src/memory/mem0.ts:258-377), hostedaddresponse-shape validation (src/memory/mem0.ts:194-198), uns - Model: opencode/zai-coding-plan/glm-5.2
- Bridge attempts: 1
No concerns — sound change, no better or existing approach found. ✅
What this audit checks
It judges the change on its merits — not whether it was tasked out in an issue. Unticketed, fast-moving work is fine; the question is whether the change is good and whether a better or existing approach should be used instead.
| Pass | What it asks |
|---|---|
| Heuristic | Vague title? Whitespace-only or cruft-bearing diff? (content signals only) |
| Duplication | Do added function/class names already exist elsewhere in the repo? |
| Value Audit | What does it do? What goal does it achieve? Is it good? Better architecture or already-exists? |
| Usefulness Audit | Does it integrate and fit? Will it hold up in real use and actually get used? |
Findings are concerns, not blocks — the human reviewer decides what to do with them.
tangletools
left a comment
There was a problem hiding this comment.
✅ Auto-approved drewstone PR — ad98b906
This PR was opened by the trusted drewstone account.
The full PR reviewer audit still runs separately and will publish findings if it detects issues.
tangletools · auto-approval · reason: drewstone_author · 2026-07-27T06:07:50Z
tangletools
left a comment
There was a problem hiding this comment.
🟢 Value Audit — sound
| Verdict | sound |
| Concerns | 0 (none) |
| Heuristic | 0.0s |
| Duplication | 0.0s |
| Interrogation | 109.3s (2 bridge agents) |
| Total | 109.3s |
💰 Value — sound
Tightens the Mem0 adapter to mode-specific typed contracts (hosted vs OSS) and proves them against the official SDK packages with a compile-time contract file plus a real local-SQLite integration test; also refreshes the build toolchain to currently-maintained versions.
- What it does: Replaces the single loose Mem0ClientLike (options typed as Record<string, unknown>) with two sharply-typed interfaces, Mem0HostedClient and Mem0OssClient, that mirror the actual shapes of mem0ai's MemoryClient and mem0ai/oss's Memory. Turns Mem0MemoryAdapterOptions into a discriminated union over mode, so appId/latestOnly are accepted only for hosted and forbidden (via
: never) for OSS. Adds tes - Goals it achieves: (1) Make silent contract drift between this adapter and upstream Mem0/Neo4j SDKs impossible to ship: the contract file forces the adapter's expected client types to compile against the real shipped package types, and the integration test exercises the real Memory class end-to-end. (2) Push mode-specific options (appId, latestOnly) from runtime convention into compile-time enforcement via the discr
- Assessment: Good change, built in the grain of the codebase. The prior mem0.ts used a deliberately loose Mem0ClientLike with
options?: Record<string, unknown>everywhere; the new code sharpens exactly those boundaries while leaving the adapter's actual runtime behavior (search/write/clear, pending-write probes, scoped-delete verification) untouched. It is strictly more type-safe than the Neo4j adapter in sr - Better / existing approach: none — this is the right approach. Searched for an existing contract-proof or type-compat pattern (grep for Mem0ClientLike, Mem0HostedClient, Mem0OssClient across src/ and tests/; checked tests/contracts/ for prior art; reviewed src/memory/neo4j.ts for the adjacent adapter's typing style). The contract file is the first of its kind here, the loose Mem0ClientLike is fully removed with no duplicatio
- Model: opencode/zai-coding-plan/glm-5.2
- Bridge attempts: 2
- Bridge warning: opencode/kimi-for-coding/k2p7: bridge stream ended without value-audit content
🎯 Usefulness — sound
Tightens the Mem0 adapter's client contract to match official mem0ai hosted/OSS types and adds a real SQLite-backed integration test plus a CI-gated type-level contract check; fully reachable through the existing AgentMemoryAdapter pipeline.
- Integration: The adapter returns AgentMemoryAdapter (src/memory/mem0.ts:110), which is the type consumed throughout the memory pipeline: branch composition (src/memory/branch.ts:37,45), experiment cells (src/memory/experiment/cell.ts:106), recovery (src/memory/experiment/recovery.ts:206), benchmarks (src/benchmarks/memory-runner.ts:215), and the improvement loop (src/memory/improvement/run.ts:47). The new cont
- Fit with existing patterns: Fits the established adapter pattern cleanly. The Neo4j adapter already exposes a structural client shape (src/memory/neo4j.ts:16
client: object); this PR moves Mem0 from a single looseMem0ClientLiketo mode-specific structural types that mirror the two official clients, and a discriminated union onmodethat matches every place the adapter already branches on mode (src/memory/mem0.ts:139,1 - Real-world viability: Beyond the happy path: the new integration test (tests/memory/mem0-oss.integration.test.ts) stands up a local embedding HTTP server, writes to real SQLite vector + history DBs, asserts file sizes, exercises search/history/clear, and pins that only
/v1/embeddingsis hit (no external calls) — so it guards the actual OSS lifecycle, not a mock. The adapter's pre-existing error paths (unscoped clear - Model: opencode/zai-coding-plan/glm-5.2
- Bridge attempts: 1
No concerns — sound change, no better or existing approach found. ✅
What this audit checks
It judges the change on its merits — not whether it was tasked out in an issue. Unticketed, fast-moving work is fine; the question is whether the change is good and whether a better or existing approach should be used instead.
| Pass | What it asks |
|---|---|
| Heuristic | Vague title? Whitespace-only or cruft-bearing diff? (content signals only) |
| Duplication | Do added function/class names already exist elsewhere in the repo? |
| Value Audit | What does it do? What goal does it achieve? Is it good? Better architecture or already-exists? |
| Usefulness Audit | Does it integrate and fit? Will it hold up in real use and actually get used? |
Findings are concerns, not blocks — the human reviewer decides what to do with them.
Summary
@tangle-network/agent-knowledge@6.1.0on Eval0.130.1and Interface0.35.03.1.2, TypeScript to7.0.2, GitHub Actions, and replace tsup with tsdownProof
Upstream limits