Skip to content

feat: add Slack approval buttons for tool execution in DMs - #796

Merged
zmanian merged 14 commits into
stagingfrom
feat/slack-approval-buttons
Mar 12, 2026
Merged

zmanian merged 14 commits into
stagingfrom
feat/slack-approval-buttons

Conversation

@PierreLeGuen

Copy link
Copy Markdown
Contributor

Summary

  • Send Block Kit interactive Approve/Deny buttons via Slack relay when a tool requires user approval in DM contexts
  • Auto-deny approval-requiring tools in non-DM relay channels to prevent prompt injection and stuck AwaitingApproval threads
  • Add event_type to relay metadata so send_status can distinguish DMs from channel messages

Follow-up to #790.

Test plan

  • E2E: DM the bot → trigger approval-requiring tool → buttons appear → click Approve → tool executes → buttons replaced with "Approved by @user"
  • Verify approval buttons only appear in DMs, not in shared channels
  • Verify only the original requester can click Approve/Deny (sender_id check)
  • cargo clippy and cargo test pass

- 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]
@github-actions github-actions Bot added scope: agent Agent core (agent loop, router, scheduler) size: M 50-199 changed lines risk: medium Business logic, config, or moderate-risk modules labels Mar 10, 2026
@gemini-code-assist

Copy link
Copy Markdown
Contributor

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request enhances the bot's interaction with users on Slack by introducing a robust approval mechanism for tool execution. It enables interactive approval buttons directly within direct messages, while simultaneously preventing unauthorized tool execution in shared channels by automatically denying approval requests outside of DMs. This ensures secure and controlled tool usage, improving both user experience and system integrity.

Highlights

  • Slack Approval Buttons: Implemented sending interactive Approve/Deny buttons using Slack Block Kit via the Slack relay for tools requiring user approval in direct message contexts.
  • Auto-Denial in Non-DM Channels: Added logic to automatically deny approval-requiring tools in non-DM relay channels to prevent prompt injection and avoid 'AwaitingApproval' threads from getting stuck.
  • Event Type Metadata: Included 'event_type' in the relay metadata to enable 'send_status' to differentiate between direct messages and channel messages.
Changelog
  • src/agent/dispatcher.rs
    • Added conditional logic to auto-deny tools requiring approval if the message originates from a non-DM relay channel.
    • Generated a synthetic error message for auto-denied tools.
  • src/channels/relay/channel.rs
    • Included 'event_type' in the metadata passed to the relay.
    • Overhauled the 'send_status' function to handle 'ApprovalNeeded' status updates by constructing and sending Slack Block Kit messages with Approve/Deny buttons.
    • Ensured approval buttons are only sent in direct message contexts.
    • Extracted necessary metadata like 'channel_id', 'thread_id', 'team_id', and 'sender_id' for constructing the Slack message payload.
Activity
  • Implemented core functionality to send interactive Slack approval buttons for tools.
  • Added safeguards to auto-deny approval-requiring tools in non-DM channels.
  • Ensured 'event_type' is propagated in relay metadata for proper message context.
  • Conducted extensive E2E testing to verify button functionality, DM exclusivity, and sender ID checks.
  • Confirmed 'cargo clippy' and 'cargo test' pass.
  • This PR is a follow-up to feat: add channel-relay integration for Slack #790, indicating iterative development.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution. ↩

@github-actions github-actions Bot added the contributor: regular 2-5 merged PRs label Mar 10, 2026

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces interactive approval buttons for tool execution in Slack DMs, a great enhancement for user experience and security. It also correctly auto-denies approval-requiring tools in shared channels to prevent prompt injection and stuck threads.

My review has identified a couple of issues:

  • A high-severity bug in the auto-denial logic that could lead to confusing double error messages for the user.
  • A critical security vulnerability related to sender_id verification, which could potentially bypass the approval authorization check, violating the rule for verifying user IDs for resource access.

I've provided detailed comments and suggestions for both issues. Once these are addressed, this will be a solid feature.

Note: Security Review did not run due to the size of the PR.

Comment thread src/channels/relay/channel.rs Outdated
Comment thread src/agent/dispatcher.rs Outdated

@zmanian zmanian left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review: feat: add Slack approval buttons for tool execution in DMs

Good feature -- auto-denying approval-requiring tools in shared relay channels prevents stuck threads and prompt injection from other users.

Design concerns:

  1. Dead code in dispatcher.rs -- Lines 529-535 push to preflight and assign pf_idx but never use either. The let _ = pf_idx; suppresses the warning, but the preflight.push() is completely unnecessary since you immediately continue past the tool. Remove both the push and the let _ = pf_idx;.

  2. Channel detection is fragile -- message.channel.ends_with("-relay") is a string convention that could break if channel naming changes. Consider adding a channel_type field to IncomingMessage or checking a more robust metadata flag.

  3. params_display leaks tool parameters -- The Block Kit message includes serde_json::to_string_pretty(&parameters) which could contain sensitive data. Per project guidelines, tool parameters must be redacted before sending to external channels. Use redact_params() before formatting the display string.

  4. Missing StatusUpdate::ApprovalNeeded variant -- The PR uses this enum variant in send_status but I don't see it added to the StatusUpdate enum in this diff. Is it added in a dependency PR?

Minor:

  • Good defensive guard in send_status (checking event_type != "direct_message" even though dispatcher already gates)
  • value_str being the full JSON payload as the button value is fine for Slack's 2000-char limit but could hit that limit with large metadata -- consider truncating or using a request_id lookup instead

Would like the dead code and param redaction addressed before merge.

…uit 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
@PierreLeGuen
PierreLeGuen force-pushed the feat/slack-approval-buttons branch from 19990c6 to c07346d Compare March 10, 2026 19:04
…tion

- 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

@zmanian zmanian left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-review: feat: add Slack approval buttons for tool execution in DMs

The single commit at 19:02 UTC does not appear to address any of the previous review feedback. The same issues remain:

1. Dead code / incorrect preflight handling in dispatcher.rs (still present)

Lines 529-535 push to preflight with PreflightOutcome::Runnable, assign pf_idx, then continue. This is wrong on two levels:

  • The let _ = pf_idx; is a dead-code suppression hack.
  • Marking as Runnable then continuing means the post-flight phase will see a Runnable tool with no corresponding result, producing a confusing "No result available" error in addition to the synthetic context_messages.push().

The correct fix (as also noted by Gemini) is to use PreflightOutcome::Rejected(...) and remove the manual context_messages.push(). The existing post-flight Rejected handler will emit the right tool result message:

preflight.push((
    tc.clone(),
    PreflightOutcome::Rejected(format!(
        "Tool '{}' requires approval and cannot run in shared channels. \
         Ask the user to message me directly (DM) to use this tool.",
        tc.name
    )),
));
continue;

2. Missing sender_id validation (still present)

sender_id defaults to "" when absent from metadata. The value_payload then contains "sender_id": "", which weakens the downstream sender check on the Slack interactivity webhook -- any user could approve if the original sender_id was empty. This should error out like channel_id does:

let sender_id = metadata
    .get("sender_id")
    .and_then(|v| v.as_str())
    .ok_or_else(|| ChannelError::SendFailed {
        name: self.name().to_string(),
        reason: "Missing sender_id for approval buttons".into(),
    })?;

3. Channel detection is fragile (still present)

message.channel.ends_with("-relay") is a string convention. If relay channel naming changes, this silently breaks security (tools that should be auto-denied will instead hang waiting for approval in shared channels). Consider a more robust mechanism -- either a channel_type field on IncomingMessage, or checking metadata for a relay-specific flag.

4. Parameter redaction -- previous concern withdrawn

I previously flagged params_display as leaking tool parameters. After tracing the code, the parameters field reaching StatusUpdate::ApprovalNeeded comes from pending.display_parameters, which is already redacted via redact_params() in dispatcher.rs:789. So the Block Kit message shows redacted params. This is fine.

5. No tests

This PR has zero test coverage. At minimum:

  • Unit test for the auto-deny logic in dispatcher (relay + non-DM -> tool rejected)
  • Unit test for send_status with ApprovalNeeded in DM context (blocks rendered correctly)
  • Unit test for send_status with ApprovalNeeded in non-DM context (returns Ok without sending)

Per project conventions, fix commits must include regression tests.

6. Button value payload size

The value_str JSON payload in the button includes instance_id, team_id, channel_id, thread_ts, request_id, and sender_id. Slack limits button value to 2000 characters. While this is unlikely to hit the limit with typical UUIDs, it's worth adding a length check or a comment documenting the constraint.


Items 1, 2, and 5 are blocking. Items 3 and 6 are strong recommendations.

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.
@PierreLeGuen
PierreLeGuen force-pushed the feat/slack-approval-buttons branch from c07346d to a0deb2b Compare March 10, 2026 20:39
@github-actions github-actions Bot added the scope: channel/web Web gateway channel label Mar 10, 2026
@PierreLeGuen
PierreLeGuen force-pushed the feat/channel-relay-integration branch 2 times, most recently from fdb67ff to aa25f85 Compare March 10, 2026 21:55

@zmanian zmanian left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-review after new commit. 1 of 3 items addressed:

  1. Dead code in dispatcher.rs -- Fixed. Clean auto-deny flow now.
  2. Channel detection fragility -- Still uses ends_with("-relay"). Non-blocking, can be a follow-up.
  3. [Security] params_display leaks tool parameters to Slack -- Still uses serde_json::to_string_pretty(&parameters) without redact_params(). Per project rules, tool parameters must be redacted before sending to external channels. Sensitive data (API keys, file contents) in tool params would be sent verbatim to Slack. This is blocking.

Fix for #3: Replace the raw serialization with redact_params(&parameters, tool.sensitive_params()) before building params_display.

(Dropping item #4 from original review -- StatusUpdate::ApprovalNeeded already existed in codebase.)

Base automatically changed from feat/channel-relay-integration to staging March 10, 2026 23:34
…l-buttons

# Conflicts:
#	src/channels/relay/channel.rs
#	src/extensions/manager.rs
#	src/extensions/registry.rs
#	src/main.rs
@github-actions github-actions Bot added size: S 10-49 changed lines and removed size: M 50-199 changed lines contributor: regular 2-5 merged PRs labels Mar 11, 2026
@github-actions github-actions Bot added the contributor: experienced 6-19 merged PRs label Mar 11, 2026

@zmanian zmanian left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-review: feat: add Slack approval buttons for tool execution in DMs

The PR scope has narrowed significantly after the staging merge -- the diff now only touches src/agent/dispatcher.rs (auto-deny logic) and a stray test_clean.db.

Previous feedback status

  1. Dead code / preflight handling -- FIXED. The old preflight.push(Runnable) + let _ = pf_idx + continue pattern is gone. The new approach pushes a synthetic tool_result directly to reason_ctx.messages and continues past the preflight/runnable push. This is clean and correct -- the auto-denied tool never enters preflight or runnable, so Phase 3 won't produce a spurious "No result available" error.

  2. Channel detection fragility -- Still uses ends_with("-relay"). Acknowledged as non-blocking, fine for a follow-up.

  3. sender_id validation / approval buttons in send_status -- No longer in this PR's diff. The relay channel's send_status is still a no-op on this branch. The PR title ("add Slack approval buttons") is misleading -- this PR only adds the auto-deny half. The actual button rendering presumably comes in a follow-up. Consider updating the PR title to match the actual scope (e.g., "auto-deny approval-requiring tools in non-DM relay channels").

  4. Parameter redaction -- Not applicable since approval buttons are not in this diff.

New issues

[Blocking] test_clean.db committed to the repo. A 590KB SQLite database file (test_clean.db) is included in this PR. This should not be committed -- add it to .gitignore or remove it from the branch.

[Non-blocking] No tests. The auto-deny logic in dispatcher.rs has zero test coverage. A unit test exercising the is_relay && !is_dm path (tool gets a synthetic rejection, loop continues) would be valuable. Per project conventions, fix/feature commits should include regression tests. Given the small scope of this change, this is a strong recommendation but not blocking.

Summary

The dispatcher auto-deny logic is correct and clean. The one blocking issue is the stray test_clean.db binary. Remove it and this is ready to merge (assuming the PR title/description are updated to reflect the actual scope).

@zmanian zmanian left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-review: feat: add Slack approval buttons for tool execution in DMs

The PR scope has narrowed significantly after the staging merge -- the diff now only touches src/agent/dispatcher.rs (auto-deny logic) and a stray test_clean.db.

Previous feedback status

  1. Dead code / preflight handling -- FIXED. The old preflight.push(Runnable) + let _ = pf_idx + continue pattern is gone. The new approach pushes a synthetic tool_result directly to reason_ctx.messages and continues past the preflight/runnable push. This is clean and correct -- the auto-denied tool never enters preflight or runnable, so Phase 3 won't produce a spurious "No result available" error.

  2. Channel detection fragility -- Still uses ends_with("-relay"). Acknowledged as non-blocking, fine for a follow-up.

  3. sender_id validation / approval buttons in send_status -- No longer in this PR's diff. The relay channel's send_status is still a no-op on this branch. The PR title ("add Slack approval buttons") is misleading -- this PR only adds the auto-deny half. The actual button rendering presumably comes in a follow-up. Consider updating the PR title to match the actual scope (e.g., "auto-deny approval-requiring tools in non-DM relay channels").

  4. Parameter redaction -- Not applicable since approval buttons are not in this diff.

New issues

[Blocking] test_clean.db committed to the repo. A 590KB SQLite database file (test_clean.db) is included in this PR. This should not be committed -- add it to .gitignore or remove it from the branch.

[Non-blocking] No tests. The auto-deny logic in dispatcher.rs has zero test coverage. A unit test exercising the is_relay && !is_dm path (tool gets a synthetic rejection, loop continues) would be valuable. Per project conventions, fix/feature commits should include regression tests. Given the small scope of this change, this is a strong recommendation but not blocking.

Summary

The dispatcher auto-deny logic is correct and clean. The one blocking issue is the stray test_clean.db binary. Remove it and this is ready to merge (assuming the PR title/description are updated to reflect the actual scope).

- 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
@github-actions github-actions Bot added size: M 50-199 changed lines and removed size: S 10-49 changed lines labels Mar 11, 2026
@PierreLeGuen

Copy link
Copy Markdown
Contributor Author

Status: Review feedback addressed

Pushed c6946c7:

Fixed

  1. Dead code / incorrect preflight handling — Auto-deny now uses PreflightOutcome::Rejected instead of manually pushing to reason_ctx.messages. The post-flight Rejected handler properly records the error in the turn and emits the tool result message.

  2. Tests added — Two regression tests:

    • test_relay_non_dm_auto_deny_decision — verifies relay+non-DM triggers auto-deny, relay+DM does not, non-relay does not
    • test_relay_auto_deny_message_format — verifies rejection message content
  3. Removed test_clean.db artifact from branch.

Previously addressed (in earlier commits)

  • sender_id validation and params_display redaction — these were in an earlier version of the PR that included send_status approval button rendering. The current PR scope is only the dispatcher auto-deny logic (29 lines in dispatcher.rs). The approval button UI code will come in a follow-up.

Non-blocking (noted, not fixed)

  • Channel detection (ends_with("-relay")) — can be improved with a channel_type field in a follow-up

CI

  • Zero clippy warnings, all tests pass

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 <noreply@anthropic.com>
@github-actions github-actions Bot added size: L 200-499 changed lines and removed size: M 50-199 changed lines labels Mar 11, 2026
@PierreLeGuen

Copy link
Copy Markdown
Contributor Author

Status: All review feedback addressed

Pushed 47e2b54 — restores the Block Kit approval buttons that were accidentally dropped during the staging merge, plus addresses all remaining review items.

Review item tracker

# Item Status
1 Dead code / preflight handling in dispatcher FIXED (c6946c7) — uses PreflightOutcome::Rejected
2 sender_id defaults to "" — security risk FIXED (47e2b54) — sender_id now required, returns SendFailed if missing
3 Channel detection fragile (ends_with("-relay")) Non-blocking — follow-up
4 params_display leaks tool parameters N/A — params already redacted upstream via redact_params() in dispatcher
5 Missing tests FIXED — 2 dispatcher tests (c6946c7) + 4 send_status tests (47e2b54)
6 Button value payload size (Slack 2000 char limit) Documented in code comment
7 test_clean.db committed FIXED (47e2b54) — removed
8 PR title misleading (no buttons code) FIXED — buttons code restored
9 send_status Block Kit buttons dropped by merge FIXED (47e2b54) — full implementation restored

What changed in 47e2b54

src/channels/relay/channel.rs:

  • Replaced no-op send_status with full Block Kit Approve/Deny button implementation
  • Only fires for StatusUpdate::ApprovalNeeded in DM context; all other variants are no-ops
  • Required sender_id and channel_id — errors if missing (review item feat: adding Web UI #2)
  • Payload size constraint documented (review item Codex/feature parity pr hook #6)
  • 4 new regression tests: non-approval noop, non-DM skip, missing channel_id error, missing sender_id error

test_clean.db: Removed.

Diff against staging

src/agent/dispatcher.rs       |  78 +++++++++++++++
src/channels/relay/channel.rs | 228 ++++++++++++++++++++++++++++++++++++++++++-
2 files changed, 303 insertions(+), 3 deletions(-)

Verification

  • cargo clippy --all --all-features — zero warnings
  • cargo test relay — all 35 relay tests pass (including 6 new)
  • cargo fmt — clean

@zmanian zmanian left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All previous blocking items are addressed. The current diff is clean and correct.

Security: Parameters reaching send_status are already redacted (display_parameters from redact_params in dispatcher.rs:859). sender_id and channel_id are both required with proper error returns. Auto-deny in non-DM relay channels prevents prompt injection and stuck AwaitingApproval state. DM-only guard in send_status is good defense-in-depth.

Code correctness: No .unwrap() in production code. PreflightOutcome::Rejected pattern is now used correctly in dispatcher auto-deny. Error handling is consistent with project conventions.

Tests: Good coverage -- non-approval noop, non-DM skip, missing channel_id, missing sender_id, auto-deny decision logic.

Non-blocking notes for follow-up:

  • ends_with("-relay") channel detection is fragile (acknowledged in prior reviews, fine as follow-up)
  • Button value_str length is unchecked against Slack's 2000-char limit -- unlikely to hit with UUIDs but worth a bounds check or comment
  • The approve_tool/deny_tool action_id handlers are presumably on the relay service side -- confirm the callback flow works end-to-end in integration testing

@zmanian
zmanian merged commit c94ecf1 into staging Mar 12, 2026
9 checks passed
@zmanian
zmanian deleted the feat/slack-approval-buttons branch March 12, 2026 18:38
@ironclaw-ci ironclaw-ci Bot mentioned this pull request Mar 12, 2026
bkutasi pushed a commit to bkutasi/ironclaw that referenced this pull request Mar 28, 2026
* 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 nearai#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 nearai#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 <noreply@anthropic.com>

* fix: apply rustfmt formatting to dispatcher test code

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
drchirag1991 pushed a commit to drchirag1991/ironclaw that referenced this pull request Apr 8, 2026
* 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 nearai#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 nearai#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 <noreply@anthropic.com>

* fix: apply rustfmt formatting to dispatcher test code

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

contributor: experienced 6-19 merged PRs risk: medium Business logic, config, or moderate-risk modules scope: agent Agent core (agent loop, router, scheduler) scope: channel/web Web gateway channel size: L 200-499 changed lines

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants