engrm 0.1.0
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/.mcp.json +9 -0
- package/AUTH-DESIGN.md +436 -0
- package/BRIEF.md +197 -0
- package/CLAUDE.md +44 -0
- package/COMPETITIVE.md +174 -0
- package/CONTEXT-OPTIMIZATION.md +305 -0
- package/INFRASTRUCTURE.md +252 -0
- package/LICENSE +105 -0
- package/MARKET.md +230 -0
- package/PLAN.md +278 -0
- package/README.md +121 -0
- package/SENTINEL.md +293 -0
- package/SERVER-API-PLAN.md +553 -0
- package/SPEC.md +843 -0
- package/SWOT.md +148 -0
- package/SYNC-ARCHITECTURE.md +294 -0
- package/VIBE-CODER-STRATEGY.md +250 -0
- package/bun.lock +375 -0
- package/hooks/post-tool-use.ts +144 -0
- package/hooks/session-start.ts +64 -0
- package/hooks/stop.ts +131 -0
- package/mem-page.html +1305 -0
- package/package.json +30 -0
- package/src/capture/dedup.test.ts +103 -0
- package/src/capture/dedup.ts +76 -0
- package/src/capture/extractor.test.ts +245 -0
- package/src/capture/extractor.ts +330 -0
- package/src/capture/quality.test.ts +168 -0
- package/src/capture/quality.ts +104 -0
- package/src/capture/retrospective.test.ts +115 -0
- package/src/capture/retrospective.ts +121 -0
- package/src/capture/scanner.test.ts +131 -0
- package/src/capture/scanner.ts +100 -0
- package/src/capture/scrubber.test.ts +144 -0
- package/src/capture/scrubber.ts +181 -0
- package/src/cli.ts +517 -0
- package/src/config.ts +238 -0
- package/src/context/inject.test.ts +940 -0
- package/src/context/inject.ts +382 -0
- package/src/embeddings/backfill.ts +50 -0
- package/src/embeddings/embedder.test.ts +76 -0
- package/src/embeddings/embedder.ts +139 -0
- package/src/lifecycle/aging.test.ts +103 -0
- package/src/lifecycle/aging.ts +36 -0
- package/src/lifecycle/compaction.test.ts +264 -0
- package/src/lifecycle/compaction.ts +190 -0
- package/src/lifecycle/purge.test.ts +100 -0
- package/src/lifecycle/purge.ts +37 -0
- package/src/lifecycle/scheduler.test.ts +120 -0
- package/src/lifecycle/scheduler.ts +101 -0
- package/src/provisioning/browser-auth.ts +172 -0
- package/src/provisioning/provision.test.ts +198 -0
- package/src/provisioning/provision.ts +94 -0
- package/src/register.test.ts +167 -0
- package/src/register.ts +178 -0
- package/src/server.ts +436 -0
- package/src/storage/migrations.test.ts +244 -0
- package/src/storage/migrations.ts +261 -0
- package/src/storage/outbox.test.ts +229 -0
- package/src/storage/outbox.ts +131 -0
- package/src/storage/projects.test.ts +137 -0
- package/src/storage/projects.ts +184 -0
- package/src/storage/sqlite.test.ts +798 -0
- package/src/storage/sqlite.ts +934 -0
- package/src/storage/vec.test.ts +198 -0
- package/src/sync/auth.test.ts +76 -0
- package/src/sync/auth.ts +68 -0
- package/src/sync/client.ts +183 -0
- package/src/sync/engine.test.ts +94 -0
- package/src/sync/engine.ts +127 -0
- package/src/sync/pull.test.ts +279 -0
- package/src/sync/pull.ts +170 -0
- package/src/sync/push.test.ts +117 -0
- package/src/sync/push.ts +230 -0
- package/src/tools/get.ts +34 -0
- package/src/tools/pin.ts +47 -0
- package/src/tools/save.test.ts +301 -0
- package/src/tools/save.ts +231 -0
- package/src/tools/search.test.ts +69 -0
- package/src/tools/search.ts +181 -0
- package/src/tools/timeline.ts +64 -0
- package/tsconfig.json +22 -0
package/SWOT.md
ADDED
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
# SWOT Analysis — Engrm
|
|
2
|
+
|
|
3
|
+
## Strengths
|
|
4
|
+
|
|
5
|
+
### S1: Own the Full Stack
|
|
6
|
+
We control both the plugin (Engrm) and the backend (Candengo Vector). No dependency on third-party vector DB pricing, availability, or API changes. We can optimise the entire pipeline end-to-end.
|
|
7
|
+
|
|
8
|
+
### S2: Production-Grade Vector Search
|
|
9
|
+
Candengo Vector uses BGE-M3 (state-of-the-art multilingual embeddings) with hybrid dense+sparse search and optional cross-encoder reranking. This is measurably superior to ChromaDB's default embeddings. Better search = more relevant memories surfaced = better developer experience.
|
|
10
|
+
|
|
11
|
+
### S3: Self-Hosted / Privacy-First
|
|
12
|
+
Developers and enterprises increasingly resist sending code context to third-party clouds. Candengo Vector can be self-hosted on-premise. No code leaves the organisation's infrastructure.
|
|
13
|
+
|
|
14
|
+
### S4: Offline-First Architecture
|
|
15
|
+
SQLite as local source of truth with sync outbox means the system works perfectly without internet. This is critical for developers who work on trains, planes, coffee shops, or behind corporate firewalls.
|
|
16
|
+
|
|
17
|
+
### S5: Multi-Tenant from Day One
|
|
18
|
+
Candengo Vector's site_id/namespace model was built for multi-tenancy. Team memory, per-project isolation, and cross-project discovery are architectural features, not afterthoughts.
|
|
19
|
+
|
|
20
|
+
### S6: Workpack Ecosystem
|
|
21
|
+
No competitor has curated knowledge packs. Workpacks transform the product from "memory" to "memory + expertise" — a significantly more valuable proposition.
|
|
22
|
+
|
|
23
|
+
### S7: Existing Infrastructure
|
|
24
|
+
Candengo Vector is already running in production with ingest, search, query, and admin APIs. We're building on proven infrastructure, not starting from scratch.
|
|
25
|
+
|
|
26
|
+
### S8: Market Timing
|
|
27
|
+
The AI coding agent market is in explosive growth (Claude Code, OpenClaw 100k+ stars, Cursor, Windsurf). Memory is the #1 requested feature across all agent communities. First mover with a cross-device, cross-agent solution captures the category.
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## Weaknesses
|
|
32
|
+
|
|
33
|
+
### W1: No Existing User Base for Memory Products
|
|
34
|
+
Candengo's current customers use it for domain knowledge RAG (hospitality, business intelligence). Developer tooling is a new market for us — different buyer persona, different distribution channels.
|
|
35
|
+
|
|
36
|
+
### W2: MCP Ecosystem Immaturity
|
|
37
|
+
The Model Context Protocol is young. OpenClaw's MCP support is still Phase 0 (merged Feb 2026). Breaking changes to MCP could require rework. Agent-specific plugin formats may fragment the market.
|
|
38
|
+
|
|
39
|
+
### W3: Small Team
|
|
40
|
+
Building and maintaining plugins for multiple agents (Claude Code, OpenClaw, future agents) requires ongoing effort across different ecosystems, languages (TypeScript, Python), and release cycles.
|
|
41
|
+
|
|
42
|
+
### W4: Developer Tooling Distribution
|
|
43
|
+
Reaching developers requires different go-to-market than enterprise SaaS. GitHub presence, developer relations, documentation quality, and community engagement are table stakes. This is a capability we need to build.
|
|
44
|
+
|
|
45
|
+
### W5: Observation Quality Dependency
|
|
46
|
+
The value of shared memory depends on the quality of captured observations. If the extraction produces noisy, redundant, or inaccurate observations, shared memory amplifies noise rather than signal. Quality filtering is essential but hard to get right.
|
|
47
|
+
|
|
48
|
+
### W6: Candengo Vector Search Limitations
|
|
49
|
+
Currently no metadata field filtering in the search API (only site_id, namespace, source_type). This limits query flexibility. Needs to be added before launch.
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
## Opportunities
|
|
54
|
+
|
|
55
|
+
### O1: Cross-Agent Memory Standard
|
|
56
|
+
If we establish Engrm as the de facto shared memory layer, we become the "Twilio of agent memory" — the infrastructure everyone builds on. The MCP standard makes this possible by providing a common interface.
|
|
57
|
+
|
|
58
|
+
### O2: Workpack Marketplace Revenue
|
|
59
|
+
Curated knowledge packs (security, testing, framework patterns, compliance) are recurring revenue with high margins. Enterprise customers will pay for proprietary workpacks and custom knowledge curation.
|
|
60
|
+
|
|
61
|
+
### O3: Auto-Generated Workpacks
|
|
62
|
+
With enough observations across users (anonymised, opt-in), we can detect cross-cutting patterns and automatically generate workpacks. "90% of FastAPI projects hit this CORS issue" → auto-curated pattern. This creates a data flywheel that competitors can't replicate.
|
|
63
|
+
|
|
64
|
+
### O4: Team Analytics Dashboard
|
|
65
|
+
Visibility into team knowledge patterns: "What are the most common issues?" "Which areas have knowledge gaps?" "How is AI-assisted productivity trending?" This is valuable to engineering managers and CTOs.
|
|
66
|
+
|
|
67
|
+
### O5: Enterprise Compliance
|
|
68
|
+
SOC 2, GDPR, on-premise deployment. Enterprise engineering teams want AI tools but face compliance barriers. Self-hosted Engrm with audit logging and data residency controls addresses this directly.
|
|
69
|
+
|
|
70
|
+
### O6: Integration with Alchemy Platform
|
|
71
|
+
AIMY (our conversational AI) + Engrm creates a powerful combination: AIMY conducts project interviews, stores context in Candengo Vector, and all AI agents across the platform share that knowledge. End-to-end Alchemy integration is a unique selling point.
|
|
72
|
+
|
|
73
|
+
### O7: OpenClaw's Massive Community
|
|
74
|
+
OpenClaw has 100k+ GitHub stars and a rapidly growing community that's actively looking for memory solutions. Being an early, high-quality memory plugin for OpenClaw could drive massive adoption.
|
|
75
|
+
|
|
76
|
+
### O8: CI/CD and DevOps Integration
|
|
77
|
+
Beyond coding agents, the memory layer could capture observations from CI/CD pipelines, deployment logs, incident reports. "Last time this test failed, the fix was..." — extending memory beyond interactive sessions.
|
|
78
|
+
|
|
79
|
+
### O9: Language and Framework Workpacks
|
|
80
|
+
Partner with framework communities to create official workpacks: "Django Best Practices by Django Core Team", "React Patterns by Meta Engineers". Co-branded content drives distribution and credibility.
|
|
81
|
+
|
|
82
|
+
---
|
|
83
|
+
|
|
84
|
+
## Threats
|
|
85
|
+
|
|
86
|
+
### T1: Anthropic / OpenAI Building Native Memory
|
|
87
|
+
If Claude Code or ChatGPT build first-party cross-device memory, our plugin becomes redundant for those platforms. Mitigation: cross-agent support (no single-platform dependency), self-hosted differentiation, workpack ecosystem as defensible moat.
|
|
88
|
+
|
|
89
|
+
### T2: claude-mem Going Cross-Device
|
|
90
|
+
claude-mem already has remote Chroma support in its config. If the maintainer adds cross-device sync, our Claude Code differentiation weakens. Mitigation: superior search quality (BGE-M3 vs ChromaDB defaults), team features, and multi-agent support. Our clean-room build means no licensing entanglement regardless.
|
|
91
|
+
|
|
92
|
+
### T3: mem0 Capturing the Market
|
|
93
|
+
mem0 has funding, brand recognition, and multi-agent support. If they move fast on cross-device and self-hosted options, they could capture the market before us. Mitigation: self-hosted positioning (mem0 is SaaS-only), workpack ecosystem, developer trust through open-source plugin.
|
|
94
|
+
|
|
95
|
+
### T4: MCP Standard Fragmentation
|
|
96
|
+
If major agents abandon MCP for proprietary plugin formats, our "write once, work everywhere" value proposition breaks. Mitigation: the MCP standard has strong momentum (Anthropic backing, broad adoption), and we can always add agent-specific adapters.
|
|
97
|
+
|
|
98
|
+
### T5: Privacy Concerns with Shared Memory
|
|
99
|
+
Developers may resist syncing their code observations to any remote server, even self-hosted. Observations inevitably capture code patterns, file paths, and problem descriptions. Mitigation: secret scrubbing, sensitivity classification, clear data policies, and the self-hosted option.
|
|
100
|
+
|
|
101
|
+
### T6: Backend Substitution
|
|
102
|
+
Someone forks the plugin and swaps Candengo Vector for Pinecone/Weaviate/etc., offering a competing service. Mitigation: FSL-1.1-ALv2 license explicitly prohibits competing uses. Developers can self-host with Candengo Vector (encouraged), but cannot build a competing hosted product. Sentinel (the premium AI audit feature) is in a separate private repo, not published. The deep Candengo Vector integration (provisioning, account routing, hybrid search pipeline) also makes casual swaps non-trivial.
|
|
103
|
+
|
|
104
|
+
### T7: Quality at Scale
|
|
105
|
+
As team sizes grow, observation volume increases. Without good quality filtering, relevance decay, and deduplication, search results degrade. This is a hard problem that requires ongoing investment.
|
|
106
|
+
|
|
107
|
+
---
|
|
108
|
+
|
|
109
|
+
## Strategic Position Summary
|
|
110
|
+
|
|
111
|
+
```
|
|
112
|
+
HIGH IMPACT
|
|
113
|
+
│
|
|
114
|
+
┌─────────────┼─────────────┐
|
|
115
|
+
│ THREATS │ OPPORTUNITIES│
|
|
116
|
+
│ │ │
|
|
117
|
+
│ T1: Native │ O1: Memory │
|
|
118
|
+
│ platform │ standard │
|
|
119
|
+
│ memory │ │
|
|
120
|
+
│ │ O2: Workpack │
|
|
121
|
+
│ T3: mem0 │ marketplace │
|
|
122
|
+
│ momentum │ │
|
|
123
|
+
│ │ O7: OpenClaw │
|
|
124
|
+
│ │ community │
|
|
125
|
+
├─────────────┼──────────────┤
|
|
126
|
+
│ WEAKNESSES │ STRENGTHS │
|
|
127
|
+
│ │ │
|
|
128
|
+
│ W1: New │ S1: Full │
|
|
129
|
+
│ market │ stack │
|
|
130
|
+
│ │ │
|
|
131
|
+
│ W4: Dev │ S2: Superior │
|
|
132
|
+
│ distrib. │ search │
|
|
133
|
+
│ │ │
|
|
134
|
+
│ W3: Small │ S4: Offline │
|
|
135
|
+
│ team │ first │
|
|
136
|
+
│ │ │
|
|
137
|
+
└─────────────┼──────────────┘
|
|
138
|
+
│
|
|
139
|
+
LOW IMPACT
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
## Key Strategic Moves
|
|
143
|
+
|
|
144
|
+
1. **Lead with open-source plugin** → drives adoption, builds trust, creates community
|
|
145
|
+
2. **Differentiate on self-hosted + offline-first** → mem0 can't match this
|
|
146
|
+
3. **Workpacks as moat** → unique value that compounds over time
|
|
147
|
+
4. **OpenClaw community first** → 100k+ stars, actively seeking memory solutions
|
|
148
|
+
5. **Team features from day one** → enterprise upsell path built in
|
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
# Sync Architecture — Engrm
|
|
2
|
+
|
|
3
|
+
## Date: 2026-03-10
|
|
4
|
+
## Status: Reviewed & Confirmed
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Overview
|
|
9
|
+
|
|
10
|
+
Bidirectional sync between local SQLite instances and Candengo Vector, enabling cross-device and cross-agent team memory. Every device maintains a full local cache — search is always local-first and fast, even offline.
|
|
11
|
+
|
|
12
|
+
```
|
|
13
|
+
Device A (Claude Code) Device B (Codex CLI)
|
|
14
|
+
┌───────────────────┐ ┌───────────────────┐
|
|
15
|
+
│ SQLite (primary) │───push──→ │ SQLite (primary) │───push──→
|
|
16
|
+
│ FTS5 search │ │ │ FTS5 search │ │
|
|
17
|
+
│ offline-first │←──pull───┤ │ offline-first │←──pull───┤
|
|
18
|
+
└───────────────────┘ │ └───────────────────┘ │
|
|
19
|
+
▼ ▼
|
|
20
|
+
┌──────────────────────────┐
|
|
21
|
+
│ Candengo Vector │
|
|
22
|
+
│ + Sync Metadata Layer │
|
|
23
|
+
│ BGE-M3 embeddings │
|
|
24
|
+
│ hybrid search │
|
|
25
|
+
└──────────────────────────┘
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
## Confirmed Decisions
|
|
31
|
+
|
|
32
|
+
### 1. SQLite is Source of Truth Per-Device
|
|
33
|
+
|
|
34
|
+
**Status**: Confirmed ✓
|
|
35
|
+
|
|
36
|
+
- Each device has its own SQLite with WAL mode, FTS5 external content
|
|
37
|
+
- Offline-first: all operations work without network
|
|
38
|
+
- Vector downtime never blocks local operations
|
|
39
|
+
- Push failures queue in outbox with exponential backoff
|
|
40
|
+
|
|
41
|
+
### 2. Candengo Vector is the Sync Hub (Not Queried at Search Time)
|
|
42
|
+
|
|
43
|
+
**Status**: Confirmed with modification ✓
|
|
44
|
+
|
|
45
|
+
- Vector stores embeddings + metadata for team-wide semantic search (future Phase 4+)
|
|
46
|
+
- **Change**: Add a thin **sync metadata layer** rather than overloading Vector with sync-specific queries
|
|
47
|
+
- Sync metadata tracks: `(source_id, ingested_at_server, action, payload_json)`
|
|
48
|
+
- This decouples the sync protocol from the vector search engine
|
|
49
|
+
- Vector remains a search index; sync metadata is the replication log
|
|
50
|
+
|
|
51
|
+
**Rationale**: Vector databases are optimised for similarity search, not for "list all documents modified since X". A separate sync metadata table (in the Candengo web backend) is cleaner and more maintainable.
|
|
52
|
+
|
|
53
|
+
### 3. Same MCP Server for All Agents
|
|
54
|
+
|
|
55
|
+
**Status**: Confirmed ✓
|
|
56
|
+
|
|
57
|
+
Validated working with:
|
|
58
|
+
- Claude Code (`.mcp.json`, stdio) — **tested live**
|
|
59
|
+
- Codex CLI (`config.toml`, stdio) — **tested live, all 6 tools working**
|
|
60
|
+
- Cursor, Windsurf, Cline, VS Code Copilot, Zed (all MCP stdio compatible)
|
|
61
|
+
|
|
62
|
+
**Change**: Auto-detect agent from MCP `clientInfo` during `initialize` handshake instead of hardcoding `"claude-code"`. The protocol already exchanges this — we just need to read it.
|
|
63
|
+
|
|
64
|
+
**No agent-specific logic in the MCP server.** Hooks are agent-specific by nature (different agents have different hook systems), but the MCP server stays generic.
|
|
65
|
+
|
|
66
|
+
### 4. Token-Budgeted Context with Blended Scoring
|
|
67
|
+
|
|
68
|
+
**Status**: Confirmed with modifications ✓
|
|
69
|
+
|
|
70
|
+
- Token budget (default 800) — implemented Sprint 1
|
|
71
|
+
- Blended scoring: quality * 0.6 + recency * 0.4 — implemented Sprint 2
|
|
72
|
+
- Tiered output: top 3 detailed (facts-first), rest title-only — implemented Sprint 1
|
|
73
|
+
|
|
74
|
+
**Changes**:
|
|
75
|
+
- Make `token_budget` and quality/recency weights **configurable** in settings.json
|
|
76
|
+
- Cap pinned observations at **5 detailed** in context injection (prevent pinned observations consuming entire budget)
|
|
77
|
+
- CWD-aware boosting deferred to Phase 4 (dogfood)
|
|
78
|
+
|
|
79
|
+
### 5. Knowledge Supersession
|
|
80
|
+
|
|
81
|
+
**Status**: Confirmed with modification ✓
|
|
82
|
+
|
|
83
|
+
- `superseded_by` column, manual agent-initiated supersession — implemented Sprint 2
|
|
84
|
+
|
|
85
|
+
**Change**: Allow supersession **chains** (depth 2-3) instead of blocking already-superseded observations. When superseding, resolve to the current chain head automatically:
|
|
86
|
+
```
|
|
87
|
+
Agent says: "supersede #1"
|
|
88
|
+
#1 already superseded by #2
|
|
89
|
+
→ System resolves: supersede #2 (the chain head) instead
|
|
90
|
+
→ All previous versions (#1, #2) remain archived
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
**Change**: Add supersession events to the **sync outbox** — lifecycle changes (not just new observations) need to propagate to other devices.
|
|
94
|
+
|
|
95
|
+
**Keep manual**: Automatic supersession (detecting same topic) is too risky — title similarity could accidentally supersede unrelated observations. Revisit in Phase 4.
|
|
96
|
+
|
|
97
|
+
### 6. Bidirectional Sync Protocol
|
|
98
|
+
|
|
99
|
+
**Status**: Design confirmed ✓
|
|
100
|
+
|
|
101
|
+
See detailed design below.
|
|
102
|
+
|
|
103
|
+
---
|
|
104
|
+
|
|
105
|
+
## Push Protocol (Local → Vector)
|
|
106
|
+
|
|
107
|
+
Already designed in PLAN.md Phase 3.2. No changes.
|
|
108
|
+
|
|
109
|
+
```
|
|
110
|
+
1. Observation saved to local SQLite
|
|
111
|
+
2. Added to sync_outbox (status: pending)
|
|
112
|
+
3. Background timer (every 30s): flush pending outbox items
|
|
113
|
+
4. POST /v1/ingest with observation data + metadata
|
|
114
|
+
5. On success: mark outbox item as synced
|
|
115
|
+
6. On failure: exponential backoff (30s, 60s, 120s, max 5min)
|
|
116
|
+
7. Source ID: {user_id}-{device_id}-obs-{local_id}
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
**New**: Also push lifecycle changes (supersession, archival) to a sync change feed endpoint, not just new observations.
|
|
120
|
+
|
|
121
|
+
---
|
|
122
|
+
|
|
123
|
+
## Pull Protocol (Vector → Local)
|
|
124
|
+
|
|
125
|
+
### Change Feed Approach
|
|
126
|
+
|
|
127
|
+
**Do NOT use `/v1/search` for pull.** Search is "find relevant documents for a query." Pull is "give me all changes since cursor X." These are fundamentally different.
|
|
128
|
+
|
|
129
|
+
**Endpoint**: `GET /v1/sync/changes?cursor=X&namespace=Y&limit=1000`
|
|
130
|
+
|
|
131
|
+
Returns a stream of change events:
|
|
132
|
+
```json
|
|
133
|
+
{
|
|
134
|
+
"changes": [
|
|
135
|
+
{
|
|
136
|
+
"cursor": "2026-03-10T16:00:00.000Z",
|
|
137
|
+
"source_id": "david-macbook-obs-42",
|
|
138
|
+
"action": "create",
|
|
139
|
+
"payload": { /* full observation data as JSON */ }
|
|
140
|
+
},
|
|
141
|
+
{
|
|
142
|
+
"cursor": "2026-03-10T16:05:00.000Z",
|
|
143
|
+
"source_id": "david-macbook-obs-42",
|
|
144
|
+
"action": "supersede",
|
|
145
|
+
"superseded_by_source_id": "david-macbook-obs-55"
|
|
146
|
+
}
|
|
147
|
+
],
|
|
148
|
+
"next_cursor": "2026-03-10T16:05:00.000Z",
|
|
149
|
+
"has_more": false
|
|
150
|
+
}
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
### Critical: Server-Side Cursor, Not Client Timestamps
|
|
154
|
+
|
|
155
|
+
**Do NOT use client-side `created_at_epoch` as the high-water-mark.** Clock skew between devices means observations can arrive at Vector with misleading timestamps.
|
|
156
|
+
|
|
157
|
+
Use **server-assigned ingestion timestamp** (`cursor`) as the ordering key. This is monotonic and eliminates clock skew issues.
|
|
158
|
+
|
|
159
|
+
### Pull Flow
|
|
160
|
+
|
|
161
|
+
```
|
|
162
|
+
1. On startup: read last_pull_cursor from sync_state table
|
|
163
|
+
2. GET /v1/sync/changes?cursor=last_pull_cursor&namespace=N&limit=1000
|
|
164
|
+
3. For each change:
|
|
165
|
+
a. Parse source_id → extract user_id, device_id, local_id
|
|
166
|
+
b. Skip if source_id matches current device (it's our own data)
|
|
167
|
+
c. If action=create:
|
|
168
|
+
- Check remote_observations table for existing source_id
|
|
169
|
+
- If exists: update (in case of re-push after edit)
|
|
170
|
+
- If new: insert into observations + FTS5 index
|
|
171
|
+
- Record mapping in remote_observations table
|
|
172
|
+
d. If action=supersede:
|
|
173
|
+
- Look up local_id for superseded source_id
|
|
174
|
+
- Apply supersession locally (archive, remove from FTS)
|
|
175
|
+
e. If action=delete:
|
|
176
|
+
- Look up and remove locally
|
|
177
|
+
4. Update last_pull_cursor in sync_state
|
|
178
|
+
5. If has_more: repeat from step 2 (pagination)
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
### Pull Frequency
|
|
182
|
+
|
|
183
|
+
- **On startup**: Paginated backfill (1000 at a time, non-blocking)
|
|
184
|
+
- **Periodic**: Every 60 seconds (less frequent than push at 30s — pull is less latency-sensitive)
|
|
185
|
+
- **On-demand**: Not needed initially
|
|
186
|
+
|
|
187
|
+
### First Install Backfill
|
|
188
|
+
|
|
189
|
+
New device joining a team should NOT slam all observations into SQLite at once. Instead:
|
|
190
|
+
- Pull in pages of 1000, oldest first
|
|
191
|
+
- Update cursor after each page
|
|
192
|
+
- First session can start immediately with whatever has been pulled so far
|
|
193
|
+
- Background continues pulling remaining pages
|
|
194
|
+
|
|
195
|
+
### Pull Scope
|
|
196
|
+
|
|
197
|
+
Pull **ALL projects** for the user's namespace, not just the current CWD project. Reason: switching directories shouldn't require a re-pull. The data is small and lifecycle management keeps it bounded.
|
|
198
|
+
|
|
199
|
+
---
|
|
200
|
+
|
|
201
|
+
## New Schema: Remote Observations Mapping
|
|
202
|
+
|
|
203
|
+
Migration v3:
|
|
204
|
+
|
|
205
|
+
```sql
|
|
206
|
+
CREATE TABLE remote_observations (
|
|
207
|
+
source_id TEXT PRIMARY KEY, -- {user_id}-{device_id}-obs-{local_id}
|
|
208
|
+
local_id INTEGER NOT NULL, -- local observations.id
|
|
209
|
+
pulled_at_epoch INTEGER NOT NULL -- when we pulled it
|
|
210
|
+
);
|
|
211
|
+
|
|
212
|
+
CREATE INDEX idx_remote_obs_local ON remote_observations(local_id);
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
This maps between remote identity (source_id) and local autoincrement ID. Needed for:
|
|
216
|
+
- Dedup on pull (don't re-insert what we already have)
|
|
217
|
+
- Applying remote supersession (look up local_id from source_id)
|
|
218
|
+
- Identifying which observations are "ours" vs "theirs"
|
|
219
|
+
|
|
220
|
+
---
|
|
221
|
+
|
|
222
|
+
## Gotchas & Mitigations
|
|
223
|
+
|
|
224
|
+
### 1. Clock Skew Between Devices
|
|
225
|
+
|
|
226
|
+
**Risk**: Two machines with different system clocks produce misleading `created_at_epoch` values.
|
|
227
|
+
**Mitigation**: Sync protocol uses server-assigned timestamps for ordering, never client timestamps. Client `created_at_epoch` is preserved for display only.
|
|
228
|
+
|
|
229
|
+
### 2. SQLite Concurrent Access (Hooks vs MCP Server)
|
|
230
|
+
|
|
231
|
+
**Risk**: Hook processes open separate `MemDatabase` instances. If the MCP server is simultaneously writing, lock contention can occur.
|
|
232
|
+
**Mitigation**: WAL mode allows concurrent reads. For writes, SQLite's built-in locking handles this — hooks are short-lived and writes are fast. Monitor during dogfood. If contention becomes an issue, have hooks communicate via a local queue file instead of opening the DB directly.
|
|
233
|
+
|
|
234
|
+
### 3. Storage Growth from Full Pull
|
|
235
|
+
|
|
236
|
+
**Risk**: 5-person team × 100 obs/day × 3 months = 45K observations cached locally (90-225MB).
|
|
237
|
+
**Mitigation**: Lifecycle management (aging → archival → purge) bounds growth. Compacted observations are much smaller. Consider pulling **metadata-only** for team observations (not your own), with lazy detail fetch via `get_observations` — would cut pull bandwidth and storage by ~80%.
|
|
238
|
+
|
|
239
|
+
### 4. FTS5 External Content Mode
|
|
240
|
+
|
|
241
|
+
**Risk**: If observations are updated (not just superseded), FTS5 index must be manually updated.
|
|
242
|
+
**Mitigation**: Current code handles insert and delete. Add update logic when we implement "merge facts into existing observation" (the dedup merge path).
|
|
243
|
+
|
|
244
|
+
### 5. Schema Evolution vs Vector Metadata
|
|
245
|
+
|
|
246
|
+
**Risk**: New SQLite columns (e.g., `superseded_by`) may not exist in Vector metadata.
|
|
247
|
+
**Mitigation**: Never rely on Vector metadata for anything except search filtering. The sync metadata layer stores the full observation as JSON — that's the pull source, not Vector's chunked content.
|
|
248
|
+
|
|
249
|
+
### 6. Secret Observations
|
|
250
|
+
|
|
251
|
+
**Risk**: `sensitivity: "secret"` observations are never synced but stored in plaintext locally.
|
|
252
|
+
**Mitigation**: Acceptable for Phase 3. Consider encrypting narrative/facts for secret observations in Phase 5 (public launch).
|
|
253
|
+
|
|
254
|
+
### 7. Disaster Recovery
|
|
255
|
+
|
|
256
|
+
**Risk**: If Vector loses data, no mechanism to reconstruct.
|
|
257
|
+
**Mitigation**: Add `engrm sync --full` CLI command that re-pushes all local observations. Since each device has its own data, all devices running `--full` reconstructs the complete dataset.
|
|
258
|
+
|
|
259
|
+
---
|
|
260
|
+
|
|
261
|
+
## Implementation Order
|
|
262
|
+
|
|
263
|
+
| Step | Description | Blocks |
|
|
264
|
+
|------|-------------|--------|
|
|
265
|
+
| 1 | Auto-detect agent from MCP `clientInfo` | Nothing |
|
|
266
|
+
| 2 | Make token_budget + scoring weights configurable | Nothing |
|
|
267
|
+
| 3 | Fix supersession chains (resolve to head) | Step 5 |
|
|
268
|
+
| 4 | Build Candengo Vector REST client (`src/sync/client.ts`) | Step 5, 7 |
|
|
269
|
+
| 5 | Implement push engine (outbox flush + lifecycle events) | Step 7 |
|
|
270
|
+
| 6 | Design sync metadata endpoint on Candengo backend | Step 7 |
|
|
271
|
+
| 7 | Implement pull engine (cursor-based, paginated) | Nothing |
|
|
272
|
+
| 8 | Add `remote_observations` mapping table (migration v3) | Step 7 |
|
|
273
|
+
| 9 | Add `engrm sync --full` disaster recovery | Nothing |
|
|
274
|
+
| 10 | Multi-agent `init` CLI (Claude Code, Codex, Cursor) | Nothing |
|
|
275
|
+
|
|
276
|
+
Steps 1-3 can be done immediately (no server-side dependencies).
|
|
277
|
+
Steps 4-8 are Phase 3 core (require Candengo Vector API prep).
|
|
278
|
+
Steps 9-10 are Phase 3.5 (polish).
|
|
279
|
+
|
|
280
|
+
---
|
|
281
|
+
|
|
282
|
+
## Multi-Agent Compatibility
|
|
283
|
+
|
|
284
|
+
| Agent | Config Format | Config Location | Hook Support | Status |
|
|
285
|
+
|-------|--------------|-----------------|-------------|--------|
|
|
286
|
+
| Claude Code | JSON | `.mcp.json` | PostToolUse, Stop, SessionStart | Live ✓ |
|
|
287
|
+
| Codex CLI | TOML | `~/.codex/config.toml` | TBD (different hook system) | MCP verified ✓ |
|
|
288
|
+
| Cursor | UI Settings | Cursor settings | None (MCP tools only) | Compatible |
|
|
289
|
+
| Windsurf | UI Settings | Windsurf settings | None (MCP tools only) | Compatible |
|
|
290
|
+
| Cline | JSON | VS Code settings | None (MCP tools only) | Compatible |
|
|
291
|
+
| VS Code Copilot | JSON | VS Code settings | None (MCP tools only) | Compatible |
|
|
292
|
+
| Zed | JSON | Zed settings | None (MCP tools only) | Compatible |
|
|
293
|
+
|
|
294
|
+
**Key insight**: Agents without hook support can still use all MCP tools manually. The agent calls `save_observation` explicitly instead of auto-capturing via hooks. Context injection works via `session_context` tool call (most agents support "tool use on session start" or similar).
|