npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

opencode-session-store

v1.2.1

Published

Persist all OpenCode session data to .opencode/sessions/ as structured JSON files

Readme

opencode-session-store

Persist all OpenCode session data to .opencode/sessions/ as structured JSON files.

Captures every message, tool call, sub-agent interaction, permission prompt, and model error — so nothing is lost when sessions are compacted or the context window fills up.

Installation

// .opencode/opencode.json
{
  "plugin": ["opencode-session-store"]
}

On next startup, OpenCode will automatically install and load the plugin.

What It Captures

| Data | Source | |---|---| | User prompts | client.session.messages() — diff-fetched on every event | | Assistant replies | same — role: "assistant" with text + reasoning + tool parts | | Tool calls & results | type: "tool" parts with input/output/error state | | Sub-agent tasks | type: "subtask" parts with agent name and prompt | | File attachments | type: "file" parts (images, documents, etc.) | | Permission dialogs | permission.updated / permission.replied events | | Model/provider errors | message.error on turns + session.error events | | Compaction markers | session.compacted event — timestamp logged | | Sub-agent hierarchy | session.created with parentID — parent→child links stored |

Storage Format

One file per session in .opencode/sessions/{sessionID}.json:

{
  "_id": "ses_abc123",
  "title": "Fix auth bug",
  "startedAt": "2026-07-29T10:28:32.000Z",
  "updatedAt": "2026-07-29T10:46:36.000Z",
  "compactedAt": null,
  "parentId": null,
  "subSessions": ["ses_child456"],
  "turns": [
    {
      "role": "user",
      "messageId": "msg_user_1",
      "timestamp": "2026-07-29T10:28:32.000Z",
      "parts": [{ "type": "text", "text": "Fix the auth bug in login.ts" }]
    },
    {
      "role": "assistant",
      "messageId": "msg_asst_1",
      "timestamp": "2026-07-29T10:28:35.000Z",
      "parts": [
        { "type": "thinking", "text": "Let me look at the login code..." },
        { "type": "text", "text": "I'll read the file first." },
        {
          "type": "tool",
          "toolName": "read",
          "toolInput": { "filePath": "src/login.ts" },
          "toolOutput": "function login...",
          "isError": false
        }
      ]
    },
    {
      "role": "assistant",
      "messageId": "msg_asst_2",
      "timestamp": "2026-07-29T10:28:45.000Z",
      "parts": [
        {
          "type": "subtask",
          "agent": "general",
          "prompt": "Find similar auth patterns",
          "description": "Search codebase for auth patterns"
        }
      ]
    },
    {
      "role": "assistant",
      "messageId": "msg_asst_3",
      "timestamp": "2026-07-29T10:29:00.000Z",
      "parts": [],
      "error": {
        "name": "APIError",
        "message": "The latest version of this model is only available hosted in China and requires explicit opt in",
        "statusCode": 403,
        "isRetryable": false
      }
    }
  ],
  "permissions": [
    {
      "type": "asked",
      "timestamp": "2026-07-29T10:28:50.000Z",
      "permission": "bash",
      "command": "rm -rf /tmp/test",
      "question": "Run this command?"
    },
    {
      "type": "replied",
      "timestamp": "2026-07-29T10:28:51.000Z",
      "permission": "bash",
      "allowed": false
    }
  ],
  "errors": [
    {
      "timestamp": "2026-07-29T10:29:00.000Z",
      "name": "APIError",
      "message": "The latest version of this model is only available hosted in China and requires explicit opt in",
      "statusCode": 403,
      "sessionID": "ses_abc123"
    }
  ]
}

Index File

A manifest of all sessions is kept at .opencode/sessions/index.json, sorted by startedAt ascending. This allows listing and ordering sessions chronologically without scanning the directory or loading every session file.

{
  "sessions": [
    {
      "id": "ses_abc123",
      "title": "Fix auth bug",
      "startedAt": "2026-07-29T10:28:32.000Z",
      "updatedAt": "2026-07-29T10:46:36.000Z",
      "parentId": null
    }
  ]
}

The index is kept in sync automatically — entries are added on session creation and updated whenever session metadata changes.

How It Works

  1. On session.created: creates the session file with metadata
  2. On session.updated / message.*: schedules a debounced sync (500ms)
  3. On session.compacted: marks compaction timestamp, does a full sync
  4. On permission.*: records permission prompts and user responses
  5. On tool.execute.after: triggers a sync to catch new tool results
  6. On session.error: records model/provider failures to the session-level errors array

Each sync calls client.session.messages() and diffs against known message IDs, appending only new turns. Assistant turns that fail with an error are kept even when they have no content parts; the error is stored on the turn.

Events Handled

  • session.created / session.updated / session.deleted / session.compacted / session.error
  • message.updated / message.part.updated / message.removed
  • permission.updated / permission.replied
  • tool.execute.after

Robustness

  • Session files are parsed leniently: trailing NUL bytes and stray control characters (corruption) are stripped before parsing, so a damaged file does not break the plugin.
  • If a session file is missing or unreadable, the next sync rebuilds it from the authoritative session.messages() API data instead of failing silently.
  • Per-session syncs are serialized so concurrent events cannot interleave read-modify-write cycles.
  • On startup the plugin scans existing session files and reports any corruption.

Diagnostics

Sync failures and detected corruption are written to .opencode/sessions/sync-errors.log (one line per event, timestamped). The plugin never prints to stdout.