Skip to content

feat(talon): audit reaction approval attempts - #4348

Merged
John Kennedy (jkennedyvz) merged 1 commit into
mainfrom
john/talon/reaction-approval-audit
Jun 28, 2026
Merged

feat(talon): audit reaction approval attempts#4348
John Kennedy (jkennedyvz) merged 1 commit into
mainfrom
john/talon/reaction-approval-audit

Conversation

@jkennedyvz

@jkennedyvz John Kennedy (jkennedyvz) commented Jun 27, 2026

Copy link
Copy Markdown
Contributor

Stacked on #4345.

Adds host-side tool_approval.reaction audit events for reaction approval attempts. The event records provider, stable hashed channel conversation/prompt/sender references, emoji, decision, match status, and resolution mode. Raw provider IDs are included only when DEEPAGENTS_TALON_APPROVAL_LOG_RAW_IDS=true; raw provider metadata, approval prompt text, arbitrary message text, and tool arguments are not logged. Matched reaction approvals use operator_reaction as the resolution so they can be distinguished from text approvals.

When this is useful

Reaction approvals are a one-tap action that's easy to fire off and easy to miss-context (wrong message, wrong channel, stale prompt). Without an audit trail there's no way to answer "did someone try to approve that tool call, and why didn't it take?" after the fact. These events let operators and security reviewers trace every reaction attempt — matched or ignored — back to a specific provider, conversation, prompt message, and sender, without leaking raw IDs or prompt/message contents by default. Distinguishing operator_reaction from text approve/deny also makes it possible to monitor how often the reaction path is actually used versus the text fallback.

Example

An operator reacts 👍 to the approval prompt. The host emits a structured tool_approval.reaction event:

{
  "event": "tool_approval.reaction",
  "provider": "slack",
  "channel_conversation_ref": "9f1c…a4e2",
  "prompt_message_ref": "b7d3…0c91",
  "reacting_sender_ref": "e2a8…77f0",
  "emoji": "👍",
  "decision": "approve",
  "match_status": "matched",
  "resolution": "operator_reaction"
}

Conversation/prompt/sender references are stable hashes (via stable_log_ref), so the same IDs correlate across events without exposing the underlying Slack IDs. Ignored attempts are logged too, with a precise resolution explaining why:

  • no_pending_approval — reaction arrived with no pending tool approval
  • unsupported_emoji — emoji wasn't 👍/👎
  • missing_prompt_message_id — channel didn't supply a prompt message id (falls back to text)
  • provider_mismatch / conversation_mismatch / message_mismatch — reaction didn't target the prompt that issued the request
  • sender_missing / sender_mismatch — reactor wasn't the initiating operator (when sender is known)

For debugging a specific incident, set DEEPAGENTS_TALON_APPROVAL_LOG_RAW_IDS=true to additionally include raw_channel_conversation_id, raw_prompt_message_id, and raw_reacting_sender_id. Sensitive values — provider metadata, approval prompt text, arbitrary message text, and tool arguments — are never logged regardless of that flag.

Validation run:

  • uv run --group test pytest --disable-socket --allow-unix-socket tests/test_host.py --timeout 10
  • make lint_diff
  • make test

@github-actions github-actions Bot added feature New feature/enhancement or request for one internal User is a member of the `langchain-ai` GitHub organization size: M 200-499 LOC talon labels Jun 27, 2026
@jkennedyvz
John Kennedy (jkennedyvz) marked this pull request as ready for review June 27, 2026 22:57

@open-swe open-swe 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.

✅ Open SWE Review: No issues found

Open SWE reviewed this PR and found no potential bugs to report.

Open in WebView Open SWE trace

Base automatically changed from john/talon/reaction-approval-contract to main June 28, 2026 22:12
@jkennedyvz
John Kennedy (jkennedyvz) force-pushed the john/talon/reaction-approval-audit branch from 7388ba9 to 7d83caa Compare June 28, 2026 22:17
@jkennedyvz
John Kennedy (jkennedyvz) merged commit d7895c4 into main Jun 28, 2026
49 checks passed
@jkennedyvz
John Kennedy (jkennedyvz) deleted the john/talon/reaction-approval-audit branch June 28, 2026 22:21
John Kennedy (jkennedyvz) pushed a commit that referenced this pull request Jun 30, 2026
> [!CAUTION]
> Merging this PR will automatically publish to **PyPI** and create a
**GitHub release**.

For the full release process, see
[`.github/RELEASING.md`](https://github.com/langchain-ai/deepagents/blob/main/.github/RELEASING.md).

---

_Everything below this line will be the GitHub release body._

---


##
[0.0.2](deepagents-talon==0.0.1...deepagents-talon==0.0.2)
(2026-06-30)


### Features

* **talon:** `DEEPAGENTS_TALON_RECURSION_LIMIT` env var
([#4354](#4354))
([82d1eac](82d1eac))
* **talon:** add reaction approval routing
([#4345](#4345))
([3fe8c0c](3fe8c0c))
* **talon:** add Telegram channel adapter, CLI wiring, and offset
persistence
([#4097](#4097))
([7c87cec](7c87cec))
* **talon:** add tool approval env override
([#4349](#4349))
([d26481d](d26481d))
* **talon:** audit reaction approval attempts
([#4348](#4348))
([d7895c4](d7895c4))
* **talon:** ingest Telegram approval reactions
([#4346](#4346))
([437af0b](437af0b))


### Bug Fixes

* **talon:** default workspace to current directory
([#4099](#4099))
([5e337ae](5e337ae))

---

_Everything above this line will be the GitHub release body._

---

> [!NOTE]
> A **New Contributors** section is appended to the GitHub release notes
automatically at publish time (see [Release
Pipeline](https://github.com/langchain-ai/deepagents/blob/main/.github/RELEASING.md#release-pipeline),
step 2).

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Marcelo5444 pushed a commit to Marcelo5444/deepagents that referenced this pull request Jul 30, 2026
Stacked on langchain-ai#4345.

Adds host-side `tool_approval.reaction` audit events for reaction
approval attempts. The event records provider, stable hashed channel
conversation/prompt/sender references, emoji, decision, match status,
and resolution mode. Raw provider IDs are included only when
`DEEPAGENTS_TALON_APPROVAL_LOG_RAW_IDS=true`; raw provider metadata,
approval prompt text, arbitrary message text, and tool arguments are not
logged. Matched reaction approvals use `operator_reaction` as the
resolution so they can be distinguished from text approvals.

## When this is useful

Reaction approvals are a one-tap action that's easy to fire off and easy
to miss-context (wrong message, wrong channel, stale prompt). Without an
audit trail there's no way to answer "did someone try to approve that
tool call, and why didn't it take?" after the fact. These events let
operators and security reviewers trace every reaction attempt — matched
or ignored — back to a specific provider, conversation, prompt message,
and sender, without leaking raw IDs or prompt/message contents by
default. Distinguishing `operator_reaction` from text `approve`/`deny`
also makes it possible to monitor how often the reaction path is
actually used versus the text fallback.

## Example

An operator reacts 👍 to the approval prompt. The host emits a structured
`tool_approval.reaction` event:

```json
{
  "event": "tool_approval.reaction",
  "provider": "slack",
  "channel_conversation_ref": "9f1c…a4e2",
  "prompt_message_ref": "b7d3…0c91",
  "reacting_sender_ref": "e2a8…77f0",
  "emoji": "👍",
  "decision": "approve",
  "match_status": "matched",
  "resolution": "operator_reaction"
}
```

Conversation/prompt/sender references are stable hashes (via
`stable_log_ref`), so the same IDs correlate across events without
exposing the underlying Slack IDs. Ignored attempts are logged too, with
a precise `resolution` explaining why:

- `no_pending_approval` — reaction arrived with no pending tool approval
- `unsupported_emoji` — emoji wasn't 👍/👎
- `missing_prompt_message_id` — channel didn't supply a prompt message
id (falls back to text)
- `provider_mismatch` / `conversation_mismatch` / `message_mismatch` —
reaction didn't target the prompt that issued the request
- `sender_missing` / `sender_mismatch` — reactor wasn't the initiating
operator (when sender is known)

For debugging a specific incident, set
`DEEPAGENTS_TALON_APPROVAL_LOG_RAW_IDS=true` to additionally include
`raw_channel_conversation_id`, `raw_prompt_message_id`, and
`raw_reacting_sender_id`. Sensitive values — provider metadata, approval
prompt text, arbitrary message text, and tool arguments — are never
logged regardless of that flag.

Validation run:
- `uv run --group test pytest --disable-socket --allow-unix-socket
tests/test_host.py --timeout 10`
- `make lint_diff`
- `make test`

Co-authored-by: Deep Agent <agent@deepagents.dev>
Marcelo5444 pushed a commit to Marcelo5444/deepagents that referenced this pull request Jul 30, 2026
> [!CAUTION]
> Merging this PR will automatically publish to **PyPI** and create a
**GitHub release**.

For the full release process, see
[`.github/RELEASING.md`](https://github.com/langchain-ai/deepagents/blob/main/.github/RELEASING.md).

---

_Everything below this line will be the GitHub release body._

---


##
[0.0.2](langchain-ai/deepagents@deepagents-talon==0.0.1...deepagents-talon==0.0.2)
(2026-06-30)


### Features

* **talon:** `DEEPAGENTS_TALON_RECURSION_LIMIT` env var
([langchain-ai#4354](langchain-ai#4354))
([82d1eac](langchain-ai@82d1eac))
* **talon:** add reaction approval routing
([langchain-ai#4345](langchain-ai#4345))
([3fe8c0c](langchain-ai@3fe8c0c))
* **talon:** add Telegram channel adapter, CLI wiring, and offset
persistence
([langchain-ai#4097](langchain-ai#4097))
([7c87cec](langchain-ai@7c87cec))
* **talon:** add tool approval env override
([langchain-ai#4349](langchain-ai#4349))
([d26481d](langchain-ai@d26481d))
* **talon:** audit reaction approval attempts
([langchain-ai#4348](langchain-ai#4348))
([d7895c4](langchain-ai@d7895c4))
* **talon:** ingest Telegram approval reactions
([langchain-ai#4346](langchain-ai#4346))
([437af0b](langchain-ai@437af0b))


### Bug Fixes

* **talon:** default workspace to current directory
([langchain-ai#4099](langchain-ai#4099))
([5e337ae](langchain-ai@5e337ae))

---

_Everything above this line will be the GitHub release body._

---

> [!NOTE]
> A **New Contributors** section is appended to the GitHub release notes
automatically at publish time (see [Release
Pipeline](https://github.com/langchain-ai/deepagents/blob/main/.github/RELEASING.md#release-pipeline),
step 2).

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature New feature/enhancement or request for one internal User is a member of the `langchain-ai` GitHub organization size: M 200-499 LOC talon

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant