This pull request's diff hasn't been prepared yet. Log in with GitHub to have us prepare it for review.

GO-7585 Discussions: typing and agent activity #2407

Merged
requilence opened 8:02am on October 13, 2026 wanted to merge 5 commits into anyproto/anytype-ts develop from
go-7585-discussion-activity

Pull Request Overview

  • Opened on October 13, 2026
  • Status Merged
  • Commit count 5 with first commit October 13, 2026

Total Delta

0 Total Diff Delta

Open Days

Open 17 weekdays

Test Delta

0 Diff Delta in Test Files
Breakdown by Phase

How long has this pull request spent in each phase of its lifecycle?

Data pending calculation for pull request

Author avatar

GO-7585 Discussions: typing and agent activity

GO-7585. This is the desktop side of anyproto/anytype-heart#3326, which adds an optional reply_to to chat status. Discussions now show who is typing and what an agent is doing while it works on a comment, using the same pubsub channels and UI pieces as chats.

Typing


  • The comment forms publish typing on typing/<discussionId>. A reply form sends replyTo = its post, and the main composer sends none.

  • Typing goes out only when the text changes while the form is focused, so restoring a draft or moving the caret doesn't count. It stops on blur, on submit and on unmount. Edit mode, read-only discussions and a discussion that doesn't exist yet (before the first comment) publish nothing.


  • presence.typing() publishes at once when the reply target changes, as it already did for a block change. A received replyTo is kept in S.Presence.

Agent activity


  • The section retains status/<discussionId> with chatStatus.retain(space, discussionId, 'discussion'). Chats keep the default 'chat' mode and behave as before.


  • decodeStatus reads reply_to. In discussion mode the journal keeps one open group per publisher:


    • Only the publisher's own comment seals its group, with after = that comment. Other people commenting don't split an agent's run.

    • A new item with a different reply_to seals the group without after and opens a new one.




  • discussionActivity() (lib/chatStatus/timeline.ts, pure) decides placement:


    • A finished run goes under the comment it produced. A run whose comment was deleted is hidden.

    • Everything else (open runs, live status text, typing) goes to the end of the thread its reply_to resolves to.

    • If reply_to is missing or its message isn't loaded, it goes at the discussion level above the main composer. It moves into the thread once the message arrives (S.Comment.getThreadRoot plus an observable layout revision).



UI



  • Typing row: moved out of block/chat.tsx into a shared TypingIndicator in block/chat/status.tsx. Chat renders it with no visual change.


  • Compact groups: StatusGroup gains isCompact, which draws a group as one line in that typing row, e.g. "Agent Β· 3 tool calls Β· 1 running Β· Reading…". It shows dots while running and βœ“ or ! when done. Expanded, it shows the chat's tool-call items unchanged. Storybook has CompactRunning and CompactFinished stories.


  • New component: component/comment/activity.tsx with CommentActivity (thread end or discussion level) and CommentActivityAttached (under a comment's head).


  • Styles: .typingIndicator and .chatStatusGroup now apply outside .block.blockChat, with no values changed. Chat-only layout stays scoped.

Setting

Settings β†’ Personal β†’ Messaging β†’ "Show others when I'm typing" is on by default. When it's off:
- Nothing is published from chats or comments. A stop that's already owed still goes out with the 3 s idle timer.
- The switch is broadcast to all tabs through the main process, since each tab publishes its own typing.
- You still see others typing, and editor caret presence on pages is not affected.

Middleware

This needs anyproto/anytype-heart#3326 for reply_to, because an older heart rejects a status body that includes it. The PR doesn't bump middleware.version; that follows once the heart PR is in a release.

Against an older heart, typing works and status without reply_to shows at the discussion level.

For design review



  • Spacing: the activity rows use spacing copied from the neighbouring elements: 0 16px 16px inside a post, 0 16px 12px at the discussion level, and a 28px indent for replies.


  • Avatar ring: the typing row's avatar ring uses --color-bg-primary, which shows as a faint 2px ring on the tinted post background. It's left as is.

Known limitations


  • An agent posting under the user's own account has the same identity as the user. The user's own comment then seals that agent's group.

  • A finished run attached to a comment doesn't count down to stale by itself; it refreshes when something else updates.

Test plan

Tried in the app against a heart built from anytype-heart#3326. The full list for QA:
- [ ] publish-chat-status on a discussion without reply_to: a running row at the discussion level, then the agent's comment with the collapsed run under its head
- [ ] The same with reply_to naming a reply deep in a thread: the row at that thread's end
- [ ] reply_to naming a message that isn't loaded: discussion level, moving into the thread when the message arrives
- [ ] Typing a reply on a second device: the typing row at that thread's end. In the main composer: at the discussion level
- [ ] Someone commenting while the agent runs doesn't split the agent's group
- [ ] "Show others when I'm typing" off: the second device sees no typing from chats or comments, in every open tab
- [ ] Chat typing and tool calls look and behave as before

New tests: lib/presence.test.ts, plus cases in chatStatus/model.test.ts, session.test.ts, timeline.test.ts and store/comment.test.ts.
- bun run typecheck passes. This also fixes the 4 .at() errors in chatStatus/session.test.ts that are already on develop.
- Lint is clean.
- The full vitest run has the same 90 failures as develop and nothing new.

Spec: docs/plans/2026-10-12-discussion-activity-spec.md.

PR was closed without comments.