From a9cfcf70c13c4999a2d3d5e7dac45680c13f72cb Mon Sep 17 00:00:00 2001 From: Teknium <127238744+teknium1@users.noreply.github.com> Date: Sat, 12 Sep 2026 00:00:24 -0700 Subject: [PATCH] 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). --- apps/desktop/src/app/contrib/latest-actions.test.ts | 12 ++++++++++++ apps/desktop/src/app/contrib/latest-actions.ts | 3 +++ 2 files changed, 15 insertions(+) diff --git a/apps/desktop/src/app/contrib/latest-actions.test.ts b/apps/desktop/src/app/contrib/latest-actions.test.ts index f719e8438a..57756bce20 100644 --- a/apps/desktop/src/app/contrib/latest-actions.test.ts +++ b/apps/desktop/src/app/contrib/latest-actions.test.ts @@ -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) diff --git a/apps/desktop/src/app/contrib/latest-actions.ts b/apps/desktop/src/app/contrib/latest-actions.ts index 41fa4949d8..433c6411d3 100644 --- a/apps/desktop/src/app/contrib/latest-actions.ts +++ b/apps/desktop/src/app/contrib/latest-actions.ts @@ -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),