Skip to content

Commit 4a0016f

Browse files
scszcoderclaude
andcommitted
ws037: card-on-entry — dispatch under synthetic name (fix ws033b held-forever)
ws033b held a name-less product card waiting for its name to resolve, expiring the hold only when a NEW frame arrived. But card retransmits are a sub-second burst (live capture: 15 frames in 1.02s) against an 8s hold, so after the burst no frame ever triggers expiry -> the card was held FOREVER -> stuck (the exact symptom ws033b meant to fix, via a different mechanism). ws033c: drop the hold. A name-less card gets a stable synthetic name (card:<conv>) and dispatches immediately; the conv|text dedup prevents a duplicate if a named retransmit resolves the real name later. Verified by replaying the captured frames through the reader: 0 -> 1 dispatch. 转人工 handling intentionally left as-is (it arrives as a fusion handover routing event, not a chat message; ws_reader correctly extracts nothing). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent cf1e1fe commit 4a0016f

1 file changed

Lines changed: 12 additions & 31 deletions

File tree

  • agent/ec_skills/browser_use_extension/hooks/external/feige_chat

agent/ec_skills/browser_use_extension/hooks/external/feige_chat/ws_observer.py

Lines changed: 12 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@
2222
import base64
2323
import logging
2424
import os
25-
import time
2625
from typing import Any
2726

2827
from . import ws_reader, ws_session
@@ -75,11 +74,6 @@ async def inject_frame_on_detection_tab(frame_bytes: bytes, timeout: float = 3.0
7574

7675
_DISPATCH_ENV = "ECAN_FEIGE_WS_DISPATCH"
7776

78-
# ws033b: how long to HOLD a name-less product card (card-on-entry) waiting for
79-
# its conversation name to resolve before dispatching it under a synthetic
80-
# (conversation-keyed) name so a card-ONLY customer is still answered.
81-
_NAMELESS_CARD_HOLD_S = 8.0
82-
8377

8478
async def start_ws_shadow_observer(session: Any, target_id: str, label: str = "",
8579
dispatch_fn=None) -> Any:
@@ -157,10 +151,6 @@ async def start_ws_shadow_observer(session: Any, target_id: str, label: str = ""
157151

158152
seen: set = set()
159153
stats = {"frames": 0, "msgs": 0}
160-
# ws033b: conv_id -> monotonic ts a name-less product card was first seen,
161-
# so a card-on-entry is HELD (not dropped, not dispatched name-less) until
162-
# name_for_talk() resolves or the hold window expires.
163-
pending_nameless_cards: dict = {}
164154
_socket_sid = [None] # ws009: remember the tab that actually holds the socket
165155

166156
# ws029: expose the detection-tab page-socket inject to other subsystems
@@ -290,36 +280,27 @@ def _on_frame(params, session_id=None):
290280
_nm = ws_session.name_for_talk(m.conversation_id)
291281
if _nm:
292282
m.customer_name = _nm
293-
pending_nameless_cards.pop(m.conversation_id, None)
294283
logger.info(
295284
f"[FEIGE-WS-CARD] attributed name-less "
296285
f"{m.msg_type or 'frame'} to cust={_nm!r} via "
297286
f"conv={m.conversation_id} text={m.text[:60]!r}"
298287
)
299-
# ws033b: a product card shared ON ENTRY arrives BEFORE any named
300-
# frame on its conversation, so name_for_talk() is still '' here.
301-
# The original ws033 DROPPED it (`continue`) without recording,
302-
# betting a later retransmit would be attributable — but for a
303-
# card-ONLY conversation the name NEVER resolves, so the card was
304-
# lost FOREVER (bot keeps asking "发下商品图片"). Option 2: HOLD
305-
# it. Skip WITHOUT recording in `seen` (so the card's own
306-
# retransmits — 5x/s, for minutes — keep re-driving this branch)
307-
# until name_for_talk resolves (a retransmit then falls through
308-
# and dispatches under the real name) OR the hold window expires,
309-
# at which point we attribute the card to its conversation id so
310-
# a card-only customer is still answered instead of going silent.
288+
# ws033c: a product card shared ON ENTRY has no name yet, and for
289+
# a card-ONLY conversation no named frame ever arrives. ws033
290+
# dropped it forever (`continue`); ws033b tried a time-hold but it
291+
# NEVER expired — card retransmits are a sub-second burst (live:
292+
# 15 frames in 1.02s) and the expiry was only re-checked on frame
293+
# arrival, so after the burst the card was held forever -> stuck.
294+
# Just attribute it to a stable synthetic name and dispatch NOW;
295+
# the conv|text dedup below prevents a duplicate if a named
296+
# retransmit resolves the real name later (that card is deduped).
311297
if m.msg_type == "template_card" and not m.customer_name:
312-
_first = pending_nameless_cards.setdefault(
313-
m.conversation_id, time.monotonic())
314-
if time.monotonic() - _first < _NAMELESS_CARD_HOLD_S:
315-
continue # within hold window — wait for the name
316298
m.customer_name = f"card:{m.conversation_id}"
317299
if f"card|{m.conversation_id}|{m.text}" not in seen:
318300
logger.info(
319-
f"[FEIGE-WS-CARD] nameless card hold expired "
320-
f"({_NAMELESS_CARD_HOLD_S:.0f}s) conv={m.conversation_id}"
321-
f" — dispatching under synthetic name "
322-
f"{m.customer_name!r} text={m.text[:60]!r}")
301+
f"[FEIGE-WS-CARD] nameless card -> synthetic name "
302+
f"{m.customer_name!r} conv={m.conversation_id} "
303+
f"text={m.text[:60]!r}")
323304
# ws027: product-card frames retransmit (5x in <1s) with
324305
# UNSTABLE msg_ids — field-3 is sometimes the real id,
325306
# sometimes a conv-like snowflake — so an msg_id-keyed dedup

0 commit comments

Comments
 (0)