Back to Openviking

OpenViking Memory Extension for Pi Coding Agent

examples/pi-coding-agent-extension/README.md

0.4.516.1 KB
Original Source

OpenViking Memory Extension for Pi Coding Agent

Long-term semantic memory for Pi sessions, powered by OpenViking. Recall happens automatically before every prompt, capture happens after every turn, and sessions are committed for persistent memory extraction — all via pi's native extension API.

Design informed by lessons from all three OpenViking agent plugins: synchronous recall from OpenClaw, production-hardened capture/ranking from Claude Code, and anti-patterns dodged from Hermes's stale prefetch approach. See DESIGN.md for the full design spec with comparison tables and event flow diagrams.

Quick Start

Prerequisites

  • Pi coding agent installed (npm i -g @mariozechner/pi-coding-agent)
  • Node.js 18+ (for the extension's TypeScript runtime)
  • An OpenViking server reachable — local or remote

1. Have an OpenViking server reachable

Either run one locally or point at a remote one. The quickstart guide walks through both options. Default port is 1933; local mode runs without authentication.

Verify it's up:

bash
curl http://localhost:1933/health   # or your remote URL

2. Install the extension

Copy the extension directory into pi's extension folder:

bash
mkdir -p ~/.pi/agent/extensions
cp -r examples/pi-coding-agent-extension ~/.pi/agent/extensions/openviking

Pi auto-discovers extensions in ~/.pi/agent/extensions/ — no explicit registration needed. The extension loads on next pi invocation.

3. Configure (optional)

The extension ships with defaults that work out-of-the-box for a local OpenViking server (http://127.0.0.1:1933, no auth). To connect to a remote server or tune behavior, edit ~/.pi/agent/extensions/openviking/config.json:

json
{
  "enabled": true,
  "endpoint": "https://your-openviking-server.example.com",
  "apiKey": "<your-api-key>",
  "account": "my-team",
  "user": "alice",
  "agentId": "pi"
}

All config fields can also be overridden via environment variables:

Env VarConfig fieldDefault
OPENVIKING_URLendpointhttp://127.0.0.1:1933
OPENVIKING_API_KEYapiKey"" (no auth)
OPENVIKING_ACCOUNTaccount""
OPENVIKING_USERuser""
OPENVIKING_AGENT_IDagentId"pi"

Env vars take priority over config.json.

4. Start Pi

bash
pi

The extension shows an [OpenViking] status line on startup. Tools (viking_search, viking_remember, etc.) are registered automatically. Memories persist across sessions — no additional setup.

Configuration Reference

Tuning fields

All fields below live in config.json. Defaults are shown.

FieldDefaultDescription
enabledtrueSet false to disable the extension entirely
endpoint(local)OpenViking server URL
apiKey""API key for remote servers
account""Multi-tenant account (X-OpenViking-Account)
user""Multi-tenant user (X-OpenViking-User)
agentId"pi"Agent identity (X-OpenViking-Agent)

Recall tuning

FieldDefaultDescription
recallBudget2000Token budget for inline recall content
recallMaxContentChars500Per-item content cap for search results
recallPreferAbstracttruePrefer L0 abstract over L2 full body when available
recallLimit6Max memories to inject per prompt
recallScoreThreshold0.35Min relevance score (0–1)
recallMinQueryLength3Skip recall for queries shorter than N characters

Capture tuning

FieldDefaultDescription
syncTurnstrueEnable auto-capture of conversation turns
captureMode"semantic""semantic" (always capture) or "keyword" (trigger-based)
captureMaxLength24000Max sanitized text length for the capture decision
captureAssistantTurnstrueInclude assistant turns (text + tool USE inputs)
captureToolResultsfalseInclude tool result output (noisy — off by default)
commitTokenThreshold20000Pending-token threshold for client-driven commit
commitOnShutdowntrueAuto-commit session on exit

Injection tuning

FieldDefaultDescription
profileBudget10000Token budget for user profile block
resumeContextBudget2000Token budget for archive overview on session resume
indexBudget2000Token budget for the knowledge index (map of what OV knows)

Misc

FieldDefaultDescription
mirrorMemoryWritestrueMirror built-in memory writes into the OV session
writeQueueFlushInterval5000Write queue flush interval (ms)
writeQueueFlushThreshold5Write queue flush after N turns
bypassPatterns[]Glob patterns to skip extension processing
logLevel"error""silent", "error", or "info"

Architecture

┌──────────────────────────────────────────────────────┐
│                    Pi Coding Agent                    │
│                                                      │
│  session_start  before_agent_start  context  turn_end│
│  session_before_compact  session_shutdown            │
└────────┬──────────────────┬───────────┬──────────────┘
         │                  │           │
         │  ┌───────────────▼───────────▼────────┐
         │  │   extension modules (.ts)           │
         │  │   client / sync / recall / tools    │──────►  OpenViking
         │  └─────────────────────────────────────┘        Server
         │                                                (HTTP API)
         │  ┌──────────────────────────────────────┐
         └──►  7 registered LLM tools              │
            │  viking_search / viking_read / …     │
            └──────────────────────────────────────┘

The extension is a single directory of TypeScript files loaded by pi's jiti transpiler — no build step, no npm dependencies, no MCP server. All communication goes over HTTP to the OpenViking REST API.

Event Flow

Pi EventExtension Action
session_startHealth check → create/find OV session → build profile block → build memory index → register tools
before_agent_startFallback tool registration (for pi -c resume) + inject archive overview
contextSearch OV with current prompt → inject <relevant-memories> block
turn_endExtract user + assistant turns → queue writes to OV session
session_before_compactCommit pending messages before pi rewrites the transcript
session_shutdownFinal commit so the last window is archived

Recall: Synchronous, Not Stale

Unlike Hermes's stale prefetch (recall from previous turn's query, injected one turn late), this extension searches OpenViking with the current user prompt via pi's context event. Results are injected into the same turn as <relevant-memories> blocks. This means:

  • First turn of a session gets relevant context immediately
  • Topic switches within a session get correct recall
  • No waiting for the next turn to see relevant memories

Memory Pollution Prevention

Before pushing turns to OpenViking, the extension strips injected context blocks (<openviking-context>, <relevant-memories>, ``) to prevent a self-referential pollution loop where recall context is captured back as user messages.

Tool Use Preservation

Tool capture preserves agent-authored inputs ([tool: read]\n<path>) and drops raw tool results by default. The memory extractor sees what the agent did, not megabytes of raw output.

LLM Tools

The extension registers 7 tools that pi's model can invoke on demand:

ToolDescription
viking_searchSemantic search across memories, resources, and skills
viking_readRead a viking:// URI at abstract / overview / full level
viking_browseList directory contents or stat a viking:// URI
viking_rememberStore a fact or preference into long-term memory
viking_forgetDelete a memory by URI or search query
viking_add_resourceIngest a URL into OpenViking for indexed retrieval
viking_archive_expandExpand an archived session back into raw conversation

The canonical /viking command (type /viking in pi's chat) displays connection status, session info, and accepts commit for manual synchronous commit.

Compared to Pi's Built-in Memory

Pi has a built-in MEMORY.md file system. This extension complements it:

FeatureBuilt-in MEMORY.mdOpenViking extension
StorageFlat markdownVector DB + structured extraction
SearchLoaded into context wholesaleSemantic similarity + ranking + token budget
ScopePer-projectCross-project, cross-session, cross-agent
CapacityContext-limitedUnlimited (server-side storage)
ExtractionManual rulesLLM-powered entity / preference / event extraction
SubagentsSame as parentIsolated session + typed agent namespace

Compared to Claude Code Plugin

Both plugins share the same core design (informed by each other):

FeatureClaude Code PluginPi Extension
ArchitectureHook scripts (.mjs) + MCP delegationNative TypeScript extension
Recall timingSynchronous (UserPromptSubmit hook)Synchronous (context event)
Tool deliveryOV server's MCP endpoint (9 tools)pi.registerTool() (7 tools)
Write pathDetached worker (async)Async promise (pi's event loop)
Installationclaude plugin install + setup scriptCopy directory → auto-discovered
Memory indexNone (flashlight search model)Built (map model — model sees what OV knows)
Subagent isolationExplicit hook managementNatural process-level isolation

Extension Structure

See DESIGN.md for the full design specification — comparison of all three OV plugins, detailed event flow, design rationale, and implementation guidance useful for building OV extensions for any agent harness.

pi-coding-agent-extension/
├── config.json          # Default configuration (edit to customize)
├── config.ts            # Config loader (defaults + config.json merge)
├── client.ts            # OpenViking HTTP client (fetch + response envelope)
├── sync.ts              # Turn capture, write queue, session lifecycle
├── recall.ts            # Synchronous recall with ranking + budget
├── tools.ts             # 7 registered LLM tools + /viking command
├── index-builder.ts     # Memory index builder (knowledge map)
├── index.ts             # Extension entry point (event handlers)
└── README.md

All TypeScript files are loaded directly by pi's built-in jiti transpiler — zero dependencies beyond Node.js.

Troubleshooting

SymptomCauseFix
Extension not loadingenabled: false in config.jsonSet "enabled": true
No recall on first promptOpenViking server not running or wrong URLcurl http://localhost:1933/health
Tools not showing after pi -c resumeKnown pi issue (tools not re-registered on resume)Workaround built in — tools register in before_agent_start
Extension crashes on loadWrong OV server URL or network issueCheck logLevel and server accessibility
No memories extractedWrong embedding/extraction model in OV configCheck OV's embedding / vlm configuration

License

Apache-2.0 — same as OpenViking.