fix(desktop): forward optional composer handlers through the latest-actions adapter

latestChatActions rebuilds the ChatView handler bag field by field, so an
optional handler added to ChatActions but not to the adapter is silently
dropped before it reaches ChatView. Live CDP probe on a built Desktop: the
wiring controller had onAttachPastedText, ChatView received undefined, and
a 4,500-char paste stayed inline. onAttachPrCommentUrl and onSteerHidden
(already on main) were dropped the same way on the main chat surface; the
session-tile path passes them directly and was unaffected.

Forward all three via latestOptional and pin the class with one invariant
test: every handler present on the actions bag is present on the adapted
bag (red on the previous adapter).
This commit is contained in:
Teknium
2026-09-12 00:00:24 -07:00
parent ab7f03049e
commit a9cfcf70c1
2 changed files with 15 additions and 0 deletions

View File

@@ -11,6 +11,8 @@ function makeChatActions(): ChatActions {
onAddUrl: vi.fn(),
onAttachDroppedItems: vi.fn(),
onAttachImageBlob: vi.fn(),
onAttachPastedText: vi.fn(),
onAttachPrCommentUrl: vi.fn(),
onBranchInNewChat: vi.fn(),
onCancel: vi.fn(),
onDeleteSelectedSession: vi.fn(),
@@ -25,6 +27,7 @@ function makeChatActions(): ChatActions {
onRestoreToMessage: vi.fn(),
onRetryResume: vi.fn(),
onSteer: vi.fn(),
onSteerHidden: vi.fn(),
onSubmit: vi.fn(),
onThreadMessagesChange: vi.fn(),
onToggleSelectedPin: vi.fn(),
@@ -49,6 +52,15 @@ function makeSidebarActions(): SidebarActions {
}
describe('latestActions adapters', () => {
it('forwards every present handler — an optional one the adapter forgets never reaches ChatView', () => {
const actions = makeChatActions()
const adapted = latestChatActions(actions)
for (const key of Object.keys(actions) as (keyof ChatActions)[]) {
expect(typeof adapted[key], key).toBe('function')
}
})
it('dereferences the latest steer handler from a stable actions object', async () => {
const staleSteer = vi.fn(async () => false)
const latestSteer = vi.fn(async () => true)

View File

@@ -34,6 +34,8 @@ export function latestChatActions(actions: ChatActions): ChatActions {
onAddUrl: (...args) => actions.onAddUrl(...args),
onAttachDroppedItems: (...args) => actions.onAttachDroppedItems(...args),
onAttachImageBlob: (...args) => actions.onAttachImageBlob(...args),
onAttachPastedText: latestOptional(() => actions.onAttachPastedText),
onAttachPrCommentUrl: latestOptional(() => actions.onAttachPrCommentUrl),
onBranchInNewChat: latestOptional(() => actions.onBranchInNewChat),
onCancel: (...args) => actions.onCancel(...args),
onDeleteSelectedSession: (...args) => actions.onDeleteSelectedSession(...args),
@@ -48,6 +50,7 @@ export function latestChatActions(actions: ChatActions): ChatActions {
onRestoreToMessage: latestOptional(() => actions.onRestoreToMessage),
onRetryResume: (...args) => actions.onRetryResume(...args),
onSteer: (...args) => actions.onSteer(...args),
onSteerHidden: latestOptional(() => actions.onSteerHidden),
onSubmit: (...args) => actions.onSubmit(...args),
onThreadMessagesChange: (...args) => actions.onThreadMessagesChange(...args),
onToggleSelectedPin: (...args) => actions.onToggleSelectedPin(...args),