Skip to content

[Feature Request] Origin label for engine-opened turns that preserves the chat-stream index contract #660

Description

@tommy0103

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.tsx handleScrollToMessage 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:

  1. 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).
  2. Renders the origin from the persisted acpTurnOrigin field (already in history entries from fix(cli,shared): render engine-opened agent turns as their own history entries #653).

Candidate directions (maintainer's call):

  • 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.

How can we reproduce it?

  1. Run a build with fix(cli,shared): render engine-opened agent turns as their own history entries #653/fix(cli): lifecycle guards for engine-opened turns (fork/resend, exit cleanup, Stop routing, replay window) #654 and a stamped Kimi runtime.
  2. Let the agent schedule a short one-shot cron and wait for it to fire.
  3. 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

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.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    status:needs-issue-bodyIssue does not meet the Bug or Feature form requirements

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions