nodeenv doesn't support major-only version specifiers. Use lts
which resolves to the latest LTS release.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The disk threshold was unreliable on hosts with separate /home or /var
mounts where df underreports free space. Simplify the pre-flight to a
RAM-only check.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@chat-adapter/discord@4.27.0 includes vercel/chat#256, which fixes the
Discord adapter unconditionally setting payload.content alongside
payload.embeds when posting a card. In 4.26.0 every Discord card
appeared twice (text content above the embed, identical content inside
the embed) — every new install reproduced this on the welcome tour and
on every approval card.
The other 7 skills bump in lockstep because @chat-adapter/discord@4.27.0
depends on chat@4.27.0 while @chat-adapter/<other>@4.26.0 depend on
chat@4.26.0. Mixing the cohort produces a TypeScript dual-version
conflict between the bridge and adapter ChatInstance types.
Files updated (one line per file in each pnpm install command):
- add-discord (the user-visible bug fix)
- add-gchat, add-github, add-linear, add-slack, add-teams, add-telegram,
add-whatsapp-cloud (cohort consistency)
Out of scope: add-imessage, add-matrix, add-webex, add-resend use
third-party packages with independent versioning.
Closes#2264
The send_card MCP tool wrote outbound rows with type='card' but the
chat-sdk-bridge deliver() had no branch for them, so the payload fell
through to the text fallback (where text is undefined) and silently
returned without calling the adapter. delivery.ts then marked the
message delivered with platformMsgId=undefined and the user saw nothing.
Add a dedicated card branch mirroring the ask_question structure:
- Build Card from title, description, and string-or-{text} children
- Render only URL actions as LinkButtons (send_card is fire-and-forget
per its docstring, so callback buttons would have nowhere to land)
- Drop empty cards with a warn log instead of posting blank
- Fall back text: content.fallbackText > description > title
Affects every Chat SDK adapter that goes through the bridge: Discord,
Telegram, Slack, Teams, GChat, GitHub, Linear, WhatsApp Cloud, iMessage,
Matrix, Webex, Resend.
Tests: adds five cases covering normal render, action filtering,
link-button rendering, empty-card skip, and a regression check that
non-card chat-sdk payloads still flow through the text branch.
Closes#2263
Remove step 2d (whatsapp-resolve-lids.ts) which pre-created duplicate
messaging_groups rows keyed by @lid alongside the phone-keyed rows.
This caused split sessions — the same contact got separate sessions
depending on which JID format arrived.
With the Baileys v7 upgrade (PR #2259 on channels), the adapter
resolves every LID to a phone JID via extractAddressingContext before
the message reaches the router, making dual rows unnecessary.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Two friction points in the Telegram channel's "Open Telegram" card,
both surfaced when running setup on a VM-via-SSH where the user's
local laptop has no Telegram client installed:
1. The opening sentence read "Opening @yourbot in Telegram so it's
ready when the pairing code shows up." On a headless device that's
misleading — nothing is auto-opened, the user has to click the
link or use their phone. Rewrite as a direct, action-led
instruction on the headless flow only:
Open @yourbot in Telegram now — the pairing code is coming next,
and that's where you'll send it.
Plus a "Get started: <url>" line and a full-strength mobile
fallback hint inside the card so headless users have all
self-serve options visible.
On non-headless the original status-style line stays accurate
(`xdg-open` / `open` does fire for users with Telegram desktop
installed), so the card stays a single line.
2. Clicking `https://t.me/yourbot` silently fails when the user's
local device has no Telegram client. Non-headless gains:
- a "(must be installed here)" qualifier on the confirm prompt
so users without Telegram desktop know up-front;
- a single combined dim fallback line below the prompt:
"If browser does not appear, please visit: <url> — or
search for @yourbot on your mobile."
Direct `p.confirm` + `openUrl` instead of `confirmThenOpen` for
the non-headless branch so we control the dim line fully (single
combined line vs the helper's default URL-only line).
Headless layout drives the same self-serve content via the card body
itself; no confirm prompt fires there.
NanoClaw is known to not run reliably on GCE instances. Detect via DMI
during pre-flight (between the spec check and root warning) and let the
user abort before sinking time into bootstrap.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Pre-flight check in nanoclaw.sh that detects available RAM and free disk
on the project-root partition (Linux + macOS) before the bootstrap
spinner runs. Below 3700 MB RAM or 20 GB free disk, surfaces a "likely
cannot run" warning with a Try-anyway prompt defaulting to abort. The
3700 MB floor sits below 4 GB because "4 GB" VMs typically report
3700–3900 MB after kernel reserves (Hetzner CX21 ≈ 3814, AWS t3.medium
≈ 3800). Cheaper to fail here than to wait through pnpm install on a
host that can't run the agent container. Diagnostic events fire on
continue/abort.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
setup/lib/windowed-runner.ts was the one place on main still printing
elapsed time as raw seconds (`(170s)`) instead of using the
minute-aware `fmtDuration` helper from #2108. Two spots — the live
spinner suffix that ticks during the build, and the
success/error completion suffix — both now go through `fmtDuration`,
so anything past 60 seconds renders as `Xm Ys` (e.g. `2m 50s`) like
the rest of the setup flow.
The miss happened because a separate PR (closed) was supposed to
remove the timer entirely from this file, so #2108 deliberately
skipped it. With that other PR closed, applying `fmtDuration` here
is the consistent fix.
Pure formatting change. The helper itself is unchanged from #2108;
behavior under 60s is identical (`Xs`); behavior past 60s now
matches everywhere else.
Step 1 of the Telegram channel's BotFather instructions used to read:
1. Open Telegram and message @BotFather
Two small UX issues with that:
- "BotFather" reads slightly sketchy without context — a first-time
user has no way to know it's the official, sanctioned account
rather than an impersonator.
- Typing the username from memory leaves room for picking a typo'd
impostor account (Telegram has many @BotF4ther / @BotFAther / etc.
look-alikes).
Update the line so the official-bot framing is part of the instruction
itself:
1. Open Telegram and message @BotFather — Telegram's official bot
for creating and managing bots
One-line change in the existing note() body. No new dependencies, no
asset churn, no other behavior change.
Claude Code 2.1.116+ treats SDK `allowedTools` as a hard whitelist:
servers whose namespace isnt listed are filtered out before the agent
ever sees them, regardless of `permissionMode: bypassPermissions` or
any `permissions.allow` in settings. The static TOOL_ALLOWLIST only
contained `mcp__nanoclaw__*`, so any MCP wired via add_mcp_server (or
directly in container.json) was silently dropped.
Derive `mcp__<sanitized-name>__*` entries at the SDK call site from
the already-aggregated `this.mcpServers` map, mirroring the SDKs own
sanitization rule (chars outside [A-Za-z0-9_-] become _).
Prior diagnosis by @jsboige in #2028 (withdrawn, not upstreamed).
The OneCLI installer (curl onecli.sh/install | sh) doesn't pass
--remove-orphans to docker compose up. After the upstream service rename
(app -> onecli), the legacy onecli-app-1 container keeps :10254 bound
and crashes the new bring-up. This breaks /migrate-v2.sh on any host
that has a pre-rename OneCLI installed.
Workaround: before invoking the installer, remove containers in the
"onecli" compose project whose service name isn't in the v2 set
({onecli, postgres}). Label-keyed and no-op on fresh installs.
Filed upstream; remove this once the installer adds --remove-orphans.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The previous approach deleted the v1 unit file and symlinked it to v2,
making rollback impossible. Now we just disable v1 and leave the file
on disk so users can switch back with a single command.
Also adds rollback instructions to the migration summary output.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
After migration keeps v2, the old unslugged `nanoclaw.service` (or
`com.nanoclaw.plist`) was only disabled — the unit file stayed on disk.
A `systemctl --user restart nanoclaw` would start v1 instead of v2.
Now the migration removes the old file and symlinks it to the v2 unit,
so the legacy name transparently starts v2. Handles systemd (Linux/WSL)
and launchd (macOS). Idempotent — skips if the symlink already exists.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The migration script has interactive prompts and streams progress
output that gets collapsed when run via Claude Code's Bash tool.
Add a TTY guard that exits early with instructions to use the !
prefix instead.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The credential proxy already reads ANTHROPIC_AUTH_TOKEN (credential-proxy.ts
line 33) and uses it for OAuth-mode authentication, but setup/verify.ts did
not include it in its credential-detection regex. Users with
ANTHROPIC_AUTH_TOKEN in .env saw 'CREDENTIALS: missing' even though their
credentials were valid at runtime.
Add ANTHROPIC_AUTH_TOKEN to the regex and add a matching test case.
Closes gh-853
Container typecheck and bun install gracefully skip when bun isn't
installed on the host. Linux service restart now detects the actual
systemd service name instead of hardcoding 'nanoclaw'.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The skill was written for v1 and missed several v2 changes: container
rebuild after merge, dependency install for both pnpm and bun lockfiles,
container typecheck, channel/provider branch update awareness, and
platform-aware service restart instructions.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>