Repository navigation
Busy ack rewrites {d}/{t} typed in the user's own message #1634
Description
Implementation: FlyBot (ours) — PR follows within 24h. Do not pick up without a pickup-ok comment on this issue.
Written-by: FlyBot Lead
Claimed-by: FlyBot Lead
Claim-Id: 1bab102216224d0f902e6261bda01285
Branch: dev/1634-busy-ack-rewrites-d-t-typed-in-the-user
Base: 331dd62
At: 2026-10-29T04:37:30Z
Implementation: FlyBot (ours) — PR follows within 24h. Do not pick up without a pickup-ok comment.
Written-by: FlyBot Lead
What happens
The busy ack (#1584, landed in #1630) quotes the human's message back inside 「…」 / "…". If that message itself contains the letters {d} or {t}, the quoted part is rewritten: {d} becomes the running tool's description and {t} gets dropped (empty when there is no tool words), so the ack no longer echoes what the person wrote.
Reproduced through the real host path (install(), live={"s1":{turn:{},tool:"bash"}}, chatOwners containing s1, uiLanguage zh-CN):
- message
把 echo ${d} 改成 echo ${t}→ ackSaw "把 echo $running a command 改成 echo ${t}" — I’m running a command right now, back on it in a moment. - message
python 的 f"{d}" 也要改with tooledit→ ack「python 的 f"改文件"…」收到,手上在改文件,这步完了就来。
The second one is the worst shape: {d} silently becomes 「改文件」 inside the user's own code, so the ack attributes words to the person that they never wrote.
Why
core/src/host/busy-ack.js:117:
const fill = index => pool[index % pool.length].replaceAll('{t}', topic).replaceAll('{d}', doing ?? '')Two passes over the same string. The first pass inserts topic, which is text taken from the user's message (topicOf(), :27-49); the second pass then scans the freshly inserted user text as if it were template markup. Any {d} / {t} the person typed is therefore treated as a placeholder.
Repro (unit level, no host needed)
composeBusyAck({ message: '改 ${d} 这行', tool: 'bash', uiLanguage: 'zh-CN' })Current output contains 「改 topicOf() clips the topic at TOPIC_MAX.zh = 16 chars, so a long sentence hides the effect behind the ellipsis.
Expected
Text quoted from the message is echoed verbatim: {d} and {t} typed by the person stay {d} and {t} in the ack. Only the template's own placeholders are substituted.
Scope
S, one line plus tests:
busy-ack.js:117— substitute in a single pass so inserted text is never rescanned, e.g.replace(/\{(t|d)\}/g, (_, key) => key === 't' ? topic : (doing ?? '')). Keep theprevious-avoidance loop and everything else as it is.- Tests in
core/test/busy-ack.test.js(and the client mirror if it renders the same text): a message containing{d}and one containing{t}, with and without a running tool — the ack quotes them unchanged; plus the existing 10 green cases untouched.
Not this issue (observed, out of scope)
-
ackLanguage()(:17-25) decideszhwhencjk * 2 >= latin, so a Chinese sentence carrying many Latin characters (a code snippet, a path) is answered in English:把 echo ${d} 改成 echo ${t}→ 4 CJK vs ~10 Latin →en. That is a judgement call about density, not a correctness bug; tracked separately if it is worth changing. - The ack only fires in a Bot's own chat, never in rooms (
shouldNoteBusyAck()needsownChat). That is the gap fix(chat): busy / background send stays silent — need a short spoken acknowledgment #1584 left on the room side; separate work.
Activity
HugoXK commentedon Oct 29, 2026
Release
Written-by: FlyBot Lead
Released: 1bab102216224d0f902e6261bda01285
Reason: stood down
At: 2026-10-29T05:25:19Z