Repository navigation
feat(native-chat): show which agent a chat message is from, and open it #25888
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
8135e88
55aac43
eda094a
fb82a9f
f3f3259
cc32e42
2f75dcc
255c873
c24fd4d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -120,6 +120,11 @@ export function MobileNativeChatQueuedMessages({ | |
| <ListEnd size={14} color={colors.textMuted} strokeWidth={2} /> | ||
| )} | ||
| <View style={styles.textColumn}> | ||
| {card.attribution ? ( | ||
| <Text style={styles.caption} numberOfLines={1}> | ||
| {card.attribution} | ||
|
Comment on lines
+124
to
+125
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win Keep the full sender attribution readable. When an agent has a long task name or a card lists multiple senders, |
||
| </Text> | ||
| ) : null} | ||
| {/* Two lines, not the desktop's one: the phone row has no hover title to read the rest. */} | ||
| <Text style={styles.body} numberOfLines={2}> | ||
| {card.text} | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,57 @@ | ||
| import { describe, expect, it } from 'vitest' | ||
| import type { AgentMessageSource } from '../../../src/shared/agent-session-message-source' | ||
| import { agentMessageAttribution } from './mobile-agent-message-attribution' | ||
| import { mobileQueuedMessageCards } from './mobile-structured-queued-message-cards' | ||
|
|
||
| function from(names: (string | null)[]): AgentMessageSource { | ||
| return { | ||
| kind: 'agent', | ||
| senders: names.map((name, index) => ({ | ||
| party: { address: `term_${index}`, terminalHandle: `term_${index}`, orcaSessionId: null }, | ||
| name | ||
| })), | ||
| orchestration: null | ||
| } | ||
| } | ||
|
|
||
| describe('who another agent’s message is from, on mobile', () => { | ||
| it('names nothing on the person’s own message', () => { | ||
| expect(agentMessageAttribution('From', undefined)).toBeNull() | ||
| }) | ||
|
|
||
| it('names each sender, an unnamed one as an agent, and counts the rest', () => { | ||
| expect(agentMessageAttribution('Message from', from(['Coder']))).toBe('Message from Coder') | ||
| expect(agentMessageAttribution('From', from([]))).toBe('From an agent') | ||
| expect(agentMessageAttribution('From', from(['A', 'B', 'C', 'D']))).toBe('From A, B, C +1') | ||
| }) | ||
|
|
||
| it('keeps two senders that share a name two, and counts only the senders it leaves out', () => { | ||
| expect(agentMessageAttribution('From', from(['Codex', 'Codex']))).toBe('From Codex, Codex') | ||
| expect(agentMessageAttribution('From', from([null, null]))).toBe('From an agent, an agent') | ||
| expect(agentMessageAttribution('From', from(['Codex', 'Codex', 'Codex', 'Codex']))).toBe( | ||
| 'From Codex, Codex, Codex +1' | ||
| ) | ||
| }) | ||
|
|
||
| it('puts the line on the queued card, read through the shared reader', () => { | ||
| const body = { | ||
| kind: 'message' as const, | ||
| role: 'user' as const, | ||
| blocks: [{ type: 'text' as const, text: 'You have 1 orchestration message.' }] | ||
| } | ||
| const cards = mobileQueuedMessageCards( | ||
| [ | ||
| { | ||
| messageId: 'agent', | ||
| position: 1, | ||
| state: 'waiting', | ||
| body: { ...body, from: from(['Coder']) } | ||
| }, | ||
| { messageId: 'person', position: 2, state: 'waiting', body } | ||
| ], | ||
| [], | ||
| { pendingPrompt: false } | ||
| ) | ||
| expect(cards.map((card) => card.attribution)).toEqual(['From Coder', null]) | ||
| }) | ||
| }) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,22 @@ | ||
| // The plain line naming who sent another agent's message; not a link on mobile yet. | ||
|
|
||
| import { | ||
| agentMessageSendersShown, | ||
| type AgentMessageSource | ||
| } from '../../../src/shared/agent-session-message-source' | ||
|
|
||
| const UNNAMED_SENDER = 'an agent' | ||
|
|
||
| /** Null for the person's own message. */ | ||
| export function agentMessageAttribution( | ||
| lead: 'From' | 'Message from', | ||
| source: AgentMessageSource | undefined | ||
| ): string | null { | ||
| if (!source) { | ||
| return null | ||
| } | ||
| const { shown, more } = agentMessageSendersShown(source) | ||
| // One name per sender, repeats included: two senders that share a name are still two. | ||
| const names = shown.map((sender) => sender.name ?? UNNAMED_SENDER) | ||
| return `${lead} ${names.length > 0 ? names.join(', ') : UNNAMED_SENDER}${more > 0 ? ` +${more}` : ''}` | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Scale the sender label with
fontScale.When a person enlarges chat text,
Prosescales but this new label stays attypography.metaSize. ApplyfontScaleto the attribution text so the sender remains readable at the selected size.