# OpenClaw Adapter

context-mode plugin for the [OpenClaw](https://github.com/openclaw) gateway, targeting **Pi Agent** sessions.

## Overview

**OpenClaw** is the gateway/platform that manages agent sessions, extensions, and tool routing. **Pi Agent** is OpenClaw's coding agent — it runs within OpenClaw and provides Read, Write, Edit, and Bash tools for software development tasks.

The context-mode adapter hooks into Pi Agent sessions specifically, intercepting tool calls to route data-heavy operations through the sandbox and tracking session events for compaction recovery.

### Supported Configurations

- **Pi Agent sessions** with coding tools (Read/Write/Edit/Bash) — fully supported.
- **Custom agents** with coding tools — may work but are untested. The adapter relies on tool names matching Pi Agent's conventions.

## Installation

### Quick install

```bash
npm run install:openclaw
```

This runs `scripts/install-openclaw-plugin.sh`, which handles building, extension setup, runtime registration, and gateway restart.

### Prerequisites

- **Node.js** must be in PATH (required for the build and registration steps)
- **OpenClaw must have been started once** — the script needs `openclaw.json`, which OpenClaw creates on first launch
- **`OPENCLAW_STATE_DIR`** must point to your OpenClaw state directory (default: `/openclaw`). Pass it as an argument: `npm run install:openclaw -- /path/to/state`

### Manual install

For advanced users or custom setups:

```bash
bash scripts/install-openclaw-plugin.sh [OPENCLAW_STATE_DIR]
```

See [`scripts/install-openclaw-plugin.sh`](../../scripts/install-openclaw-plugin.sh) for details.

### Troubleshooting

**"openclaw.json not found"**
OpenClaw creates this file on first launch. Start OpenClaw once (`openclaw gateway start`), then re-run the install script. This is the most common issue for users who install context-mode before ever starting OpenClaw.

**"OPENCLAW_STATE_DIR (/path) does not exist. Is OpenClaw installed?"**
The state directory doesn't exist at the expected path. If you installed OpenClaw via npm (not git clone), check where it stores state — common locations are `~/.openclaw` or `/openclaw`. Pass the correct path: `npm run install:openclaw -- /path/to/state`.

**Plugin installed but not loading**
Clear the jiti cache (`rm -f /tmp/jiti/context-mode-*.cjs`) and restart the gateway. If the issue persists, verify the plugin appears in `openclaw plugins list`.

**Plugin loads but `ctx_*` tools are missing from the agent's tool list**
The plugin registers its hooks via `api.on(...)` / `api.registerCommand(...)`, but the agent-callable `ctx_*` tools live in the MCP server (`server.bundle.mjs`). OpenClaw surfaces them by spawning the server as an MCP sidecar declared in `mcp.servers.context-mode`. The install script writes this entry automatically (step 5); if you configured OpenClaw manually, verify with `openclaw mcp list` and add it if missing:

```bash
openclaw mcp set context-mode \
  "{\"command\":\"node\",\"args\":[\"/absolute/path/to/context-mode/server.bundle.mjs\"]}"
openclaw gateway restart
```

After the restart, the agent's tool inventory should include `context-mode__execute`, `context-mode__search`, `context-mode__fetch-index`, and the rest of the `ctx_*` surface (OpenClaw prefixes MCP-sourced tools with the server name).

## Hook Registration

The adapter uses two different registration APIs, matching OpenClaw's internal architecture:

- **`api.on()`** for lifecycle and tool hooks: `session_start`, `before_tool_call`, `after_tool_call`, `before_compaction`, `after_compaction`, `before_prompt_build`, `before_model_resolve`. These are typed event emitters with structured payloads.
- **`api.registerHook()`** for command hooks: `command:new`, `command:reset`, `command:stop`. These use colon-delimited names and the generic hook registration system.

Using the wrong API (e.g., `api.registerHook("before_tool_call", ...)`) registers silently but the hook never fires. This distinction is critical.

### Synchronous `register()`

OpenClaw silently discards the return value of `register()`. If `register()` is async, all hooks registered inside it are lost. The adapter uses the initPromise pattern:

```typescript
register(api): void {
  const initPromise = (async () => { /* async setup */ })();
  api.on("after_tool_call", async (e) => {
    await initPromise;
    // handle event
  });
}
```

## Session Continuity

| Hook | Method | Status |
|---|---|---|
| `after_tool_call` | `api.on()` | Working |
| `before_compaction` | `api.on()` | Working |
| `session_start` | `api.on()` | Working |
| `command:new` | `api.registerHook()` | Working |
| `command:reset` | `api.registerHook()` | Working |
| `command:stop` | `api.registerHook()` | Working |

### Graceful Degradation

If compaction hooks fail to fire (e.g., on older OpenClaw versions), the adapter falls back to **DB snapshot reconstruction** — rebuilding session state from the events already persisted in SQLite by `after_tool_call`. This produces a less precise snapshot than the PreCompact path but preserves critical state (active files, tasks, errors).

## Previously Known Upstream Issues

Both issues below have been resolved in upstream OpenClaw:

- **[#4967](https://github.com/openclaw/openclaw/issues/4967)** — Compaction hooks not firing. Closed as duplicate of [#3728](https://github.com/openclaw/openclaw/issues/3728); fix merged.
- **[#5513](https://github.com/openclaw/openclaw/issues/5513)** — `api.on()` hooks not invoked for tool lifecycle events. Fixed in [PR #9761](https://github.com/openclaw/openclaw/pull/9761).

## Minimum Version

**Required: OpenClaw >2026.1.29**

This is the first release that includes the `api.on()` fix from [PR #9761](https://github.com/openclaw/openclaw/pull/9761), which shipped on 2026-01-29.

**What breaks on older versions:** Lifecycle hooks registered via `api.on()` — including `before_compaction`, `after_compaction`, `session_start`, and tool interception hooks — may silently fail to fire.

**Graceful degradation:** If compaction hooks don't fire, the adapter falls back to DB snapshot reconstruction, rebuilding session state from events already persisted by `after_tool_call`. This produces a less precise snapshot than the PreCompact path but preserves critical state (active files, tasks, errors). The adapter will not crash on older versions, but compaction recovery quality will be reduced.

## Workspace Routing

The adapter includes a workspace router (`src/adapters/openclaw/workspace-router.ts`) that resolves project paths from Pi Agent session metadata, ensuring session databases and routing instructions are scoped per-workspace.

## Key Files

| File | Purpose |
|---|---|
| `src/adapters/openclaw/plugin.ts` | Main plugin entry (sync register, initPromise pattern) |
| `src/adapters/openclaw/workspace-router.ts` | Workspace path resolution for session scoping |
| `.openclaw-plugin/` | Plugin manifest (index.ts, openclaw.plugin.json, package.json) |
| `scripts/install-openclaw-plugin.sh` | One-shot installer |
