Brings the imessage channel to parity with recent telegram/discord
hardening:
- Permission-relay capability: declare claude/channel/permission,
handle inbound permission_request notifications by fanning out to
allowlisted DM chats + self-chat, intercept "yes/no <id>" replies
after the gate check and emit structured permission events instead
of relaying as chat. Groups excluded per single-user-mode policy.
- Global unhandledRejection/uncaughtException handlers so the server
logs instead of dying silently.
- IMESSAGE_STATE_DIR env override for the state directory.
- .unref() on both setInterval timers so they don't block shutdown.
- stdin EOF / SIGTERM / SIGINT shutdown handler that closes chat.db
and exits cleanly instead of leaving a zombie poll loop.
Adds zod as a direct dep (already transitively present via the MCP SDK)
for the notification handler schema.
Complete the plugin side of anthropics/claude-cli-internal#23061 (permission
prompts over channels).
Capability: both servers now declare
experimental["claude/channel/permission"]
which tells CC they can relay permission requests. This capability asserts the
server authenticates the replier — gate()/access.allowFrom filters
non-allowlisted senders before handleInbound runs.
Outbound (CC → user): setNotificationHandler for
notifications/claude/channel/permission_request
formats the tool name, description, and input preview into a human-readable
message and sends it to every allowlisted DM. Groups are excluded — the
security thread resolution was "single-user mode for official plugins."
Inbound (user → CC): PERMISSION_REPLY_RE intercept in handleInbound catches
"yes xxxxx" / "no xxxxx" replies, emits the structured
notifications/claude/channel/permission
event with {request_id, behavior}, reacts with checkmark/cross, and returns
without relaying the text to Claude as a chat message.
The regex is inlined from channelPermissions.ts (no cross-repo dep). IDs are
lowercased at the plugin boundary per the case-insensitive spec.
Version bumped 0.0.1 → 0.0.2 so the plugin reconciler picks up the change.
🏠 Remote-Dev: homespace
The workflow was passing deleted files to the validation script, which
failed when trying to read them. Add --diff-filter=AMRC to only process
Added, Modified, Renamed, and Copied files.
Change the stripe plugin source from local path (./external_plugins/stripe)
to git-subdir pointing to stripe/ai repo at providers/claude/plugin without
SHA pinning.
- safeName() strips <>[]\r\n; from file_name/title before they hit the
<channel> notification — delimiter chars would let an uploader break
out of the tag or forge meta entries
- download_attachment strips ext/uniqueId to alphanumeric before join()
— defense-in-depth against path traversal (file_unique_id is
Telegram-controlled so this is belt-and-braces)
- /status in a group would leak the sender's pending pairing code to
other group members, who could then pair as that user
- Commands in non-allowlisted groups confirm bot presence and enable spam
- /start now acknowledges dmPolicy === 'disabled' instead of lying
- setMyCommands scoped to private chats so the / menu only shows in DMs
Same patterns as #812/#813 for the discord channel:
- process-level unhandledRejection/uncaughtException handlers
- client.on('error') to log discord.js errors
- mcp.notification().catch() so inbound delivery failures surface
- stdin close / SIGTERM -> client.destroy() + exit (zombie fix)
- .unref() the approval-check interval
- client.login().catch() to log+exit on bad token instead of crashing
Discord is inherently more resilient than telegram (discord.js
auto-reconnects, no 409 equivalent), but these gaps were still there.
Message edits don't trigger push notifications on the user's device.
Update system instructions and edit_message tool description to steer
the assistant toward edit-for-progress + new-reply-on-completion.
Fixes#786
Hardcoded ~/.claude/channels/<name>/ meant only one bot per machine.
Respect TELEGRAM_STATE_DIR / DISCORD_STATE_DIR so users can run
multiple bots with separate tokens and allowlists.
Also fixed README path ('in your project' -> '~/...') to match the code.
Fixes#792
During /mcp reload or when a zombie from a previous session still holds
the polling slot, the new process gets 409 Conflict on its first
getUpdates and dies immediately. Retry with backoff until the slot
frees — typically within a second or two.
Also handles the two-sessions case: the second Claude Code instance
keeps retrying (with a clear message about what's happening) and takes
over when the first one exits.
Fixes#804#794, partial #788 (issue 4)
When the MCP stdio transport closes, the bot kept polling Telegram as
a zombie process — holding the token and causing 409 Conflict for the
next session.
- Listen for stdin end/close and SIGTERM/SIGINT -> bot.stop() + exit
- Force-exit after 2s if bot.stop() stalls on the long-poll timeout
- unref the approval-check interval so it doesn't keep us alive
Fixes#793, partial #788 (issue 3)
The bot would silently stop delivering messages after the first error:
grammy's default handler calls bot.stop() on any middleware throw, and
void bot.start() / void mcp.notification() swallow rejections with no log.
- bot.catch(): log and keep polling on handler errors
- bot.start().catch(): log when polling dies (bad token, 409, network)
- mcp.notification().catch(): log when inbound delivery to Claude fails
- process-level unhandledRejection/uncaughtException as a safety net
Fixes#756#759#761#777#809, partial #788
The bot token is a credential. Tighten perms on load so hand-written
or pre-existing .env files get locked down, and update the configure
skill to chmod after writing. No-op on Windows.
Deep-mode allows bounded local computation but must NOT use WebFetch
or WebSearch. Finding the solution on AoPS is not solving the problem.
Adds explicit NO WEB prompt block and orchestrator self-restraint note.
Found by Ralph's test run (skill solved 5/6 then started fetching
dgrozev.wordpress.com and artofproblemsolving.com for P6).
- Drop /reload-plugins (redundant, you restart with --channels next)
- Fix token save path: .claude/channels/ not ~/.claude/channels/
- Clarify bot only responds once channel is running (pairing step)
Both MCP servers run on Bun, but this wasn't documented. Add a
Prerequisites section with the install command so users don't hit
a missing-runtime error on first setup.
The skill that addresses the Proof-or-Bluff gap: self-verified 85.7% IMO
becomes <5% under human grading. Uses fresh-context verifiers armed with
specific failure patterns (not generic 'check logic').
Validated: 17/18 IMO+Putnam 2025 solved, 0 false positives, 2 novel proofs.
See eval data in anthropic monorepo sandbox/sandbox/ralph/math_skills/.
Remove the three chat bridge plugins from external_plugins/ and their
corresponding entries in marketplace.json.
Co-authored-by: Claude <noreply@anthropic.com>
Adds a sort check as a second step in the existing validate-marketplace
workflow. The script supports --fix to sort in place.
Sorts the existing 86 entries — pure reorder, no content change.
Previously grouped loosely by kind (LSPs first, then internal, then
external); now strictly alphabetical so insertion point is unambiguous.
* Add CI workflow to validate marketplace.json on PRs
Add a GitHub Actions workflow that validates marketplace.json is
well-formed JSON with a plugins array whenever PRs modify it. Includes:
- validate-marketplace.ts: Bun script that parses and validates the JSON
- validate-marketplace.yml: GH Actions workflow triggered on PR changes
- test-marketplace-check.js: Unit tests for the validation logic
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Strengthen marketplace validator and remove orphaned test file
- validate-marketplace.ts: check duplicate names and required fields
(name, description, source) per entry, not just valid JSON
- remove .github/workflows/test-marketplace-check.js: tested a
checkMarketplaceViolations function that doesn't exist in the PR,
and was in workflows/ instead of scripts/
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Tobin South <tobin.south@gmail.com>