- Fix formatting in session-utils.store.ts
- Regenerate Swift protocol files with TOO_MANY_REQUESTS error code
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add transferBatchState() method to EmbeddingService to preserve failure
tracking when the service is recreated during reindex operations
- Update manager.createEmbeddingService() to transfer state by default
- Skip state transfer when switching to fallback provider (fresh start)
- Add mock rateLimiter to send.test.ts to fix test failures
- Add error handling in audit-log.ts for ENOENT during test cleanup
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
chat.inject is a privileged operation that injects assistant messages into
transcripts. The injection sanitization was being called but its result was
ignored - the original message was used anyway.
Rather than using the sanitized text (which would be identical since
useBoundaries=false for assistant content), remove the sanitization entirely
because:
1. This injects assistant content, not user input - boundary markers are inappropriate
2. Injection patterns detect user prompt manipulations, not assistant responses
3. This is a privileged gateway operation requiring authenticated access
This addresses the new finding in the post-fix audit.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Fix all 7 issues identified in the ChatGPT audit of transformation work:
1. Android build break: Remove duplicate normalizeMainKey/isCanonicalMainSessionKey
from NodeGatewaySync.kt (they're already in SessionKey.kt). Align both Android
and iOS implementations to use "main" as canonical (case-insensitive match).
2. Rate limiting: Wire rateLimiter through GatewayRequestContext and enforce:
- Auth failure backoff (recordAuthFailure on auth failure, clearAuthFailure on success)
- Request rate limiting before handleGatewayRequest
- Channel message rate limiting in send handler
- Add TOO_MANY_REQUESTS error code
3. Injection defenses: Call sanitizeIncomingMessage in chat.send and chat.inject
handlers to detect prompt injection attempts.
4. RBAC enforcement: Add canAccessAgent check in chat.send handler and
canExecuteCommand check in node-host runner for exec commands.
5. WhatsApp encrypted backup restore: Update maybeRestoreCredsFromBackup to
handle encrypted backup files (.bak.enc -> .json.enc restoration).
6. Exec blocklist bypass: Add evaluateBlocklist check in argv-only code path
in runner.ts (previously only checked for rawCommand path).
7. iOS voice transcript: Fix line 329 in NodeAppModel.swift to use computed
'key' variable instead of original 'sessionKey' parameter.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Remove unnecessary escape character in exec-blocklist regex
- Fix header spread in clawdhub client to handle all HeadersInit types
- Remove unused InjectionSeverity import from chat-sanitize test
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Create a unified error module (src/errors/index.ts) that provides:
- ClawdbotError: Base error class with code and metadata support
- Common utilities: getErrorMessage, getErrorCode, getErrorStatus,
formatErrorForLog, formatErrorForUser, wrapError
- Type guards: isClawdbotError, isError
- Re-exports of domain-specific errors for discoverability
Domain errors remain co-located with their modules for cohesion.
This index provides a single discovery point without creating coupling.
Part of Phase 2: Architecture Cleanup
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Extract cohesive functionality from session-utils.ts (644 LOC) into
focused submodules while maintaining full backwards compatibility:
- session-utils.avatar.ts: Avatar resolution (data URIs, files, URLs)
- session-utils.agents.ts: Agent listing and discovery
- session-utils.store.ts: Store resolution and loading
The main session-utils.ts now orchestrates and re-exports all functions
preserving the existing public API. Core session listing functions
(deriveSessionTitle, loadSessionEntry, classifySessionKey, etc.) remain
in the main file.
Part of Phase 2: Architecture Cleanup
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Security Documentation:
- docs/security/threat-model.md: Comprehensive threat model covering
channels, tools, gateway, local files, browser automation, and plugins
- docs/security/data-handling.md: Data handling policy covering storage,
retention, user rights (access, export, delete), and compliance
Doctor Security Check:
- Added credential encryption check to doctor-security.ts
- Warns about unencrypted WhatsApp/Web credentials
This completes Phase 1 Security Hardening (Tasks 1.1-1.6).
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Pairing Store Hardening:
- Increased pairing code length from 8 to 16 chars (80-bit entropy)
- Added HMAC-SHA256 signatures for store integrity verification
- Added rate limiting (10 attempts/minute per channel)
- Store is automatically reset if signature verification fails
- New ApproveChannelPairingCodeResult type includes rateLimited case
Exec Approval Nonces (Replay Protection):
- Added one-time-use nonces for approval socket requests
- Nonces are generated with 32 bytes of entropy (base64url encoded)
- Nonces expire after 5 minutes with automatic cleanup
- Responses must include matching nonce to be accepted
- Exported functions for testing: generateApprovalNonce, verifyAndConsumeNonce, isNonceValid
Tests:
- 11 pairing store tests (6 new security tests)
- 38 exec-approvals tests (8 new nonce tests)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Support macOS Keychain via `security` command
- Support Linux Secret Service via `secret-tool` (libsecret)
- Fallback to AES-256-GCM encrypted files with PBKDF2 key derivation
- Machine-derived encryption key for file fallback
- Utilities: generateSecureToken, hashSecret, verifySecretHash
- Migration helpers for moving plain-text credentials to secure storage
- Comprehensive test suite (27 tests)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add tiered blocklist (critical/high/medium) for dangerous commands
- Block: rm -rf /, dd to disk, mkfs, halt/reboot, fork bombs, etc.
- Block: sudo, passwd, visudo, iptables, user management
- Warn but allow: command substitution, eval, curl POST, chmod 777
- Integrate blocklist into evaluateShellAllowlist (checked before allowlist)
- Add comprehensive test suite (47 tests)
- Export blocklist utilities for use by other modules
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Adds `messages` config option to session-memory hook (default: 15).
Fixes filter order bug - now filters user/assistant messages first,
then slices to get exactly N messages. Previously sliced first which
could result in fewer messages when non-message entries were present.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Adds a new dmScope option that includes accountId in session keys,
enabling isolated sessions per channel account for multi-bot setups.
- Add 'per-account-channel-peer' to DmScope type
- Update session key generation to include accountId
- Pass accountId through routing chain
- Add tests for new routing behavior (13/13 passing)
Closes#3094
Co-authored-by: Sebastian Almeida <89653954+SebastianAlmeida@users.noreply.github.com>
The MiniMax provider config was updated to use api.minimax.chat
instead of api.minimax.io in PR #3064, but the test expectation
was not updated.
🤖 Generated with Claude Code
- Re-export DirectoryConfigParams and ChannelDirectoryEntry from channels/targets
- Remove unused ChannelDirectoryEntry and resolveDiscordAccount imports
- Fix parseDiscordTarget calls to not pass incompatible options type
- Fix unused catch parameter
Fixes CI build failures on main.
🤖 Generated with Claude Code
Regular Telegram groups (without Topics/Forums enabled) can send
message_thread_id when users reply to messages. This was incorrectly
being used to create separate session keys like '-123:topic:42',
causing each reply chain to get its own conversation context.
Now resolveTelegramForumThreadId only returns a thread ID when the
chat is actually a forum (is_forum=true). For regular groups, the
thread ID is ignored, ensuring all messages share the same session.
DMs continue to use messageThreadId for thread sessions as before.
When sending Discord messages via cron jobs or the message tool,
usernames like "john.doe" were incorrectly treated as channel names,
causing silent delivery failures.
This fix adds a resolveDiscordTarget() function that:
- Queries Discord directory to resolve usernames to user IDs
- Falls back to standard parsing for known formats
- Enables sending DMs by username without requiring explicit user:ID format
Changes:
- Added resolveDiscordTarget() in targets.ts with directory lookup
- Added parseAndResolveRecipient() in send.shared.ts
- Updated all outbound send functions to use username resolution
Fixes#2627
MiniMax has updated their API. The previous configuration used an
incorrect endpoint (api.minimax.io/anthropic) with anthropic-messages
format, which no longer works.
Changes:
- Update MINIMAX_API_BASE_URL to https://api.minimax.chat/v1
- Change API format from anthropic-messages to openai-completions
- Remove minimax from isAnthropicApi check in transcript-policy
This fixes the issue where MiniMax API calls return no results.
Wraps plugin.actions.listActions() in a try/catch so a single
broken channel plugin cannot crash the entire agent boot sequence.
Errors are logged once per plugin+message (deduped) via
defaultRuntime.error() and the call gracefully returns an empty
array instead of propagating the exception.
Fixes: 'Cannot read properties of undefined (reading listActions)'
after the clawdbot→moltbot rename left some plugin state undefined.