Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 39 additions & 14 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,18 +51,26 @@ Tests verify:
### File Structure
```
opencode-working-memory/
├── index.ts # Plugin entry point (exports PluginModule)
├── index.ts # Dual V1/V2 server entry point
├── tui.ts # TUI entry point discovered beside index.ts
├── src/
│ ├── plugin.ts # Main plugin implementation
│ ├── runtime.ts # Host-agnostic memory logic
│ ├── codebuddy-bridge.ts # Two-way Markdown bridge with CodeBuddy memory
│ ├── oh-my-pi-bridge.ts # Two-way Markdown bridge with oh-my-pi local memory
│ ├── plugin.ts # V1 adapter
│ ├── plugin-v2.ts # V2 adapter
│ ├── tui-plugin.ts # V2 TUI + V1 TUI dual export
│ ├── tui-plugin-v1.ts # Legacy V1 TUI implementation
│ ├── extractors.ts # Memory extraction logic
│ ├── workspace-memory.ts # Workspace memory management
│ ├── session-state.ts # Session state tracking
│ ├── storage.ts # File storage utilities
│ ├── paths.ts # Path utilities
│ ├── opencode.ts # OpenCode SDK types
│ ├── opencode.ts # V1 OpenCode SDK helpers
│ └── types.ts # Type definitions
├── tests/
│ ├── plugin.test.ts # Plugin hook tests
│ ├── plugin.test.ts # V1 adapter and runtime hook tests
│ ├── plugin-v2.test.ts # V2 adapter and TUI tests
│ ├── extractors.test.ts # Extractor tests
│ └── workspace-memory.test.ts # Workspace memory tests
├── package.json # Plugin manifest
Expand Down Expand Up @@ -221,16 +229,26 @@ const canonical = normalizeText(text); // Lowercase, strip punctuation, collaps

### Plugin Entry Point

`index.ts` exposes both host adapters from one default export:

```typescript
// index.ts
import { WorkingMemoryPlugin } from "./src/plugin-v2.ts";
import { MemoryV2Plugin } from "./src/plugin.ts";

export default {
id: "working-memory",
server: MemoryV2Plugin,
...WorkingMemoryPlugin, // V2: { id, setup }
server: MemoryV2Plugin, // V1: server(input, options)
};
```

Shared behavior lives in `src/runtime.ts`. The adapters only translate host
payloads:

- `src/plugin.ts` — V1 string hooks and `client.session.*` calls.
- `src/plugin-v2.ts` — `ctx.session.hook`, `ctx.tool.hook`, `ctx.event.subscribe`.
- `src/tui-plugin.ts` — V2 keymap/slash command plus the V1 TUI export.

### Workspace Memory Files

- **Location**: `~/.local/share/opencode-working-memory/workspaces/{workspaceKey}/workspace-memory.json`
Expand Down Expand Up @@ -261,25 +279,32 @@ export default {

## Plugin Hooks

### `experimental.chat.system.transform`
### `experimental.chat.system.transform` (V1) / `session.hook("context")` (V2)

Injects workspace memory and hot session state into system prompt.
Injects workspace memory and hot session state into the system prompt. V2 pushes
one `{ type: "text", text }` part per memory block. The same hook runs the
CodeBuddy and oh-my-pi Markdown bridges (throttled by `syncIntervalMs`) and
appends their memory indexes.

### `tool.execute.after`
### `tool.execute.after` (V1) / `tool.hook("execute.after")` (V2)

- Tracks active files (read, grep, edit, write actions)
- Tracks open errors from failed commands
- Clears errors when commands succeed
- Ignores `exitCode === undefined`
- V2 reads `metadata.exit` and also records `todowrite` snapshots

### `experimental.session.compacting`
### `experimental.session.compacting` (V1) / `session.hook("compaction")` (V2)

Extracts workspace memory candidates from conversation, applies quality gate and deduplication.
V1 replaces the compaction prompt with a `---`-free template. V2 keeps
OpenCode's own compaction prompt and appends the memory rules and numbered refs
as a system part.

### `event`
### `event` (V1) / `ctx.event.subscribe()` (V2)

- `session.compacted`: Promote session decisions to workspace memory
- `session.deleted`: Clean up session state files
- `session.compacted` (V1) / `session.compaction.ended` (V2): parse the summary
and promote memory candidates to workspace memory
- `session.deleted`: promote owned pending memories, then clean up session state

## Debugging & Testing

Expand Down
32 changes: 32 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,38 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [1.8.0] - 2026-10-21

### Added

- Added an optional two-way CodeBuddy memory bridge, an integration for sharing workspace memory with other local harness agents. OpenCode exports active workspace memories as `opencode-<id>.md` files into CodeBuddy's project memory directory and maintains a marked block in its `MEMORY.md`; CodeBuddy topic files are indexed in the OpenCode prompt and can be promoted with `shared: true`.
- Added an optional two-way bridge for oh-my-pi's `local` memory backend. OpenCode exports memories as `[opencode:<id>]` lesson lines in `learned.md`, imports edits and deletions back, indexes oh-my-pi lessons, and imports recent ones as `omp`-tagged memories.
- Added `tests/codebuddy-bridge.test.ts` for export, edit import, shared-file import, sensitivity filtering, idempotency, and deletion handling.
- Added `tests/oh-my-pi-bridge.test.ts` for Markdown preservation, edit import, deletion handling, lesson import, sensitivity filtering, idempotency, and missing-file recovery.
- Added `atomicWriteText` to the storage layer for generated Markdown mirrors.

### Changed

- CodeBuddy edits to `opencode-*.md` files are imported back into workspace memory; deleting one supersedes the memory.
- The bridge is disabled by default and skips credential-looking files unless `includeSensitive` is enabled.

## [1.7.0] - 2026-10-21

### Added

- Added an OpenCode V2 plugin adapter (`src/plugin-v2.ts`) using `Plugin.define`, `ctx.session.hook("context"|"compaction")`, `ctx.tool.hook("execute.after")`, and `ctx.event.subscribe()`.
- Added a V2 TUI plugin that registers the `/memory` slash command through `ctx.keymap.layer` and native dialogs.
- Added `tui.ts` beside `index.ts` so V2 discovers the TUI entrypoint for local directory plugins; published packages expose it through `exports["./tui"]`.
- Added `tests/plugin-v2.test.ts` covering hook registration, system-part injection, shell error tracking, `todowrite` snapshots, compaction summaries, session cleanup, and the TUI command.

### Changed

- Extracted host-agnostic memory behavior into `src/runtime.ts`; `src/plugin.ts` is now the V1 adapter.
- `index.ts` exports both shapes (`setup` for V2, `server` for V1) so one package works on both hosts.
- V2 keeps OpenCode's own compaction prompt and appends memory rules and numbered refs as a system part instead of replacing the prompt.
- V2 records todo snapshots from the `todowrite` tool because V2 has no session todo read API.
- Workspace memory and session state paths are unchanged, so existing memory survives the upgrade.

## [1.6.7] - 2026-05-31

### Fixed
Expand Down
138 changes: 131 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ Working memory is context that **remembers what matters, fades what changes, and

OpenCode Working Memory preserves project decisions, preferences, and references across compactions and sessions, while keeping active files and unresolved errors fresh for the current session — with no manual tools or extra LLM/API calls.

It can also share that memory with other local coding agents. **CodeBuddy Code and oh-my-pi are supported today; support for more local harness agents is in progress.**

## Why This Exists

OpenCode compaction keeps conversations manageable, but important context can still get lost over time.
Expand All @@ -32,6 +34,7 @@ Use it when you want your agent to remember things like:
| Inspect memory locally | Use `/memory` in the OpenCode TUI to browse status, help, and searchable current `[M#]` memories. |
| Stay out of the way | Memory is injected automatically and piggybacks on OpenCode compaction — no manual tools, no extra LLM/API calls. |
| Keep memory clean | Quality guards filter noise, redact credentials, dedupe repeats, and let weak memories fade. |
| Share memory across agents | A two-way bridge lets other local harness agents read and edit the same workspace memory. CodeBuddy Code and oh-my-pi are supported now; more harnesses are in progress. |

```text
remember this ──► workspace memory ──► /memory
Expand All @@ -42,7 +45,33 @@ compaction ─────► reinforce / replace ──► selective prompt con

## Installation

New users: add OpenCode Working Memory to both OpenCode plugin configs.
### OpenCode V2 (recommended)

Add the package to the `plugins` array in `opencode.json`:

```json
{
"$schema": "https://opencode.ai/config.json",
"plugins": ["opencode-working-memory"]
}
```

V2 discovers the CLI/TUI entrypoint from the package's `./tui` export automatically, so there is no separate `tui.json` step. Restart OpenCode after changing the config.

To run a local checkout, point at the plugin directory (V2 requires a directory, not an `index.ts` file):

```json
{
"$schema": "https://opencode.ai/config.json",
"plugins": ["/home/mirro/coding/opencode-working-memory"]
}
```

Local directory plugins keep the server and TUI entrypoints side by side: V2 discovers `tui.ts` next to `index.ts`. The published package exposes the same module through `exports["./tui"]`.

### OpenCode V1

V1 keeps the original two-file setup:

`.opencode/opencode.json`:

Expand All @@ -62,13 +91,11 @@ New users: add OpenCode Working Memory to both OpenCode plugin configs.
}
```

Existing users: keep your current `.opencode/opencode.json` config and add only the `.opencode/tui.json` block above to enable the native `/memory` TUI menu.

Then restart OpenCode. Memory activates automatically, and `/memory` appears in the TUI slash command menu.
Both adapters share the same on-disk memory format, so switching between V1 and V2 keeps existing workspace memory.

### Strict OpenAI-Compatible Providers
### Strict OpenAI-Compatible Providers (V1 only)

Some providers, including GWDG/Qwen endpoints, reject requests with multiple system messages. If your provider returns `System message must be at the beginning`, enable merged system prompts in `.opencode/opencode.json`:
Some V1 providers, including GWDG/Qwen endpoints, reject requests with multiple system messages. If your provider returns `System message must be at the beginning`, enable merged system prompts in `.opencode/opencode.json`:

```json
{
Expand All @@ -77,7 +104,7 @@ Some providers, including GWDG/Qwen endpoints, reject requests with multiple sys
}
```

This keeps memory context available by appending workspace memory and hot session state into the first system message. Leave this option off for providers that already accept multiple system messages; the default separated prompts are more cache-friendly.
This keeps memory context available by appending workspace memory and hot session state into the first system message. Leave this option off for providers that already accept multiple system messages; the default separated prompts are more cache-friendly. V2 injects memory as separate system parts and ignores this option.

Fallback for older OpenCode/plugin hosts that do not pass plugin tuple options:

Expand Down Expand Up @@ -111,6 +138,103 @@ Use `/memory` when you want to inspect what the agent currently remembers withou

Compaction output already appears through OpenCode's built-in conversation flow. This plugin does not add duplicate compaction notices.

## Shared Memory with Other Local Harness Agents

OpenCode Working Memory can act as the shared memory layer for the coding agents you already run locally, so a decision captured in OpenCode is still available when you switch to another harness. **CodeBuddy Code and oh-my-pi are supported today; compatibility with other local harness agents is in progress.**

Each integration follows the same rules: it uses the target agent's native memory format and directory, only writes files that OpenCode owns, never rewrites another agent's files, and stays disabled until you configure it. No hooks or patches are required in the other agent.

### CodeBuddy Code

The optional CodeBuddy bridge shares memory both ways without changing CodeBuddy's code. It is disabled by default because it writes into CodeBuddy's memory directory.

Enable it in V2 `opencode.json`:

```json
{
"$schema": "https://opencode.ai/config.json",
"plugins": [
{
"package": "opencode-working-memory",
"options": {
"codeBuddyBridge": {
"includeGlobal": true,
"includeSensitive": false
}
}
}
]
}
```

For a local checkout, use the plugin directory as `package`.

How it works:

- OpenCode exports each active workspace memory as `opencode-<id>.md` in CodeBuddy's project memory directory: `~/.codebuddy/projects/<compressed-workdir>/memory/`.
- It maintains one marked block in that directory's `MEMORY.md`; CodeBuddy's own index lines are preserved.
- CodeBuddy's project (and optionally global) topic files are indexed in OpenCode's system prompt. The agent reads the full file on demand with `read`/`grep`, so the 51-file / 17 KB index does not have to fit in OpenCode's memory budget.
- Editing an `opencode-*.md` file and saving it updates the matching OpenCode memory on the next OpenCode request. Deleting it supersedes the memory.
- To promote a CodeBuddy topic file into OpenCode's own JSON memory (and compaction flow), add `shared: true` to its frontmatter.
- Files whose names or descriptions look credential-related (`password`, `token`, `ssh`, `vps`, `access`, etc.) are skipped unless `includeSensitive: true`.

Defaults:

| Option | Default | Purpose |
|---|---|---|
| `includeGlobal` | `false` | Also index/import `~/.codebuddy/memories/global` |
| `includeSensitive` | `false` | Include credential-looking files |
| `maxIndexEntries` | `14` | CodeBuddy entries in the OpenCode prompt |
| `maxIndexChars` | `1600` | Character budget for that index |
| `maxImportedShared` | `12` | CodeBuddy `shared: true` files imported into JSON |
| `syncIntervalMs` | `3000` | Minimum gap between full scans |

CodeBuddy reads its memory at session start, so start a new CodeBuddy session to see newly exported OpenCode memories. OpenCode sees CodeBuddy edits on its next request.

### oh-my-pi (local memory backend)

The oh-my-pi bridge shares memory with omp's `local` memory backend, which stores plain Markdown. Keep `memory.backend: local` in `~/.omp/agent/config.yml`, then enable the bridge in V2 `opencode.json`:

```json
{
"$schema": "https://opencode.ai/config.json",
"plugins": [
{
"package": "opencode-working-memory",
"options": {
"ohMyPiBridge": {
"includeSensitive": false
}
}
}
]
}
```

How it works:

- oh-my-pi keeps per-project memory under `~/.omp/agent/memories/<encoded-workdir>/` and injects `memory_summary.md` plus `learned.md` into its system prompt. Only the `local` backend is supported; `mnemopi`, `hindsight`, and `sharpshooter` are not.
- OpenCode exports active workspace memories as lesson lines prefixed `[opencode:<id>]` in `learned.md`, at the head of the lesson list. Every other line in the file is preserved.
- oh-my-pi `learn` lessons are indexed in OpenCode's prompt, and up to 12 recent lessons are imported as read-mostly JSON memories tagged `omp`. Deleting an oh-my-pi lesson supersedes its imported copy, and re-adding it reactivates that memory.
- Editing an `[opencode:<id>]` line updates the matching OpenCode memory on the next request. Deleting the line supersedes it.
- The bridge never writes `memory_summary.md`, `MEMORY.md`, or `skills/`; those belong to oh-my-pi's consolidation pipeline.
- Credential-looking lessons are skipped unless `includeSensitive: true`.

Defaults:

| Option | Default | Purpose |
|---|---|---|
| `homeDir` | `~/.omp` | oh-my-pi config root |
| `agentDir` | `<homeDir>/agent` | Agent state directory |
| `includeSensitive` | `false` | Include credential-looking lessons |
| `maxExported` | `12` | OpenCode memories written to `learned.md` |
| `maxImportedLessons` | `12` | oh-my-pi lessons imported into JSON memory |
| `maxIndexEntries` | `14` | oh-my-pi lessons listed in the OpenCode prompt |
| `maxIndexChars` | `1600` | Character budget for that index |
| `syncIntervalMs` | `3000` | Minimum gap between full scans |

oh-my-pi builds its memory prompt when a session starts, so start a new omp session to see newly exported OpenCode memories. OpenCode sees oh-my-pi edits on its next request.

## How It Works

OpenCode Working Memory adds durable memory without making extra LLM/API calls.
Expand Down
Loading