Skip to content

detain/openclaw-sess

Repository files navigation

🦞 sess

Named session snapshots for OpenClaw.
Save, load, rename, copy, and branch your conversations — one command.

tests npm ClawHub native plugin MIT license node


Why sess?

Your OpenClaw conversations are valuable. The plan you brainstormed at 2am, the debugging context you built up over three hours, the prompt chain that finally gave you the right answer — all of it lives in one linear transcript that any /new command blows away forever.

sess turns that transcript into something you can checkpoint, branch, and return to, the same way git turns a directory of files into something you can commit, branch, and return to.

sess save before-refactor      # checkpoint
# …try a risky prompt direction…
sess load before-refactor      # nope, roll back
sess copy before-refactor v2   # branch into a new line of thought

One command. Seven verbs. Zero surprises.


Features

💾 Named snapshots save, load, list, delete, rename, copy, new — the seven verbs you'd expect and nothing you wouldn't
🌿 Branching sess copy main side-quest forks a conversation; explore without losing your place
🛟 Auto-backup before risky ops Every load and new first snapshots the live session as pre-load~<ts> — you can't accidentally clobber an hour of work
Auto-save (opt-in) Roll-forward autosave~ slot after every turn; turn your laptop off mid-thought and pick up exactly where you left off
🔖 Tags & notes sess save v1 --note="before the refactor" --tags=env=dev,ticket=OPS-142
🧹 Retention Config cap + background pruner; auto-managed snapshots evict first, your named ones stay
🛠️ Five surfaces, one dispatcher Slash command · agent tool · CLI subcommand · HTTP API · gateway RPC
🔒 Safe by default 0600 files in a 0700 directory, strict name allowlist, atomic tmp → rename writes, never concatenates user input into paths
🧩 Pure native plugin Uses definePluginEntry, api.runtime.*, and typed registerTool / registerHttpRoute / registerService — no reach-ins, no private APIs

Install

From ClawHub (recommended)

openclaw plugins install sess

From npm

openclaw plugins install @detain/openclaw-sess

From source (local dev)

git clone https://github.com/detain/openclaw-sess.git
openclaw plugins install ./openclaw-sess

Then enable it in your OpenClaw config:

{
  "plugins": {
    "entries": {
      "sess": {
        "enabled": true,
        "config": {
          "autoSave": true,
          "maxSnapshots": 100,
          "allowOverwrite": false
        }
      }
    }
  }
}

Restart the gateway and you're done:

openclaw gateway restart
openclaw plugins inspect sess    # verify it loaded cleanly

Quick start

# Start a conversation with your agent, do some work, then:
sess save working-context

# List what you've saved
sess list
# NAME                      MSGS   SIZE     UPDATED     MODEL
# ────────────────────────────────────────────────────────────
# working-context           23     14.2K    12s ago     openai/gpt-4o

# Branch it to try something risky
sess copy working-context experiment

# …try the risky thing, doesn't pan out…
sess load working-context   # back where you were, automatically

# Start fresh but keep the old context available
sess new --backup

Command reference

sess save <name>

Snapshot the active session. Fails if <name> already exists unless you pass --overwrite or set allowOverwrite: true in config.

sess save v1
sess save v2 --note="after adding the cache layer"
sess save prod-debug --tags=env=prod,ticket=OPS-142

sess load <name>

Restore a snapshot into the active session. The live transcript is automatically backed up as pre-load~<timestamp> first; pass --no-backup to skip.

sess load v1
sess load v1 --no-backup

sess list

Show every snapshot, newest first.

sess list
sess list --prefix=v           # only names starting with "v"
sess list --tag=env=prod       # only snapshots tagged env=prod
sess list --limit=5            # cap output
sess list --include-auto       # show autosave~, pre-load~*, etc.
sess list --json               # machine-readable output

sess new [<name>]

Start fresh. Auto-backs up the current session first as pre-new~<ts>.

sess new                              # clear and go
sess new side-quest                   # clear and save the fresh state as "side-quest"
sess new forked --from=v1             # seed the new session from snapshot v1
sess new --no-backup                  # skip the pre-new backup

sess copy <src> <dst>

Duplicate a snapshot. The primary way to branch a conversation.

sess copy main experiment
sess copy v1 v1-try-openrouter --note="swapping providers"

sess rename <old> <new>

sess rename experiment v3-final

sess delete <name>

Remove a snapshot. Supports prefix-wildcard mass-delete with a required --confirm:

sess delete dead-end
sess delete --prefix=pre-load~ --confirm       # clean up auto-backups

Aliases

ls, showlist · restore, getload · rm, remove, deldelete · mv, moverename · cp, clone, branchcopy · reset, freshnew


Five ways to drive it

sess exposes one dispatcher through five surfaces so it fits whatever workflow you're already in.

1. Slash command (in-chat)

/sess save v1
/sess list

2. Agent tool (let the model manage its own memory)

When exposeTool: true (default), your agent gains a sess_manage tool it can invoke during a turn:

"Save this as debug-session-aug-5 before we try the rewrite."

→ model calls sess_manage({ action: "save", name: "debug-session-aug-5" })

3. CLI

openclaw sess list
openclaw sess save v1 --note="works!"
openclaw sess load v1

4. HTTP API

All endpoints require gateway auth. See HTTP API below for the full shape.

curl -H "Authorization: Bearer $TOKEN" \
  https://localhost:7777/sess/list

curl -X POST -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name":"v1","note":"from cron"}' \
  https://localhost:7777/sess/save

5. Gateway RPC (in-process)

From another plugin or a gateway client:

await gateway.call("sess.invoke", { action: "list" });
await gateway.call("sess.invoke", { action: "save", name: "v1" });

Configuration

All fields in plugins.entries.sess.config:

Field Type Default Description
storageDir string "sessions" Where snapshots live. Relative → resolved against the agent dir. Supports ~ expansion.
autoSave boolean false Snapshot the live session into autoSaveSlot after every turn.
autoSaveSlot string "autosave~" Name of the rolling auto-save snapshot (trailing ~ marks it auto-managed).
maxSnapshots integer 50 Hard cap. 0 disables pruning.
allowOverwrite boolean false Default behavior of sess save when a name already exists.
exposeTool boolean true Register sess_manage as an optional agent tool.
httpRoutes boolean true Mount /sess/* HTTP routes on the gateway.
pruneIntervalMinutes integer 60 Background retention tick. 0 disables.
tags object {} Default tags applied to every new snapshot.

HTTP API

All requests require normal OpenClaw gateway auth.

Method Path Body Response
GET /sess/list — query params: prefix, tag, limit, includeAuto SnapshotMeta[]
POST /sess/save { name, note?, tags?, overwrite? } { text, data: SnapshotMeta }
POST /sess/load { name, backup? } { text, data: SnapshotMeta }
POST /sess/new { name?, from?, backup?, note? } { text, data }
POST /sess/copy { from, to, overwrite?, note? } { text, data: SnapshotMeta }
POST /sess/rename { from, to, overwrite? } { text, data: SnapshotMeta }
POST /sess/delete { name } or { prefix, confirm: true } { text, data }

Errors come back with status 400 and an { error } or { text } body.


Architecture

┌──────────────────────── user surfaces ────────────────────────┐
│  slash command   agent tool   CLI   HTTP routes   gateway RPC │
└──────────┬──────────────────────────────────────────┬─────────┘
           │  all five normalize into an argv[]        │
           ▼                                           ▼
┌──────────────────────────────────────────────────────────────┐
│                      src/command.ts                           │
│      parseArgv + dispatch()  →  subcommand handlers           │
└──────────────┬────────────────────────────────┬──────────────┘
               │                                │
               ▼                                ▼
      ┌────────────────┐              ┌────────────────────┐
      │  src/store.ts  │              │ src/subcommands/*  │
      │  (filesystem)  │              │  (business logic)  │
      └────────────────┘              └────────────────────┘

Design rules this plugin follows:

  • Single dispatcher. Every surface (command / tool / CLI / HTTP / RPC) lowers to the same dispatch(rt, argv) call. One code path to test, one set of behaviors to document, no drift.
  • Strict name validation at the boundary. No user string touches the filesystem without passing NAME_PATTERN. Path traversal is structurally impossible.
  • Atomic writes. <name>.sess.json.tmprename(). A crash mid-write can leave an orphan tmp file but can't corrupt the live snapshot.
  • Runtime via createPluginRuntimeStore. No unguarded module-level let state — the runtime is registered on plugin init and throws a clear error if anything tries to reach it before register(api) has run.
  • Narrow SDK imports. openclaw/plugin-sdk/plugin-entry and openclaw/plugin-sdk/runtime-store only — we never reach through the monolithic root barrel.

Development

git clone https://github.com/detain/openclaw-sess.git
cd openclaw-sess
npm install
npm test                          # run vitest
npm run typecheck                 # tsc --noEmit

Running the full suite

npm test -- --run                 # one-shot
npm run test:watch                # watch mode

Installing locally for manual testing

openclaw plugins install ./
openclaw gateway restart
openclaw plugins inspect sess

FAQ

Where do snapshots actually live?

By default, <agentDir>/sessions/<name>.sess.json. Set storageDir in config to point elsewhere. Files are chmod 0600, directory is 0700.

What happens if I have two OpenClaw agents running?

Because storageDir resolves relative paths against the active agent dir, each agent gets its own snapshot pool by default. Set an absolute storageDir if you want them to share.

Is `sess load` destructive?

Not by default — we snapshot the live session as pre-load~<timestamp> first. Pass --no-backup to skip that, or sess delete --prefix=pre-load~ --confirm to clean them up later.

What's the `~` suffix about?

A convention, not syntax. Names ending in ~ are "auto-managed" and evict first when the retention cap kicks in. autosave~, pre-load~<ts>, and pre-new~<ts> all use it. Your named snapshots stay safe.

Does this work with OpenClaw bundles (Claude / Codex / Cursor)?

No — sess is a full native plugin (registers capabilities, has a runtime). Bundles can't do that. But you can freely mix: install sess as a native plugin alongside any bundles you're using.

Can I disable any of the five surfaces?

Yes. exposeTool: false drops the agent tool, httpRoutes: false drops the HTTP API. The slash command, CLI, and gateway RPC are always on because they have no security surface beyond what OpenClaw itself enforces.


Contributing

PRs welcome. Please:

  1. Keep the single-dispatcher invariant — every new feature should lower to an argv passed to dispatch().
  2. Add tests. There's no config option for "how much coverage do you want" — there's just the coverage.
  3. Don't add new top-level SDK subpath imports without checking scripts/lib/plugin-sdk-entrypoints.json in the OpenClaw repo.

License

MIT © Detain


Built with ☕ and too many conversations I wish I could go back to.

About

Named session snapshots for OpenClaw — save, load, rename, copy, and branch conversations from one unified sess command.

Topics

Resources

License

Stars

1 star

Watchers

0 watching

Forks

Packages

 
 
 

Contributors