gavrielc e92b245399 feat(v2): OneCLI 0.3.1 — approvals, credential collection, threaded routing
Three features built on top of @onecli-sh/sdk 0.3.1, landed together because
they share wiring surfaces (session DB schema, delivery dispatcher, Chat SDK
bridge, channel adapter contract).

## OneCLI manual-approval handler

* `src/onecli-approvals.ts` — long-polls OneCLI via the SDK's
  `configureManualApproval`; on each request, delivers an `ask_question` card
  to the admin agent group's first messaging group, persists a
  `pending_approvals` row, and waits on an in-memory Promise resolved by the
  admin's button click or an expiry timer. Expired cards are edited to
  "Expired (...)" and a startup sweep flushes any rows left over from a
  previous process.
* Short 11-byte approval id (`oa-<8 base36>`) instead of the SDK's UUID so the
  Telegram 64-byte `callback_data` limit is respected; the OneCLI UUID stays
  in the persisted payload for audit.
* Migration 003 consolidated: `pending_approvals` now has the OneCLI-aware
  columns from the start (`agent_group_id`, `channel_type`, `platform_id`,
  `platform_message_id`, `expires_at`, `status`), `session_id` relaxed to
  nullable so cross-session approvals fit.
* `handleQuestionResponse` in `src/index.ts` now routes OneCLI approvals
  through `resolveOneCLIApproval` before falling back to the
  session-bound approval path.

## Credential collection from chat

New `trigger_credential_collection` MCP tool — the agent researches a
third-party API, calls the tool with `{name, hostPattern, headerName,
valueFormat, description}`, and blocks until the host reports saved, rejected,
or failed. The credential value never enters the agent's context: the user
submits it into a Chat SDK Modal on the host side, the host writes it to
OneCLI via a thin facade (`src/onecli-secrets.ts` — shells out to
`onecli secrets create`, shape mirrors the SDK we expect upstream), and only
the status string flows back to the container via a system message.

* `src/credentials.ts` — host-side handler: delivers the card to the
  conversation's own channel (not the admin channel — credential collection
  is a user-facing flow, distinct from admin approval), persists a
  `pending_credentials` row, drives the submit → `createSecret` → notify
  pipeline. Falls back gracefully when the channel doesn't support modals.
* `src/db/credentials.ts` + migration 005: `pending_credentials` table.
* `src/channels/chat-sdk-bridge.ts`: renders a `credential_request` card,
  handles the `nccr:` action prefix by opening a Modal with a TextInput,
  registers an `onModalSubmit` handler for the `nccm:` callback prefix.
* `container/agent-runner/src/mcp-tools/credentials.ts`: the blocking MCP
  tool, mirroring the `ask_user_question` polling pattern.
* `container/agent-runner/src/db/messages-in.ts`: `findCredentialResponse`
  helper to pick up the system message the host writes back.

## Threaded adapter routing

The destination layer previously didn't carry thread context, so agent replies
to Discord always landed in the root channel regardless of which thread the
inbound came from.

* `ChannelAdapter.supportsThreads: boolean` — declared by every channel skill
  at `createChatSdkBridge`. Threaded: Discord, Slack, Teams, Google Chat,
  Linear, GitHub, Webex. Non-threaded: Telegram, WhatsApp Cloud, Matrix,
  Resend, iMessage.
* `src/router.ts`: non-threaded adapters strip `threadId` at ingest (threads
  collapse to channel-level sessions). Threaded adapters override the
  wiring's `session_mode` to `'per-thread'` so each thread = a session
  (except `agent-shared`, which is preserved as a cross-channel intent the
  adapter can't know about).
* `session_routing` table in `inbound.db` — single-row default reply routing
  written by the host on every container wake from
  `session.messaging_group_id` + `session.thread_id`. Forward-compat
  `CREATE TABLE IF NOT EXISTS` handles older session DBs lazily.
* `container/agent-runner/src/db/session-routing.ts` — container-side reader.
* `send_message` / `send_file` / `ask_user_question` / `send_card` /
  scheduling tools all default their routing (channel, platform, **and**
  thread) from the session when no explicit `to` is given. Explicit `to`
  uses the destination's channel with `thread_id = null` (cross-destination
  sends start a new conversation elsewhere).
* `poll-loop.ts::sendToDestination` (the final-text single-destination
  shortcut) now inherits `thread_id` from `RoutingContext` too — this was
  the root cause of Discord replies landing in the root channel even after
  `send_message` was wired correctly.

## Related cleanups

* `src/container-runner.ts`: OneCLI agent identifier switched from the lossy
  folder-derived string to `agent_group.id`, making `getAgentGroup(externalId)`
  a trivial reverse lookup for per-agent scoping.
* `wakeContainer` race fix via an in-flight promise map — concurrent wakes
  during the async buildContainerArgs / OneCLI `applyContainerConfig` window
  no longer double-spawn containers against the same session directory.
* `src/db/db-v2.test.ts`: dropped the brittle `expect(row.v).toBe(N)` schema
  version assertion — it had to be bumped on every migration addition.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 17:18:21 +03:00
2026-03-18 19:43:46 +09:00
2026-03-18 19:43:46 +09:00
2026-04-06 00:37:34 +03:00

NanoClaw

An AI assistant that runs agents securely in their own containers. Lightweight, built to be easily understood and completely customized for your needs.

nanoclaw.dev  •   docs  •   中文  •   日本語  •   Discord  •   34.9k tokens, 17% of context window


Why I Built NanoClaw

OpenClaw is an impressive project, but I wouldn't have been able to sleep if I had given complex software I didn't understand full access to my life. OpenClaw has nearly half a million lines of code, 53 config files, and 70+ dependencies. Its security is at the application level (allowlists, pairing codes) rather than true OS-level isolation. Everything runs in one Node process with shared memory.

NanoClaw provides that same core functionality, but in a codebase small enough to understand: one process and a handful of files. Claude agents run in their own Linux containers with filesystem isolation, not merely behind permission checks.

Quick Start

gh repo fork qwibitai/nanoclaw --clone
cd nanoclaw
claude
Without GitHub CLI
  1. Fork qwibitai/nanoclaw on GitHub (click the Fork button)
  2. git clone https://github.com/<your-username>/nanoclaw.git
  3. cd nanoclaw
  4. claude

Then run /setup. Claude Code handles everything: dependencies, authentication, container setup and service configuration.

Note: Commands prefixed with / (like /setup, /add-whatsapp) are Claude Code skills. Type them inside the claude CLI prompt, not in your regular terminal. If you don't have Claude Code installed, get it at claude.com/product/claude-code.

Philosophy

Small enough to understand. One process, a few source files and no microservices. If you want to understand the full NanoClaw codebase, just ask Claude Code to walk you through it.

Secure by isolation. Agents run in Linux containers (Apple Container on macOS, or Docker) and they can only see what's explicitly mounted. Bash access is safe because commands run inside the container, not on your host.

Built for the individual user. NanoClaw isn't a monolithic framework; it's software that fits each user's exact needs. Instead of becoming bloatware, NanoClaw is designed to be bespoke. You make your own fork and have Claude Code modify it to match your needs.

Customization = code changes. No configuration sprawl. Want different behavior? Modify the code. The codebase is small enough that it's safe to make changes.

AI-native.

  • No installation wizard; Claude Code guides setup.
  • No monitoring dashboard; ask Claude what's happening.
  • No debugging tools; describe the problem and Claude fixes it.

Skills over features. Instead of adding features (e.g. support for Telegram) to the codebase, contributors submit claude code skills like /add-telegram that transform your fork. You end up with clean code that does exactly what you need.

Best harness, best model. NanoClaw runs on the Claude Agent SDK, which means you're running Claude Code directly. Claude Code is highly capable and its coding and problem-solving capabilities allow it to modify and expand NanoClaw and tailor it to each user.

What It Supports

  • Multi-channel messaging - Talk to your assistant from WhatsApp, Telegram, Discord, Slack, or Gmail. Add channels with skills like /add-whatsapp or /add-telegram. Run one or many at the same time.
  • Isolated group context - Each group has its own CLAUDE.md memory, isolated filesystem, and runs in its own container sandbox with only that filesystem mounted to it.
  • Main channel - Your private channel (self-chat) for admin control; every group is completely isolated
  • Scheduled tasks - Recurring jobs that run Claude and can message you back
  • Web access - Search and fetch content from the Web
  • Container isolation - Agents are sandboxed in Docker (macOS/Linux), Docker Sandboxes (micro VM isolation), or Apple Container (macOS)
  • Credential security - Agents never hold raw API keys. Outbound requests route through OneCLI's Agent Vault, which injects credentials at request time and enforces per-agent policies and rate limits.
  • Agent Swarms - Spin up teams of specialized agents that collaborate on complex tasks
  • Optional integrations - Add Gmail (/add-gmail) and more via skills

Usage

Talk to your assistant with the trigger word (default: @Andy):

@Andy send an overview of the sales pipeline every weekday morning at 9am (has access to my Obsidian vault folder)
@Andy review the git history for the past week each Friday and update the README if there's drift
@Andy every Monday at 8am, compile news on AI developments from Hacker News and TechCrunch and message me a briefing

From the main channel (your self-chat), you can manage groups and tasks:

@Andy list all scheduled tasks across groups
@Andy pause the Monday briefing task
@Andy join the Family Chat group

Customizing

NanoClaw doesn't use configuration files. To make changes, just tell Claude Code what you want:

  • "Change the trigger word to @Bob"
  • "Remember in the future to make responses shorter and more direct"
  • "Add a custom greeting when I say good morning"
  • "Store conversation summaries weekly"

Or run /customize for guided changes.

The codebase is small enough that Claude can safely modify it.

Contributing

Don't add features. Add skills.

If you want to add Telegram support, don't create a PR that adds Telegram to the core codebase. Instead, fork NanoClaw, make the code changes on a branch, and open a PR. We'll create a skill/telegram branch from your PR that other users can merge into their fork.

Users then run /add-telegram on their fork and get clean code that does exactly what they need, not a bloated system trying to support every use case.

RFS (Request for Skills)

Skills we'd like to see:

Communication Channels

  • /add-signal - Add Signal as a channel

Requirements

Architecture

Channels --> SQLite --> Polling loop --> Container (Claude Agent SDK) --> Response

Single Node.js process. Channels are added via skills and self-register at startup — the orchestrator connects whichever ones have credentials present. Agents execute in isolated Linux containers with filesystem isolation. Only mounted directories are accessible. Per-group message queue with concurrency control. IPC via filesystem.

For the full architecture details, see the documentation site.

Key files:

  • src/index.ts - Orchestrator: state, message loop, agent invocation
  • src/channels/registry.ts - Channel registry (self-registration at startup)
  • src/ipc.ts - IPC watcher and task processing
  • src/router.ts - Message formatting and outbound routing
  • src/group-queue.ts - Per-group queue with global concurrency limit
  • src/container-runner.ts - Spawns streaming agent containers
  • src/task-scheduler.ts - Runs scheduled tasks
  • src/db.ts - SQLite operations (messages, groups, sessions, state)
  • groups/*/CLAUDE.md - Per-group memory

FAQ

Why Docker?

Docker provides cross-platform support (macOS, Linux and even Windows via WSL2) and a mature ecosystem. On macOS, you can optionally switch to Apple Container via /convert-to-apple-container for a lighter-weight native runtime. For additional isolation, Docker Sandboxes run each container inside a micro VM.

Can I run this on Linux or Windows?

Yes. Docker is the default runtime and works on macOS, Linux, and Windows (via WSL2). Just run /setup.

Is this secure?

Agents run in containers, not behind application-level permission checks. They can only access explicitly mounted directories. Credentials never enter the container — outbound API requests route through OneCLI's Agent Vault, which injects authentication at the proxy level and supports rate limits and access policies. You should still review what you're running, but the codebase is small enough that you actually can. See the security documentation for the full security model.

Why no configuration files?

We don't want configuration sprawl. Every user should customize NanoClaw so that the code does exactly what they want, rather than configuring a generic system. If you prefer having config files, you can tell Claude to add them.

Can I use third-party or open-source models?

Yes. NanoClaw supports any Claude API-compatible model endpoint. Set these environment variables in your .env file:

ANTHROPIC_BASE_URL=https://your-api-endpoint.com
ANTHROPIC_AUTH_TOKEN=your-token-here

This allows you to use:

  • Local models via Ollama with an API proxy
  • Open-source models hosted on Together AI, Fireworks, etc.
  • Custom model deployments with Anthropic-compatible APIs

Note: The model must support the Anthropic API format for best compatibility.

How do I debug issues?

Ask Claude Code. "Why isn't the scheduler running?" "What's in the recent logs?" "Why did this message not get a response?" That's the AI-native approach that underlies NanoClaw.

Why isn't the setup working for me?

If you have issues, during setup, Claude will try to dynamically fix them. If that doesn't work, run claude, then run /debug. If Claude finds an issue that is likely affecting other users, open a PR to modify the setup SKILL.md.

What changes will be accepted into the codebase?

Only security fixes, bug fixes, and clear improvements will be accepted to the base configuration. That's all.

Everything else (new capabilities, OS compatibility, hardware support, enhancements) should be contributed as skills.

This keeps the base system minimal and lets every user customize their installation without inheriting features they don't want.

Community

Questions? Ideas? Join the Discord.

Changelog

See CHANGELOG.md for breaking changes, or the full release history on the documentation site.

License

MIT

Description
No description provided
Readme MIT 16 MiB
Languages
TypeScript 90.1%
Shell 8.4%
Python 0.8%
Dockerfile 0.3%
Swift 0.3%