Skip to content

Latest commit

 

History

History
1519 lines (1157 loc) · 59.5 KB

File metadata and controls

1519 lines (1157 loc) · 59.5 KB

Context Chronicle - Architecture Design

Overview

Context Chronicle is a verifiable memory system and tool firewall designed for long-running AI programming conversations. It addresses context loss, constraint violations, and decision regression.


Core Principles

1. Raw Events Are the Sole Source of Truth

All fact judgments must trace back to raw events. Compressed summaries, sub-agent reports, and Memory Packets are derivatives and cannot replace original evidence.

2. Local First, Zero Cloud Dependency

Uses SQLite local storage by default with no external services required. Optional cloud sync is available, but core functionality works completely offline.

3. Cross-Platform Consistency

Supports Windows, Linux, and macOS, as well as both TUI and Desktop variants of Claude Code.

4. Hybrid Architecture

  • MCP Server: Cross-tool universal capabilities (Cursor, Windsurf, Claude Code)
  • Claude Code Hooks: Deep integration capabilities (event interception, compaction protection)

5. System Boundaries

Responsible OK Not Responsible NO
Event recording, constraint management, tool firewall Network request interception, HTTP proxy
Local SQLite storage and FTS5 full-text search Real-time filesystem monitoring, git hooks
Vector indexing (requires external embedding provider) Cloud sync, multi-machine collaboration
Model switch detection and context bridging IDE plugins, editor extensions
Plugin conflict detection (known plugins) General process management, resource limits
Automatic key/sensitive data redaction Encrypted storage, access control

System Architecture

+------------------------------------------------------+
|            Claude Code / Cursor / Windsurf           |
+------------------------------------------------------+
|                                                      |
|  +----------------+          +------------------+  |
|  | OpenCode       |          |   MCP Client     |  |
|  | Lifecycle      |          |   (Universal)    |  |
|  | Adapter (TS)   |          |                  |  |
|  +-------+--------+          +--------+---------+  |
+----------+-----------------------------+------------+
           |                             |
           | stdio                       | stdio
           down                             down
   +--------------------+      +----------------------+
   |  OpenCodeAdapter   |      |   MCP Server         |
   |  (TypeScript)      |      |   (TypeScript)       |
   +--------------------+      +----------------------+
   | 9 lifecycle hooks: |      | ToolHandler interface |
   | - onSessionStart   |      | (handler-interface)   |
   | - onUserPromptSubmit|     | Map-based router      |
   | - onPreToolUse     |      | (router.ts)           |
   | - onPostToolUse    |      | 6 unified tools:     |
   | - onPreCompact     |      | - memorySearch       |
   | - onPostCompact    |      | - memoryManage       |
   | - onStop (auditGate)|     | - memoryStatus       |
   | - onSubagentStop   |      | - memoryCompact      |
   | - onModelSwitch    |      | - guardedBash        |
   +--------+-----------+      | - memoryHistory      |
            |                  +----------+-----------+
           |                             |
           +----------+------------------+
                      down
+----------------------------+
|     Storage Layer          |
+----------------------------+
| SqliteBackend implements   |
| 4 role interfaces from     |
| src/storage/repository.ts: |
| - EventRepository          |
| - ConstraintRepository     |
| - KGRepository             |
| - AuditRepository          |
|                            |
| Delegated to specialized   |
| storage classes:           |
| - EventStorage (events.ts) |
| - PacketStorage (packets)  |
| - DecisionStorage          |
| - KGStorage (kg-store.ts)  |
|                            |
| - SQLite (Raw Events)      |
| - FTS5 (Full-text Search)  |
| - sqlite-vec-backend       |
|   (vec0 + cosine)          |
| - session_links            |
|   (cross-DB search)        |
| - memory_packets           |
|   (constraints / facts /   |
|    hypotheses / decisions) |
| - Decision Timeline        |
| - Rolling Compaction State |
| - Memory Decay State       |
|   (halfLifeTurns metadata) |
+----------------------------+

System Overview (Mermaid)

graph TB
    subgraph "AI Client (OpenCode / Cursor / Windsurf)"
        OC[OpenCode Lifecycle Adapter]
        MC[MCP Client]
    end

    subgraph "Context Chronicle Core"
        ADAPTER[OpenCodeAdapter<br/>9 lifecycle hooks]
        MCP_SRV[MCP Server<br/>6 unified tools]
    end

    subgraph "Storage Layer"
        SQLITE[(SQLite<br/>event_log + FTS5)]
        VEC[(sqlite-vec<br/>vec0 + cosine)]
        KG[(Knowledge Graph<br/>entities + relations)]
    end

    subgraph "Subsystems"
        COMPACT[Rolling Compaction<br/>pressure -> textrank -> audit -> reinject]
        DECAY[Memory Decay<br/>context-distance decay<br/>dual-dimension model]
        FIREWALL[Tool Firewall<br/>regex + constraint rules]
        SEARCH[Hybrid Search<br/>FTS5 + vector -> RRF -> LLM rank]
        AUDIT[Stop Gate<br/>7-dimension completion check]
        EMBED[Embedding Pipeline<br/>9 providers + auto-pipeline]
    end

    OC --> ADAPTER
    MC --> MCP_SRV
    ADAPTER --> SQLITE
    ADAPTER --> COMPACT
    ADAPTER --> DECAY
    ADAPTER --> FIREWALL
    ADAPTER --> AUDIT
    MCP_SRV --> SQLITE
    MCP_SRV --> SEARCH
    MCP_SRV --> KG
    EMBED --> VEC
    SEARCH --> VEC
    SEARCH --> SQLITE
    COMPACT --> SQLITE
    COMPACT --> EMBED
    DECAY --> COMPACT
    DECAY --> SQLITE
Loading

Compaction Pipeline

flowchart LR
    EVENTS[New Events] --> PRESSURE[Pressure Score<br/>entropy + importance]
    PRESSURE --> CHECK{Pressure >=<br/>threshold?}
    CHECK -->|No| SKIP[Skip - below watermark]
    CHECK -->|Yes| TEXTRANK[TextRank Summarization<br/>extractive + constraints]
    TEXTRANK --> AUDIT_C[Compaction Audit<br/>integrity + coverage check]
    AUDIT_C --> PASS{Audit<br/>passed?}
    PASS -->|Yes| REINJECT[Build Reinjection<br/>active constraints + tasks]
    PASS -->|No| FAIL[Mark failed - retry later]
    REINJECT --> WATERMARK[Update Watermark]
    REINJECT --> INDEX[Index in Vector Store]
    REINJECT --> DECAY_META[Record halfLifeTurns<br/>in CompactionMetadata]
Loading

Search Pipeline

flowchart LR
    QUERY[User Query] --> FTS[FTS5 Keyword Search<br/>with CJK auto-routing]
    QUERY --> VEC_S[Vector Cosine Search<br/>vec0 + sqlite-vec]
    FTS --> MERGE[RRF Merger<br/>Reciprocal Rank Fusion]
    VEC_S --> MERGE
    MERGE --> DEDUP[Deduplicate + Filter]
    DEDUP --> RERANK{Results > 8?}
    RERANK -->|Yes| LLM[LLM Re-ranker<br/>subagent template]
    RERANK -->|No| OUTPUT[Return Results<br/>token-budgeted]
    LLM --> OUTPUT
Loading

Lifecycle Hook Flow

sequenceDiagram
    participant Host as OpenCode Host
    participant Adapter as OpenCodeAdapter
    participant DB as SQLite Backend
    participant Gate as Stop Gate

    Host->>Adapter: onSessionStart(sessionId)
    Adapter->>DB: load active constraints + decisions
    Adapter-->>Host: projectBrief

    Host->>Adapter: onUserPromptSubmit(message)
    Adapter->>DB: insert event, detect decisions/constraints
    Adapter->>Adapter: check compaction trigger

    Host->>Adapter: onPreToolUse(tool, args)
    Adapter->>DB: firewall check against constraints
    Adapter-->>Host: allowed/blocked verdict

    Host->>Adapter: onPostToolUse(result)
    Adapter->>DB: record tool result

    Host->>Adapter: onModelSwitch(old, new)
    Adapter->>DB: assess context risk, build bridge

    Host->>Adapter: onPreCompact(session)
    Adapter->>Adapter: rolling compaction decision
    Adapter-->>Host: hostCompactionDisabled

    Host->>Adapter: onPostCompact(summary)
    Adapter->>DB: save compaction state + reinjection

    Host->>Adapter: onStop(session)
    Adapter->>Gate: evaluate completion
    Gate-->>Adapter: passed/blocked + issues
    Adapter-->>Host: blocked decision + auto-fix injection
Loading

Data Model

Core Table Structure

1. Raw Event Log

CREATE TABLE event_log (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  sequence_id INTEGER NOT NULL UNIQUE,
  timestamp INTEGER NOT NULL,
  session_id TEXT NOT NULL,
  project_path TEXT,
  event_type TEXT NOT NULL CHECK(event_type IN ('user_message', 'assistant_message', 'tool_call', 'tool_result', 'system_event', 'subagent_message')),
  actor TEXT NOT NULL CHECK(actor IN ('user', 'claude', 'system', 'subagent')),

  -- Tool call specific
  tool_name TEXT,
  command TEXT,

  -- Result
  result_summary TEXT,
  result_full TEXT,

  -- Firewall
  firewall_verdict TEXT CHECK(firewall_verdict IN ('allowed', 'blocked', 'warned', NULL)),

  -- Visibility level
  visibility TEXT NOT NULL DEFAULT 'user' CHECK(visibility IN ('user', 'internal', 'secret')),

  -- Evidence chain (JSON array of event IDs)
  evidence_refs TEXT DEFAULT '[]',

  -- Metadata
  metadata_json TEXT DEFAULT '{}',

  -- Subagent & model metadata (v3)
  subagent_id TEXT,
  parent_session_id TEXT,
  model_name TEXT,

  -- Event integrity
  integrity_hash TEXT
);

Design Notes:

  • id: Auto-incrementing row id (primary key)
  • sequence_id: Monotonically increasing, used to determine strict ordering
  • project_path: Nullable (some events originate outside any project context)
  • actor: Discriminator between user / claude / system / subagent, drives reranker role weights
  • event_type + actor + firewall_verdict + visibility enable post-hoc filtering and audit
  • integrity_hash: SHA-256 chain for tamper detection (v4 migration)

2. Constraints

There is no separate constraints table. Constraints are stored as memory_packets rows with packet_type='constraint':

CREATE TABLE memory_packets (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  session_id TEXT,
  created_at INTEGER NOT NULL,
  expires_at INTEGER,
  packet_type TEXT NOT NULL CHECK(packet_type IN ('constraint', 'fact', 'hypothesis', 'decision')),
  category TEXT,
  content TEXT NOT NULL,
  confidence REAL DEFAULT 1.0 CHECK(confidence >= 0 AND confidence <= 1.0),

  -- Evidence chain (JSON array of event IDs from event_log)
  evidence_chain TEXT NOT NULL DEFAULT '[]',

  -- Scope
  scope_type TEXT DEFAULT 'session' CHECK(scope_type IN ('session', 'project', 'global')),

  -- Vector embedding (stored as JSON array or binary blob)
  embedding TEXT,

  -- Metadata
  metadata_json TEXT DEFAULT '{}'
);

Design Notes:

  • Constraints are unified with facts/hypotheses/decisions under one table distinguished by packet_type
  • Must trace back to raw events (evidence_chain)
  • scope_type controls lifetime: session, project, or global
  • FTS5 dual index (memory_packets_fts + memory_packets_fts_cjk) supports keyword search

3. Decision Timeline

CREATE TABLE decisions (
  id TEXT PRIMARY KEY,
  parent_id TEXT,
  created_at INTEGER NOT NULL,
  question TEXT NOT NULL,
  chosen_option TEXT NOT NULL,
  rejected_options TEXT DEFAULT '[]',
  rationale TEXT,
  evidence_refs TEXT DEFAULT '[]',
  status TEXT NOT NULL DEFAULT 'active' CHECK(status IN ('active', 'superseded', 'reverted')),

  FOREIGN KEY (parent_id) REFERENCES decisions(id)
);

Design Notes:

  • Tracks technical solution evolution (e.g. Redis -> in-memory LRU)
  • Records rejected options (prevents old solutions from resurfacing)
  • parent_id forms a self-referencing tree of decision revisions

Storage Architecture

Role-Based Interface Design

The storage layer uses four role interfaces defined in src/storage/repository.ts:

  • EventRepository — Event CRUD, FTS5 search, sequence numbering, integrity chain
  • ConstraintRepository — Memory packet lifecycle, constraint management, expiration cleanup
  • KGRepository — Knowledge graph entity/relation CRUD, decision timeline, graph search
  • AuditRepository — Raw SQL access, table statistics, audit reports, integrity verification, session linking, lifecycle management

Delegation Pattern

SqliteBackend (src/storage/sqlite-backend.ts) implements all four interfaces via the implements clause:

export class SqliteBackend implements EventRepository, ConstraintRepository, KGRepository, AuditRepository {

It delegates domain-specific operations to four specialized storage classes:

SqliteBackend (facade)
  +-- EventStorage (src/storage/events.ts)      — Event CRUD, FTS, integrity chain
  +-- PacketStorage (src/storage/packets.ts)    — Memory packet/constraint lifecycle
  +-- DecisionStorage (src/storage/decisions.ts) — Decision CRUD, timeline
  +-- KGStorage (src/storage/kg-store.ts)       — Entity/relation CRUD, CTE traversal

Cross-cutting infrastructure methods (raw SQL, audit reports, session linking, lifecycle) are implemented directly on SqliteBackend rather than delegated.

Supporting Modules

File Purpose
src/storage/fts-utils.ts FTS5 query escaping, CJK LIKE clause builder
src/storage/cjk-detector.ts CJK character detection for FTS5 routing
src/storage/kg-types.ts KG type definitions (EntityType, KGRelation)
src/storage/kg-validation.ts KG entity/relation input validation
src/storage/db-connection-manager.ts Connection pool and CompactionStateRow
src/storage/audit-manager.ts Audit domain logic
src/storage/session-link-manager.ts Cross-session link management
src/storage/session-cleanup-manager.ts Expired session cleanup
src/storage/model-switch.ts Model switch tracking
src/storage/event-origin.ts Event provenance tracking
src/storage/sqlite-backend-utils.ts Audit types, init/migration helpers

MCP Layer Architecture

ToolHandler Interface

Every MCP tool conforms to the ToolHandler interface defined in src/mcp/handler-interface.ts:

export interface ToolHandler<TArgs = Record<string, unknown>> {
  readonly name: string;
  readonly description: string;
  readonly inputSchema: z.ZodType<TArgs>;
  execute(args: TArgs): Promise<ToolResult>;
}

Each handler packages its own Zod schema, MCP description, and execution logic as a single unit.

Map-Based Router

The old switch-statement tool dispatcher has been replaced by a Map<string, ToolHandler> registry in src/mcp/router.ts:

const handlers = new Map<string, ToolHandler>();

function registerHandler(handler: ToolHandler): void {
  handlers.set(handler.name, handler);
}

export async function callTool(name: string, args: unknown) {
  const handler = handlers.get(name);
  if (!handler) throw ContextChronicleError.config(`Unknown tool: ${name}`);
  return handler.execute(parseOrThrow(handler.inputSchema, args));
}

Tools are registered at module load time via registerHandler(). The callTool() function looks up the handler by name, validates arguments with Zod, and delegates execution.

Handler Files

9 handler files in src/mcp/handlers/:

File Exported Handler Tools Served
search.ts searchHandler memorySearch
memory-status.ts manageHandler, statusHandler memoryManage, memoryStatus
memory-constraints.ts constraintHandler memoryCompact
memory-tools.ts guardedBashHandler guardedBash
history.ts historyHandler memoryHistory
graph.ts graphHandler Internal KG operations
memory-audit.ts (action exports, no standalone handler) RAG audit/clean/backfill actions
memory-embedding.ts (action exports, no standalone handler) Embedding config, decay settings
memory-utils.ts Shared LogLevel type guard (utility only)

Handlers that serve multiple actions (e.g. memoryManage) use internal switch-on-action dispatch within their execute() method.


CLI Layer Architecture

Command Utilities

src/cli/command-utils.ts provides two shared entry points used by 8 CLI commands:

  • openDb() — Resolves paths via getPaths(), calls requireInit(), opens SqliteBackend, returns DbContext { db, paths }
  • requireInit(paths) — Exits with code 1 if the database file does not exist, printing Not initialized. Run: context-chronicle init

Commands using openDb(): audit, compaction, embedding, export, metrics, rag, search, status.

Commands NOT using openDb() (they have their own initialization paths): install, uninstall, init, setup, doctor, config, feature, logs.

CLI Entry Points

16 command files in src/cli/commands/, registered in the CLI router:

audit.ts          compaction.ts     config.ts        doctor.ts
embedding.ts      export.ts         feature.ts       init.ts
install.ts        logs.ts           metrics.ts       rag.ts
search.ts         setup.ts          status.ts        uninstall.ts

Install/Uninstall System

Diff-Based Precision Uninstall

Install and uninstall use an InstallDiff contract (src/types/install-diff.ts) that records exactly what was added during installation:

export interface InstallDiff {
  version: 1;
  installedAt: string;
  targetPath: string; // Absolute path to opencode.json
  backupPath: string; // Snapshot backup path
  mcpKeys: string[]; // MCP keys we added
  pluginEntries: string[]; // Plugin entries we appended
  commandKeys: string[]; // Slash command keys we added
  sections: string[]; // Top-level sections we added
  compactionChanged: { from: boolean | undefined; to: false } | null;
  snapshotCreated: boolean;
}

The diff records only entries that did not already exist — user-owned pre-existing entries are never recorded and therefore never removed on uninstall.

Install Flow

PluginInstallManager.install()
  +-- Lazy import SqliteBackend  (keeps CLI startup light)
  +-- Init SQLite database
  +-- OpenCodeConfigManager.applyPluginConfig()
  |     +-- Create snapshot backup (.context-chronicle.backup)
  |     +-- Write MCP server, plugin entry, lifecycle config, slash commands
  |     +-- Set compaction.auto = false (first install)
  |     +-- Compute InstallDiff (delta only)
  |     +-- Save install-state.json
  +-- Lazy import ContextChronicleDoctor → run diagnostics
  +-- Smoke test (verify dist/*.js exist)
  +-- TUI plugin auto-install

Uninstall Flow (Two-Tier Fallback)

PluginInstallManager.uninstall()
  +-- Tier 1: restoreFromInstallDiff()
  |     Reads install-state.json → parses InstallDiff
  |     Applies inverse diff (removes only recorded keys)
  |     Preserves user changes made after install
  |     Falls through if: install-state.json missing/corrupt
  +-- Tier 2: Full snapshot restore
        Copies .context-chronicle.backup → opencode.json
        Warns if user made changes after install
        Last-resort fallback

Snapshot Fallback

At install time, a complete snapshot of opencode.json is saved to <opencode.json>.context-chronicle.backup. This serves as a last-resort recovery path when the diff-based undo fails.

Emergency Cleanup

scripts/emergency-cleanup.cjs is a zero-dependency Node.js script that removes all context-chronicle entries from opencode.json when the normal uninstall command is unavailable (e.g. deleted dist/ files). It attempts snapshot restoration first, then manually removes MCP server, plugin, slash commands, and the contextChronicle section.


Resilience Design

Bundled CLI and MCP Server

scripts/build.js uses esbuild to produce self-contained CJS bundles for cli.ts and mcp-server.ts. Deleting individual compiled files in dist/ no longer breaks the uninstall command — the bundles contain all application code inline. Native addons (better-sqlite3, sqlite-vec) remain external since esbuild cannot bundle C++ modules.

Lazy Imports

PluginInstallManager uses await import() for SqliteBackend and ContextChronicleDoctor inside the install() method body rather than at the top of the file. This keeps the CLI entry point lightweight — context-chronicle --help does not need to load SQLite or the doctor module. The embedding CLI commands use the same pattern for createProviderFromPreset.

Diff-Based Recovery

The InstallDiff system allows precise undo — only entries installed by context-chronicle are removed. Any user edits to opencode.json between install and uninstall are preserved.

Emergency Cleanup

scripts/emergency-cleanup.cjs provides a nuclear option with zero external dependencies. It can be run directly (node scripts/emergency-cleanup.cjs) or copy-pasted into a Node.js REPL when dist/ files are missing.


Search Strategy

Hybrid Search Architecture

Query
  down
+----------------+
| Query Router   |  <- Determines which search paths are needed
+----+-----------+
     |
  +--+--+-------------+
  down     down             down
FTS5  Vector   Structured
Search Search   Query
  |     |             |
  +-----+------+------+
               down
       +---------------+
       |  RRF Merger   |  <- Fuses multi-path results
       +-------+-------+
               down
       +---------------+
       |   Reranker    |  <- Re-ranks
       +-------+-------+
               down
       +---------------+
       | Context       |  <- Context expansion
       | Expander      |
       +-------+-------+
               down
         Memory Packet

1. FTS5 Full-Text Search

  • Precise keyword matching
  • Supports boolean queries
  • Suitable for: command names, file paths, error codes

2. Vector Semantic Search

  • Primary: Uses sqlite-vec-backend.ts (sqlite-vec extension: vec0 virtual table + vec_distance_cosine for native, high-performance vector search)
  • Fallback: BLOB storage + JS cosine similarity - only activated when the native sqlite-vec extension is unavailable
  • Supports 28 cross-vendor Embedding presets
  • Suitable for: semantic similarity queries
  • See Embedding Subsystem section below for details

3. RRF (Reciprocal Rank Fusion)

function rrf(results: SearchResult[][], k = 60): SearchResult[] {
  const scores = new Map<string, number>();

  for (const resultSet of results) {
    resultSet.forEach((doc, rank) => {
      const score = 1 / (k + rank + 1);
      scores.set(doc.id, (scores.get(doc.id) || 0) + score);
    });
  }

  return sortByScore(scores);
}

4. Reranking Factors

  • Role weight: user > tool > assistant
  • Time decay: Recency weighting
  • Constraint signal: Items containing "don't"/"must" get priority
  • Scope match: Current project gets priority

5. Knowledge Graph Enhanced Search (graph-search.ts)

  • On top of FTS5 results, extends relevant context through entity-relationship graph traversal
  • Recursive CTE graph traversal (depth configurable, default 2 hops)
  • Graph-aware search provides higher context recall than pure FTS5

6. CJK Multilingual Search

  • Automatically detects CJK characters in queries -> routes to trigram-tokenized FTS5 tables
  • Short terms (1-2 CJK characters) automatically fall back to LIKE '%...%'
  • 3 parallel CJK FTS5 tables: event_log_fts_cjk, memory_packets_fts_cjk, kg_entities_fts_cjk
  • Storage overhead: approximately 15-25MB increase for a 100K event database

Knowledge Graph Subsystem

Data Model

kg_entities -+- kg_relations -+- kg_entities
             |                |
             +- kg_entity_types (whitelist)

Entity types: agent, sub_agent, agents_md_rule, project_file, decision, constraint, memory_packet, task

Relation types: agent_spawned, agent_referenced, file_imports, file_tests, decision_affects, decision_conflicts, constraint_applies, task_produces, event_involves, relates_to

Anti-Contamination Mechanisms

  1. source_type filtering: compaction_artifact is automatically excluded
  2. SHA-256 dedup: content_hash UNIQUE constraint
  3. entity_type whitelist: validateEntity() rejects unknown types

MCP Actions (via memoryManage)

  • entity - Add entities or relations
  • get-entity - Query entities and their relations (supports depth and pagination)
  • list - FTS5 full-text search across graph entities
  • rag-audit - Health check and statistics

Tool Firewall

How It Works

User request -> LLM decision -> Ready to execute tool
                                down
                           PreToolUse Hook
                                down
                         +--------------+
                         | Check        |
                         | constraint   |
                         | ledger       |
                         +------+-------+
                                |
                 +--------------+--------------+
                 down              down              down
              Allow          Warn           Block
                 |              |              |
                 +--------------+--------------+
                                down
                        Execute tool / Deny

Check Logic

async function checkFirewall(tool: string, args: any) {
  // 1. Read active constraints
  const constraints = await db.getActiveConstraints({
    type: 'project',
    id: currentProjectId,
  });

  // 2. Pattern matching
  for (const rule of constraints) {
    if (matchesPattern(tool, args, rule.content)) {
      return {
        allowed: false,
        reason: rule.content,
        evidence: await buildEvidenceChain(rule.source_event_ids),
        suggestion: generateSuggestion(args, rule),
      };
    }
  }

  // 3. Allow
  return { allowed: true };
}

Built-in Rule Patterns

Pattern Match Logic Example
npm_forbidden Command contains npm install / npm i "use pnpm not npm"
protected_path Path matches regex "do not edit generated files"
forbidden_dep Dependency name matches "no Redis allowed"

LLM Sub-Agent

Memory Retrieval Agent

Responsibilities:

  • Retrieve candidate evidence
  • Judge evidence validity
  • Generate Memory Packet

Workflow:

Query
  down
Hybrid search (returns candidate chunks)
  down
LLM sub-agent analysis
  down
Memory Packet (short, structured, traceable)

Prompt Template:

You are a memory verification agent. Analyze retrieved evidence.

Query: {query}

Candidates:
1. [seq: 123, role: user] "This project uses pnpm"
2. [seq: 456, role: assistant] "Should we use npm?"
3. [seq: 789, role: user] "No, pnpm only"

Task:
- Identify which are facts vs questions vs rejections
- Filter outdated or superseded information
- Return structured Memory Packet

Output JSON:
{
  "usable_facts": [...],
  "rejected_facts": [...],
  "uncertain": [...]
}

Subagent Template System

Context Chronicle dispatches specialized subagents for reranking, entity extraction, constraint staleness detection, decision auditing, and session summarization. All subagents follow a strict text-only, JSON-output protocol to prevent tool-calling loops and output drift.

Module

src/plugin/subagent-templates/index.ts (42 lines, split across 3 template files: templates/search.ts, templates/audit.ts, templates/compaction.ts) - 8 templates, dynamic assembly via buildPrompt().

Shared Preamble Blocks

Block Purpose
NO_TOOLS_PREAMBLE Universal constraint: no tool calls, no file reads, no markdown, JSON only
THINKING_INSTRUCTION Structured reasoning in <thinking> blocks before JSON output
CONFIDENCE_REQUIREMENT Mandatory 0.0-1.0 confidence scoring on all outputs

Dynamic Assembly: buildPrompt()

buildPrompt(templateName, template, vars, context?, options?) -> string

Wraps any template with context-aware metadata, retry policies, active constraints, thinking instructions, and disallowed-tool lists. Accepts optional TemplateContext (remaining tokens, session ID, constraints) and TemplateOptions (max retries, latency budget, partial-ok, model hint, template version).

8 Templates (6 Enhanced + 2 New)

Template Key Purpose
RERANK_TEMPLATE RERANK Query-intent classification, diversity-constrained result reranking with score breakdown
ENTITY_EXTRACT_TEMPLATE ENTITY_EXTRACT Hierarchical entity extraction with coreference resolution, configurable depth (shallow/medium/deep)
CONFLICT_RESOLUTION_TEMPLATE CONFLICT_RESOLUTION Cross-session constraint conflict detection with temporal resolution and evidence chains
RAG_AUDIT_TEMPLATE RAG_AUDIT Pollution classification (stale/contradictory/hallucinated), coverage gap detection, remediation guidance
STALE_CONSTRAINT_TEMPLATE STALE_CONSTRAINT Constraint staleness audit using context-distance decay model, review priority (p0/p1/p2)
FACT_CHECK_TEMPLATE FACT_CHECK Claim decomposition and source verification with uncertainty handling (high/medium/low/hearsay)
DECISION_AUDIT_TEMPLATE DECISION_AUDIT Post-hoc decision adherence audit: drift detection, reversal detection, completeness scoring
SESSION_SUMMARY_TEMPLATE SESSION_SUMMARY Structured session summary for context bridging: goal, decisions, progress, open questions, hooks

Template Registry

export const TEMPLATE_REGISTRY: Record<string, string> = {
  RERANK,
  ENTITY_EXTRACT,
  CONFLICT_RESOLUTION,
  RAG_AUDIT,
  STALE_CONSTRAINT,
  FACT_CHECK,
  DECISION_AUDIT,
  SESSION_SUMMARY,
};

Interfaces

  • TemplateContext: remainingTokens, sessionId, activeConstraints, currentTime
  • TemplateOptions: maxRetries, partialOk, disallowedTools, latencyBudgetMs, modelHint, templateVersion

All templates share a common failure-output pattern: {"error": "reason"} for unrecoverable failures, {"flags": []} or equivalent empty response for no-detection scenarios.


Embedding Subsystem

Responsible for converting text into vector representations. It is the foundation of hybrid search and RAG backfill.

Module Composition

File Responsibility
src/embedding/presets/ (13 files) Built-in provider presets (28 entries across 11 provider groups, covering all major providers)
src/embedding/registry.ts Single provider registry and factory (createProviderFromPreset()); primary entry point for provider creation
src/embedding/provider.ts Legacy compatibility shim with simple provider implementations (OpenAI, Cohere, Mock) and createProvider() factory
src/embedding/openai-compatible-provider.ts OpenAI compatible protocol generic implementation (covers OpenAI / Azure / DashScope / SiliconFlow / Ollama / Zhipu / Volcengine)
src/embedding/cohere-provider.ts Cohere-specific adapter (embed-v4.0 etc.)
src/embedding/voyage-provider.ts Voyage-specific adapter (voyage-4-large etc.)
src/embedding/jina-provider.ts Jina-specific adapter (embeddings-v3 / clip-v2)
src/embedding/google-provider.ts Google Gemini Embedding specific adapter
src/embedding/dashscope-provider.ts DashScope Alibaba Cloud Bailian specific adapter
src/embedding/baidu-provider.ts Baidu Qianfan specific adapter
src/embedding/tencent-provider.ts Tencent Hunyuan specific adapter
src/embedding/base-http-provider.ts Abstract base class shared by HTTP-based provider implementations
src/embedding/auto-pipeline.ts Automatic embedding pipeline (init / reindex / refresh)
src/embedding/sqlite-vec-backend.ts sqlite-vec extension: vec0 virtual table + vec_distance_cosine native search (BLOB fallback)
src/embedding/model-validator.ts Embedding model validation and health check
src/embedding/vector-index-manager.ts Vector index rebuild and management

Total: 16 source files, 8 concrete provider implementations, 28 presets. registry.ts is the primary entry point for provider creation; provider.ts remains as a compatibility shim for consumers of the old createProvider() API.

Note: Zhipu, Volcengine, and SiliconFlow do not have standalone provider files - they are implemented through openai-compatible-provider.ts + preset definitions in presets/. Note: provider.js / provider.d.ts in build output are compatibility shims for consumers that import from the old path; they are thin wrappers around registry.ts.

Data Flow

input text
   down
embedding use <id>     <- CLI selects provider, writes to config.json
   down
embedding test         <- Health check (probes connectivity + dimensions)
   down
embed(text)            <- Returns Float32Array
   down
sqlite-vec-backend.ts  <- Stored in vec0 table; queried via vec_distance_cosine

Switching and Rebuilding

After switching providers, embedding reindex must be run, as vector dimensions are incompatible across providers.


Rolling Compaction Subsystem

Core mechanism: plugin takes over compaction, dynamic N backfill, prevents LLM context window from overflowing.

Module Composition

File Responsibility
src/compaction/pressure-score.ts Calculates current session pressure score (token count / window capacity / growth rate)
src/compaction/watermark.ts Sets compaction trigger thresholds + historical watermarks
src/compaction/reinjection.ts Builds 10-section structured reinjection template with dynamic N backfill
src/compaction/prompt-entropy.ts Detects prompt entropy increase (repetition/redundancy signals) to trigger early compaction
src/compaction/decay.ts Memory decay: context-distance-based constraint trustworthiness calculation
src/compaction/engine.ts Core compaction engine: TextRank extraction, audit, state persistence
src/compaction/rolling-engine.ts Rolling compaction orchestration with CompactionMetadata (schemaVersion, halfLifeTurns, qualityTier, compactionDurationMs)

State Machine

Idle
  down pressure > high_watermark
Compress
  down generates state-snapshot
Reinject
  down evaluates criticality, injects N raw events
Idle

CLI Entry Points

  • compaction status - View current pressure / watermark / mode
  • compaction mode <auto|manual|off> - Switch modes
  • compaction disable-host - Disable host's native compaction
  • compaction mode strict-plugin-owned - Enable plugin takeover of compaction

Memory Decay System

Constraints lose trustworthiness over time - not by calendar age but by context distance (turns without reference). A constraint that has not been mentioned for many turns is less reliable than one recently reaffirmed, regardless of wall-clock time.

Core Model: Dual-Dimension Decay

The decay formula uses a dual-dimension model where three factors multiply:

adjustedDecay = baseDecay x reinforcementFactor x topicDriftFactor
trustworthiness = 1 - adjustedDecay
  • baseDecay: Exponential decay via 1 - e^(-lambda x turnDistance) where lambda = ln(2) / halfLifeTurns
  • reinforcementFactor (0.0-2.0): How strongly the constraint was reaffirmed or contradicted
    • 0.0 = explicitly negated/superseded -> 100% decay
    • 0.1 = explicitly reaffirmed -> strongly retained
    • 1.0 = no reinforcement, standard decay
  • topicDriftFactor (0.5-2.0): How related the current conversation is to the constraint topic
    • 0.5 = highly related -> slower decay
    • 2.0 = unrelated -> accelerated decay

Configurable Half-Life

Preset Half-Life Label Use Case
30 turns 30 Tight Rapidly changing projects, frequent pivots
50 turns 50 Moderate Active iteration phases
80 turns 80 Balanced (default) General-purpose development
120 turns 120 Relaxed Stable projects with long-running tasks
200 turns 200 Loose Mature codebases, well-established conventions
500 turns 500 Very loose Archival constraints, rarely-changing policies
Infinity Infinity Never decay Permanent, non-negotiable constraints

Implementation

  • src/compaction/decay.ts: calculateDecay(DecayInput) -> DecayResult - pure function implementing the dual-dimension model
  • Configuration: config.json -> memoryDecay.halfLifeTurns (default: 80)
  • Constants: MEMORY_DECAY_DEFAULT_HALF_LIFE = 80, MEMORY_DECAY_PRESETS = [30, 50, 80, 120, 200, 500, Infinity]
  • Integration with STALE_CONSTRAINT_TEMPLATE: The staleness audit subagent receives the configured halfLifeTurns and applies context-distance decay to identify outdated constraints
  • Integration with compaction: CompactionMetadata.halfLifeTurns is persisted alongside every compaction so downstream tools can apply the correct decay model

Data Flow

Constraint referenced at turn N
   down
turnDistance = currentTurn - N
   down
calculateDecay({ turnDistance, halfLifeTurns, reinforcementFactor, topicDriftFactor })
   down
trustworthiness score (0.0-1.0)
   down
needsReview flag if trustworthiness < 0.5
   down
STALE_CONSTRAINT_TEMPLATE audit via subagent

Stop Gate (auditGate)

Core security mechanism: forcibly finalizes when the LLM is about to stop generating. Controlled by the auditGate feature toggle.

Trigger Location

src/audit/final-gate.ts - Triggered by onStop lifecycle hook (in src/plugin/hooks/stop.ts).

Smart Audit Trigger

Uses a dual-track decision algorithm (shouldTriggerAudit()):

  • Forced triggers (any satisfied -> audit): firewall block rate > 30%, >=2 blocks, repeated corrections, compaction triggered, >=2 subagents
  • Weighted triggers (total score >= 50 -> audit): token consumption, duration, tool variety, decision count, constraint changes

Simple tasks skip the audit entirely; complex tasks trigger an auto-fix loop (max 3 retries) before blocking completion.

Behavior

  1. Summarizes all active constraints from this session
  2. Summarizes incomplete decisions (status != done)
  3. Writes an audit event (event_type = 'stop_gate')
  4. Injects key summary into the next system prompt, enabling full task graph reconstruction across session restores
  5. Auto-fix loop: on audit failure, injects blocking issues into the conversation for the agent to resolve
  6. After 3 consecutive failures, requires manual review

Invariants

  • Stop Gate cannot be disabled (hard constraint)
  • Once triggered, at least 1 audit event must be produced
  • On failure, degrades to warning log without affecting the main flow

Lifecycle Harness

OpenCodeTUI plugin lifecycle and adapter layer.

Module Composition

File Responsibility
src/plugin/lifecycle.ts Plugin install / enable / disable / uninstall state machine
src/plugin/opencode-adapter.ts OpenCodeTUI adapter (Hook protocol, config injection, stdio communication)
src/tui/plugin.ts TUI settings panel: route + Ctrl+P command palette registration
src/tui/settings.tsx SolidJS interactive settings panel: bilingual EN/ZH, feature toggles, presets

State Machine

absent -> installed -> enabled -> active
                          down
                       disabled -> active
                          down
                       uninstalled

Entry Points

Driven by three CLI commands: context-chronicle install / uninstall / doctor:

  • install - Registers OpenCodeTUI plugin + writes lifecycle config
  • uninstall - Deregisters and cleans up
  • doctor - Checks registration integrity, stdio connectivity, config consistency

Session Linking

Cross-session vector search: link multiple session databases and search across them as a unified corpus.

Data Model

CREATE TABLE session_links (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  source_session TEXT NOT NULL,
  linked_db_path TEXT NOT NULL,
  linked_session TEXT NOT NULL,
  created_at INTEGER NOT NULL,
  label TEXT,
  metadata TEXT DEFAULT '{}',
  UNIQUE(source_session, linked_db_path, linked_session)
);

Module Composition

File Responsibility
src/storage/session-links-schema.sql Schema definition for session_links table
src/storage/sqlite-backend.ts linkSession() - insert/delete links; validateDbPath() - verify DB integrity; checkLinkedDbHealth() - health probe
src/retrieval/multi-session-search.ts searchAcrossLinkedSessions() - opens linked DBs as SqliteVecBackend instances, queries in parallel, merges via RRF
src/mcp/handlers/memory.ts link-session / unlink-session / list-linked actions under memoryManage

Search Flow

Query -> Embedding -> searchAcrossLinkedSessions()
  +-- Main DB: vec0 cosine search
  +-- Linked DB 1: vec0 cosine search
  +-- Linked DB 2: vec0 cosine search
  +-- RRF Merge -> ranked results with source_session/source_db tags

MCP Actions

Action Description
link-session Link another session DB for cross-session search
unlink-session Remove a linked session
list-linked List all currently linked sessions

File Logging System

Structured file-based logging for all lifecycle hooks and internal operations.

Architecture

  • FileLogWriter (src/utils/logger.ts): Buffered JSON Lines writer with configurable flush interval (5s) and retention (7 days)
  • Logger class: Tag-based logger with levels (debug/info/warn/error) for subsystems: compaction, embedding, tool, model, session, prompt, stop
  • Log path: ~/.opencode/context-chronicle/logs/cc-YYYY-MM-DD.log
  • Hook integration: client.app.log() calls in every lifecycle hook (compact.ts, model.ts, prompt.ts, stop.ts, session.ts, tool.ts)

JSON Lines Format

{
  "ts": "2026-06-24T10:30:00.000Z",
  "lvl": "info",
  "tag": "compaction",
  "session": "ses_abc",
  "msg": "Compaction triggered at seq 150"
}

Configuration

Env Variable Default Description
CC_LOG_LEVEL info Minimum log level (debug/info/warn/error)
CC_LOG_RETENTION 7 Days to retain log files
Max total size 50 MB Hard limit across all log files

CLI

  • context-chronicle logs - View and filter log files
  • context-chronicle logs --level error - Show errors only
  • context-chronicle logs --tag compaction - Filter by subsystem tag

Design Notes

  • ConsolePatcher (formerly src/utils/console-patcher.ts) has been removed. All logging now goes through FileLogWriter.
  • Failed log writes never crash the application (best-effort semantics).
  • Logs auto-clean on startup: files older than retention are deleted; oldest files trimmed if total exceeds 50 MB.

Cross-Platform Adaptation

Windows Specifics

1. Path Handling

const isWindows = process.platform === 'win32';
const installPath = isWindows
  ? path.join(process.env.USERPROFILE!, '.opencode', 'context-chronicle')
  : path.join(process.env.HOME!, '.opencode', 'context-chronicle');

2. Vector Search Approach

  • Approach: sqlite-vec-backend.ts - Uses sqlite-vec extension with vec0 virtual table and vec_distance_cosine for native vector search. Falls back to BLOB + JS cosine similarity if the extension cannot be loaded.
  • Advantages: Near-native performance via extension, cross-platform compatible, backups aligned with SQLite
  • Tradeoff: Requires sqlite-vec native extension; fallback provides degraded performance for large datasets, but this project's session-level data is under 10K, so impact is minimal

3. Hooks (TypeScript Lifecycle Adapter)

Lifecycle hooks are implemented in TypeScript via OpenCodeAdapter (src/plugin/opencode-adapter.ts). OpenCode invokes the adapter over stdio; no external hook scripts are deployed.

{
  "plugin": ["context-chronicle"]
}

The adapter registers nine lifecycle methods (onSessionStart, onUserPromptSubmit, onPreToolUse, onPostToolUse, onPreCompact, onPostCompact, onStop, onSubagentStop, onModelSwitch) directly through OpenCode's plugin API.

Linux/macOS

Path

~/.opencode/context-chronicle/
  +-- config.json
  +-- db.sqlite

Plugin Config

{
  "plugin": ["context-chronicle"]
}

Auto Detection

function getInstallPath() {
  switch (process.platform) {
    case 'win32':
      return path.join(process.env.USERPROFILE!, '.opencode', 'context-chronicle');
    case 'darwin':
      return path.join(process.env.HOME!, '.opencode', 'context-chronicle');
    case 'linux':
      return path.join(process.env.HOME!, '.opencode', 'context-chronicle');
    default:
      throw new Error(`Unsupported platform: ${process.platform}`);
  }
}

Configuration Management

config.json Structure

{
  "storage": {
    "backend": "sqlite",
    "path": "~/.opencode/context-chronicle/db.sqlite"
  },
  "embedding": {
    "provider": "openai",
    "apiKey": "${OPENAI_API_KEY}",
    "model": "text-embedding-3-small",
    "dimension": 1536
  },
  "llm": {
    "provider": "anthropic",
    "apiKey": "${ANTHROPIC_API_KEY}",
    "model": "claude-3-haiku-20240307"
  },
  "firewall": {
    "mode": "warn",
    "customRules": []
  },
  "retrieval": {
    "fts5Weight": 0.5,
    "vectorWeight": 0.5,
    "maxResults": 10
  },
  "memoryDecay": {
    "halfLifeTurns": 80
  }
}

Multi-Vendor Support

interface ProviderConfig {
  openai?: {
    apiKey: string;
    baseURL?: string;
  };
  anthropic?: {
    apiKey: string;
  };
  deepseek?: {
    apiKey: string;
    baseURL?: string;
  };
  kimi?: {
    apiKey: string;
  };
}

Performance Optimization

1. Index Strategy

-- Composite indexes
CREATE INDEX idx_event_scope_seq
ON event_log(session_id, sequence_id);

CREATE INDEX idx_event_project_seq
ON event_log(project_id, sequence_id);

-- Constraints are stored in memory_packets with packet_type='constraint',
-- indexed by the memory_packets FTS5 table and filtered at query time.

2. Batch Operations

// Batch vector insertion
async function indexBatch(events: Event[]) {
  const embeddings = await embeddingProvider.embed(events.map((e) => e.content));

  const stmt = db.prepare(`
    INSERT INTO vector_index (sequence_id, embedding)
    VALUES (?, ?)
  `);

  db.transaction(() => {
    events.forEach((event, i) => {
      stmt.run(event.sequence_id, Buffer.from(new Float32Array(embeddings[i]).buffer));
    });
  })();
}

3. Cache Strategy

// LRU cache for search results
const cache = new LRU({
  max: 100,
  ttl: 3600000, // 1 hour
});

async function cachedSearch(query: string) {
  if (cache.has(query)) {
    return cache.get(query);
  }

  const result = await hybridSearch(query);
  cache.set(query, result);
  return result;
}

Security and Privacy

1. Sensitive Information Filtering

const SECRET_PATTERNS = [
  /sk-[a-zA-Z0-9]{48}/, // OpenAI key
  /sk_live_[a-zA-Z0-9]+/, // Stripe key
  /ghp_[a-zA-Z0-9]{36}/, // GitHub token
  /password\s*=\s*['\"]?([^'"\s]+)/i,
];

function sanitize(content: string): string {
  let sanitized = content;
  for (const pattern of SECRET_PATTERNS) {
    sanitized = sanitized.replace(pattern, '[REDACTED]');
  }
  return sanitized;
}

2. Permission Domain Isolation

-- System prompts and developer prompts are excluded from user search
CREATE TABLE event_log (
  ...
  visibility TEXT NOT NULL DEFAULT 'user',
  -- user | internal | secret
);

-- Automatically filtered during search
SELECT * FROM event_log
WHERE visibility = 'user'
AND content MATCH ?;

3. Runtime Security Controls

Control Implementation
Config sandbox ohmyopenagent-adapter.ts parses JSON or runs JS in a locked vm context without require/process/global.
Shell-less tool execution guardedBash.ts and subagent-invoker.ts spawn with shell: false and validate tokens before execution.
Interpreter escape blocking bash -c, node -e, python -c, ruby -e, perl -e, php -r, and similar flags are rejected.
Minimal child environment Only PATH, HOME/USERPROFILE, TEMP/TMP, and locale vars are forwarded; secrets are stripped.
SSRF / DNS rebinding validateResolvedIps() resolves hostnames at request time and blocks private/loopback unless allowLoopback is set.
Event integrity SHA-256 chain computed over sanitized, stored values; verifyEventIntegrity() recomputes from DB rows.
Binary integrity postinstall-smart.cjs verifies better-sqlite3 prebuilt binaries against scripts/prebuild-manifest.json.
Path hardening Model cache and session links are restricted to allowed bases; data dirs use 0o700.
JSON safety safeParseJson() drops __proto__/constructor/prototype keys to prevent prototype pollution.

Test Strategy

Tests are organized in domain subdirectories mirroring src/ — 96 test files, 1311 tests total (excluding tests/load).

Domain Coverage

Domain Path Focus
Audit tests/audit/ Stop gate, integrity, task context, coordinator
Compaction tests/compaction/ Rolling, watermarks, reinjection, entropy
Retrieval tests/retrieval/ Hybrid search, ranking, RAG, multi-session
MCP tests/mcp/ Protocol, tool contracts, handler tests
Storage tests/storage/ SQLite, FTS5, vector, CJK, model-switch
Plugin tests/plugin/ Install, uninstall, config, adapter
KG tests/kg/ Entity CRUD, relation, graph search
Embedding tests/embedding/ sqlite-vec backend, presets, providers
Security tests/security/ Firewall, sanitize, FTS5 escape, API key headers
TUI tests/tui/ Settings panel, plugin callbacks
Utils tests/utils/ Logger, memory monitor, perf tracker
CLI tests/cli/ Log viewer command
Scenarios tests/scenarios/ Lifecycle, logging e2e, conflict, bridge
Property tests/property/ RRF+search invariants (fuzz)
Load tests/load/ 10k event performance (manual, excluded from CI)

Test Utilities

tests/test-utils.ts exports createTestDb() which creates an isolated SqliteBackend in the OS temp directory with automatic WAL/joural file cleanup. Non-isolation tests use SqliteBackend(':memory:') for in-memory databases.

import { createTestDb } from './test-utils';

describe('MyModule', () => {
  const { db, cleanup } = createTestDb();
  afterAll(cleanup);

  it('should work correctly', () => {
    // db is a fully functional SqliteBackend
  });
});

Coverage

Configured in jest.config.js: global thresholds at branches >= 25%, functions >= 30%, lines >= 40%, statements >= 40%. Per-glob thresholds were removed because Jest 29 interprets coverageThreshold keys as file path matches, not globs, making them silently no-op. The single global floor is robust across Jest patch releases and CI matrix jobs.

See TESTING.md for comprehensive testing guide.

Cross-Platform Testing

CI (ci.yml) runs a 6-cell test matrix (3 OS x 2 Node versions) with one exclusion:

os: [ubuntu-latest, windows-latest, macos-latest]
node-version: ['18', '22']
exclude: windows-latest x node 18

fail-fast: false

The windows-latest x Node 18 combination is excluded because better-sqlite3@12.11.1 does not ship a Windows prebuilt binary for the Node 18 ABI, and GitHub-hosted Windows runners lack a compatible Visual Studio C++ toolchain for node-gyp rebuilds. Windows contributors on Node 18 must install the "Desktop development with C++" Visual Studio workload or upgrade to Node 22 (recommended, pinned by .nvmrc).

Each matrix cell runs: pnpm install, pnpm rebuild better-sqlite3, pnpm tsc --noEmit, pnpm run lint, pnpm run build, and pnpm test (excluding tests/load). Coverage is uploaded to Codecov. A gating job (test) depends on the full matrix passing.


Roadmap

Core Delivery (Completed)

  • MCP Server (6 unified tools, JSON-RPC stdio, modular handlers in src/mcp/)
  • FTS5 full-text search + LIKE fallback
  • Constraint management + Memory Packet lifecycle
  • CLI tools (init/status/search/setup/install/doctor/embedding/compaction)
  • Tool firewall + guardedBash
  • Vector search (sqlite-vec native search with fallback behavior)
  • OpenCode TUI hooks (user message / tool call / compaction)
  • Long-running task detection + audit coordinator + auto-correction
  • Decision Timeline + Compaction Engine

Plugin Takeover Rolling Context (Completed)

  • Embedding subsystem (17 provider/infrastructure areas + 29 presets + registry + TypeDoc)
  • Rolling compaction subsystem (pressure-score + watermark + reinjection + prompt-entropy)
  • Stop Gate (final-gate forced finalization + cross-session recovery)
  • Lifecycle Harness (lifecycle.ts + opencode-adapter.ts)
  • CLI extensions (install/uninstall/doctor + embedding + compaction)
  • MCP extensions (6 unified tools, modular split: src/mcp/handlers/)
  • Current scale: 160 .ts source files, 96 test files, 1311 tests

Subsequent Plans (Completed)

  • Attachment file path injection (AttachmentExtractor + forgetting detection algorithm)
  • System instruction protection (SystemFileRegistry + pointer/index mode handling)
  • RAG data audit and annotation (EventOrigin + RAGDataAuditor)
  • Compaction progress display (client.tui.showToast API)
  • RAG search engine enhancement (useVector default true + vector search + auto-search + LLM sub-agent)
  • Current baseline: 96 test files / 1311 tests (historical snapshot: 45/644 at initial review)

Future Plans

  • [PLANNED] Team collaboration
  • [PLANNED] Cloud sync (optional)
  • [PLANNED] Web Dashboard

Contributing Guide

See CONTRIBUTING.md


License

Apache-2.0 License - see package.json and LICENSE

2026-06-20 Full Acceptance Review Notes

  • Package manager policy: this project is pnpm-only; npm is prohibited for installing dependencies. CI, install scripts, and documentation examples should all use pnpm.
  • OpenCode TUI policy: the current config schema uses ~/.config/opencode/opencode.json, top-level mcp and top-level plugin. The old mcpServers is considered only for historical compatibility.
  • (Historical baseline 2026-06-20): 45 suites / 644 tests. As of 2026-06-30: 93 test files / 1281 tests.
  • Documentation completeness: rescanned all 24 Markdown files in the project (excluding node_modules/, dist/, coverage/), relative links intact, old npm / mcpServers / 177 tests / QUICKSTART references have been cleaned up.