Files
opencode-working-memory/docs/architecture.md
T
Ralph Chang c8c7dbed3b chore: ignore superpowers plans and update architecture doc
- Add docs/superpowers/plans/ to .gitignore
- Remove tracked plan files from git
- Update docs/architecture.md:
  - Change primary extraction format from XML to 'Memory candidates:'
  - Mark XML format as legacy/deprecated
  - Fix hot session state injection example
2026-04-27 14:53:07 +08:00

11 KiB

Architecture Documentation

Overview

The Working Memory Plugin implements a three-layer memory architecture designed to preserve context across OpenCode session compactions.

┌─────────────────────────────────────────────────────────────┐
│  LAYER 1: WORKSPACE MEMORY (Long-term, cross-session)      │
│  • Persistent storage: ~/.local/share/opencode-working-...  │
│  • Types: feedback | project | decision | reference        │
│  • Sources: explicit | compaction | manual                  │
│  • Limits: 5200 chars / 28 entries                          │
│  • Survives: session reset, compaction (same workspace)    │
└─────────────────────────────────────────────────────────────┘
                              ↓
┌─────────────────────────────────────────────────────────────┐
│  LAYER 2: HOT SESSION STATE (Short-term, per-session)        │
│  • Session-scoped tracking: active files, open errors       │
│  • Storage: sessions/{sessionID}.json                       │
│  • Auto-extracted from tool usage patterns                  │
│  • Cleared: on new session start                             │
└─────────────────────────────────────────────────────────────┘
                              ↓
┌─────────────────────────────────────────────────────────────┐
│  LAYER 3: NATIVE OPENCODE STATE                             │
│  • Uses OpenCode's built-in todos during compaction          │
│  • No additional storage required                            │
│  • Delegated to OpenCode's native features                   │
└─────────────────────────────────────────────────────────────┘

Layer 1: Workspace Memory

Purpose

Long-term memory that persists across sessions within the same workspace. Perfect for:

  • Project conventions and patterns
  • Important decisions that span sessions
  • User preferences for this codebase

Storage

  • Location: ~/.local/share/opencode-working-memory/workspaces/{workspaceKey}/workspace-memory.json
  • Workspace Key: First 16 chars of sha256(realpath(workspaceRoot))
  • Schema:
    {
      version: 1,
      workspace: { root: string, key: string },
      limits: { maxRenderedChars: 5200, maxEntries: 28 },
      entries: LongTermMemoryEntry[],
      updatedAt: string
    }
    

Entry Types

Type Purpose Example
feedback User preferences "User prefers functional React components"
project Project-level info "This monorepo uses turborepo"
decision Important decisions "Use PostgreSQL for primary database"
reference Key references "API endpoints defined in src/api/"

Entry Sources

Source Confidence How Added
explicit 1.0 User said "remember this"
compaction 0.75 Extracted during compaction
manual varies Programmatically added

Memory Extraction

During compaction, the plugin scans for Memory candidates: sections:

Memory candidates:
- [decision] Use npm cache for plugin loading
- [project] This repo uses TypeScript with strict mode

Legacy Format: The plugin also accepts <workspace_memory_candidates> XML blocks for backward compatibility, but this format is deprecated.

Quality Gate: Not all candidates become memories. The plugin rejects:

  • Git commit hashes (e.g., abc1234)
  • Raw errors (e.g., Error: something failed)
  • Stack traces
  • Path-heavy facts (>50% paths)
  • Very short text (<20 chars)

Deduplication

Memories are deduplicated using canonical text matching:

  1. Normalize: lowercase, strip punctuation, collapse whitespace
  2. Hash the canonical text
  3. Keep the entry with highest confidence

System Prompt Injection

Workspace memory is injected at the top of every message:

<workspace_memory>
- [decision] Use npm cache for plugin loading, not npm link
- [project] This repo uses opencode-agenthub plugin system
- [reference] Storage: ~/.local/share/opencode-working-memory/...
</workspace_memory>

Layer 2: Hot Session State

Purpose

Track current session context automatically:

  • What files are you working on?
  • What errors are currently open?
  • What decisions were made recently?

Storage

  • Location: ~/.local/share/opencode-working-memory/workspaces/{workspaceKey}/sessions/{hashedSessionID}.json
  • Schema:
    {
      version: 1,
      sessionID: string,
      turn: number,
      updatedAt: string,
      activeFiles: ActiveFile[],
      openErrors: OpenError[],
      recentDecisions: SessionDecision[]
    }
    

Active Files

Automatically tracked from tool.execute.after events:

Action Weight
edit 50
write 45
grep 30
read 20

Files are ranked by: ACTION_WEIGHT[action] + count * 3

Open Errors

Tracked from tool.execute.after events when exitCode !== 0:

Category Trigger Pattern
typecheck TS####: or TypeScript errors
test Test failures
lint ESLint warnings/errors
build Build failures
runtime Error:, TypeError:, etc.

False Positive Guards:

  • Commands like git log, cat with "error" in output are ignored
  • Only actual command failures (exitCode !== 0) trigger errors
  • exitCode === undefined is ignored (no error created, no error cleared)

Error Fingerprinting

Errors are fingerprinted by:

  1. Extract error message summary
  2. Generate fingerprint: first 12 chars of sha256(summary)
  3. Group similar errors by fingerprint

recentDecisions

Short-term decisions made this session. Candidates for promotion to workspace memory during compaction.

System Prompt Injection

Hot session state is injected after workspace memory:

---

Hot session state (current session):

active_files:
- src/plugin.ts (edit, 18x)
- tests/plugin.test.ts (edit, 5x)

open_errors: (none)

recent_decisions:
- Use frozen workspace memory snapshots for cache stability

pending_memories:
- [decision] Parser supports 3 candidate formats

Layer 3: Native OpenCode State

Purpose

Delegate task tracking to OpenCode's native features.

Behavior

  • Uses OpenCode's built-in todos during compaction
  • No additional storage or injection required
  • Allows the agent to manage task lists natively

Plugin Hooks

The plugin hooks into OpenCode lifecycle events:

experimental.chat.system.transform

Injects workspace memory and hot session state into system prompt.

tool.execute.after

  • Tracks active files (read, grep, edit, write actions)
  • Tracks open errors from failed commands
  • Clears errors when commands succeed
  • Ignores exitCode === undefined (successful commands without explicit exit codes)

experimental.session.compacting

Extracts workspace memory candidates from conversation. Applies quality gate, deduplication, and source priority.

event (session.compacted, session.deleted)

  • session.compacted: Promote session decisions to workspace memory
  • session.deleted: Clean up session state files

Quality Guarantees

No False Positive Errors

// Bad: Would create false positive
"Error: something failed" in output

// Good: Actually failed
exitCode === 1 && output.includes("Error")

// Good: Actually succeeded
exitCode === 0 (clears errors for that category)

// Good: Ignore ambiguous cases
exitCode === undefined  skip error tracking

Negative Memory Filtering

// Correctly interpreted
"don't remember this"  NOT added to memory
"不要記住這個"  NOT added to memory
"remember this"  added to memory candidates

Canonical Deduplication

// Same memory (after normalization)
"Use npm cache for plugins"
"USE NPM CACHE for plugins!!"
"use npm cache for plugins."

// All map to same canonical key
canonical("Use npm cache for plugins") === "use npm cache for plugins"

Compaction Quality Gate

// Rejected (not valuable as long-term memory)
"4832b38 fix: something"  // git hash
"Error: something failed"  // raw error
"at Object.method (file.ts:42)"  // stack trace
"/Users/x/project/file.ts /Users/x/project/other.ts"  // path-heavy

// Accepted
"[decision] Use npm cache for plugin loading"  // good pattern

File System Layout

~/.local/share/opencode-working-memory/
└── workspaces/
    └── {workspaceKey}/
        ├── workspace-memory.json      # Long-term memory
        └── sessions/
            └── {hashedSessionID}.json  # Session state

Workspace Key

// First 16 chars of SHA-256 hash of workspace root realpath
const workspaceKey = sha256(realpath(workspaceRoot)).slice(0, 16)

Performance Considerations

Memory Budgets

Layer Max Chars Max Entries
Workspace Memory 5200 28
Hot Session State 1200 8 files, 3 errors

Injection Overhead

  • Workspace memory: ~200-500 chars per message
  • Hot session state: ~200-400 chars per message
  • Total: ~400-900 chars per message (minimal)

Storage Footprint

  • Workspace memory: ~2-5 KB per workspace
  • Session state: ~1-3 KB per session
  • Auto-cleanup on workspace/session deletion

Extension Points

Custom Memory Types

Add new types in src/types.ts:

export type LongTermType = "feedback" | "project" | "decision" | "reference" | "custom";

Custom Error Categories

Add new categories in src/types.ts:

export type ErrorCategory = "typecheck" | "test" | "lint" | "build" | "runtime" | "custom";

Custom Extraction Patterns

Modify src/extractors.ts to add new extraction patterns.

Migration Notes

Memory V1 to V2

The plugin automatically migrates old format files to the new three-layer architecture. No manual intervention needed.


Last Updated: April 2026
Implementation: src/plugin.ts, src/extractors.ts, src/workspace-memory.ts, src/session-state.ts