Released 2026-10-09 as v2026.10.0 (
7203e00d), the first release with calendar version numbers (YYYY.M.PATCH). Update with /update-nanoclaw; read Before you update first.Before you update- Breaking: if you use the OneCLI gateway, move it to exactly 1.42.0 before you rely on the update. On an older gateway every vault call fails, and setup stops with “OneCLI gateway lacks the /v1 API required by the installed SDK”.
/update-nanoclawdoes not upgrade a running gateway for you; followdocs/onecli-upgrades.mdin your NanoClaw folder, which also says when the step does not apply. Do not go past 1.42.0: 1.43 and later are not supported yet. New installs already get 1.42.0. /update-nanoclawnow installs the newest release, not the tip ofmain.NANOCLAW_UPDATE_CHANNELin.envpicks the channel:stable(the default) follows releases,betatakes release candidates, andedgefollowsmain. To keep followingmain, runpnpm exec tsx scripts/update-nanoclaw.ts set-channel --channel edgein your NanoClaw folder.- Iron Proxy installs: rerun the Iron setup once after updating so the approval front is rebuilt with this release’s checks:
pnpm exec tsx .claude/skills/add-iron-proxy/scripts/setup.tsfrom your NanoClaw folder. Add--with-controlif you use the Iron Control console.
- Iron Proxy can reach a keyless model on the same machine over plain HTTP, for example
http://host.docker.internal:<port>/v1. See Credential gateways. - The service can use an outbound proxy from
HTTPS_PROXY,HTTP_PROXYorALL_PROXY, andNO_PROXY, set in your setup shell or.env. - Iron Proxy can skip the approval card for reads that send no credential when you set
NANOCLAW_GATEWAY_UNCREDENTIALED_READS=truein.env. It is off by default. - A failed Claude run shows Claude’s own short reason in the chat for known failures, such as an invalid API key.
/update-nanoclawand rollback are more dependable. A rollback swaps yourdata/snapshot back in by rename instead of deleting it first.- New Claude groups no longer default to the
Conciseoutput style. A group that already has an output style keeps it. /add-dial-toolworks again on OneCLI gateway 1.42.0.- Security: WhatsApp moves to Baileys 7.0.0-rc14, agents can no longer redirect host file operations outside their own folders, and the other names the Claude SDK accepts for admin commands (such as
/resetfor/clear) are now admin-only too. On Linux, if your outbound proxy has a password, rerunpnpm exec tsx setup/index.ts --step serviceto move it out of your service file into a private file. See the release notes for the full list.
Released 2026-09-23 as v2.4.0 (
143db6c9). Update with /update-nanoclaw; read the two breaking items first. Upstream Updating to NanoClaw 2.4 has the full procedure.Before you update- Breaking: credential gateways install through skills, and the host refuses to start without one.
/add-oneclisupplies OneCLI and/add-iron-proxyadds optional Iron Proxy support. OneCLI stays the Standard setup default, the gateway picker appears only in Advanced setup, and upgrades keep the existing selection. SettingNANOCLAW_GATEWAY_PROVIDERalone installs nothing. Migration:/update-nanoclawinstalls the selected gateway before cutover; a hand-merged fork must run/add-onecli(or its gateway’s skill) before restarting. See Credential gateways. - Breaking: custom source that composes agent instructions must move to the new module.
src/claude-md-compose.tsis nowsrc/project-doc-compose.tsandcomposeGroupClaudeMd(group)becamecomposeGroupProjectDoc(group, groupDir, spec); the/app/CLAUDE.mdand.claude-fragmentsmounts are gone, and every instruction section now reaches the model. Standard installations need no source edits. - Claude groups with no model set move to Opus 5.5, which changes cost and usage-limit burn. Set
NANOCLAW_DEFAULT_MODELin.envorncl groups config update --id <group> --model <model>before updating to keep a model. - Codex groups with no model set start new threads on
gpt-6-astra(/add-codexpins@openai/codex0.155.1); groups with a model, and existing threads, keep theirs. Pin withncl groups config update --id <group-id> --model gpt-5.6-sol, thenncl groups restart --id <group-id>. - Claude agents default to a concise tone unless the group’s Claude settings already set
outputStyle. - Existing OpenCode installs must re-run
/add-opencode, rebuild the agent image, and restart. - Cutover stops this install’s agent containers itself.
/update-nanoclawno longer waits on an idle container; an agent mid-turn loses that turn. See Upgrading.
- Iron Proxy is an alternative local gateway with the Iron Control web console; Codex works through it, including WebSocket turns. See Credential gateways.
- Echo’s hardened image and a managed Slack app are set up through the community portal with one browser sign-in. See Echo hardened runtime.
- Install-wide model and speed knobs.
NANOCLAW_DEFAULT_MODELfills in a group’s missing model andNANOCLAW_FAST_MODE=1enables the faster, pricier tier. The host reads both from.envat startup, so restart the host after changing them; each group then picks the value up at its next container start.ncl groups config update --id <group-id> --speed fastsets the tier per group. See Configuration. - Mattermost is a channel via
/add-mattermost. See Mattermost. - The OpenCode provider is reworked with host-managed authentication and model selection. See Agent providers.
- Host restarts keep approvals and delivery state. Approval cards, retry counts, and session claims live in the central database, and a second host in the same checkout no longer takes over a running host’s
nclsocket. ncl statusshows the running host process and its connected channel adapters. Agents run on Claude Code 2.1.280, and the Slack companion skills ship onmain.
- Codex turns wait for every MCP server to start instead of completing with nothing sent; replies stay in the thread they answer; busy installs no longer see latency grow with session count.
- Quoted
.envvalues read the same in setup and the host, andWEBHOOK_PORTis read from.env. See Environment variables. /update-nanoclawloads its controller again and is correct on macOS; setup is sturdier on Linux; concurrent SQLite migrations no longer fail setup.ncl tasks updaterejects an empty--prompt;send_cardlink-only actions andask_user_questionare described to agents. See ncl reference.- Security: setup redacts resolved secrets in skill logs, the agent image is repinned to
hardened-2026-08-24on Bun 1.4.0, and pnpm’s 3-dayminimumReleaseAgegate is enforced.
- Breaking: the container runtime moves behind a session driver seam. Each session is composed as a validated, admission-checked spec and realized by a selectable driver (
src/drivers/; Docker ships built-in and stays the default). Three surfaces break. (1) Group folder names must now match the runtime label grammar: at most 63 characters of[A-Za-z0-9_-], alphanumeric at both ends. Previously legal 64-character names, trailing-/_, and unvalidated legacy imports refuse to spawn. (2) Container names change fromnanoclaw-v2-<folder>-<timestamp>to key-derivedncl-…names, and invocation changes fromdocker runtocreate+start --attach. The old human-readable name survives as thenanoclaw-container-namelabel, so name-based tooling, Docker allowlists, and audit rules should match by label instead (docker ps --filter label=nanoclaw-sessionor--filter label=nanoclaw-group-folder=<folder>). (3) Internal container helpers (hostGatewayArgs,readonlyMountArgs,stopContainer,ensureContainerRuntimeRunning,cleanupOrphans,buildContainerArgs) moved into the driver module, so customized installs importing them stop compiling. The/use-native-credential-proxyskill is retired: the spec’s admission rules refuse credential values in container env on every lane. Credentials ride the OneCLI vault, and custom Anthropic endpoints use theANTHROPIC_BASE_URL+ placeholder-token pattern from setup. Migration: runbun scripts/detect-driver-migration.ts. It detects all three surfaces and prints one finding per line with a minimal fix instruction; nothing detected means nothing to do (#3308, #3429, #3442) - Breaking: central database access is asynchronous behind
DbDriver. SQLite remains the default and existingdata/v2.dbfiles are unchanged, but custom source and installed channel or provider extensions must await central reads and writes and adopt the retyped host seams. Composition and migrations are backend-ready: a one-slot driver registry keeps backend selection insrc/db/compose.ts, andpnpm run migrateis the explicit schema-change path. Migration: follow upstreamdocs/central-db-async-migration.mdto find affected calls, preserve transaction boundaries, verify, or roll back (#3334, #3335) - Breaking: agent mailbox access goes through storage-neutral host and runner registries. The built-in SQLite implementation preserves existing session data and runtime behavior, but custom source may need to replace raw session-database access, await mailbox writes, and adopt the moved runner state and heartbeat helpers. Scheduled-task lifecycle semantics tightened in the same series: deleting an isolated task cascades its session state. Migration: follow upstream
docs/agent-mailbox-seam-migration.mdfor the detect grep, old-to-new symbol map, verification, and rollback (#3349) - Breaking: the host runtime requires Node.js 22 or newer. Node 20 is not supported by the upgraded
better-sqlite3release used for current Node runtimes (engines.nodeis now>=22). Migration: runbash setup/install-node.sh, verifynode --versionreports v22 or newer, then rerun/update-nanoclaw(#3360, #3443) - Breaking (decision gate): a new Slack experience is offered to classic single-bot installs. It brings per-agent provisioned Slack apps, agent spawning from Slack, and UX improvements. Classic Slack keeps working unchanged; the gate asks for a decision, not a forced migration. Migration: run
/migrate-slack-agents. It detects classic state and either walks the upgrade or records the choice to stay on classic; both outcomes satisfy the update requirement (#3357, #3391, #3404) - Mid-turn streaming is the only content door out of a turn. For providers that emit mid-turn text, closed
<message to="...">blocks are delivered as they are emitted, with cross-segment assembly of blocks split across streaming segments. The final result never delivers content: a turn that delivered nothing but whose result still carries content gets a wrap nudge, and the retry streams through the same mid-turn door (#3284) - Host restarts adopt running sessions instead of restarting them. A service restart no longer kills in-flight agent work; to apply image or runtime changes to a group, restart it deliberately with
ncl groups restart. Containers spawned before this release cannot be adopted and are removed at first upgraded startup (#3308) - Adapters gain lifecycle hooks and richer declarations. The additions: membership-change, session-created, post-delivery, and wizard hooks, an extend tool, a per-adapter
sessionModedeclaration, and adecline_notifyunknown-sender policy that declines while telling the sender it did (#3292, #3293, #3294, #3295, #3296, #3297, #3304, #3361) - Session specs validate runtime isolation tiers. The driver contract validates
runtimeTieragainst its isolation capabilities at spawn. The shipped Docker driver supportscontainer; trunk has noruntime_tierdatabase column or CLI setting for selecting a VM (#3442) - Upgrades are transactional.
/update-nanoclawapplies an update as a transaction with recovery on interruption, closing the gaps that could previously strand a half-upgraded install (#3195) - Container gateway wiring is typed and admission-checked. The gateway’s per-session contribution (proxy env, trust anchors, credential-stub mounts) merges into the session spec before validation instead of riding raw Docker flags, and gateway selection becomes a registry (
NANOCLAW_GATEWAY_PROVIDER, defaultonecli; installs that never set it behave as before). An unknownNANOCLAW_RUNTIME_DRIVERaborts startup, and drivers that cannot rebuild images in place denyinstall_packagesand--rebuildat request time instead of failing later (#3308) - Non-root hosts get an explicit container identity. Every non-root host now passes
--user <uid>:<gid>andHOME=/home/node.CONTAINER_MEMORY_LIMITis validated at spawn: invalid values refuse the spawn with a named error instead of a raw Docker error; blank and0still mean uncapped (#3308) - Telegram setup gets three improvements. The pairing card correctly says the code is 6 digits. The approved group connection flow is documented in the skill. The wizard offers “add another Telegram bot” when one is already configured (#3431, #3352, #3480)
/add-codexpins@openai/codex0.146.0. The previous pin defaults to a model OpenAI retires from Codex on 2026-08-31 and rejects newer models. Existing codex installs are not re-pinned by re-running the skill: edit the@openai/codexentry incontainer/cli-tools.jsonto0.146.0, rebuild the agent image, and restart (#3299)- New installs use OneCLI gateway 1.41.0. Existing 1.36.0 gateways remain compatible; see upstream
docs/onecli-upgrades.mdto upgrade. Registry sign-in verification now fails closed when a stored sign-in cannot be verified (#3277, #3339) - An unrecognized
engage_modelogs a warning instead of silently dropping the message (#3317)
- Breaking: agent templates are Agent Plugins 1.0.0 directories —
plugin.jsonreplacescontext/instructions.mdas the required manifest and discovery marker, MCP servers move from.mcp.jsonto a spec-shapedmcp.json, and persona, extra context files, and seeded tasks move under anai.nanoco.nanoclaw/extension directory. Templates become portable to other plugin clients, and any conformant third-party plugin stamps as a NanoClaw agent. Migration: re-fetch templates from the registry — the pre-plugin layout now fails with a migration error; conversion steps for a local custom template are in building a template (#3220, #2909) - Breaking: host modules use one lifecycle registry —
onShutdown()andgetShutdownCallbacks()are gone fromresponse-registry.ts. Custom host modules that imported either must move to the host lifecycle API insrc/host-lifecycle.ts. Migration: followdocs/host-lifecycle-migration.mdto detect affected code, update it, verify the cutover, or roll back (#3214) - A stamped template updates in place instead of minting a duplicate agent —
ncl groups create --template <ref>against a group that already carries that plugin prints a dry-run plan of every plugin-owned surface that would change (plugin files, skills, MCP servers, persona, context files, tasks) and flags locally customized files whose edits would be lost.--yesapplies it,--idpicks among several stamped groups,--newdeliberately stamps another agent. Memory,plugin-data/, user-added MCP servers, wiring, and sessions are never touched. Plugin-stamped MCP servers now carry an ownership marker and refuse direct edits throughncl groups config add-mcp-server/remove-mcp-serveror the agent’sadd_mcp_servertool: update the plugin and restamp instead (#2909, #3220) - Setup can stamp the first agent from a template — the wizard offers the NanoClaw template library or the local
templates/directory when creating the first agent, and--template-path <ref>or the advanced screen presets the pick. The choice survives wizard re-execs and reruns, a rerun over a partial install updates the stamped agent in place instead of duplicating it, and a stale localtemplates/warns and continues instead of aborting setup (#2909) - Plugin MCP servers can declare a working directory —
cwdinmcp.json(fixed forms./p,${PLUGIN_ROOT}[/p],${PLUGIN_DATA}[/p]) resolves to an absolute container path at runtime: providers that support it consume it natively, and Claude gets acd-then-execlaunch shim. A stdio server that omitscwdruns from the plugin root.cwdstays plugin-only — it is rejected at intake on raw payloads and stripped from stored servers that lack plugin provenance (#3220, #2909) - Remote MCP servers can use Streamable HTTP — register one with
ncl groups config add-mcp-server --name <name> --url <url>(with optional--headers) or the existing admin-approvedadd_mcp_servertool; local stdio servers keep theircommand/args/envbehavior. HTTPS is required except forlocalhostandhost.docker.internal, and URLs carrying userinfo, a fragment, or a credential-looking query parameter are rejected, so remote credentials stay OneCLI-managed (#3092) /add-tavily-toolskill — adds Tavily Search and Extract to selected agent groups as keyless remote MCP tools, bridged through a version-pinnedmcp-remote. Aimed at providers without built-inWebSearch/WebFetch, and at Tavily’s structured extraction (#3190)- Agent-to-agent messaging no longer loses to Claude Code’s built-in
SendMessage— that built-in addresses the SDK’s own in-session subagents, so an agent that had just runcreate_agentreached for it by name and gotNo agent named 'x' is currently addressable, which reads as “the group was never provisioned” while the real path,mcp__nanoclaw__send_message, was never called.SendMessagejoinsAskUserQuestionin the disallowed-tool list, so the PreToolUse hook blocks it and points at the NanoClaw equivalent (#3187) - Resolved approval cards keep their content — an approved, rejected, or expired card keeps its title and request details and swaps the buttons for a muted line naming the decision and the actor, or the timeout reason. A startup sweep closes out cards left pending across a host restart, and the request text is persisted so a delayed resolution still renders in full (#3143)
- Scheduled tasks see their scheduled time, not their creation time — a task message’s
timeis now the effective scheduled occurrence, and a task-onlycurrent_timeattribute carries the current wall clock with the weekday, in the agent group’s timezone (#3154) - Hosted iMessage setup provisions the line’s user row and prints the number to text — setup creates the user row through the project-secret API and prints the assigned phone number with one instruction: text that line once. That first message is the opt-in the delivery plane checks. Re-runs reuse the existing row, and a row that is already opted in short-circuits to success (#3181)
- Setup dispatches failure help to the picked provider — a failed step during a non-Claude install no longer pushes you to install and sign in to the Claude CLI just to see what went wrong; Claude remains a guarded fallback only when it’s already installed and signed in (#3170)
- Group-scoped agents can inspect and adjust their own wiring — an agent may list, get, and request approved updates to the wiring for the chat it is in, limited to
engage_modeandengage_pattern; wider updates, and any other agent group’s wiring, are denied. Accumulated context messages no longer engage a warm container by themselves — a follow-up turn now needs an actual trigger message (#3137) ensureUserDmgains an opt-in privacy-safe logging mode — security-sensitive flows can omit user IDs, handles, messaging-group IDs, and raw adapter errors from log data while keeping the non-identifying channel context (#3222)- Release publication tolerates GitHub API propagation — the release workflow retries bounded post-publication read-backs when the new release is not listed yet, or its immutable state is not visible yet. A real title, body, tag, or SHA mismatch still fails immediately (#3176)
/update-nanoclawhandles breaking changes that point at a documentation guide, not only at a migration skill — it reads each referenced guide and walks its detect, fix, verify, and rollback steps, and it reads its own newest version without touching the working tree. The hardened-image guide gained those sections (#3180)- The stale
add-gcal-tool,add-gmail-tool,get-qodo-rules, andqodo-pr-resolverskills were removed (#3172) - The recommended hardened agent image is repinned to
hardened-2026-08-13(#3236, #3182) - Docs: skills carry a single-responsibility integration rule, and the hardened-image guide states that
install_packagescovers apt and npm packages only (#3211, #3216)
- Breaking: iMessage unified — one
imessagechannel, local or hosted backends —/add-imessagenow installs a single channel with two pluggable backends: local (this Mac’schat.db) or hosted (a native Photon line via photon.codes, no Mac relay), chosen at install or viaIMESSAGE_BACKEND=local|hosted. The legacy Chat-SDK remote mode (IMESSAGE_SERVER_URL/IMESSAGE_API_KEY) is removed (#3164) - Breaking: agent memory is provider-agnostic — every provider now shares one OKF-compatible
memory/tree instead of provider-native memory stores, with persona moved toinstructions.prepend.md; memory reloads automatically on startup,/clear, and compaction. Groups still on legacy memory need/migrate-memorybefore their next spawn (#3012) - Hardened agent images are an opt-in setup path — setup can fetch a digest-pinned, multi-architecture image built and patched by Echo instead of building locally, with architecture, lockfile-label, and provenance checks before it’s retagged onto the same local name a build would use. Building locally stays fully supported and needs no account (#3150)
- Agent containers get more hardening by default — every spawn now runs with
--cap-drop=ALL,no-new-privileges, and Docker’s--init; a newCONTAINER_PIDS_LIMIT(default 2048) backstops fork bombs (#2748) - Per-agent-group timezone override —
ncl groups config update --timezone <IANA>overrides the install timezone for that group’s scheduling, run-log stamps, and containerTZ; passing""clears it back to the install default (#3125) - New groups can inherit an instance-wide default provider —
DEFAULT_AGENT_PROVIDERsets the provider a freshly created agent group starts on; it’s stamped once at creation, and a group’s ownncl groups config update --providerstill overrides it (#2906) - Agent templates can seed scheduled tasks — a template’s
tasks/*.mdfiles define a cron schedule, an optional script gate, and a prompt; template-seeded tasks are created paused so you can review them before activating (#3022) - MCP server approval cards show the full payload —
add_mcp_servercards now render every field being applied (name, command, args, env), not just name and command, so an approver can no longer authorize args or env they never saw; secret-shaped values are redacted with a fingerprint (#2998) - The Vercel CLI is no longer baked into every agent image — install it with
/add-vercelwhen a group actually needs to deploy (#3159) - Duplicate replies after mid-turn context compaction fixed — the SDK’s
compact_boundaryevent no longer surfaces as a synthetic result the poll loop mistakes for unwrapped output (#3083) - Claude rate-limit telemetry only aborts a turn on an actual rejection — informational
rate_limit_eventupdates no longer abort healthy turns, and a rejection is classified as transient rate-limit or billing exhaustion instead of always reading as quota (#3077) - Telegram deep links use
telegram.meinstead oft.me(#3043)
- Setup wizard channel installs are SKILL.md-driven — the bespoke per-channel setup modules (
setup/channels/<channel>.ts) are deleted;/setupnow drives the same/add-<channel>skills you run by hand, through a thin generic driver (render + ask + run, no phasing). The skills carry the whole flow innc:directive fences: operator-facing prompts (nc:operator), command output captured into variables (nc:runcapture),when:guards for either/or paths, precondition gates (effect:check), and a human barrier for manual UI steps. A lone?at any prompt hands off to Claude for help (#2958, #2972) - Host-only mount verbs —
ncl groups config add-mount/remove-mountedit a group’s additional mounts from the host socket; never runnable from inside a container. The gmail/gcal/rtk tool skills now use them instead of raw SQL (#3035) - Troubleshooting sections added to all 11 credentialed channel skills, and per-skill raw logs from every setup command (#2958)
- Warn when a real directory entry blocks a shared skill symlink instead of silently skipping it (#3002)
- Outbound messages whose channel adapter is missing route into the retry path instead of being dropped (#2996)
- The agent runner logs when an errored batch is acked as completed, so silent failures leave a trace (#2966)
- One-door task delivery —
send_messageis the only path out of a task session. A scheduled task’s final text no longer implicitly routes anywhere; the agent explicitly sends what should reach a chat, and everything else stays in the task log (#2988)
- Lean harness defaults for new agent groups — freshly created groups start with a smaller default toolset (#3031)
- WhatsApp setup warns before configuring shared mode (the assistant on your own number), and the warning title renders cleanly (#3021)
- Channel-defaults review fixes, and the WhatsApp service restarts after mode/name env writes so changes take effect (#3010)
slack-formattingandwhatsapp-formattingmoved to thechannelsbranch — trunkcontainer/skills/drops from 8 to 6 skills; installs that wire Slack or WhatsApp get the formatting skill through the channel install instead (#3009)
- All timestamps stored as ISO and displayed in local time (#3003, #3006)
- Task rows stamped with ISO timestamps (#3005)
ncl wirings createlive-refreshes destinations, provisions companion rows (as doesncl groups create), and wraps INSERT + postCreate in one transaction (#2416)
ncl taskscontrol plane — scheduled tasks move from the agent’s MCP tools to a full CLI resource: create, list, get, run, update, pause, resume, cancel, delete. Each task series runs in an isolated per-task session the host garbage-collects when the series is spent, with an optional pre-wake bash script gate. Breaking: the six scheduling MCP tools (schedule_taskand friends) are no longer exposed to agents (#2981, #2978)
nclCLI: verb-level args with per-verb deep help (ncl <resource> help <verb>), and a server-rendered human output format (#2980)
- The agent runner matches
rate_limit_eventas a top-level SDK message type (#2965)
- Agent images build asynchronously —
buildAgentGroupImageswaps its blockingexecSync docker buildfor an awaited promisifiedexec, so the single-threaded host stays responsive during the up-to-15-minute rebuild a package-install approval orncl groups restart --rebuildcan trigger. Timeout, buffered stdio, and non-zero-exit propagation are preserved (#2931)
- Security docs rewritten to match the v2 perimeter —
docs/SECURITY.mdreworked, and the staledocs/docker-sandboxes.mdanddocs/APPLE-CONTAINER-NETWORKING.mdremoved (#2945) - Dead
data/env/envsecrets mirror removed from the setup and migrate paths — channel/setup scripts no longer write a second copy of credentials to disk (#2946) - Stale architecture, scheduling, provider-config, and overlay docs corrected (#2948)
- Mount allowlist honors the
readOnlykey — the per-rootreadOnlyflag (and the top-levelnonMainReadOnlykey) that/manage-mountsand setup actually write is now respected, so read-write grants are no longer silently forced read-only. The allowlist is also read and validated per call with an mtime-keyed cache instead of being parsed once for the whole process lifetime, so a parse error is no longer cached forever (#2943)
- Agent-to-agent reply stamping fixed — the active batch’s
in_reply_tovalue lived in module-level state, but the nanoclaw MCP server runs as a separate subprocess from the poll loop, so the reply stamp was always read asnulland a2a replies fell back to host peer-affinity routing. The stamp now publishes throughsession_stateinoutbound.db(both processes already open it), with anupdated_atstaleness guard so a stamp left by a killed container isn’t reused (#2942) - Removed one-DB-era
@deprecatedshims and dead exports left over from the two-database session split (#2940)
- Re-provision a missing session folder on resolve so the documented session-reset flow works instead of erroring on the recreated session (#2937)
- Dead
nclCLI protocol vocabulary cleaned out of the command registry and framing (#2936) - Removed dead v1 config knobs and a broken pnpm auth script (#2935)
- Security-perimeter env vars are reachable under the packaged service — egress lockdown and the per-container CPU/memory caps were read from
process.envonly, but the shipped launchd/systemd service sets justPATH+HOMEand never loads.envintoprocess.env, so these knobs couldn’t be turned on the supported way. They now route through the samereadEnvFilepath as the other config keys, keepingprocess.envprecedence so dev-mode/nohup installs are unaffected (#2934)
- Colored approval buttons — approval cards render Approve/Reject as styled buttons (Slack
primary/danger) across the OneCLI, primitive, channel-approval, and sender-approval flows (#2933) /add-clidashskill — a zero-dependency, read-only web dashboard that derives its tabs and tables at runtime from any CLI emitting JSON, pre-wired forncl(agent groups, sessions, channels, users, roles) with message-activity charts, a log tail, and a read-only file viewer (#2795)
nclpositional-ID resolution fixed for generated (dashed) identifiers — the longest-registered-prefix lookup no longer mis-splits a dashed ID into command + args (#2932)
- Command-gate hardening —
/startis back in the hostFILTEREDset (a Telegram fix silently undone when host gating landed) so it’s dropped instead of reaching the agent as a normal message, and the inline admin check now callshasAdminPrivilegeinstead of ahasTable('user_roles')guard that only ever masked a missing check (fail-open removed) (#2930)
- OneCLI approval cards show what the agent is doing — when the hosted OneCLI gateway sends a structured
summaryon an approval request, the card rendersAction:plus labeled fields (e.g. the To / Subject / Body of an email send) instead of the rawMETHOD host/path+ body-preview HTTP trace. Rendering is defensive — values are coerced and length-capped, with a running budget that keeps the card under Slack’s section-block limit and reports any omitted fields; without a summary the old body-preview fallback remains (#2929)
- Removed the dead
/workspace/globalcontainer mount and untracked leftover v1 group seed files (groups/global,groups/mainCLAUDE.md) (#2928)
- Unregistered the mock agent provider from the production container barrel so it can’t be selected at runtime (#2927)
- Approved CLI commands no longer run with escalated host privilege — a
nclcommand held for approval was re-dispatched as{ caller: 'host' }when it executed, so an approved group-scoped command ran with unrestricted host scope. The approval now records the original caller context and replays the command under it, so it executes with exactly the requesting agent’s scope (#2611)
- Agent templates — folder-only templates under
templates/(context/instructions.mdplus optional context extras,.mcp.json, and askills/overlay) stamp a new group withncl groups create --template <name>: the provider-neutral instructions are inlined at the top ofCLAUDE.md/AGENTS.mdevery spawn, context extras are copied preserving their layout, MCP servers are written to container config, and the per-group skills overlay is installed. Seedocs/templates.md(#2890) - Guided setup now offers Slack Socket Mode as a first-class option in the Slack setup flow (#2885)
- Container tooling bumps:
@anthropic-ai/claude-code2.1.170 → 2.1.197 (via thecli-tools.jsonmanifest), Claude agent SDK^0.3.170→^0.3.197, Anthropic SDK^0.100.0→^0.108.0
- Inbox symlink containment — channel-inbound attachments and agent-to-agent forwarded files are written into a session’s
inbox/through a shared host-side guard (src/inbox-safety.ts): a symlinked inbox root or per-message directory is rejected, final paths are containment-checked against the session folder, and writes use exclusive-create flags so a pre-placed symlinked file can’t be followed (security, #2828) ncl messaging-groups creategained aninstancecolumn for running multiple adapters of one channel type; when--instanceis omitted it defaults to thechannel_typevalue
- Reject with reason — declining a self-mod or agent-to-agent approval can now relay a one-line note back to the requesting agent (
Your <action> request was rejected by admin: "<reason>"). A plain Reject is unchanged; an unanswered reason prompt finalizes as a plain reject after ~5 minutes /learnskill — distill or refine a reusable skill from a directory, a URL, pasted notes, or the work just done, authored to the project’s skill guidelines- Clearer agent-to-agent gate approval prompt
- [BREAKING] Chat SDK pinned to
4.29.0(was^4.24.0) —chatand the@chat-adapter/*channel adapters are version-locked, so a mismatched pair fails to typecheck. Core installs with no channel (onlycli) are unaffected. Migration: re-run each installed channel’s/add-<channel>skill to pull the matching4.29.0adapter - Per-container resource caps —
CONTAINER_CPU_LIMITandCONTAINER_MEMORY_LIMITpass through todocker runas--cpus/--memory. Both empty by default (unbounded, byte-identical spawn args to before);--memoryis a hard cap on a swapless host /update-skillsnow rebuilds the agent image when a re-applied skill changed files undercontainer/- Fixes: reap dead peer service registrations whose binary is gone; allow an env-selected agent provider in setup
- Per-message approval policies on agent-to-agent connections — an operator can gate one agent→agent edge so every message waits for human approval, without un-wiring it:
ncl policies set --from <id> --to <id> --approver <user-id>. Directed and per-pair; operator-only. Backed by the newagent_message_policiestable (migration 017) - Named approver — a policy pins a single required approver; only that user (or an owner) can clear a held message, recorded as
approver_user_idonpending_approvals(migration 018) .claudeis now mirrored into.agentsvia symlinks
- Setup parses the Claude OAuth token correctly from a wrapped PTY capture
- Codex CLI installs via the
container/cli-tools.jsonmanifest instead of a hard-coded Dockerfile layer /update-nanoclawupgrades the OneCLI gateway when its pinned version moves (versions.json), walking you throughdocs/onecli-upgrades.mdbefore the restart- Budget/billing-exhausted LLM turns (e.g. an Anthropic
403 billing_error) now reach the user instead of being silently dropped and retried
- Operator-driven provider selection in setup — the installer can select, install, and authenticate a non-default agent provider, then set it on the first agent before its first spawn. A provider registry feeds the picker and a vault-only auth walkthrough; default (Claude) installs are unaffected
- Per-group provider choice — the provider is a per-group database property, not an install-wide default:
ncl groups config update --id <group-id> --provider <name>then restart. Group creation stays provider-agnostic - Memory migrates via
/migrate-memory, never at runtime — each provider keeps its own store, so a fresh group on a different provider never inherits staleCLAUDE.*files - Container boot failures now log the last stderr lines at
warnon a non-zero exit instead of looping silently
- Container global CLIs (
@anthropic-ai/claude-code,agent-browser,vercel) are now data-driven fromcontainer/cli-tools.json, each pinned to an exact version — a skill adds a CLI by appending to the manifest instead of editing the Dockerfile
- [BREAKING]
@onecli-sh/sdk0.5.0 → 2.2.1 — now requires a OneCLI gateway with the/v1API; gateways that predate it answer404to every vault call. Sanctioned component versions are pinned inversions.jsonand theoneclisetup step enforces them. Migration:docs/onecli-upgrades.md - Slash commands now interrupt an in-flight turn instead of waiting it out
- New
onExchangeCompleteprovider hook — providers whose harness keeps no on-disk transcript can archive each exchange themselves
- Agent-surfaces capability seam (
providesAgentSurfaces) — a provider can declare it owns the composed project doc, skill links, and state dir, and the host skips its default surfaces
- Opt-in persistent memory scaffold (
usesMemoryScaffold) — providers without native memory get an idempotentmemory/tree in the agent’s host-backed workspace at boot
- Webhook raw-route registry — modules register non–Chat SDK webhooks (GitHub, payment providers, health checks) with
registerWebhookHandler()on the shared server instead of opening a second port
- Open
outbound.dbread-write inwriteOutboundDirectso direct outbound writes aren’t blocked
- Approval-resolved callback registry — modules can observe when an approval is resolved (e.g. to clear an “awaiting approval” indicator)
- Grace period for freshly-woken containers with stale processing claims — skips SLA enforcement on the tick that wakes a container, preventing a spawn-kill loop
- Record the acting user on resolved approval cards
- Add a read side (
getDeliveryAction) to the delivery action registry so module registrations can be behavior-tested
- Multi-instance adapters — run several adapters of one platform at once (e.g. three Slack apps in one workspace). A new
messaging_groups.instancedimension (migration 016) keys the adapter registry, per-instance Chat SDK state namespaces and webhook routes, and threads the instance through the router, delivery, and typing indicators. Single-instance installs are unchanged (instance defaults to the channel type) - Interactive uninstaller —
bash nanoclaw.sh --uninstallremoves one NanoClaw copy (service, containers, data, OneCLI agents) with a scan → confirm → execute flow,--dry-runand--yesflags, and per-checkout install-slug scoping. See Uninstall NanoClaw
- Egress lockdown (opt-in) — an agent group’s outbound network is forced through the OneCLI gateway, so a compromised agent can’t reach arbitrary hosts (v2.1.1). See Hardening
- v2.1.2–v2.1.4 were rapid patch follow-ups with no user-facing changes
- [BREAKING] Startup now requires an upgrade marker. The host refuses to boot unless
data/upgrade-state.jsonrecords that the install reached its version through a sanctioned path (/setup,/update-nanoclaw,/migrate-nanoclaw). Stamp it withpnpm exec tsx scripts/upgrade-state.ts set— the same command clears a tripped marker. See Upgrading - Skills retrofit — channel, provider, and tool skills made v2-conformant (minimal integration surface, a test per integration point); four skills broken on v2 were dropped
/upload-trace— upload a session trace to Hugging Face for sharing or debugging- Session-transcript rotation before resume — oversized or aged transcripts are rotated so a cold resume can’t wedge the container
/add-rtktoken-efficient CLI-proxy skill, and awhatsapp-formattingcontainer skill- Fixes: signal-cli 0.13+
listAccounts, the Photon remote-iMessage URL, channel-approval target scoping, and per-groupCLAUDE.local.mdloading
ncl destinations add/removethrough the approval flow now reach the receiver immediately — an approved destination no longer failssend_messagewithunknown destinationuntil the next container restart
Rollup release covering v2.0.55–v2.0.63; per-bump GitHub Releases begin here.
- [BREAKING] Service names are now per-install — the launchd label and systemd unit are slugged to your project root (
com.nanoclaw.<sha1(projectRoot)[:8]>,nanoclaw-<slug>.service). The oldcom.nanoclaw/nanoclaw.servicenames no longer match; find yours withsource setup/lib/install-slug.sh && launchd_label(macOS) orsystemd_unit(Linux) - Stronger
<message>-wrapping enforcement; MCP servers added viaadd_mcp_serverinherit OneCLI gateway routing; CLI-scope hardening (scopeFieldfails closed,sessions getguarded against cross-group access);/add-gmail-tooland/add-gcal-toolaligned with the v2 container-config model;qwibitai/nanoclaw→nanocoai/nanoclawswept across code and docs
- Per-group model and effort overrides —
ncl groups config update --model <model> --effort <level>, falling back to the host-configured model when unset - Container
claude-codebumped to 2.1.128
- Container config moved to the database — per-group runtime config (provider, model, packages, MCP servers, mounts, skills) lives in the
container_configstable instead ofgroups/<folder>/container.json; filesystem configs are backfilled on startup ncl groups restartwith--rebuild/--message— config edits no longer auto-kill containers; on-wake messages are picked up only by a fresh container’s first poll- Per-group
cli_scope(disabled/group/global, defaultgroup) — controls what the agent can reach vianclfrom inside its container, with cross-group result filtering
- The
ncladmin CLI — query and modify the central DB (agent groups, messaging groups, wirings, users, roles, members, destinations, sessions, approvals, dropped messages). Host transport over a Unix socket; container-side writes go through the approval flow - v1 → v2 migration —
bash migrate-v2.shfinds your v1 install, merges.env, seeds the v2 DB, copies group folders (CLAUDE.md→CLAUDE.local.md) and session data, ports tasks and channels, then hands off to/migrate-from-v1. See Migrate from v1
- Post-rewrite stabilization — roughly 44 rapid patch releases over two weeks, before the per-release changelog discipline began at v2.0.63. Not individually documented upstream; see the GitHub releases for raw notes
- Ground-up architectural rewrite with new entity model (users, roles, messaging groups, agent groups, wirings)
- Two-database session model —
inbound.db(host writes) andoutbound.db(container writes) eliminate cross-mount SQLite contention - Agent-runner moved from Node.js to Bun — runs TypeScript directly without compilation
- Shared-source agent-runner —
/app/srcis a read-only bind mount, source changes never require image rebuild tinias PID 1 for proper signal forwarding- Three-level channel isolation model with
unknown_sender_policy(strict, request_approval, public) - Per-wiring engage modes:
pattern,mention,mention-sticky - Sender scope enforcement per wiring (
allorknown) - Channel and sender approval flows with interactive cards
- Tasks stored as
messages_inrows with cron-based recurrence and series tracking - Delivery system with two-poll architecture (active 1s, sweep 60s)
- Module system for permissions, scheduling, agent-to-agent, approvals, and self-modification
- OneCLI Agent Vault is the sole credential path
- Channels moved to separate branches (trunk ships no adapters)
- Per-agent-group custom Docker images with additional packages
- Updated token count to 43.8k tokens (22% of context window)
- Added
.claude/settings.jsonwith default SDK configuration - Added
ONECLI_API_KEYconfiguration option for OneCLI gateway authentication
- Fixed Gmail OneCLI credential mode detection — properly detects when running under OneCLI Agent Vault
- Reduced setup friction and improved diagnostics output
- Added
.npmrcwith 7-day minimum release age for dependency safety
- Fixed writable global memory mount for main agent — corrected the path in container
CLAUDE.md - Fixed three issues in the Karpathy wiki skill
- Updated
init-onecliskill to useONECLI_URLvariable
- Lowered auto-compact threshold to 165k tokens for better context fidelity
- Added
/add-karpathy-llm-wikiskill — persistent wiki knowledge base per group, based on Karpathy’s LLM Wiki pattern - Added
/migrate-nanoclawskill — intent-based upgrade that extracts customizations into a migration guide and reapplies them on a clean upstream base - Added
/migrate-from-openclawskill — guided migration from OpenClaw installations - NanoClaw now suggests
/migrate-nanoclawwhen the user’s fork is far behind upstream
- Added automatic session artifact pruning on startup and daily — cleans up stale session JSONLs (7 days), debug logs (3 days), todo files (3 days), and telemetry (7 days) while preserving active sessions
- Upgraded agent SDK to 0.2.92 with auto-compact at 165k tokens
- Main agent now has direct read-write access to the SQLite database —
store/is mounted separately at/workspace/project/storeso the main group can query and write data directly - Added
requiresTriggerparameter to theregister_groupMCP tool (defaults tofalse) — controls whether messages must start with the trigger word for the group to respond
- Added reply/quoted message context support — channels can now pass
reply_to_message_id,reply_to_message_content, andreply_to_sender_namefields with messages - Reply context is rendered as
<quoted_message>XML in agent prompts, giving agents full awareness of which message a user is responding to - Database migration adds reply context columns to the messages table (nullable for backward compatibility)
- Added
/add-macos-statusbarutility skill — macOS menu bar status indicator with start/stop/restart controls - Added Telegram channel contributors (contributed by @cschmidt, @leonalfredbot-ship-it, @moktamd, @gurixs-carson)
- Auto-recover from stale Claude Code session IDs instead of retrying infinitely — detects missing session transcripts and clears the broken session for a fresh retry
- Removed built-in Ollama MCP server from core — Ollama integration is now exclusively available via the
/add-ollama-toolskill - Fixed npm audit dependency errors
- Setup skill now routes credential system by container runtime: Docker uses OneCLI Agent Vault, Apple Container uses native credential proxy
- Marked Apple Container as experimental
- Migrated
x-integrationhost.ts from pino to built-in logger (follow-up to v1.2.36 cleanup) - Fixed
stopContainer()test compatibility — mocked container-runtime so tests don’t require Docker - Cleared stale Telegram token from
.env.example
- Fixed message history overflow: when
lastAgentTimestampwas missing, all 200 messages were sent to the agent instead of respectingMAX_MESSAGES_PER_PROMPT(default 10). Added cursor recovery from last bot reply.
- Security fixes: command injection prevention in
stopContainer(name validation), mount path colon rejection, allowlist caching fix (contributed by @foxsky)
- Fixed
isMainflag preservation onregister_groupIPC updates — prevents accidental privilege stripping (contributed by @snw35)
- Fixed
.envparser crash on single-character values (contributed by @foxsky)
- [BREAKING] Replaced
pinologger with built-in logger module — removes 2 runtime dependencies. WhatsApp users must re-merge the WhatsApp fork to pick up the Baileys logger compatibility fix:git fetch whatsapp main && git merge whatsapp/main. If thewhatsappremote is not configured:git remote add whatsapp https://github.com/nanocoai/nanoclaw-whatsapp.git - Removed
yamlandzoddependencies — core runtime now uses only 3 packages - Updated Ollama skill with admin model management tools
- Channel-formatting text-style fixes for WhatsApp and Telegram (contributed by @kenbolton)
- [BREAKING] OneCLI Agent Vault replaces the built-in credential proxy. Check your runtime:
grep CONTAINER_RUNTIME_BIN src/container-runtime.ts— if it shows'container'you are on Apple Container, if'docker'you are on Docker. Docker users: run/init-oneclito install OneCLI and migrate.envcredentials to the vault. Apple Container users: re-merge the skill branch (git fetch upstream skill/apple-container && git merge upstream/skill/apple-container) then run/convert-to-apple-container— do NOT run/init-onecli(it requires Docker). The legacy credential proxy is available as an opt-in skill via/use-native-credential-proxy. - Channel tokens (Telegram, Slack, Discord) remain in
.env— only container-facing credentials (Anthropic, OpenAI, etc.) are migrated to the vault.
- Added
/add-emacschannel skill (contributed by @kenbolton) - Fixed mount-allowlist preservation —
/setupno longer overwrites existingmount-allowlist.json(contributed by @akasha-scheuermann) - Fixed Telegram migration backfill to default chats as direct messages instead of groups (contributed by @RichardCao)
- Fixed CI workflows to skip
bump-versionandupdate-tokenson forks (contributed by @shawnyeager)
- Fixed container mounts to include group folder and sessions directory (contributed by @kenbolton)
- Added
/channel-formattingskill — channel-aware text formatting for WhatsApp, Telegram, Slack, and Signal - Fixed per-group trigger pattern matching — each group can now define its own trigger word (contributed by @mrbob-git)
- Fixed
loginctl enable-lingerso systemd user service survives SSH logout (contributed by @IYENTeam) - Clarified WhatsApp phone number prompt to prevent auth failures (contributed by @ingyukoh)
- Added Telegram forum topics contributor (contributed by @flobo3)
- Fixed
isMain-based CLAUDE.md template selection during runtime group registration
- Fixed CLAUDE.md template copy when registering groups via IPC (contributed by @ingyukoh)
- Fixed diagnostics to use explicit Read tool directive for pickup instructions (contributed by @Koshkoshinsk)
- Added task scripts — scheduled tasks can now run a pre-execution bash script that conditionally wakes the agent via
wakeAgentJSON contract (contributed by @gabi-simons)
- Fixed agent-runner source cache to refresh based on file modification time instead of copying once at startup
- Removed accidentally merged Telegram channel code from main
- Removed Grammy dependency and pinned
better-sqlite3@11.10.0andcron-parser@5.5.0 - Removed auto-sync GitHub Actions
- Enabled
loginctl lingerduring setup so systemd user service survives SSH logout - Clarified WhatsApp phone number prompt format (digits only, no
+prefix) - Added CLAUDE.md template copy during IPC group registration
- Fixed timezone validation to prevent crash on POSIX-style TZ values — now validates IANA identifiers and falls back to UTC
- Expanded fork sync auto-resolve patterns and added missing forks to dispatch list
- Fixed diagnostics prompt not being shown to user (contributed by @Koshkoshinsk)
- Added auto-resolve for
package-lock.json, badge, and version conflicts during fork sync
- Added
/use-native-credential-proxyskill — opt-in restoration of the built-in.env-based credential proxy for users who prefer it over OneCLI - Removed dead
src/credential-proxy.tscode (unused since v1.2.22) - Updated token count to 39.8k tokens (20% of context window)
- Upgraded Zod dependency from v3 to v4 (
^4.3.6)
- Replaced credential proxy with OneCLI Agent Vault for container credential injection
- Updated token count to 40.7k tokens (20% of context window)
- Added opt-in diagnostics via PostHog — collects anonymous system info (OS, architecture, version, selected channels) during
/setupand/update-nanoclawwith explicit user consent - Three-option prompt: Yes (send once), No (skip), Never ask again (permanently opt out)
- No runtime telemetry — diagnostics run only in skill workflows, not in the application itself
- Added ESLint configuration with error-handling rules across the codebase
- Reduced
docker stoptimeout for faster container restarts (-t 1flag — containers are stateless)
- Added read-only
/capabilitiesand/statuscontainer-agent skills
- Tasks snapshot now refreshes immediately after IPC task mutations (contributed by @mbravorus)
- Fixed remote-control prompt auto-accept to prevent immediate exit
- Added
KillMode=processso remote-control survives service restarts (contributed by @gabi-simons)
- Added
/remote-controlcommand for host-level Claude Code access from within containers
Major architecture change: skills are now git branches, channels are separate fork repos.
Features
- Skills live as
skill/*git branches merged viagit merge— no more marketplace or plugin system - Added Docker Sandboxes announcement and manual setup guide
- Added
nanoclaw-docker-sandboxesto fork dispatch list
Breaking
- Skills are no longer installed via marketplace or plugin commands — use
git mergeworkflow
Fixes
- Fixed setup registration to use
initDatabase/setRegisteredGroupwith correct CLI commands - Auto-resolve
package-lockconflicts when merging forks - Bumped
claude-agent-sdkto ^0.2.76
Features
- PDF reader skill: Read and analyze PDF attachments
- Image vision skill: Image analysis for WhatsApp
- WhatsApp reactions skill: Emoji reactions and status tracker
Fixes
- Fixed task container to close promptly when agent uses IPC-only messaging
- Fixed WhatsApp pairing code written to file for immediate access
- Fixed WhatsApp DM-with-bot registration to use sender’s JID
- Added
LIMITto unbounded message history queries for better performance
- Fixed misleading
send_messagetool description for scheduled tasks
- Added
/add-ollamaskill for local model inference - Added
update_tasktool and return task ID fromschedule_task
- Updated
claude-agent-sdkto 0.2.68
- CI formatting fix
- Fixed
_chatJidrename tochatJidinonMessagecallback
- Added sender allowlist for per-chat access control to restrict who can interact with the agent
- Version bump (no functional changes)
Major release introducing multi-channel architecture. WhatsApp is no longer hardcoded — all channels self-register via a channel registry.
Features
- Channel registry: Channels self-register at module load time via
registerChannel()factory pattern isMainflag: Explicit boolean replaces folder-name-based main group detection- Channel-prefixed group folders: Groups use
whatsapp_main,telegram_family-chatconvention to prevent cross-channel collisions - Unconfigured channels now emit WARN logs naming the exact missing variable
Breaking
- WhatsApp moved to skill: No longer part of core — apply with
/add-whatsapp ENABLED_CHANNELSremoved: Orchestrator usesgetRegisteredChannelNames(); channels detected by credential presence- All channel skills simplified: No more
*_ONLYflags — all use self-registration pattern
Fixes
- Prevent scheduled tasks from executing twice when container runtime exceeds poll interval
Migration
Existing WhatsApp users: run/add-whatsapp in Claude Code CLI after updating.- Added CJK font support (
fonts-noto-cjk) for Chromium screenshots (contributed by @neocode24)
- Fixed wrapped WhatsApp message normalization before reading content
- Added third-party model support — use models beyond Claude
- Added
/update-nanoclawskill for syncing customized installs with upstream (replaces old/update) - Added Husky and
format:fixscript for consistent formatting
Biggest pre-1.2 release — added Slack channel, refactored Gmail, and improved CI.
Features
- Added
/add-slackskill - Restructured add-gmail skill for new architecture with graceful startup when credentials missing
- Removed deterministic caching from skills engine
- Added
.nvmrcspecifying Node 22 - CI optimization, logging improvements, and codebase formatting
- Added CONTRIBUTORS.md and CODEOWNERS
Fixes
- Fixed WhatsApp QR data handling
- Rebased core skills (Telegram, Discord, voice) to latest main
- Improved error handling for WhatsApp Web version fetch (follow-up to v1.1.1)
Features
- Added
/updateskill to pull upstream changes from within Claude Code - Replaced ‘ask the user’ with
AskUserQuestiontool in skills
Security
- Fixed critical skills path-remap root escape (including symlink traversal)
Fixes
- Fixed empty messages from polling queries
- Fixed fallback name from ‘AssistantNameMissing’ to ‘Assistant’