@flamingo-stack/openframe-frontend-core 0.0.512-1640.4220.1 → 0.0.512
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-HCKICFSZ.cjs → chunk-2WWDZR4D.cjs} +31 -31
- package/dist/{chunk-HCKICFSZ.cjs.map → chunk-2WWDZR4D.cjs.map} +1 -1
- package/dist/{chunk-FAPHWUEM.cjs → chunk-2XL7UK5Y.cjs} +5 -5
- package/dist/{chunk-FAPHWUEM.cjs.map → chunk-2XL7UK5Y.cjs.map} +1 -1
- package/dist/{chunk-XHGL7XHL.js → chunk-4NFDSMB5.js} +4 -4
- package/dist/{chunk-YJRJCJRK.cjs → chunk-5JBDT2MQ.cjs} +7 -7
- package/dist/{chunk-YJRJCJRK.cjs.map → chunk-5JBDT2MQ.cjs.map} +1 -1
- package/dist/{chunk-VL6RXIPW.cjs → chunk-67YDSTH7.cjs} +9 -9
- package/dist/{chunk-VL6RXIPW.cjs.map → chunk-67YDSTH7.cjs.map} +1 -1
- package/dist/{chunk-7UYW6C4P.js → chunk-6LMTBNLN.js} +2 -2
- package/dist/{chunk-QXFJU5I2.cjs → chunk-7VBEOGCR.cjs} +61 -61
- package/dist/{chunk-QXFJU5I2.cjs.map → chunk-7VBEOGCR.cjs.map} +1 -1
- package/dist/{chunk-WOM5RZ6T.cjs → chunk-AWLFJMOJ.cjs} +37 -37
- package/dist/{chunk-WOM5RZ6T.cjs.map → chunk-AWLFJMOJ.cjs.map} +1 -1
- package/dist/{chunk-SIRV5BCG.cjs → chunk-DQDPDUI3.cjs} +4 -4
- package/dist/{chunk-SIRV5BCG.cjs.map → chunk-DQDPDUI3.cjs.map} +1 -1
- package/dist/{chunk-Y5QFN4WH.cjs → chunk-F2YI5N7E.cjs} +13 -13
- package/dist/{chunk-Y5QFN4WH.cjs.map → chunk-F2YI5N7E.cjs.map} +1 -1
- package/dist/{chunk-IYIO2OVS.js → chunk-FI35LJ3L.js} +2 -2
- package/dist/{chunk-NZR2U5VK.cjs → chunk-GOFTC2HV.cjs} +444 -479
- package/dist/chunk-GOFTC2HV.cjs.map +1 -0
- package/dist/{chunk-6SVI63CC.js → chunk-HGZPH7S5.js} +2 -2
- package/dist/{chunk-7LHDZK7M.js → chunk-IHNUMRMU.js} +2 -2
- package/dist/{chunk-YLVVZVK6.js → chunk-IK2WRYVA.js} +4 -4
- package/dist/{chunk-XDOZN745.cjs → chunk-JIW5JI3E.cjs} +3 -3
- package/dist/{chunk-XDOZN745.cjs.map → chunk-JIW5JI3E.cjs.map} +1 -1
- package/dist/{chunk-CLEAYFOX.js → chunk-JKAX5K7C.js} +5 -5
- package/dist/{chunk-4BUHSV4D.js → chunk-JO3NPEK6.js} +2 -2
- package/dist/{chunk-ARTN2P3B.js → chunk-JOFGJYIT.js} +2 -2
- package/dist/{chunk-ZI3GAXFN.js → chunk-OT4RV6WY.js} +6 -6
- package/dist/{chunk-QUT2X2LX.cjs → chunk-RJXI25TJ.cjs} +26 -26
- package/dist/{chunk-QUT2X2LX.cjs.map → chunk-RJXI25TJ.cjs.map} +1 -1
- package/dist/{chunk-EXDW3LB5.cjs → chunk-SG25J6LV.cjs} +87 -87
- package/dist/{chunk-EXDW3LB5.cjs.map → chunk-SG25J6LV.cjs.map} +1 -1
- package/dist/{chunk-DDWLGE6D.js → chunk-V5WLIFKN.js} +325 -360
- package/dist/chunk-V5WLIFKN.js.map +1 -0
- package/dist/{chunk-OOYBU46B.cjs → chunk-VVXUUBML.cjs} +26 -26
- package/dist/{chunk-OOYBU46B.cjs.map → chunk-VVXUUBML.cjs.map} +1 -1
- package/dist/{chunk-Q4XWNC33.js → chunk-VZX3SSFL.js} +2 -2
- package/dist/{chunk-WHKVCEAB.cjs → chunk-WVZHCDB5.cjs} +11 -11
- package/dist/{chunk-WHKVCEAB.cjs.map → chunk-WVZHCDB5.cjs.map} +1 -1
- package/dist/{chunk-UMUNWABO.js → chunk-XG7FLAKQ.js} +2 -2
- package/dist/{chunk-EYUANCRA.js → chunk-ZIQMVAGA.js} +2 -2
- package/dist/components/case-studies/index.cjs +8 -8
- package/dist/components/case-studies/index.js +2 -2
- package/dist/components/chat/entity-cards/dispatch.d.ts.map +1 -1
- package/dist/components/chat/hooks/use-chat-card-item.d.ts +3 -0
- package/dist/components/chat/hooks/use-chat-card-item.d.ts.map +1 -1
- package/dist/components/chat/index.cjs +2 -2
- package/dist/components/chat/index.js +1 -1
- package/dist/components/chat/remark-strip-citations.d.ts.map +1 -1
- package/dist/components/chat/stream/chat-stream-reducer.d.ts.map +1 -1
- package/dist/components/chat/stream/message-mutations.d.ts +42 -5
- package/dist/components/chat/stream/message-mutations.d.ts.map +1 -1
- package/dist/components/chat/utils/message-segment-accumulator.d.ts.map +1 -1
- package/dist/components/contact/index.cjs +3 -3
- package/dist/components/contact/index.js +2 -2
- package/dist/components/docs/index.cjs +5 -5
- package/dist/components/docs/index.js +4 -4
- package/dist/components/embeds/index.cjs +3 -3
- package/dist/components/embeds/index.js +2 -2
- package/dist/components/faq/index.cjs +3 -3
- package/dist/components/faq/index.js +2 -2
- package/dist/components/features/index.cjs +2 -2
- package/dist/components/features/index.js +1 -1
- package/dist/components/help-center-pages/index.cjs +22 -22
- package/dist/components/help-center-pages/index.js +13 -13
- package/dist/components/index.cjs +132 -132
- package/dist/components/index.js +11 -11
- package/dist/components/meeting-scheduler/index.cjs +34 -34
- package/dist/components/meeting-scheduler/index.js +3 -3
- package/dist/components/navigation/index.cjs +2 -2
- package/dist/components/navigation/index.js +1 -1
- package/dist/components/onboarding-guides/index.cjs +5 -5
- package/dist/components/onboarding-guides/index.js +4 -4
- package/dist/components/related-content/index.cjs +3 -3
- package/dist/components/related-content/index.js +2 -2
- package/dist/components/tickets/index.cjs +6 -6
- package/dist/components/tickets/index.js +5 -5
- package/dist/components/ui/index.cjs +2 -2
- package/dist/components/ui/index.js +1 -1
- package/dist/index.cjs +2 -2
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/src/components/chat/entity-cards/dispatch.tsx +92 -99
- package/src/components/chat/hooks/use-chat-card-item.ts +9 -0
- package/src/components/chat/hooks/use-sse-chat-adapter.ts +15 -15
- package/src/components/chat/remark-strip-citations.ts +7 -5
- package/src/components/chat/stream/__tests__/chat-stream-reducer-approval-batch.test.ts +18 -0
- package/src/components/chat/stream/chat-stream-reducer.ts +12 -3
- package/src/components/chat/stream/message-mutations.ts +153 -96
- package/src/components/chat/utils/message-segment-accumulator.ts +19 -27
- package/dist/chunk-DDWLGE6D.js.map +0 -1
- package/dist/chunk-NZR2U5VK.cjs.map +0 -1
- /package/dist/{chunk-XHGL7XHL.js.map → chunk-4NFDSMB5.js.map} +0 -0
- /package/dist/{chunk-7UYW6C4P.js.map → chunk-6LMTBNLN.js.map} +0 -0
- /package/dist/{chunk-IYIO2OVS.js.map → chunk-FI35LJ3L.js.map} +0 -0
- /package/dist/{chunk-6SVI63CC.js.map → chunk-HGZPH7S5.js.map} +0 -0
- /package/dist/{chunk-7LHDZK7M.js.map → chunk-IHNUMRMU.js.map} +0 -0
- /package/dist/{chunk-YLVVZVK6.js.map → chunk-IK2WRYVA.js.map} +0 -0
- /package/dist/{chunk-CLEAYFOX.js.map → chunk-JKAX5K7C.js.map} +0 -0
- /package/dist/{chunk-4BUHSV4D.js.map → chunk-JO3NPEK6.js.map} +0 -0
- /package/dist/{chunk-ARTN2P3B.js.map → chunk-JOFGJYIT.js.map} +0 -0
- /package/dist/{chunk-ZI3GAXFN.js.map → chunk-OT4RV6WY.js.map} +0 -0
- /package/dist/{chunk-Q4XWNC33.js.map → chunk-VZX3SSFL.js.map} +0 -0
- /package/dist/{chunk-UMUNWABO.js.map → chunk-XG7FLAKQ.js.map} +0 -0
- /package/dist/{chunk-EYUANCRA.js.map → chunk-ZIQMVAGA.js.map} +0 -0
|
@@ -134,40 +134,30 @@ export function appendToTrailingAssistant(
|
|
|
134
134
|
return [...prev.slice(0, -1), { ...last, segments: merged }]
|
|
135
135
|
}
|
|
136
136
|
|
|
137
|
+
// =============================================================================
|
|
138
|
+
// Segment-projection kernel — shared scaffolding + single-rule predicates
|
|
139
|
+
// =============================================================================
|
|
140
|
+
|
|
137
141
|
/**
|
|
138
|
-
*
|
|
139
|
-
*
|
|
140
|
-
*
|
|
141
|
-
*
|
|
142
|
+
* Map every assistant message's segments through `fn`, preserving the
|
|
143
|
+
* REFERENTIAL-STABILITY CONTRACT in ONE place: `fn` returns the SAME
|
|
144
|
+
* segment reference to signal "unchanged"; untouched messages (and the
|
|
145
|
+
* whole array on a global no-op) keep their prior references. Every
|
|
146
|
+
* segment projection below rides this scaffold instead of re-implementing
|
|
147
|
+
* the changed-flag dance.
|
|
142
148
|
*/
|
|
143
|
-
|
|
149
|
+
function mapSegments(
|
|
144
150
|
prev: UnifiedChatMessage[],
|
|
145
|
-
|
|
151
|
+
fn: (s: MessageSegment) => MessageSegment,
|
|
146
152
|
): UnifiedChatMessage[] {
|
|
147
153
|
let changed = false
|
|
148
154
|
const next = prev.map((m) => {
|
|
149
155
|
if (m.role !== 'assistant' || !m.segments) return m
|
|
150
156
|
let msgChanged = false
|
|
151
157
|
const segs = m.segments.map((s) => {
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
) {
|
|
156
|
-
return s
|
|
157
|
-
}
|
|
158
|
-
const existing = s.data.executions?.[rowRequestId]
|
|
159
|
-
if (existing?.status === 'done') return s
|
|
160
|
-
msgChanged = true
|
|
161
|
-
return {
|
|
162
|
-
...s,
|
|
163
|
-
data: {
|
|
164
|
-
...s.data,
|
|
165
|
-
executions: {
|
|
166
|
-
...(s.data.executions ?? {}),
|
|
167
|
-
[rowRequestId]: { status: 'done' as const, success: false },
|
|
168
|
-
},
|
|
169
|
-
},
|
|
170
|
-
}
|
|
158
|
+
const out = fn(s)
|
|
159
|
+
if (out !== s) msgChanged = true
|
|
160
|
+
return out
|
|
171
161
|
})
|
|
172
162
|
if (!msgChanged) return m
|
|
173
163
|
changed = true
|
|
@@ -176,6 +166,130 @@ export function projectBatchRowFailureToMessages(
|
|
|
176
166
|
return changed ? next : prev
|
|
177
167
|
}
|
|
178
168
|
|
|
169
|
+
/**
|
|
170
|
+
* THE approval-resolution rule for a single segment — one predicate for
|
|
171
|
+
* both containers (the message-array projections here AND the flat
|
|
172
|
+
* segment list in `MessageSegmentAccumulator.updateApprovalStatus`), so
|
|
173
|
+
* the two paths can never drift:
|
|
174
|
+
* - `approval_request` matched on `data.requestId` → status flip;
|
|
175
|
+
* - `approval_batch` anchor (`data.approvalRequestId`, the server's
|
|
176
|
+
* `batch:<first proposalId>` — never a row id) → status flip;
|
|
177
|
+
* - `approval_batch` ROW (`toolCalls[].toolExecutionRequestId`) →
|
|
178
|
+
* tick that row's execution (check on approved, cross otherwise)
|
|
179
|
+
* without touching the batch status.
|
|
180
|
+
* Returns the SAME reference when nothing matched or changed.
|
|
181
|
+
*/
|
|
182
|
+
export function applyApprovalStatusToSegment(
|
|
183
|
+
s: MessageSegment,
|
|
184
|
+
requestId: string,
|
|
185
|
+
status: ChatApprovalStatus,
|
|
186
|
+
resolvedByName?: string | null,
|
|
187
|
+
): MessageSegment {
|
|
188
|
+
if (s.type === 'approval_request' && s.data.requestId === requestId && s.status !== status) {
|
|
189
|
+
return { ...s, status }
|
|
190
|
+
}
|
|
191
|
+
if (s.type !== 'approval_batch') return s
|
|
192
|
+
const isAnchor = s.data.approvalRequestId === requestId
|
|
193
|
+
const hasRow = s.data.toolCalls?.some((c) => c.toolExecutionRequestId === requestId)
|
|
194
|
+
if (!isAnchor && !hasRow) return s
|
|
195
|
+
const nextResolvedBy = resolvedByName ?? s.resolvedByName
|
|
196
|
+
const nextExecutions = hasRow
|
|
197
|
+
? {
|
|
198
|
+
...(s.data.executions ?? {}),
|
|
199
|
+
[requestId]: { status: 'done' as const, success: status === 'approved' },
|
|
200
|
+
}
|
|
201
|
+
: s.data.executions
|
|
202
|
+
const nextStatus = isAnchor ? status : s.status
|
|
203
|
+
if (
|
|
204
|
+
s.status === nextStatus &&
|
|
205
|
+
nextResolvedBy === s.resolvedByName &&
|
|
206
|
+
nextExecutions === s.data.executions
|
|
207
|
+
) {
|
|
208
|
+
return s
|
|
209
|
+
}
|
|
210
|
+
return {
|
|
211
|
+
...s,
|
|
212
|
+
status: nextStatus,
|
|
213
|
+
resolvedByName: nextResolvedBy,
|
|
214
|
+
data: {
|
|
215
|
+
...s.data,
|
|
216
|
+
...(nextExecutions ? { executions: nextExecutions } : {}),
|
|
217
|
+
},
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Next state for a batch row's execution slot from a tool-execution
|
|
223
|
+
* chunk — folds the never-downgrade guard (redelivered EXECUTING after
|
|
224
|
+
* EXECUTED landed) and the EXECUTED/EXECUTING ternary into ONE rule
|
|
225
|
+
* shared by the message projection and the accumulator. Returns null
|
|
226
|
+
* for the no-op case (caller keeps prior references).
|
|
227
|
+
*/
|
|
228
|
+
export function nextBatchExecution(
|
|
229
|
+
prevExec: ApprovalBatchExecutionState | undefined,
|
|
230
|
+
toolData: ToolExecutionData,
|
|
231
|
+
): ApprovalBatchExecutionState | null {
|
|
232
|
+
if (toolData.type === 'EXECUTING_TOOL' && prevExec?.status === 'done') return null
|
|
233
|
+
return toolData.type === 'EXECUTED_TOOL'
|
|
234
|
+
? { status: 'done', result: toolData.result, success: toolData.success }
|
|
235
|
+
: { status: 'executing', result: prevExec?.result, success: prevExec?.success }
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/** The executions-map triple-spread — one home for the write shape. */
|
|
239
|
+
export function withBatchExecution<S extends { data: { executions?: Record<string, ApprovalBatchExecutionState> } }>(
|
|
240
|
+
seg: S,
|
|
241
|
+
execId: string,
|
|
242
|
+
state: ApprovalBatchExecutionState,
|
|
243
|
+
): S {
|
|
244
|
+
return {
|
|
245
|
+
...seg,
|
|
246
|
+
data: { ...seg.data, executions: { ...(seg.data.executions ?? {}), [execId]: state } },
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* Flip ONLY an approval_batch segment's status — no execution writes.
|
|
252
|
+
* The click-time optimistic flip uses this instead of
|
|
253
|
+
* `projectApprovalResolutionToMessages` as defense-in-depth: even if a
|
|
254
|
+
* (misbehaving) server emitted a batchId colliding with a row's
|
|
255
|
+
* proposal id, the flip could not pre-tick that row's execution to a
|
|
256
|
+
* false success before its confirm actually ran.
|
|
257
|
+
*/
|
|
258
|
+
export function projectBatchStatusToMessages(
|
|
259
|
+
prev: UnifiedChatMessage[],
|
|
260
|
+
anchorId: string,
|
|
261
|
+
status: ChatApprovalStatus,
|
|
262
|
+
): UnifiedChatMessage[] {
|
|
263
|
+
return mapSegments(prev, (s) => {
|
|
264
|
+
if (s.type !== 'approval_batch') return s
|
|
265
|
+
if (s.data.approvalRequestId !== anchorId || s.status === status) return s
|
|
266
|
+
return { ...s, status }
|
|
267
|
+
})
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* Mark ONE batch row's confirm as FAILED (expired proposal, network
|
|
272
|
+
* error): tick its execution icon to the failure cross so the row's
|
|
273
|
+
* loader doesn't spin forever. Batch status is untouched — other rows
|
|
274
|
+
* may still be resolving.
|
|
275
|
+
*/
|
|
276
|
+
export function projectBatchRowFailureToMessages(
|
|
277
|
+
prev: UnifiedChatMessage[],
|
|
278
|
+
rowRequestId: string,
|
|
279
|
+
): UnifiedChatMessage[] {
|
|
280
|
+
return mapSegments(prev, (s) => {
|
|
281
|
+
if (
|
|
282
|
+
s.type !== 'approval_batch' ||
|
|
283
|
+
!s.data.toolCalls?.some((c) => c.toolExecutionRequestId === rowRequestId)
|
|
284
|
+
) {
|
|
285
|
+
return s
|
|
286
|
+
}
|
|
287
|
+
const existing = s.data.executions?.[rowRequestId]
|
|
288
|
+
if (existing?.status === 'done') return s
|
|
289
|
+
return withBatchExecution(s, rowRequestId, { status: 'done', success: false })
|
|
290
|
+
})
|
|
291
|
+
}
|
|
292
|
+
|
|
179
293
|
/**
|
|
180
294
|
* Upsert a standalone context-compaction segment into the trailing assistant
|
|
181
295
|
* bubble. Compaction emissions arrive as the accumulator's CUMULATIVE array —
|
|
@@ -284,23 +398,16 @@ export function mergeToolExecutionIfPresent(
|
|
|
284
398
|
seg.data.toolCalls.some((c) => c.toolExecutionRequestId === execId)
|
|
285
399
|
) {
|
|
286
400
|
const prevExec: ApprovalBatchExecutionState | undefined = seg.data.executions?.[execId]
|
|
287
|
-
//
|
|
288
|
-
// redelivery of
|
|
289
|
-
//
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
const nextExec: ApprovalBatchExecutionState =
|
|
294
|
-
toolData.type === 'EXECUTED_TOOL'
|
|
295
|
-
? { status: 'done', result: toolData.result, success: toolData.success }
|
|
296
|
-
: { status: 'executing', result: prevExec?.result, success: prevExec?.success }
|
|
401
|
+
// Shared rule (`nextBatchExecution`): folds the never-downgrade
|
|
402
|
+
// guard (JetStream redelivery of EXECUTING after EXECUTED
|
|
403
|
+
// landed — null = matched-with-no-change, caller doesn't append
|
|
404
|
+
// either) and the EXECUTED/EXECUTING ternary.
|
|
405
|
+
const nextExec = nextBatchExecution(prevExec, toolData)
|
|
406
|
+
if (nextExec === null) return prev
|
|
297
407
|
// Value-level no-op (replayed duplicate) → prior references.
|
|
298
408
|
if (sameExecutionState(prevExec, nextExec)) return prev
|
|
299
409
|
const nextSegments = [...message.segments]
|
|
300
|
-
nextSegments[j] =
|
|
301
|
-
...seg,
|
|
302
|
-
data: { ...seg.data, executions: { ...(seg.data.executions ?? {}), [execId]: nextExec } },
|
|
303
|
-
}
|
|
410
|
+
nextSegments[j] = withBatchExecution(seg, execId, nextExec)
|
|
304
411
|
const next = [...prev]
|
|
305
412
|
next[i] = { ...message, segments: nextSegments }
|
|
306
413
|
return next
|
|
@@ -346,10 +453,10 @@ export function mergeToolExecutionIfPresent(
|
|
|
346
453
|
}
|
|
347
454
|
|
|
348
455
|
/**
|
|
349
|
-
*
|
|
350
|
-
* `
|
|
351
|
-
*
|
|
352
|
-
*
|
|
456
|
+
* Project an approval resolution onto the thread — the ONE rule lives in
|
|
457
|
+
* `applyApprovalStatusToSegment` (shared with the accumulator's flat
|
|
458
|
+
* segment list); this is just its message-array projection via
|
|
459
|
+
* `mapSegments`.
|
|
353
460
|
*/
|
|
354
461
|
export function projectApprovalResolutionToMessages(
|
|
355
462
|
prev: UnifiedChatMessage[],
|
|
@@ -357,57 +464,7 @@ export function projectApprovalResolutionToMessages(
|
|
|
357
464
|
status: ChatApprovalStatus,
|
|
358
465
|
resolvedByName?: string | null,
|
|
359
466
|
): UnifiedChatMessage[] {
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
let msgChanged = false
|
|
364
|
-
const segs = m.segments.map((s) => {
|
|
365
|
-
if (s.type === 'approval_request' && s.data.requestId === requestId && s.status !== status) {
|
|
366
|
-
msgChanged = true
|
|
367
|
-
return { ...s, status }
|
|
368
|
-
}
|
|
369
|
-
if (s.type === 'approval_batch') {
|
|
370
|
-
const isAnchor = s.data.approvalRequestId === requestId
|
|
371
|
-
// Wire-native chat batches: each ROW is its own proposal
|
|
372
|
-
// (`toolExecutionRequestId` = proposalId) resolving through its
|
|
373
|
-
// own per-proposal confirm — a resolution for a row ticks that
|
|
374
|
-
// row's execution icon (check on approved, cross on rejected/
|
|
375
|
-
// failed) without touching the batch status (flipped
|
|
376
|
-
// optimistically at click time). The anchor row ALSO flips the
|
|
377
|
-
// batch status (anchor id = first proposal id).
|
|
378
|
-
const hasRow = s.data.toolCalls?.some((c) => c.toolExecutionRequestId === requestId)
|
|
379
|
-
if (!isAnchor && !hasRow) return s
|
|
380
|
-
const nextResolvedBy = resolvedByName ?? s.resolvedByName
|
|
381
|
-
const nextExecutions = hasRow
|
|
382
|
-
? {
|
|
383
|
-
...(s.data.executions ?? {}),
|
|
384
|
-
[requestId]: { status: 'done' as const, success: status === 'approved' },
|
|
385
|
-
}
|
|
386
|
-
: s.data.executions
|
|
387
|
-
const nextStatus = isAnchor ? status : s.status
|
|
388
|
-
if (
|
|
389
|
-
s.status === nextStatus &&
|
|
390
|
-
nextResolvedBy === s.resolvedByName &&
|
|
391
|
-
nextExecutions === s.data.executions
|
|
392
|
-
) {
|
|
393
|
-
return s
|
|
394
|
-
}
|
|
395
|
-
msgChanged = true
|
|
396
|
-
return {
|
|
397
|
-
...s,
|
|
398
|
-
status: nextStatus,
|
|
399
|
-
resolvedByName: nextResolvedBy,
|
|
400
|
-
data: {
|
|
401
|
-
...s.data,
|
|
402
|
-
...(nextExecutions ? { executions: nextExecutions } : {}),
|
|
403
|
-
},
|
|
404
|
-
}
|
|
405
|
-
}
|
|
406
|
-
return s
|
|
407
|
-
})
|
|
408
|
-
if (!msgChanged) return m
|
|
409
|
-
changed = true
|
|
410
|
-
return { ...m, segments: segs }
|
|
411
|
-
})
|
|
412
|
-
return changed ? next : prev
|
|
467
|
+
return mapSegments(prev, (s) =>
|
|
468
|
+
applyApprovalStatusToSegment(s, requestId, status, resolvedByName),
|
|
469
|
+
)
|
|
413
470
|
}
|
|
@@ -23,6 +23,11 @@ import type {
|
|
|
23
23
|
ChatApprovalStatus,
|
|
24
24
|
ExecutingToolState,
|
|
25
25
|
} from '../types'
|
|
26
|
+
import {
|
|
27
|
+
applyApprovalStatusToSegment,
|
|
28
|
+
nextBatchExecution,
|
|
29
|
+
withBatchExecution,
|
|
30
|
+
} from '../stream/message-mutations'
|
|
26
31
|
|
|
27
32
|
export interface AccumulatorCallbacks {
|
|
28
33
|
/** See `ApprovalResolutionHandler` (message.types) — the SSOT for the
|
|
@@ -247,25 +252,13 @@ export class MessageSegmentAccumulator {
|
|
|
247
252
|
if (!hasCall) return seg
|
|
248
253
|
|
|
249
254
|
const prev: ApprovalBatchExecutionState | undefined = seg.data.executions?.[execId]
|
|
250
|
-
//
|
|
251
|
-
//
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
return seg
|
|
255
|
-
}
|
|
256
|
-
const next: ApprovalBatchExecutionState =
|
|
257
|
-
toolData.type === 'EXECUTED_TOOL'
|
|
258
|
-
? { status: 'done', result: toolData.result, success: toolData.success }
|
|
259
|
-
: { status: 'executing', result: prev?.result, success: prev?.success }
|
|
260
|
-
|
|
255
|
+
// Shared rule (`nextBatchExecution` from message-mutations): the
|
|
256
|
+
// never-downgrade guard + EXECUTED/EXECUTING ternary — one
|
|
257
|
+
// implementation with the message-array projection.
|
|
258
|
+
const next = nextBatchExecution(prev, toolData)
|
|
261
259
|
matched = true
|
|
262
|
-
return
|
|
263
|
-
|
|
264
|
-
data: {
|
|
265
|
-
...seg.data,
|
|
266
|
-
executions: { ...(seg.data.executions ?? {}), [execId]: next },
|
|
267
|
-
},
|
|
268
|
-
}
|
|
260
|
+
if (next === null) return seg
|
|
261
|
+
return withBatchExecution(seg, execId, next)
|
|
269
262
|
})
|
|
270
263
|
return matched
|
|
271
264
|
}
|
|
@@ -432,15 +425,14 @@ export class MessageSegmentAccumulator {
|
|
|
432
425
|
* resolved card shows "by {name}"; omit it to leave any existing value untouched.
|
|
433
426
|
*/
|
|
434
427
|
updateApprovalStatus(requestId: string, status: ChatApprovalStatus, resolvedByName?: string | null): MessageSegment[] {
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
})
|
|
428
|
+
// ONE rule, two containers: `applyApprovalStatusToSegment` is the
|
|
429
|
+
// same predicate the message-array projection uses
|
|
430
|
+
// (`projectApprovalResolutionToMessages`) — anchor status flip AND
|
|
431
|
+
// batch-row execution tick, so this flat-list path can never drift
|
|
432
|
+
// from the projection again (it previously missed the row case).
|
|
433
|
+
this.segments = this.segments.map((segment) =>
|
|
434
|
+
applyApprovalStatusToSegment(segment, requestId, status, resolvedByName),
|
|
435
|
+
)
|
|
444
436
|
return this.getSegments()
|
|
445
437
|
}
|
|
446
438
|
|