* gateway: make session:patch hook typed and non-blocking
* gateway(test): add session:patch hook coverage
* docs(gateway): clarify session:patch security note
* fix: address review feedback on session:patch hook
Remove unused createInternalHookEvent import and fix doc example
to use inline event.type check matching existing hook examples.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: isolate hook payload to prevent mutation leaking into response
Shallow-copy sessionEntry and patch in the session:patch hook event
so fire-and-forget handlers cannot mutate objects used by the
response path.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: isolate session:patch hook payload (#53880) (thanks @graciegould)
---------
Co-authored-by: “graciegould” <“graciegould5@gmail.com”>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Ayaan Zaidi <hi@obviy.us>
* fix(process): auto-detect PTY cursor key mode for send-keys
When a PTY session sends smkx (\x1b[?1h) or rmkx (\x1b[?1l) to switch
cursor key mode, send-keys now detects this and encodes cursor keys
accordingly.
- smkx/rmkx detection in handleStdout before sanitizeBinaryOutput
- cursorKeyMode stored in ProcessSession
- encodeKeySequence accepts cursorKeyMode parameter
- DECCKM_SS3_KEYS for application mode (arrows + home/end)
- CSI sequences for normal mode
- Modified keys (including alt) always use xterm modifier scheme
- Extract detectCursorKeyMode for unit testing
- Use lastIndexOf to find last toggle in chunk (later one wins)
Fixes#51488
* fix: fail loud when PTY cursor mode is unknown (#51490) (thanks @liuy)
* style: format process send-keys guard (#51490) (thanks @liuy)
---------
Co-authored-by: Ayaan Zaidi <hi@obviy.us>
macOS registers Edge as 'com.microsoft.edgemac' in LaunchServices, which
differs from the CFBundleIdentifier 'com.microsoft.Edge' in the app's own
Info.plist. Without recognising the LaunchServices IDs, Edge users who set
Edge as their default browser are not detected as having a Chromium browser.
Add the four com.microsoft.edgemac* variants to CHROMIUM_BUNDLE_IDS and a
corresponding test that mocks the LaunchServices → osascript resolution
path for Edge.
* fix(cron): track and log bestEffort delivery failures, mark not delivered on partial failure
* fix(cron): cache successful results on partial failure to preserve replay idempotency
When a best-effort send partially fails, we now still cache the successful delivery results via rememberCompletedDirectCronDelivery. This prevents duplicate sends on same-process replay while still correctly marking the job as not fully delivered.
* fix(cron): preserve partial-failure state on replay (#27069)
* fix(cron): restore test infrastructure and fix formatting
* fix: clarify cron best-effort partial delivery status (#42535) (thanks @MoerAI)
---------
Co-authored-by: Ayaan Zaidi <hi@obviy.us>
* fix(telegram): validate photo dimensions before sendPhoto
Prevents PHOTO_INVALID_DIMENSIONS errors by checking image dimensions
against Telegram Bot API requirements before calling sendPhoto.
If dimensions exceed limits (width + height > 10,000px), automatically
falls back to sending as document instead of crashing with 400 error.
Tested in production (openclaw 2026.3.13) where this error occurred:
[telegram] tool reply failed: GrammyError: Call to 'sendPhoto' failed!
(400: Bad Request: PHOTO_INVALID_DIMENSIONS)
Uses existing sharp dependency to read image metadata. Gracefully
degrades if sharp fails (lets Telegram handle validation, backward
compatible behavior).
Closes: #XXXXX (will reference OpenClaw issue if one exists)
* fix(telegram): validate photo aspect ratio
* refactor: use shared telegram image metadata
* fix: fail closed on telegram image metadata
* fix: preflight invalid telegram photos (#52545) (thanks @hnshah)
---------
Co-authored-by: Bob Shah <bobshah@Macs-Mac-Studio.local>
Co-authored-by: Ayaan Zaidi <hi@obviy.us>
Groups configured with groupPolicy: open are expected to respond to all
messages. Previously, requireMention defaulted to true regardless of
groupPolicy, causing image (and other non-text) messages to be silently
dropped because they cannot carry @-mentions.
Fix: when groupPolicy is 'open' and requireMention is not explicitly
configured, resolve it to false instead of true. Users who want
mention-required behaviour in open groups can still set requireMention: true
explicitly.
Adds three regression tests covering the new default, explicit override, and
the unchanged allowlist-policy behaviour.
Closes#52553
Address Codex P1 + Greptile P2:
- Move config validation before the restart attempt so invalid config
is caught in the stop→start path (not just the already-loaded path)
- Derive service.loaded from actual isLoaded() after restart instead
of hardcoded true
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: HCL <chenglunhu@gmail.com>
After `gateway stop` (which runs `launchctl bootout`), `gateway start`
checks `isLoaded` → false → prints "not loaded" hints and exits.
The service is never re-bootstrapped, so `start` cannot recover from
`stop` — only `gateway install` works.
Root cause: src/cli/daemon-cli/lifecycle-core.ts:208-217 — runServiceStart
calls handleServiceNotLoaded which only prints hints, never attempts
service.restart() (which already handles bootstrap via
bootstrapLaunchAgentOrThrow at launchd.ts:598).
Fix: when service is not loaded, attempt service.restart() first (which
handles re-bootstrapping on all platforms). If restart fails (e.g. plist
was deleted, not just booted out), fall back to the existing hints.
The restart path is already proven: restartLaunchAgent (launchd.ts:556)
handles "not loaded" via bootstrapLaunchAgentOrThrow. This fix routes
the start command through the same recovery path.
Closes#53878
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: HCL <chenglunhu@gmail.com>
Move cleanup() after disconnect() in waitForDiscordGatewayStop so the
error listener is still active during disconnect. Add a safety error
listener in the lifecycle finally block to suppress late errors emitted
by Carbon during teardown.
Fixes the "Max reconnect attempts (0) reached after code 1006" uncaught
exception that kills the entire gateway process when a Discord WebSocket
drops and reconnection fails.
* fix(telegram): improve error messages for 403 bot not member errors
- Detect 403 'bot is not a member' errors specifically
- Provide actionable guidance for users to fix the issue
- Fixes#48273 where outbound sendMessage fails with 403
Root cause:
When a Telegram bot tries to send a message to a channel/group it's not
a member of, the API returns 403 'bot is not a member of the channel chat'.
The error message was not clear about how to fix this.
Fix:
1. Detect 403 errors in wrapTelegramChatNotFoundError
2. Provide clear error message explaining the issue
3. Suggest adding the bot to the channel/group
* fix(telegram): fix regex precedence for 403 error detection
- Group alternatives correctly: /403.*(bot.*not.*member|bot was blocked)/i
- Require 403 for both alternatives (previously bot.*blocked matched any error)
- Update error message to cover both scenarios
- Fixes Greptile review feedback
* fix(telegram): correct regex alternation precedence for 403 errors
- Fix: /403.*(bot.*not.*member|bot was blocked)/ → /403.*(bot.*not.*member|bot.*blocked)/
- Ensures 403 requirement applies to both alternatives
- Fixes Greptile review comment on PR #48650
* fix(telegram): add 'bot was kicked' to 403 error regex and message
* fix(telegram): preserve membership delivery errors
* fix: improve Telegram 403 membership delivery errors (#53635) (thanks @w-sss)
---------
Co-authored-by: Ayaan Zaidi <hi@obviy.us>
- Fixes#47924
- Prevents SVG icon from expanding and covering entire chat window
- Adds explicit 24x24px dimensions to context-notice__icon SVG
Root cause:
The SVG element lacked explicit width and height attributes,
causing it to expand to fill the parent container when the context
usage warning appears (at ~85% token limit).