Problem
TelegramBotInstance._transcribe_uploaded_voice_files records ASR usage via record_asr_usage, but nothing on that path binds a reportable usage context.
record_asr_usage appends to the current TokenUsage ContextVar. Telegram transcribes while preparing the message — before _process_user_messages_batch calls AgentManager.execute_task and before TaskTracker binds the task usage. On an unbound path that accumulator is a throwaway object nothing ever reads; when the tracker later starts it installs a fresh accumulator, so the provider-billed ASR seconds are never persisted or handed to quota_hooks.record_usage.
A reused long-lived queue task can also expose a stale binding, which would misattribute usage to a previous owner.
Impact
Every Telegram voice message is transcribed and billed by the provider but metered as nothing. Stale-binding cases can attribute usage to the wrong user.
Suggested fix
Wrap the transcription in usage_scope(owner_user_id) using the authorized owner resolved near bot.py:1652, or move the tracker boundary so the whole operation has a single explicit lifecycle. Add a behavioural test that captures the quota hook and asserts owner attribution and exactly-once reporting.
Context
Raised in review of #1425 (Root A). Deferred from that PR to keep its scope to audio/video metering against the merged primitives (#1527); the fix is a new reporting lifecycle rather than a change to the metering call itself.
Same underlying concern was raised on #997 (reviews 4786040090, 4796045078, inline 3664560548).
Relevant code: src/xagent/web/channels/telegram/bot.py:1406
Problem
TelegramBotInstance._transcribe_uploaded_voice_filesrecords ASR usage viarecord_asr_usage, but nothing on that path binds a reportable usage context.record_asr_usageappends to the currentTokenUsageContextVar. Telegram transcribes while preparing the message — before_process_user_messages_batchcallsAgentManager.execute_taskand beforeTaskTrackerbinds the task usage. On an unbound path that accumulator is a throwaway object nothing ever reads; when the tracker later starts it installs a fresh accumulator, so the provider-billed ASR seconds are never persisted or handed toquota_hooks.record_usage.A reused long-lived queue task can also expose a stale binding, which would misattribute usage to a previous owner.
Impact
Every Telegram voice message is transcribed and billed by the provider but metered as nothing. Stale-binding cases can attribute usage to the wrong user.
Suggested fix
Wrap the transcription in
usage_scope(owner_user_id)using the authorized owner resolved nearbot.py:1652, or move the tracker boundary so the whole operation has a single explicit lifecycle. Add a behavioural test that captures the quota hook and asserts owner attribution and exactly-once reporting.Context
Raised in review of #1425 (Root A). Deferred from that PR to keep its scope to audio/video metering against the merged primitives (#1527); the fix is a new reporting lifecycle rather than a change to the metering call itself.
Same underlying concern was raised on #997 (reviews 4786040090, 4796045078, inline 3664560548).
Relevant code:
src/xagent/web/channels/telegram/bot.py:1406