mirror of
https://github.com/nearai/ironclaw.git
synced 2026-09-02 23:56:24 +08:00
refactor(channels): introduce ExternalThreadId newtype at channel boundary (#2685)
* refactor(channels): introduce ExternalThreadId newtype at channel boundary External channel thread ids (Telegram chat id, web UUID, Slack thread_ts) flow as raw Option<String> through IncomingMessage, StatusUpdate, and pending-gate store. Wraps them in a validated ExternalThreadId so the compiler distinguishes boundary-layer ids from the internal ThreadId(Uuid). Maps to bug pattern from #2349, #2444, #2517 where thread-id confusion crossed a layer silently. * fix(bridge): adapt test thread_id to ExternalThreadId newtype Post-merge fix: a test added in staging (insert_and_notify_pending_gate_uses_extension_manager_for_auth_display_name) assigned a raw String to message.thread_id, but the field type became ExternalThreadId on this branch. Wrap with ExternalThreadId::from_trusted to match the other tests in the same module. * refactor(types): address review feedback — byte units, shared validate, try_-variants, dedup pending-gate * refactor(types): validate scope_thread_id + relay respond prefers typed msg.thread_id - router.rs: scope_thread_id written to PendingGate was wrapped via ExternalThreadId::from_trusted from message.conversation_scope(), which can carry untrusted WASM/metadata-sourced strings. Now validates via ExternalThreadId::new; invalid values log at debug and store None. Applied at both call sites (authentication-fallback path and generic gate-insertion path). - relay/channel.rs: respond() derived thread_id only from response or metadata — now also consults the validated msg.thread_id as the second fallback (before raw metadata) and filters empty strings so we never emit thread_ts: "" to Slack.
This commit is contained in:
@@ -461,7 +461,7 @@ async fn test_private_messages_use_chat_id_as_thread_scope() {
|
||||
.await
|
||||
.expect("message should arrive")
|
||||
.expect("stream should yield a message");
|
||||
assert_eq!(msg.thread_id.as_deref(), Some("999"));
|
||||
assert_eq!(msg.thread_id.as_ref().map(|t| t.as_str()), Some("999"));
|
||||
assert_eq!(msg.conversation_scope(), Some("999"));
|
||||
}
|
||||
|
||||
@@ -572,7 +572,7 @@ async fn test_private_dm_webhook_and_reply_use_fake_telegram_api() {
|
||||
.expect("message should arrive")
|
||||
.expect("stream should yield a message");
|
||||
assert_eq!(incoming.content, "hello from telegram dm");
|
||||
assert_eq!(incoming.thread_id.as_deref(), Some("999"));
|
||||
assert_eq!(incoming.thread_id.as_ref().map(|t| t.as_str()), Some("999"));
|
||||
|
||||
channel
|
||||
.respond(
|
||||
@@ -832,7 +832,10 @@ async fn test_group_message_with_bot_mention_emits_cleaned_content() {
|
||||
.expect("message should arrive")
|
||||
.expect("stream should yield a message");
|
||||
assert_eq!(msg.content, "status please");
|
||||
assert_eq!(msg.thread_id.as_deref(), Some("-123456789"));
|
||||
assert_eq!(
|
||||
msg.thread_id.as_ref().map(|t| t.as_str()),
|
||||
Some("-123456789")
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
@@ -938,7 +941,7 @@ async fn test_edited_message_emits_like_regular_message() {
|
||||
.expect("message should arrive")
|
||||
.expect("stream should yield a message");
|
||||
assert_eq!(msg.content, "edited telegram message");
|
||||
assert_eq!(msg.thread_id.as_deref(), Some("999"));
|
||||
assert_eq!(msg.thread_id.as_ref().map(|t| t.as_str()), Some("999"));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
@@ -2032,7 +2035,7 @@ async fn test_polling_mode_get_updates_via_fake_telegram_api() {
|
||||
.expect("stream should yield the polled message");
|
||||
|
||||
assert_eq!(msg.content, "hello from polling");
|
||||
assert_eq!(msg.thread_id.as_deref(), Some("999"));
|
||||
assert_eq!(msg.thread_id.as_ref().map(|t| t.as_str()), Some("999"));
|
||||
|
||||
// Trigger a second poll (should return empty, no new messages)
|
||||
channel
|
||||
|
||||
@@ -173,7 +173,7 @@ async fn test_ws_message_reaches_agent() {
|
||||
.expect("Agent channel closed");
|
||||
|
||||
assert_eq!(incoming.content, "hello from ws");
|
||||
assert_eq!(incoming.thread_id.as_deref(), Some("t42"));
|
||||
assert_eq!(incoming.thread_id.as_ref().map(|t| t.as_str()), Some("t42"));
|
||||
assert_eq!(incoming.channel, "gateway");
|
||||
assert_eq!(incoming.user_id, "test-user");
|
||||
|
||||
|
||||
Reference in New Issue
Block a user