Skip to content

Prefer the webhook body over refetching - #28

Merged
alex-w-99 merged 2 commits into
mainfrom
fix/inbound-mail-full-body
Jul 27, 2026
Merged

alex-w-99 merged 2 commits into
mainfrom
fix/inbound-mail-full-body

Conversation

@alex-w-99

@alex-w-99 alex-w-99 commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

Summary

Two changes to the inbound mail path and one fleet-wide version alignment.

1. _fetch_mail_body predates message.received carrying the body. It issued a get_message call on every inbound email and, on any exception, fell back to snippet, the 200-char preview. So a transient API failure silently handed the agent an email cut off mid-sentence with nothing indicating content was missing, and the healthy path paid a round-trip for a body the webhook had already delivered.

2. The plugin was anonymous to the API. The SDK announces itself in the User-Agent and accepts a caller token ahead of its own, but the plugin never set one, so its requests were indistinguishable from any other Python SDK caller. There was no way to tell which plugin, or which version of it, an agent was running.

What's here

Inbound body

  • Payload first. A whole body (body_state: "complete") is used as-is, so the common case does no network call at all.
  • Fetch only when it can help. A truncated or absent body still falls back to get_message, which is the only way to recover the remainder.
  • The fallback no longer loses content. When that fetch fails, _webhook_mail_body delivers what the webhook did carry: the truncated body plus a notice line giving the included/total character counts and the message id, or the snippet when there was no body at all. Previously a failed fetch discarded a body the payload already had.

Bounce and delivery-failure handlers are untouched, since body is null by contract on non-received events and the snippet is the only text available there.

Plugin User-Agent

  • inkbox_client_kwargs now passes user_agent_prefix, yielding inkbox-codex/<version> inkbox-python/<sdk version>. That helper is the single place client kwargs are built, so every construction site is covered by one change.
  • The version comes from installed package metadata, not a hardcoded constant. Worth noting __version__ had already drifted to 0.1.0 against a 0.1.4 pyproject, which is exactly the failure the metadata lookup avoids; both are now consistent.
  • This adds no new data channel: the User-Agent header is already sent on every request. It fills in a field that was previously blank.

Version

Realigned to 0.2.5, the shared number across the plugin fleet. Previously each repo counted independently (0.2.4 / 0.2.2 / 0.1.4 / 0.1.3 / 0.1.0), so a version told you nothing about which fleet release you were on. 0.2.5 is one above the previous fleet maximum, so every repo moves strictly up and no version number is reused for different content.

Behavior change

Inbound email turns get longer, and the steady-state inbound path makes one fewer API call. Anything downstream that assumed a mail turn was at most ~200 chars of body on the failure path will now see the whole email.

Tests

tests/test_gateway_inbound_mail_body.py covers the body helper and the fetch policy: complete body skips the fetch, truncated body fetches the remainder, failed fetch keeps the truncated body with its notice, absent body falls through to the snippet. tests/test_user_agent.py covers the token, its presence in the client kwargs, and the missing-distribution fallback. Full suite: 298 passed, 1 skipped.

message.received carries the plain-text body, so the inbound mail path
no longer needs an API round-trip to read an email. It fetched on every
inbound and fell back to the 200-char snippet whenever that call failed,
leaving the agent with an email cut off mid-sentence and no indication
anything was missing.

Use the body from the payload when it is whole. Only a truncated or
absent body is worth a fetch, and when that fetch fails the truncated
body is delivered with a notice giving the character counts and the
message id, rather than silently collapsing to the snippet.
The SDK announces itself in the User-Agent and accepts a caller token
ahead of its own, but the plugin never set one, so an inbound request
was indistinguishable from any other Python SDK caller. Without that
there is no way to tell which plugin, or which version of it, a given
agent is running.

Pass user_agent_prefix from inkbox_client_kwargs, the single place
client kwargs are built, sourced from installed package metadata so it
cannot drift from the declared version. __version__ had already drifted
to 0.1.0 against a 0.1.4 pyproject, which is exactly the failure the
metadata lookup avoids.

Version is realigned to 0.2.5, the shared number across the plugin
fleet, so the token identifies a fleet release rather than a per-repo
count.
@alex-w-99 alex-w-99 changed the title Prefer the webhook body over refetching inbound email Prefer the webhook body over refetching; identify the plugin in the User-Agent Jul 27, 2026
@alex-w-99
alex-w-99 marked this pull request as ready for review July 27, 2026 02:40
@alex-w-99
alex-w-99 merged commit f7dabb0 into main Jul 27, 2026
15 checks passed
@alex-w-99 alex-w-99 changed the title Prefer the webhook body over refetching; identify the plugin in the User-Agent Prefer the webhook body over refetching Jul 27, 2026
@alex-w-99
alex-w-99 deleted the fix/inbound-mail-full-body branch July 27, 2026 02:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant