- [BREAKING] A new Slack experience — per-agent provisioned Slack apps, agent spawning from Slack, and UX improvements — is available to classic single-bot Slack installs. Classic Slack keeps working unchanged; this gate asks for a decision, not a forced migration. New installs and non-Slack installs are unaffected. Migration: run
/migrate-slack-agents— it detects classic state (exits cleanly otherwise) and either walks the upgrade or records the choice to stay on classic; both outcomes satisfy this requirement. /add-codexnow pins@openai/codex0.146.0. The previous pin (0.138.0) defaults to GPT-5.4, which OpenAI retires from Codex on 2026-08-31 — codex-provider agents ride the CLI default model, so stock installs stop completing turns at retirement — and it rejects the newer GPT-5.6 models with a 400 asking for a newer Codex CLI. Existing codex installs are not re-pinned by re-running/add-codex(the manifest merge is keyed on package name): edit the@openai/codexentry incontainer/cli-tools.jsonto0.146.0, rebuild the agent image (./container/build.sh), and restart.- [BREAKING] Agent mailbox access now 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, update moved runner state/heartbeat helpers, drop
DeliveryActionHandler's database argument, use booleans fortrigger/onWake, and use the closed inbound-kind set. Migration: follow the agent mailbox seam migration guide for the complete detect grep, old→new symbol map, verification, and rollback. - Scheduled-task lifecycle semantics are stricter. Deleting an isolated task cascades its session state, updates refuse already-due runs, recurring selection uses the active series snapshot, and generated task timestamps retain millisecond precision.
- [BREAKING] The container runtime moves behind the session driver seam. Session containers are 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 align to the runtime label grammar — at most 63 characters of[A-Za-z0-9_-], alphanumeric at both ends — so previously-legal 64-character names, trailing-/_, and unvalidated legacy imports refuse to spawn; (2) container names and invocation change fromnanoclaw-v2-<folder>-<timestamp>to key-derivedncl-…names (the old human-readable name survives as thenanoclaw-container-namelabel) and fromdocker runtocreate+start --attach— name-based tooling, Docker-command allowlists, wrappers, and audit rules should match by label instead (docker ps --filter label=nanoclaw-session, or--filter label=nanoclaw-group-folder=<folder>); (3) internal container helpers moved into the driver module — customized installs importinghostGatewayArgs,readonlyMountArgs,stopContainer,ensureContainerRuntimeRunning,cleanupOrphans, or patchingbuildContainerArgsstop compiling. Theuse-native-credential-proxyskill is retired: the spec's admission rules refuse credential values in container env on every lane, by design — 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 in your install and prints one finding per line with a minimal fix instruction; hand the output to your coding agent. Nothing detected means nothing to do. - Host restarts now 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. Pre-seam containers (spawned before this release) cannot be adopted and are removed at first upgraded startup, exactly as the old startup cleanup did. - 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 around it, and gateway selection becomes a registry (
NANOCLAW_GATEWAY_PROVIDER, defaultonecli— an install that never sets it behaves as it always has). - Non-root hosts get an explicit container identity. Every non-root host now passes
--user <uid>:<gid>andHOME=/home/node(previously uid-1000 hosts relied on the image'snodeuser). On uid-1000 systems whose primary gid is not 1000, files the agent writes into mounted workspaces now carry the host's gid — which is the intended behavior. CONTAINER_MEMORY_LIMITis validated at spawn. Invalid values refuse the spawn with a named error instead of surfacing as a raw Docker error; blank and0still mean uncapped.- An unknown
NANOCLAW_RUNTIME_DRIVERaborts startup (new variable — installs that never set it are unaffected), and drivers that cannot rebuild images in place denyinstall_packagesand--rebuildat request time instead of failing later. - [BREAKING] The host runtime now requires Node.js 22 or newer. Node 20 is not supported by the upgraded
better-sqlite3release used for current Node runtimes. Migration: runbash setup/install-node.sh, verifynode --versionreports v22 or newer, then rerun/update-nanoclaw; stay on the previous NanoClaw release if Node cannot yet be upgraded. - New NanoClaw installs now use OneCLI gateway 1.41.0. Existing 1.36.0 gateways remain compatible because NanoClaw does not depend on any 1.41-only behavior. See the OneCLI upgrade guide to upgrade an existing gateway.
- [BREAKING] Central database access is now asynchronous behind
DbDriver. SQLite remains the default and existingdata/v2.dbfiles are unchanged, but custom source and installed channel/provider extensions must await central reads and writes and adopt the retyped host seams. Migration: follow the central database async migration guide to find affected calls, preserve transaction boundaries, update extensions, verify SQLite behavior, or roll back. - Central DB composition and migrations are backend-ready. A one-slot driver registry keeps backend selection in
src/db/compose.ts;pnpm run migrateis the explicit schema-change path, host validation can fail closed without DDL, and a shared conformance suite pins transaction, parameter, ordering, and timestamp behavior. SQLite remains the installed default.
New Contributors
- @teran13 made their first contribution in #3230
- @brentkearney made their first contribution in #3210
Contributors
Thanks to everyone who landed work in this release:
- ci: let verify-agent-image run on every PR so it can gate by @gavrielc in #3238
- ci: open the agent-image bump PR from a dispatch by @gavrielc in #3240
- verify-agent-image: pin the publisher identity, and check attestations per arch by @gavrielc in #3158
- ci: let a verified signature be the approving review by @gavrielc in #3241
- verify-agent-image: arming auto-merge is not a verdict by @gavrielc in #3243
- fix(poll-loop): stopped loops leaked their active query's follow-up poller by @gavrielc in #3268
- engine: bump OneCLI gateway pin to 1.41.0 by @amit-shafnir in #3277
- Permissions: registerChannelCardInterceptor seam before registration cards (A4) by @gavrielc in #3266
- Agent-to-agent: CreateAgentOptions.suppressCreatedNotify (A3) by @gavrielc in #3265
- Delivery: registerDeliveryBatchPreview hook over each undelivered batch (A2) by @gavrielc in #3264
- Channel registry: hot-start a registered adapter after boot (A1) by @gavrielc in #3263
- channels: Chat SDK bridge agent-mode DM surface — app-context capture, DM-thread normalization, dm-opened hook (A8 + C4) by @gavrielc in #3262
- channels: optional adapter capabilities — status-bearing setTyping, setThreadTitle, setSuggestedPrompts + registry passthroughs by @gavrielc in #3261
- permissions: 'decline_notify' unknown-sender policy — polite decline + one-line owner FYI, no approval card by @gavrielc in #3260
- setup/tooling: skill-apply heading-ordinal strip, headless browser URL surfacing, inherit-script extraction by @gavrielc in #3259
- container: mid-turn streaming is the single delivery door — cross-segment assembly, DB-backed echo suppression, no result-door sends by @gavrielc in #3284
- Preserve structured chat links by @Koshkoshinsk in #3283
- fix: idle container with no heartbeat file is exempt from the absolute-ceiling kill forever by @gavrielc in #3252
- fix(skills): stop removal docs pointing at the retired data/env mirror by @teran13 in #3230
- docs(container): tell the agent where received attachments land by @brentkearney in #3210
- fix(container): bump pnpm and npm past fixable-critical tar CVE by @gabi-simons in #3207
- Add AnyDoc document conversion skill by @amit-shafnir in #3198
- Cross-session context for multi-session agent groups (batching, delivery-resolution, and detached-state prerequisites included) by @gavrielc in #3285
- channels: generic membership-event hook on the Chat SDK bridge by @gavrielc in #3295
- agent-runner: extendTool — additive MCP tool schema and description extension by @gavrielc in #3296
- delivery: post-delivery hook with first-delivery context by @gavrielc in #3294
- router: session-created hook for brand-new engaged sessions by @gavrielc in #3293
- setup: per-channel pre-step and companion-skill declarations for the wizard by @gavrielc in #3297
- channels: bridge inbound-policy registration seam by @gavrielc in #3292
- channels: adapter-declared session-mode context defaults (threads stamp derived) by @gavrielc in #3304
- fix(update): make NanoClaw upgrades transactional by @glifocat in #3195
- cross-session context: extend new-session backfill to group conversations by @gavrielc in #3312
- skills: channel welcome addenda, matched host-side (zero cost when absent) by @gavrielc in #3313
- setup: optional automatic Slack app provisioning behind an opt-in flag by @gavrielc in #3314
- Fix Telegram skill copy list by @Koshkoshinsk in #3316
- ci: add zvi-fried to core team roster by @gabi-simons in #3348
- drivers: a session-runtime driver seam, with Docker as the built-in realization by @gavrielc in #3306
- host: route session lifecycle through the driver seam by @gavrielc in #3307
- groups: refuse to create a group over a folder that already exists undisposed by @gavrielc in #3308
- refactor(db): prepare the central database for portable drivers by @moshe-nanoco in #3332
- refactor(db): add async central database seam by @moshe-nanoco in #3333
- [BREAKING] refactor(db): adopt async central database safely by @moshe-nanoco in #3334
- refactor(db): add backend composition and portable tests by @moshe-nanoco in #3335
- fix setup ping folder compatibility by @amit-shafnir in #3350
- docs(telegram): document approved group connection flow by @amit-shafnir in #3352
- setup: --slack-agents installs the whole Slack agents feature by @Koshkoshinsk in #3357
- fix(setup): fail closed when a stored sign-in cannot be verified by @gavrielc in #3339
- feat(setup): forward optional client metadata on Slack service requests by @gavrielc in #3345
- fix(approvals): record the delivering instance on pending_approvals by @gavrielc in #3340
- fix: support current Node runtimes by @Koshkoshinsk in #3360
- fix: expose decline notification overrides by @Koshkoshinsk in #3361
- feat(cli): accept bounded JSON from stdin by @zvi-fried in #3218
- fix(add-codex): bump @openai/codex pin 0.138.0 → 0.146.0 before GPT-5.4 retires from Codex (2026-08-31) by @chiptoe-svg in #3299
- Log a warning for unrecognized engage_mode instead of silent drop by @wakqasahmed in #3317
- feat: add agent mailbox seam and registry by @zvi-fried in #3349
- fix(setup): manage template agents after install by @amit-shafnir in #3365
- fix(setup): allow choosing a different Slack workspace by @Koshkoshinsk in #3383
- fix(permissions): disclose shared agent-group scope on approval cards by @Koshkoshinsk in #3386
- fix(setup): drop stale Slack experimental/public-URL copy, modal-agnostic app-creation walkthrough by @Koshkoshinsk in #3389
- feat(setup): finish Slack app installs the workspace must approve by @gavrielc in #3399
- setup: the managed Slack experience becomes the default by @gavrielc in #3404
- feat: add /migrate-slack-agents upgrade path for classic Slack installs by @Koshkoshinsk in #3391
- fix(permissions): assert the scope warning via its constant, not a copy of it by @gavrielc in #3407
- docs+setup: announce one-click Slack agents by @gavrielc in #3421
- feat(drivers): ratify the attach surface — a driver describes its exec argv by @gavrielc in #3429
- fix: restore stable CI required check by @zvi-fried in #3430
- [main] fix(providers): accept provider file events by @zvi-fried in #3402
- [main] fix(matrix): use a refresh-safe ESM patch by @zvi-fried in #3403
- [main] ci: test registry-backed skills by @zvi-fried in #3424
- feat(setup): add Dial to the channel picker + wizard/skills (runChannelSkill model) by @OmriBenShoham in #3050
- chore(container): bump claude-code to 2.1.238 and agent SDK to 0.3.238 by @gavrielc in #3439
- fix(add-dial-number): use nc directives by @zvi-fried in #3433
- feat(drivers): validate runtimeTier against driver isolation capabilities; select the tier from group config by @gavrielc in #3442
- fix(upgrade-state): accept a version-matching marker when Git cannot identify the checkout by @gavrielc in #3444
- build: drop better-sqlite3 from onlyBuiltDependencies — use its bundled prebuilds by @gavrielc in #3443
- fix(setup): skip Slack auto-provisioning when a bot is already saved by @Koshkoshinsk in #3390
- fix(telegram-setup): pairing card says 6 digits by @amit-shafnir in #3431
- fix(chat-sdk): polling adapters do not open the webhook server by @amit-shafnir in #3434
- feat(setup): carry the adapter instance through pairing, init-first-agent and the CLI welcome by @amit-shafnir in #3478
- docs(add-telegram): add-another-bot path, instance-aware pairing and wiring by @amit-shafnir in #3479
- feat(setup): the wizard offers "add another Telegram bot" when one is already configured by @amit-shafnir in #3480
- chore(release): v2.3.0 by @gavrielc in #3495
Full Changelog: v2.2.0...v2.3.0