the-campfire 0.3.0 → 0.3.2

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/README.md CHANGED
@@ -1203,17 +1203,17 @@ It operates as a non-blocking observer: no agent message is ever delayed by it,
1203
1203
 
1204
1204
  | Layer | What it does |
1205
1205
  |-------|-------------|
1206
- | **Semantic Memory** | Stores observations, decisions, and patterns from each agent session as vector embeddings in a local LanceDB database. Any session can query this shared knowledge base for relevant context before starting a task. |
1206
+ | **Semantic Memory** | Stores observations, decisions, and patterns from each session in a local LanceDB database, scoped by namespace (global / repo / session / agent). Memories decay over time unless reused, and relevant ones are auto-recalled into a session's next prompt. See [Semantic Memory](#semantic-memory-details) below. |
1207
1207
  | **Deliberation Engine** | Proposes structured decisions across sessions (e.g. "which approach should we use?"). Connected viewers and agents can respond; the engine aggregates votes with role-weighted majority and resolves to `approved`, `rejected`, or `synthesized`. |
1208
1208
  | **Capability Discovery** | Each session self-reports its strengths, available tools, and context usage. When routing a task, the engine scores all connected sessions and picks the best fit. Confidence probes can be sent to agents in real time to verify self-reported capabilities. |
1209
1209
  | **Shared Context Stream** | A live think-aloud stream where agents can inject thoughts and observations. The engine detects semantic links (agrees, disagrees, builds on, contradicts) between fragments and tracks consensus scores across the session group. |
1210
1210
 
1211
1211
  **How it works end-to-end:**
1212
1212
 
1213
- 1. When an agent produces output, the CI layer silently extracts observations and stores them as `MemoryFragment` records (with vector embeddings if an embedding provider is configured).
1214
- 2. When a user sends a message, the CI layer queries the memory store for relevant context and prepends it to the message giving agents access to knowledge from past sessions.
1213
+ 1. When an agent produces output, the CI layer extracts observations, decisions, and patterns and stores them as `MemoryFragment` records in the right namespace (with vector embeddings if an embedding provider is configured). Thinking-block content is scrubbed before anything is stored.
1214
+ 2. When a user sends a message, the CI layer recalls the most relevant memories from the repo, agent, and global namespaces (under a 250 ms budget so chat is never blocked) and prepends them to the message. The recalled items are shown to the user as a collapsible "recalled context" chip on that message.
1215
1215
  3. Browser clients can send `memory_query`, `memory_store`, `deliberation_respond`, `route_task`, and `inject_thought` messages over WebSocket. The server handles them and broadcasts results back to all connected viewers.
1216
- 4. Sessions consolidate their episodic memories into distilled `ConsolidatedKnowledge` entries when they end.
1216
+ 4. Episodic fragments are consolidated into distilled `ConsolidatedKnowledge` triggered on turn boundaries, idle, session end, or manually — via a JUDGE → DISTILL → CONSOLIDATE pipeline (see below).
1217
1217
 
1218
1218
  **Embedding providers:**
1219
1219
 
@@ -1225,16 +1225,25 @@ Vector search requires an embedding provider. Configure one in **Settings**:
1225
1225
  | `ollama` | `nomic-embed-text` (default) | 768 | Requires local Ollama instance |
1226
1226
  | `none` | — | — | Fragments stored without embeddings; metadata-only search |
1227
1227
 
1228
- Without an embedding provider, memory still works — queries fall back to a full scan filtered by session, repo root, tags, and type.
1228
+ Without an embedding provider, memory still works — recall falls back to a recency- and confidence-ranked scan over the relevant namespaces (so recent repo decisions still surface, just without semantic similarity).
1229
+
1230
+ <a id="semantic-memory-details"></a>
1231
+
1232
+ **How Semantic Memory works:**
1233
+
1234
+ - **Namespaces** — every memory lives in a scope: `global` (cross-repo conventions), `repo:<hash>` (one repository), `session:<id>` (one session's episodic notes), and `agent:<backend>` (backend-specific quirks). Recall for a session draws from repo, agent, and global scopes with per-namespace depth limits.
1235
+ - **Decay & reinforcement** — memories lose weight over time on a per-namespace half-life; being recalled reinforces a memory and extends its life (capped, so nothing becomes immortal). Low-weight, consolidated memories are eventually evicted. You can **pin** a memory so it never decays.
1236
+ - **Consolidation (JUDGE → DISTILL → CONSOLIDATE)** — raw fragments are filtered and clustered locally, then distilled into durable knowledge by an LLM (via your configured OpenRouter key). If no key is set, it falls back to a simple concatenation, badged as such in the UI — consolidation is never blocked on configuration.
1237
+ - **UI** — the Memory panel shows per-namespace counts and decayed-weight bars with pin/unpin controls; **Settings → Memory** exposes per-namespace decay half-lives, reinforce multipliers, and recall depths.
1229
1238
 
1230
1239
  **Storage:**
1231
1240
 
1232
- All CI data is stored locally under `~/.campfire/memory/lancedb/` — no external service required.
1241
+ All CI data is stored locally under `~/.campfire/memory/` — no external service required. Schema is versioned via `meta.json` and migrated automatically from earlier versions.
1233
1242
 
1234
1243
  | Table | Purpose |
1235
1244
  |-------|---------|
1236
- | `fragments.lance` | Episodic memory fragments with embeddings |
1237
- | `consolidated.lance` | Distilled knowledge synthesized from sessions |
1245
+ | `fragments_v2` | Episodic memory fragments (namespace-scoped, with embeddings when available) |
1246
+ | `consolidated_v2` | Distilled knowledge synthesized from fragments |
1238
1247
 
1239
1248
  Capability data is stored as JSON in `~/.campfire/capabilities/` and learning history is appended to `~/.campfire/capability-learning.jsonl`.
1240
1249
 
@@ -1260,12 +1269,12 @@ curl -X POST http://localhost:4567/api/sessions/route-task \
1260
1269
 
1261
1270
  | Group | Endpoints |
1262
1271
  |-------|-----------|
1263
- | Memory | `GET/POST /sessions/:id/memory`, `GET /sessions/:id/memory/query`, `POST /sessions/:id/memory/consolidate`, `GET /memory/global` |
1272
+ | Memory | `GET/POST /sessions/:id/memory`, `GET /sessions/:id/memory/query`, `GET /sessions/:id/memory/overview`, `POST /sessions/:id/memory/consolidate`, `POST /memory/pin`, `GET /memory/global` |
1264
1273
  | Deliberation | `GET /sessions/:id/deliberations`, `GET /sessions/:id/deliberations/:proposalId`, `POST .../respond`, `POST .../resolve` |
1265
1274
  | Capabilities | `POST /sessions/route-task`, `GET /capabilities`, `GET /capabilities/history`, `POST /capabilities/feedback` |
1266
1275
  | Shared Context | `GET /sessions/:id/context/stream`, `GET /sessions/:id/context/consensus`, `GET /sessions/:id/context/thread/:fragmentId` |
1267
1276
 
1268
- Architecture details are documented in [`CLAUDE.md`](CLAUDE.md). A design study for the next iteration of the memory layer lives at [`docs/design/semantic-memory-v2.md`](docs/design/semantic-memory-v2.md).
1277
+ Architecture details are documented in [`CLAUDE.md`](CLAUDE.md). The full semantic-memory design (namespaces, decay, retrieval scoring, and the consolidation pipeline) is written up in [`docs/design/semantic-memory-v2.md`](docs/design/semantic-memory-v2.md).
1269
1278
 
1270
1279
  ---
1271
1280
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "the-campfire",
3
- "version": "0.3.0",
3
+ "version": "0.3.2",
4
4
  "type": "module",
5
5
  "description": "Campfire \u2014 collaborative web platform for AI coding agents. Run Claude Code, Codex, Goose, Aider, and more from one browser UI with real-time collaboration, permission voting, and automation.",
6
6
  "license": "MIT",
@@ -24,7 +24,7 @@
24
24
  */
25
25
 
26
26
  import type { BrowserIncomingMessage, BrowserOutgoingMessage, BackendType } from "./session-types.js";
27
- import { storeFragment, queryFragments, queryForEnrichment } from "./semantic-memory.js";
27
+ import { storeFragment, queryFragments, queryForEnrichment, warmMemory } from "./semantic-memory.js";
28
28
  import type { GitContext, MemoryType, EnrichmentResult } from "./semantic-memory.js";
29
29
  import { consolidate } from "./memory-consolidation.js";
30
30
  import { deliberationEngine } from "./deliberation-engine.js";
@@ -355,6 +355,15 @@ export class CollectiveIntelligenceLayer {
355
355
  });
356
356
  }
357
357
 
358
+ /**
359
+ * Fire-and-forget: open the memory store's tables when a session becomes
360
+ * ready so the session's first user message enriches within budget. Called
361
+ * from WsBridge on session init; never awaited, never throws.
362
+ */
363
+ warmForSession(repoRoot: string, backendType: BackendType): void {
364
+ void warmMemory({ repoRoot, backendType }).catch(() => { /* best-effort */ });
365
+ }
366
+
358
367
  // ─── Session lifecycle ────────────────────────────────────────────────────
359
368
 
360
369
  /**
@@ -1254,6 +1254,28 @@ export interface NamespaceOverviewEntry {
1254
1254
  * Per-namespace fragment stats for the memory panel: count, average decayed
1255
1255
  * weight, and pinned count, over [session:<id>, repo:<hash>, agent:<backend>, global].
1256
1256
  */
1257
+ /**
1258
+ * Open the LanceDB connection and the tables a subsequent enrichment query
1259
+ * will touch, so the first real `queryForEnrichment` for a session lands
1260
+ * within the 250 ms budget instead of paying the cold connect+open cost
1261
+ * (~230 ms on a fresh process) and getting skipped. Best-effort and
1262
+ * reinforcement-free — it must never throw into session startup, and it must
1263
+ * not touch access counts (it is not a real recall).
1264
+ */
1265
+ export async function warmMemory(opts: { repoRoot: string; backendType: string }): Promise<void> {
1266
+ try {
1267
+ const ns = opts.repoRoot ? repoNamespace(opts.repoRoot) : "global";
1268
+ // getNamespaceOverview opens the fragments table + connection;
1269
+ // getKnowledgeByNamespace opens the consolidated table. Neither reinforces.
1270
+ await Promise.all([
1271
+ getNamespaceOverview({ sessionId: "warm", repoRoot: opts.repoRoot, backendType: opts.backendType }),
1272
+ getKnowledgeByNamespace(ns),
1273
+ ]);
1274
+ } catch {
1275
+ // Warming is an optimization; a cold first query still works, just slower.
1276
+ }
1277
+ }
1278
+
1257
1279
  export async function getNamespaceOverview(opts: {
1258
1280
  sessionId: string;
1259
1281
  repoRoot: string;
@@ -1038,6 +1038,7 @@ export class WsBridge {
1038
1038
  this.persistSession(session);
1039
1039
  this.injectDetectedEnvironmentMcp(session);
1040
1040
  this.agentMcpBridge?.onSessionReady(session.id, backendType, session.state.cwd);
1041
+ this.collectiveIntelligence?.warmForSession(session.state.repo_root || session.state.cwd || "", backendType);
1041
1042
  } else if (msg.type === "session_update") {
1042
1043
  session.state = { ...session.state, ...msg.session, backend_type: backendType };
1043
1044
  this.refreshGitInfo(session, { notifyPoller: true });
@@ -1382,6 +1383,7 @@ export class WsBridge {
1382
1383
  this.persistSession(session);
1383
1384
  this.injectDetectedEnvironmentMcp(session);
1384
1385
  this.agentMcpBridge?.onSessionReady(session.id, session.backendType, session.state.cwd);
1386
+ this.collectiveIntelligence?.warmForSession(session.state.repo_root || session.state.cwd || "", session.backendType);
1385
1387
  } else if (msg.subtype === "status") {
1386
1388
  session.state.is_compacting = msg.status === "compacting";
1387
1389