I set up native Claude Code to function as a long-term assistant à la OpenClaw. Here's my dev log. The TL;DR is that it kinda-sorta works, but with caveats. The building blocks are there but wiring it all together is painful!

If you just want the code, it's here.

The goal

I want to run a persistent Claude Code session in a container with root access so that the agent can go nuts, but not escape the container. I've experimented with nanobot via signal-cli. It was fine, but had two downsides:

  1. Signal doesn't like third-party clients so signal-cli gets blocked sometimes, and
  2. I already pay for a Claude Max subscription, and I'd rather use that than pay for API costs.

I could of course use e.g. Discord or Telegram instead of Signal, but I'm already sending lots of sensitive data to Anthropic -- I'd prefer not to also send it in plaintext to some other third party. And you'll never catch me installing Telegram.

My requirements:

  • The agent should run a persistent session (with memory etc.)
  • It should run in a locked-down container with root access so it can install whatever it needs
  • I want to pay for this via my Claude subscription using the official Claude Code CLI

Will Anthropic be mad at me?

As I'm writing this, Anthropic has just blocked OpenClaw from using Claude subscriptions. They're trying to be intentional about their capacity and prioritizing usage of their own products. Fair enough!

This got me wondering if my long-running agent was really in the spirit of how they intend Claude Code to be used, or if it runs the risk of being blocked, too. I concluded that it's probably okay: the goal is to use their own tool to its full potential, and with the recent release of channels it sure seems like they're encouraging OpenClaw-like usage.

Initial attempt

There are two main paths towards this: either via remote control or channels. Using channels would require me to still communicate via a third-party messaging tool, so I decided to try remote control. This lets me chat to Claude via the iOS app or Claude Code for Web.

This is, in theory, pretty simple: you just need to run

claude --dangerously-skip-permissions --resume <session>

You can start a remote control session by interactively running /remote-control, but I want to avoid interactivity: the agent should be ready just when I run docker compose up. Luckily, you can set the remoteControlAtStartup: true setting in your ~/.claude.json to achieve this.

Handling interactive prompts

I hit a lot of similar roadblocks. Claude Code is primarily designed for interactive use and thus you'll hit a lot of prompts you need to manually accept. I saw permission prompts for things like: onboarding, various safety/risk disclaimers, whether to trust a given project, and running in the home folder.

I worked around this by running Claude from a subfolder in the home directory, and setting a bunch of config options in ~/.claude.json and ~/.claude/settings.json. Eventually, this was enough to start Claude without any permission prompts.

With all that done, things work reasonably well! When you start the container, you get a session that you can talk to from the Claude app.

Scheduled tasks

We're still missing an important feature from OpenClaw/NanoClaw/etc.: the ability to schedule tasks.

Claude Code has two native features that get close, but neither is quite right. Scheduled tasks is almost exactly what we want, but all scheduled tasks expire after 7 days. That makes it a no-go. There's also web scheduled tasks, but this runs the task in a Claude Code for Web instance, not in the local session.

There's a workaround, though. I mentioned Channels earlier -- that's a new feature that lets you push external events into a session. I built a scheduler service with two components:

  1. MCP tools that Claude can call to schedule tasks, and
  2. A channel that injects prompts from scheduled tasks into the session when they trigger.

Then, by setting CLAUDE_CODE_DISABLE_CRON=1 we can disable the built-in (but limited) scheduled tasks feature. Unfortunately there's no way to disable web scheduled tasks, so I added a note in the root CLAUDE.md to never use these.

To use your own channels you need to run Claude Code with the --dangerously-load-development-channels flag, which always shows an interactive prompt on startup. I couldn't find a way to work around this, so I had to use a hacky expect script that starts Claude Code, waits 10 seconds, and then sends an enter keypress to accept the prompt.

Persistent sessions

We're making progress. But there's still something missing: whenever the container restarts, we get an entirely new session.

You'd think that you could use the --resume <uuid> flag, but unfortunately remote control sessions are currently not resumable. So, we'll have to rely on some kind of memory system instead.

Memory

Claude Code has auto-memory built in -- the agent can decide that something is worth remembering and save it to ~/.claude/projects/<project>/memory/. That's a good start!

I was initially planning to build out "dream" functionality like Nanobot that regularly cleans up memories and extracts new ones from past conversations. It turns out that Anthropic is already building this into Claude Code. It's currently feature-flagged, so for now I'm just running the extracted prompt on a cron job every two hours if any new messages were sent in that time.

Notifications

Once I got this far, things were working pretty much as I wanted until I discovered one critical shortcoming: the Claude app doesn't send you a notification when the agent sends a message (e.g. due to a scheduled task). That breaks the whole proactive-agent aspect of this. So, I eventually settled on a Matrix integration (also using channels) -- but you could use whatever you want here.

One issue with communicating over channels is that the agent's output doesn't get sent to the channel by default. Instead, the model has to use a reply tool for the channel to send you a message. In other words, the model might write something and you never receive a Matrix message if it forgets to use the correct tool! So far, I'm working around this by adding a note in my top-level CLAUDE.md telling the agent to always talk to the user via the reply tool. Luckily, if it comes down to it, you can always log in to Claude Code for Web and use the remote control functionality for a complete view.

Now that we're using Channels to talk to the agent, do we still need the remote control feature? No, not really! I'm keeping it for myself because it's nice for debugging and there's no real downside, but you could definitely run this without it. That would even let you use the --resume flag for a single long-term session. I may do that in the future if I find that the memory functionality isn't quite there.

Follow elsewhere