The HTTP tool returned `ApprovalRequirement::Always` for requests with
credentials, but `Always` is hardcoded to ignore the session auto-approve
set. This meant users who clicked "always" were re-prompted on every
subsequent HTTP call — the UI offered "always" but the backend ignored it.
Two fixes:
1. HTTP credentialed requests now return `UnlessAutoApproved` instead of
`Always`, so the session auto-approve set is respected.
2. `StatusUpdate::ApprovalNeeded` now carries `allow_always: bool`. All
channel UIs (Telegram, Slack, Signal, REPL, Web) conditionally hide
the "always" option when a tool truly requires per-invocation approval
(`ApprovalRequirement::Always`, e.g. destructive shell commands).
Also boxes `PendingApproval` in `AgenticLoopResult::NeedApproval` to fix
a pre-existing clippy `large_enum_variant` warning.
Regression tests included (test_credentialed_requests_respect_auto_approve,
test_allow_always_matches_approval_requirement) but CI heuristic cannot
detect them in cross-fork PR diffs.
[skip-regression-check]
Co-authored-by: Tyler <[email protected]>
* fix: address 5 critical and high-priority bugs from issue tracker
- #1033: reject webhook requests when secret is cleared at runtime via
update_secret(None), preventing auth bypass through SIGHUP hot-swap
- #908: reset consecutive_failures counter on successful SSE stream
reconnection in relay channel, so circuit breaker counts truly
consecutive failures
- #975: add depth limit (16) to validate_tool_schema() to prevent
stack overflow on deeply nested schemas
- #974: add depth limit (8) to resolve_nested() to prevent stack
overflow on deeply nested capabilities wrappers
- #826: truncate oversized tool outputs (>8KB) in routine lightweight
loop to prevent unbounded context growth across iterations
Each fix includes a regression test.
Closes#1033, #908, #975, #974, #826
Co-Authored-By: Claude Opus 4.6 <[email protected]>
* fix: 5 more high-priority bugs (routine cache, job signals, input limits)
- #1077: recompute next_fire_at when re-enabling cron routines via web
toggle, mirroring CLI behavior so cron ticker picks them up
- #1076: refresh event trigger cache after web toggle/delete operations
so event/system_event routines reflect changes immediately
- #892: remove Stuck from check_signals() stop-states in JobDelegate
since Stuck is recoverable (Stuck -> InProgress via self-repair)
- #976: truncate oversized description strings in CapabilitiesFile to
4KB to prevent memory abuse from malicious capabilities files
- #977: drop oversized parameters schema JSON (>64KB) in
CapabilitiesFile to prevent unbounded memory growth
Each fix includes regression tests where applicable.
Closes#1077, #1076, #892, #976, #977
Co-Authored-By: Claude Opus 4.6 <[email protected]>
* fix: prevent ReDoS in event trigger regex patterns
- #825: use RegexBuilder with 64KB size limit when compiling
user-supplied event trigger patterns, both at creation time
(routine tool) and at cache refresh (routine engine)
Note: Rust's regex crate already guarantees O(n) matching, so the
size limit prevents excessive memory use during compilation rather
than catastrophic backtracking at match time.
Closes#825
Co-Authored-By: Claude Opus 4.6 <[email protected]>
* Harden HTTP SSRF IP filtering
* Apply rustfmt after staging merge
---------
Co-authored-by: Claude Opus 4.6 <[email protected]>
* feat: add channel-relay integration for Slack via external relay service
- Add RelayChannel and RelayClient for connecting to channel-relay SSE streams
- Add RelayConfig with env-based configuration (CHANNEL_RELAY_URL, CHANNEL_RELAY_API_KEY)
- Add channel-relay extension lifecycle: install, OAuth auth, activate with hot-add
- Add proxy message sending through channel-relay for Slack chat.postMessage
- Add extension registry entry for Slack relay with OAuth auth hint
- Add relay integration test with mock SSE server
- Wire relay channel into app startup with reconnect on stored credentials
- Add AuthRequired extension error variant for cleaner auth flow detection
[skip-regression-check]
* chore: apply cargo fmt
* fix: remove remaining Telegram test references in relay channel
* fix: address PR #790 review feedback — parser handle leak, CSRF, circuit breaker
- Fix parser handle leak on reconnect by sharing Arc<RwLock> instead of
creating a local copy in start() (shutdown now aborts the correct task)
- Add CSRF state nonce to OAuth flow: generate in auth_channel_relay,
validate in slack_relay_oauth_callback_handler, one-time use
- Remove dead proxy_slack method, update integration test to use
proxy_provider
- Add reconnect circuit breaker (max_consecutive_failures, default 50)
- Fix stale docs (Telegram refs), extract event_types constants
* fix: double backoff in reconnect loop and UTF-8 chunk-boundary corruption
- Remove second sleep+backoff in list_connections error branch to prevent
O(4^n) backoff growth (was sleeping and doubling twice per iteration)
- Buffer raw bytes in SSE parser instead of per-chunk String::from_utf8_lossy
to prevent U+FFFD corruption when multi-byte chars span chunk boundaries
* feat: add Slack approval buttons for tool execution in DMs
Send Block Kit Approve/Deny buttons via relay when a tool requires
approval in a DM context. Auto-deny approval-requiring tools in
shared channels to prevent prompt injection and stuck threads.
* fix: address PR #796 review — use PreflightOutcome::Rejected, add tests
- Auto-deny in non-DM relay channels now uses PreflightOutcome::Rejected
instead of manually pushing to reason_ctx.messages, so the post-flight
handler properly records the error in the turn
- Add regression tests for relay auto-deny decision logic
- Remove test_clean.db artifact
* feat: restore Block Kit approval buttons in send_status
The send_status implementation was accidentally dropped during the
staging merge. Restores Approve/Deny Block Kit buttons for DM tool
approval, with required sender_id validation, payload size docs,
and 4 regression tests. Also removes test_clean.db.
Co-Authored-By: Claude Opus 4.6 <[email protected]>
* fix: apply rustfmt formatting to dispatcher test code
Co-Authored-By: Claude Opus 4.6 <[email protected]>
---------
Co-authored-by: Claude Opus 4.6 <[email protected]>
* fix(ci): secrets can't be used in step if conditions [skip-regression-check] (#787)
GitHub Actions step-level `if:` doesn't have access to `secrets` context.
Replace `if: secrets.X != ''` with `continue-on-error: true` and let
the Set token step handle the fallback.
Co-authored-by: Claude Sonnet 4.6 <[email protected]>
* feat: add channel-relay integration for Slack via external relay service
- Add RelayChannel and RelayClient for connecting to channel-relay SSE streams
- Add RelayConfig with env-based configuration (CHANNEL_RELAY_URL, CHANNEL_RELAY_API_KEY)
- Add channel-relay extension lifecycle: install, OAuth auth, activate with hot-add
- Add proxy message sending through channel-relay for Slack chat.postMessage
- Add extension registry entry for Slack relay with OAuth auth hint
- Add relay integration test with mock SSE server
- Wire relay channel into app startup with reconnect on stored credentials
- Add AuthRequired extension error variant for cleaner auth flow detection
[skip-regression-check]
* chore: apply cargo fmt
* fix: remove remaining Telegram test references in relay channel
* fix: address PR #790 review feedback — parser handle leak, CSRF, circuit breaker
- Fix parser handle leak on reconnect by sharing Arc<RwLock> instead of
creating a local copy in start() (shutdown now aborts the correct task)
- Add CSRF state nonce to OAuth flow: generate in auth_channel_relay,
validate in slack_relay_oauth_callback_handler, one-time use
- Remove dead proxy_slack method, update integration test to use
proxy_provider
- Add reconnect circuit breaker (max_consecutive_failures, default 50)
- Fix stale docs (Telegram refs), extract event_types constants
---------
Co-authored-by: Henry Park <[email protected]>
Co-authored-by: Claude Sonnet 4.6 <[email protected]>