You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Builtin Kimi Code (stamped runtime), applies to any agent with engine-opened turns
What happened?
This is a design request, not a defect: after #653/#654, an engine-opened turn (a cron fire, a task wake) renders as its own assistant turn with a persisted acpTurnOrigin — but it has no visual cue saying where it came from. It appears out of nowhere between two user turns, with no user message and no origin label.
A first attempt (an eyebrow divider as a separate chat-stream item, tried in #654 and reverted) showed the constraint that makes this non-trivial: the chat stream's coordinate contract is history index == chat items index. session-chat-interface.tsxhandleScrollToMessage does history.findIndex(h => h.id === historyId) and feeds the result straight into chatStreamRef.current?.scrollToIndex(index), and search navigation scrolls by the same raw index. Any stream item that does not correspond 1:1 to a history entry (like the divider) shifts every scroll target after the first engine-opened turn by one row.
What did you expect?
An origin affordance for engine-opened turns (e.g. a "Scheduled task" / "Background task" label, and eventually the triggering cron prompt) that:
Does not add, remove, or reorder chat-stream items relative to history entries (or ships with an explicit index-mapping layer that all scrollToIndex callers go through).
In-turn eyebrow: render the label inside the assistant turn's own row structure (first content block / turn header), leaving stream items 1:1 with history entries. No new item type; no index drift.
Index-mapping layer: make scrollToIndex callers resolve through message ids or a history→stream index map instead of raw history.findIndex — a larger refactor that would also tolerate future non-message items.
Header chip on the following user/assistant turn: attach the origin cue to the engine turn's own header metadata row.
Let the agent schedule a short one-shot cron and wait for it to fire.
Observe the cron turn appear as a bare assistant message with no origin cue.
How often does it happen?
Every time (whenever an engine-opened turn renders).
Relevant log output
# The reverted divider approach, in code:# build-chat-stream-items.ts emitted { type: 'engine_turn' } between history entries,# and session-chat-interface.tsx:4406 then scrolled to history.findIndex(id) — off by N.
Additional context
Persisted field: SessionHistoryInput.acpTurnOrigin (values like cron_job, task).
The coordinate contract is documented implicitly by components/ai-gui/AGENTS.md ("do not call vlistRef.scrollToIndex elsewhere") and the two call sites named above.
Before submitting
I searched the existing issues and did not find a duplicate.
This report concerns an open-source component in this repository, not a hosted service, Web or mobile app, account, or billing issue.
This is not a security vulnerability; security reports follow the repository's security policy.
I removed credentials, private source, conversations, prompts, personal data, and other sensitive information.
Affected area
Desktop app
Installation method
Built from source
Lody version or commit
2055c00 (main) + the engine-turn fix stack (PR #653 / #654)
Operating system
macOS 15 arm64 (platform-independent)
Agent or runtime
Builtin Kimi Code (stamped runtime), applies to any agent with engine-opened turns
What happened?
This is a design request, not a defect: after #653/#654, an engine-opened turn (a cron fire, a task wake) renders as its own assistant turn with a persisted
acpTurnOrigin— but it has no visual cue saying where it came from. It appears out of nowhere between two user turns, with no user message and no origin label.A first attempt (an eyebrow divider as a separate chat-stream item, tried in #654 and reverted) showed the constraint that makes this non-trivial: the chat stream's coordinate contract is
history index == chat items index.session-chat-interface.tsxhandleScrollToMessagedoeshistory.findIndex(h => h.id === historyId)and feeds the result straight intochatStreamRef.current?.scrollToIndex(index), and search navigation scrolls by the same raw index. Any stream item that does not correspond 1:1 to a history entry (like the divider) shifts every scroll target after the first engine-opened turn by one row.What did you expect?
An origin affordance for engine-opened turns (e.g. a "Scheduled task" / "Background task" label, and eventually the triggering cron prompt) that:
scrollToIndexcallers go through).acpTurnOriginfield (already in history entries from fix(cli,shared): render engine-opened agent turns as their own history entries #653).Candidate directions (maintainer's call):
scrollToIndexcallers resolve through message ids or a history→stream index map instead of rawhistory.findIndex— a larger refactor that would also tolerate future non-message items.How can we reproduce it?
How often does it happen?
Every time (whenever an engine-opened turn renders).
Relevant log output
Additional context
SessionHistoryInput.acpTurnOrigin(values likecron_job,task).components/ai-gui/AGENTS.md("do not callvlistRef.scrollToIndexelsewhere") and the two call sites named above.Before submitting