feat(core): propagate event_id to GatewayReply.reply_to#619
Merged
Conversation
…ly_to Add origin_event_id field to ChannelRef so the gateway event_id survives the message handling pipeline and appears in GatewayReply.reply_to. This enables the gateway to correlate replies with inbound events (e.g. LINE reply token cache in #608). Changes: - Add origin_event_id: Option<String> to ChannelRef - Remove #[allow(dead_code)] from GatewayEvent.event_id - Fill origin_event_id from event_id in gateway event handler - Use origin_event_id in GatewayAdapter::send_message reply_to - Propagate through create_thread - Set origin_event_id: None for Discord, Slack, cron (non-gateway)
|
All PRs must reference a prior Discord discussion to ensure community alignment before implementation. Please edit the PR description to include a link like: This PR will be automatically closed in 3 days if the link is not added. |
added 3 commits
April 28, 2026 18:50
Manual impl of Hash, Eq, PartialEq so that origin_event_id does not affect channel identity. Two ChannelRefs pointing to the same channel are equal regardless of which gateway event they originated from.
- origin_event_id_excluded_from_eq: same channel, different event IDs are equal - origin_event_id_excluded_from_hash: HashMap treats them as same key - origin_event_id_survives_clone: simulates create_thread propagation
Explain that reply_to carries the inbound event_id for reply correlation (e.g. LINE reply token lookup).
thepagent
approved these changes
Apr 28, 2026
chaodu-agent
pushed a commit
to iamninihuang/openab
that referenced
this pull request
Apr 28, 2026
…elog - Status: Proposed → Accepted - Add sequence diagram showing hybrid Reply/Push dispatch flow - Update changelog to v0.2 with openabdev#608 and openabdev#619 references - Add @iamninihuang as co-author
This was referenced Apr 28, 2026
thepagent
pushed a commit
that referenced
this pull request
Apr 28, 2026
* feat(gateway): implement hybrid LINE reply/push strategy This implementation adds a stateful token cache in the gateway to prioritize the free LINE Reply API. It also includes an auto-fill logic for the reply_to field to avoid modifying the OAB core. * fix(gateway): track last_event_id per channel to prevent cross-group race conditions * fix(gateway): address PR #608 review findings - Remove auto-fill reply_to mechanism that could attach replies to wrong LINE messages under concurrent traffic (blocker from shaun-agent, 超渡法師, 普渡法師). If OAB sends empty reply_to, gateway now correctly falls back to Push API instead of guessing the latest event. - Extract reply token from cache before making HTTP call so Mutex is not held across network I/O (blocker from shaun-agent, 超渡法師, 普渡法師). - Unify sweep TTL with handler TTL (REPLY_TOKEN_TTL_SECS) to eliminate dead-weight cache entries in 50-60s range (普渡法師). - Add secrets.env to .gitignore (shaun-agent, 超渡法師). - Run rustfmt --edition 2021 (shaun-agent). - Update ADR Consequences to reflect hybrid reply/push strategy (超渡法師). * docs(gateway): clarify global reply_token_cache vs per-connection semantics LINE reply tokens are single-use. Document that the first OAB client to remove() a cached token gets the free Reply API call; others fall back to Push API. This is correct behavior but was not documented, which could confuse maintainers (普渡法師). * docs: update feature request to reflect removal of auto-fill mechanism The Per-Client Last Event Tracker was removed during review because it caused incorrect reply-token association in group chats. Update the feature request doc to match the actual implementation: gateway does not infer reply_to, it falls back to Push API when reply_to is empty or not found in cache (普渡法師 doc nit). * docs(adr): remove stale auto-fill description from hybrid strategy The 4-step hybrid strategy description still referenced the removed Per-Client Last Event Tracker auto-fill mechanism. Updated to accurately describe the actual implementation: event_id-keyed cache lookup with empty reply_to falling back to Push API. * refactor(gateway): address review nits on PR #608 - Switch ReplyTokenCache from tokio::sync::Mutex to std::sync::Mutex; critical sections are short and never held across .await (超渡法師). - Log LINE Reply API 400 response body for easier debugging (超渡法師). - Guard secrets.env source in run_gateway.sh with existence check (超渡法師). - Align sweep interval with REPLY_TOKEN_TTL_SECS (50s) (超渡法師). * fix(gateway): prevent duplicate delivery on Reply API 5xx/network error - Only fallback to Push API on 4xx (token invalid/expired). For 5xx and network errors, the message may have already been delivered — do NOT fallback to avoid duplicate delivery (擺渡法師 major finding). - Update ADR message flow step 7 to reflect hybrid Reply/Push strategy instead of Push-only (擺渡法師 doc nit). * fix(gateway): narrow Reply API fallback to explicit token-unusable only Only fallback to Push API when LINE's 400 response body explicitly indicates the reply token is invalid or expired. All other failures (5xx, other 4xx, network/timeout errors) are treated as ambiguous delivery — logged as error and dropped to prevent duplicate delivery (擺渡法師 refined major finding, 普渡法師 concurred). * fix(gateway): address review findings on PR #608 - Fix operator precedence bug in Reply API 400 fallback: add explicit parentheses so (invalid AND reply token) OR expired is evaluated correctly - Change lock().unwrap() to lock().unwrap_or_else(|e| e.into_inner()) at all 3 cache lock sites to recover from mutex poison instead of panicking - Add REPLY_TOKEN_CACHE_MAX (10k) bound: skip insert and log warning when cache is full, gracefully degrading to Push API * docs: remove feature-requests/line-reply-api.md Feature is now implemented and documented in docs/adr/line-adapter.md. Keeping a feature request doc for a shipped feature is misleading. * revert: remove .gitignore change (secrets.env not needed) * docs(adr): update line-adapter with hybrid dispatch diagram and changelog - Status: Proposed → Accepted - Add sequence diagram showing hybrid Reply/Push dispatch flow - Update changelog to v0.2 with #608 and #619 references - Add @iamninihuang as co-author * docs: remove run_gateway.sh, add LINE env vars to gateway README - Remove run_gateway.sh (README already documents how to run; script used port 9090 conflicting with README default 8080) - Add LINE_CHANNEL_SECRET and LINE_CHANNEL_ACCESS_TOKEN to env vars table - Add POST /webhook/line to endpoints table * docs(adr): restore original Date, add Last Updated field --------- Co-authored-by: iamninihuang <iamninihuang@users.noreply.github.com> Co-authored-by: iamninihuang <mic-733ao@example.com> Co-authored-by: 超渡法師 <chaodu-agent@openab.dev> Co-authored-by: 超渡法師 <chaodu@openab.dev>
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.
Problem
OAB core receives
event_idin everyGatewayEventbut never uses it — the field is marked#[allow(dead_code)]. WhenGatewayAdapter::send_messagebuilds aGatewayReply, it always setsreply_to: String::new().This means the gateway cannot correlate replies with inbound events. Specifically, PR #608 (hybrid LINE Reply/Push API) caches reply tokens keyed by
event_id, but the cache never hits becausereply_tois always empty.Solution
Add
origin_event_id: Option<String>toChannelRefand propagate it through the message handling pipeline:src/adapter.rs: Addorigin_event_id: Option<String>field toChannelRef. Manual impl ofHash/Eq/PartialEqexcludes this field so channel identity is unaffected.src/gateway.rs:#[allow(dead_code)]fromGatewayEvent.event_idorigin_event_idfromevent_idwhen constructingChannelReffrom gateway eventsorigin_event_idinsend_messageto populateGatewayReply.reply_tocreate_threadso replies in new threads still carry the event IDorigin_event_id: None(no behavioral change for non-gateway adapters)Tests
origin_event_idsemantics:origin_event_id_excluded_from_eq— same channel, different event IDs are equalorigin_event_id_excluded_from_hash— HashMap treats them as same keyorigin_event_id_survives_clone— simulates create_thread propagationImpact
reply_tonow carries the originatingevent_id, enabling feat(gateway): implement hybrid LINE reply/push strategy #608's reply token cache to function correctlyorigin_event_idisNone,reply_toremains""Unblocks #608