| ... | ... | @@ -117,12 +117,26 @@ function settledTurnStateForSessionStatus( |
| 117 | 117 | } |
| 118 | 118 | } |
| 119 | 119 | |
| 120 | // Runs for every thread event (including streaming deltas) against every |
| 121 | // thread the server has ever seen, so copy the array rather than map it. |
| 120 | 122 | function updateThread( |
| 121 | 123 | threads: ReadonlyArray<OrchestrationThread>, |
| 122 | 124 | threadId: ThreadId, |
| 123 | 125 | patch: ThreadPatch, |
| 124 | | ): OrchestrationThread[] { |
| 125 | | return threads.map((thread) => (thread.id === threadId ? { ...thread, ...patch } : thread)); |
| 126 | ): ReadonlyArray<OrchestrationThread> { |
| 127 | const index = threads.findIndex((thread) => thread.id === threadId); |
| 128 | return index === -1 ? threads : patchThreadAt(threads, index, patch); |
| 129 | } |
| 130 | |
| 131 | /** For callers that already located the thread and must not scan again. */ |
| 132 | function patchThreadAt( |
| 133 | threads: ReadonlyArray<OrchestrationThread>, |
| 134 | index: number, |
| 135 | patch: ThreadPatch, |
| 136 | ): ReadonlyArray<OrchestrationThread> { |
| 137 | const next = threads.slice(); |
| 138 | next[index] = { ...threads[index]!, ...patch }; |
| 139 | return next; |
| 126 | 140 | } |
| 127 | 141 | |
| 128 | 142 | /** Patch that swaps a thread's links and re-derives the legacy single-PR field from them. */ |
| ... | ... | @@ -767,7 +781,8 @@ export function projectEvent( |
| 767 | 781 | event.type, |
| 768 | 782 | "payload", |
| 769 | 783 | ); |
| 770 | | const thread = nextBase.threads.find((entry) => entry.id === payload.threadId); |
| 784 | const threadIndex = nextBase.threads.findIndex((entry) => entry.id === payload.threadId); |
| 785 | const thread = nextBase.threads[threadIndex]; |
| 771 | 786 | if (!thread) { |
| 772 | 787 | return nextBase; |
| 773 | 788 | } |
| ... | ... | @@ -815,7 +830,7 @@ export function projectEvent( |
| 815 | 830 | |
| 816 | 831 | return { |
| 817 | 832 | ...nextBase, |
| 818 | | threads: updateThread(nextBase.threads, payload.threadId, { |
| 833 | threads: patchThreadAt(nextBase.threads, threadIndex, { |
| 819 | 834 | messages: cappedMessages, |
| 820 | 835 | updatedAt: event.occurredAt, |
| 821 | 836 | }), |
| ... | ... | @@ -1055,7 +1070,8 @@ export function projectEvent( |
| 1055 | 1070 | "payload", |
| 1056 | 1071 | ).pipe( |
| 1057 | 1072 | Effect.map((payload) => { |
| 1058 | | const thread = nextBase.threads.find((entry) => entry.id === payload.threadId); |
| 1073 | const threadIndex = nextBase.threads.findIndex((entry) => entry.id === payload.threadId); |
| 1074 | const thread = nextBase.threads[threadIndex]; |
| 1059 | 1075 | if (!thread) { |
| 1060 | 1076 | return nextBase; |
| 1061 | 1077 | } |
| ... | ... | @@ -1069,7 +1085,7 @@ export function projectEvent( |
| 1069 | 1085 | |
| 1070 | 1086 | return { |
| 1071 | 1087 | ...nextBase, |
| 1072 | | threads: updateThread(nextBase.threads, payload.threadId, { |
| 1088 | threads: patchThreadAt(nextBase.threads, threadIndex, { |
| 1073 | 1089 | activities, |
| 1074 | 1090 | updatedAt: event.occurredAt, |
| 1075 | 1091 | }), |