fix(runtime): drop time_of_last_update from A2A and AG-UI ping responses - #542
Merged
Conversation
The A2A and AG-UI ping handlers reset the status-update timestamp to the
current time on every ping, so `time_of_last_update` always reported "now"
rather than the time the status last changed — making the field meaningless
on those paths (unlike app.py, which only stamps it on a status transition).
The Runtime data plane parses `time_of_last_update` as optional and never
uses its value: session keep-alive is driven entirely by the `status` field
(`HealthyBusy` keeps the session active). Dropping the field from the A2A and
AG-UI responses removes the misleading value without affecting platform
behavior. app.py is left unchanged.
- a2a.py / ag_ui.py: ping returns `{"status": ...}`; remove the now-dead
last_status_update_time tracking and unused `import time`
- tests: assert `time_of_last_update` is absent from A2A/AG-UI ping responses
Contributor
✅ No Breaking Changes DetectedNo public API breaking changes found in this PR. |
Contributor
|
Claude Security Review: the review did not analyze this PR (model took 0 turns). See the run for details; a later push or re-run is needed. |
A prior commit message stated the Runtime platform "never uses" and "discards" time_of_last_update. That is inaccurate. The platform consumes the field to detect session idleness: it expects the timestamp to reflect when the status last changed. The A2A and AG-UI handlers set time_of_last_update to the current time on every ping, so the platform saw a continuous status change and never considered the session idle — the idle session timeout never fired and sessions persisted until MaxLifetime, exhausting the session quota. Dropping the field (this change) is the correct fix: with the field absent, the platform tracks status changes using its own clock, so the idle timeout works as configured. app.py is unaffected — it already stamps the timestamp only on a status change.
Contributor
|
Claude Security Review: the review did not analyze this PR (model took 0 turns). See the run for details; a later push or re-run is needed. |
nborges-aws
approved these changes
Jun 22, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
The A2A (
a2a.py) and AG-UI (ag_ui.py)/pinghandlers settime_of_last_updateto the current time on every ping, instead of reporting when the status last changed. This PR dropstime_of_last_updatefrom the A2A and AG-UI ping responses, which now return{"status": ...}.app.pyis intentionally left unchanged.Why this is the fix
The Runtime platform consumes
time_of_last_updateto detect session idleness — it expects the timestamp to reflect when the status last changed. Because the A2A and AG-UI handlers re-stamped it to "now" on every ping (every poll interval), the platform saw a continuous status change and never treated the session as idle. As a result:IdleRuntimeSessionTimeout) never fired.MaxLifetimeinstead of being released when idle.ServiceQuotaExceededException).Overriding the ping handler did not help, because the SDK set
time_of_last_updateregardless of the handler's return value — so there was no customer workaround.Dropping the field is the correct fix: with
time_of_last_updateabsent, the platform tracks status changes using its own clock, and the idle timeout works as configured.app.pyis unaffected — it already stamps the timestamp only on an actual status change, so its idle behavior was already correct.Changes
src/bedrock_agentcore/runtime/a2a.py—_handle_pingreturns{"status": status.value}; removed the per-pinglast_status_update_timetracking (and itsnonlocal) and the now-unusedimport time.src/bedrock_agentcore/runtime/ag_ui.py— same change; removed theself._last_status_update_timeattribute and now-unusedimport time.time_of_last_updateis absent from the response.test_app.pyis unchanged (the HTTP app still emits the field, correctly, on status transition).Backward compatibility
Removing the field is non-breaking: the platform treats
time_of_last_updateas optional, so omitting it is accepted. Agents on older SDK versions (still sending the field) and agents on this version (omitting it) both pass the health check.Testing
uv run pytest tests/bedrock_agentcore/runtime/test_a2a.py tests/bedrock_agentcore/runtime/test_ag_ui.py tests/bedrock_agentcore/runtime/test_app.py→ 191 passedruff checkclean on all changed filesNote
A companion internal docs change updates the AgentCore Runtime protocol-contract pages to document
time_of_last_updatecorrectly — as an optional field that must reflect the last status change, with a warning against re-stamping it on every ping.