@flamingo-stack/openframe-frontend-core 0.0.511 → 0.0.512-1640.4209.1

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.
Files changed (98) hide show
  1. package/dist/{chunk-IXCBVJRU.cjs → chunk-4GAVZKHG.cjs} +9 -9
  2. package/dist/{chunk-IXCBVJRU.cjs.map → chunk-4GAVZKHG.cjs.map} +1 -1
  3. package/dist/{chunk-RXFBW2EJ.cjs → chunk-534P3NJ3.cjs} +7 -7
  4. package/dist/{chunk-RXFBW2EJ.cjs.map → chunk-534P3NJ3.cjs.map} +1 -1
  5. package/dist/{chunk-X7AJ3J42.js → chunk-6ETOEANJ.js} +362 -215
  6. package/dist/chunk-6ETOEANJ.js.map +1 -0
  7. package/dist/{chunk-TNZOUHHN.js → chunk-6QPWXKUM.js} +4 -4
  8. package/dist/{chunk-ZSIEM7CL.js → chunk-76ZIKD4J.js} +2 -2
  9. package/dist/{chunk-NNWSGOUP.js → chunk-A7MD5U35.js} +2 -2
  10. package/dist/{chunk-LEWQRAZ4.cjs → chunk-CY7M2OSZ.cjs} +31 -31
  11. package/dist/{chunk-LEWQRAZ4.cjs.map → chunk-CY7M2OSZ.cjs.map} +1 -1
  12. package/dist/{chunk-OMN7A6RP.cjs → chunk-EEGK4TEH.cjs} +26 -26
  13. package/dist/{chunk-OMN7A6RP.cjs.map → chunk-EEGK4TEH.cjs.map} +1 -1
  14. package/dist/{chunk-IUZMWMVX.cjs → chunk-EJIOXYJ2.cjs} +61 -61
  15. package/dist/{chunk-IUZMWMVX.cjs.map → chunk-EJIOXYJ2.cjs.map} +1 -1
  16. package/dist/{chunk-PRMR6WKC.js → chunk-EQRNQWXA.js} +2 -2
  17. package/dist/{chunk-RU3SCECX.js → chunk-FBW47BZ7.js} +2 -2
  18. package/dist/{chunk-PXQQFRUD.cjs → chunk-FOTOJSBJ.cjs} +5 -5
  19. package/dist/{chunk-PXQQFRUD.cjs.map → chunk-FOTOJSBJ.cjs.map} +1 -1
  20. package/dist/{chunk-C4EUXZSA.cjs → chunk-GCPMAP2K.cjs} +3 -3
  21. package/dist/{chunk-C4EUXZSA.cjs.map → chunk-GCPMAP2K.cjs.map} +1 -1
  22. package/dist/{chunk-YGS7BLSN.js → chunk-HJSWBSMO.js} +2 -2
  23. package/dist/{chunk-LVAJK2U2.js → chunk-JIRDHSAM.js} +2 -2
  24. package/dist/{chunk-7YV67K3I.cjs → chunk-LJXROUP3.cjs} +87 -87
  25. package/dist/{chunk-7YV67K3I.cjs.map → chunk-LJXROUP3.cjs.map} +1 -1
  26. package/dist/{chunk-TYVHM67X.js → chunk-LTABGSW6.js} +5 -5
  27. package/dist/{chunk-NKA3O4L6.js → chunk-MO647IKL.js} +2 -2
  28. package/dist/{chunk-ZBQV7WH7.js → chunk-MPAISYAF.js} +2 -2
  29. package/dist/{chunk-U4WPFX5K.cjs → chunk-ONAGMSOD.cjs} +26 -26
  30. package/dist/{chunk-U4WPFX5K.cjs.map → chunk-ONAGMSOD.cjs.map} +1 -1
  31. package/dist/{chunk-TR7AMHGB.js → chunk-PE4CBJUE.js} +6 -6
  32. package/dist/{chunk-A5YZUV6B.cjs → chunk-PSYNV2VB.cjs} +11 -11
  33. package/dist/{chunk-A5YZUV6B.cjs.map → chunk-PSYNV2VB.cjs.map} +1 -1
  34. package/dist/{chunk-J4VB4EHW.js → chunk-U5F5PAWV.js} +4 -4
  35. package/dist/{chunk-GHXTF35X.cjs → chunk-VFFEZ66K.cjs} +353 -206
  36. package/dist/chunk-VFFEZ66K.cjs.map +1 -0
  37. package/dist/{chunk-JXMNLH7F.js → chunk-VQQBYAKR.js} +2 -2
  38. package/dist/{chunk-DPLXHG7H.cjs → chunk-XEBFFFDB.cjs} +37 -37
  39. package/dist/{chunk-DPLXHG7H.cjs.map → chunk-XEBFFFDB.cjs.map} +1 -1
  40. package/dist/{chunk-2CE725NN.cjs → chunk-Y3VWQEFO.cjs} +13 -13
  41. package/dist/{chunk-2CE725NN.cjs.map → chunk-Y3VWQEFO.cjs.map} +1 -1
  42. package/dist/{chunk-4SG55XVA.cjs → chunk-ZMC4WIBZ.cjs} +4 -4
  43. package/dist/{chunk-4SG55XVA.cjs.map → chunk-ZMC4WIBZ.cjs.map} +1 -1
  44. package/dist/components/case-studies/index.cjs +8 -8
  45. package/dist/components/case-studies/index.js +2 -2
  46. package/dist/components/chat/entity-cards/deleted-data-card.d.ts +27 -0
  47. package/dist/components/chat/entity-cards/deleted-data-card.d.ts.map +1 -0
  48. package/dist/components/chat/entity-cards/dispatch.d.ts.map +1 -1
  49. package/dist/components/chat/hooks/use-chat.d.ts.map +1 -1
  50. package/dist/components/chat/index.cjs +2 -2
  51. package/dist/components/chat/index.js +1 -1
  52. package/dist/components/contact/index.cjs +3 -3
  53. package/dist/components/contact/index.js +2 -2
  54. package/dist/components/docs/index.cjs +5 -5
  55. package/dist/components/docs/index.js +4 -4
  56. package/dist/components/embeds/index.cjs +3 -3
  57. package/dist/components/embeds/index.js +2 -2
  58. package/dist/components/faq/index.cjs +3 -3
  59. package/dist/components/faq/index.js +2 -2
  60. package/dist/components/features/index.cjs +2 -2
  61. package/dist/components/features/index.js +1 -1
  62. package/dist/components/help-center-pages/index.cjs +22 -22
  63. package/dist/components/help-center-pages/index.js +13 -13
  64. package/dist/components/index.cjs +132 -132
  65. package/dist/components/index.js +11 -11
  66. package/dist/components/meeting-scheduler/index.cjs +34 -34
  67. package/dist/components/meeting-scheduler/index.js +3 -3
  68. package/dist/components/navigation/index.cjs +2 -2
  69. package/dist/components/navigation/index.js +1 -1
  70. package/dist/components/onboarding-guides/index.cjs +5 -5
  71. package/dist/components/onboarding-guides/index.js +4 -4
  72. package/dist/components/related-content/index.cjs +3 -3
  73. package/dist/components/related-content/index.js +2 -2
  74. package/dist/components/tickets/index.cjs +6 -6
  75. package/dist/components/tickets/index.js +5 -5
  76. package/dist/components/ui/index.cjs +2 -2
  77. package/dist/components/ui/index.js +1 -1
  78. package/dist/index.cjs +2 -2
  79. package/dist/index.js +1 -1
  80. package/package.json +1 -1
  81. package/src/components/chat/entity-cards/deleted-data-card.tsx +61 -0
  82. package/src/components/chat/entity-cards/dispatch.tsx +17 -0
  83. package/src/components/chat/hooks/use-chat.ts +174 -15
  84. package/dist/chunk-GHXTF35X.cjs.map +0 -1
  85. package/dist/chunk-X7AJ3J42.js.map +0 -1
  86. /package/dist/{chunk-TNZOUHHN.js.map → chunk-6QPWXKUM.js.map} +0 -0
  87. /package/dist/{chunk-ZSIEM7CL.js.map → chunk-76ZIKD4J.js.map} +0 -0
  88. /package/dist/{chunk-NNWSGOUP.js.map → chunk-A7MD5U35.js.map} +0 -0
  89. /package/dist/{chunk-PRMR6WKC.js.map → chunk-EQRNQWXA.js.map} +0 -0
  90. /package/dist/{chunk-RU3SCECX.js.map → chunk-FBW47BZ7.js.map} +0 -0
  91. /package/dist/{chunk-YGS7BLSN.js.map → chunk-HJSWBSMO.js.map} +0 -0
  92. /package/dist/{chunk-LVAJK2U2.js.map → chunk-JIRDHSAM.js.map} +0 -0
  93. /package/dist/{chunk-TYVHM67X.js.map → chunk-LTABGSW6.js.map} +0 -0
  94. /package/dist/{chunk-NKA3O4L6.js.map → chunk-MO647IKL.js.map} +0 -0
  95. /package/dist/{chunk-ZBQV7WH7.js.map → chunk-MPAISYAF.js.map} +0 -0
  96. /package/dist/{chunk-TR7AMHGB.js.map → chunk-PE4CBJUE.js.map} +0 -0
  97. /package/dist/{chunk-J4VB4EHW.js.map → chunk-U5F5PAWV.js.map} +0 -0
  98. /package/dist/{chunk-JXMNLH7F.js.map → chunk-VQQBYAKR.js.map} +0 -0
@@ -11,8 +11,10 @@
11
11
  import { useState, useCallback, useEffect, useRef } from 'react'
12
12
  import { useSSE, type StreamFn, type StreamFnExtraOptions } from './use-sse'
13
13
  import type {
14
+ ApprovalBatchSegment,
14
15
  Message,
15
16
  MessageSegment,
17
+ PendingToolCallData,
16
18
  ToolExecutionData,
17
19
  } from '../types/message.types'
18
20
  import { buildChatRefKey } from '../types/chat.types'
@@ -256,12 +258,136 @@ export function useChat({
256
258
  { hidden: true },
257
259
  )
258
260
  }
259
- currentAssistantSegmentsRef.current.push({
260
- ...seg,
261
- onApprove: wrappedApprove,
262
- onReject: wrappedReject,
263
- })
264
- updateLastAssistantMessage([...currentAssistantSegmentsRef.current])
261
+ // ── BATCH COALESCING ────────────────────────────────────
262
+ // One assistant turn can propose N tool calls (§2a batch
263
+ // operations — "delete both", "close all three"). N stacked
264
+ // cards force N Approve clicks; consecutive proposals in the
265
+ // SAME turn coalesce into ONE `approval_batch` (the bulk-
266
+ // approval component) whose single Approve/Reject resolves
267
+ // every proposal sequentially through the same per-proposal
268
+ // confirm endpoint. `decision_resolved` frames then tick the
269
+ // batch's per-row execution icons (see the handler below).
270
+ const toToolCall = (d: Record<string, any>): PendingToolCallData => {
271
+ const fields = Array.isArray(d?.fields)
272
+ ? (d.fields as Array<{ label: string; value: string }>)
273
+ : []
274
+ const detailField =
275
+ fields.find((f) => /^(title|subject|task|name)$/i.test(f.label)) ?? fields[0]
276
+ const base = typeof d?.command === 'string' && d.command ? d.command : 'Tool call'
277
+ return {
278
+ toolExecutionRequestId: String(d?.requestId ?? ''),
279
+ toolName: String(d?.approvalType ?? 'tool'),
280
+ toolTitle: detailField ? `${base} — ${detailField.value}` : base,
281
+ requiresApproval: true,
282
+ // Field rows become the expandable args block so the
283
+ // per-proposal detail from the single-card view stays
284
+ // reachable inside the batch.
285
+ toolCallArguments:
286
+ fields.length > 0
287
+ ? Object.fromEntries(fields.map((f) => [f.label, f.value]))
288
+ : null,
289
+ }
290
+ }
291
+ // Anchor a batch by its FIRST proposal id (stable across row
292
+ // appends); handlers derive the id list from the rows so no
293
+ // side-channel state is needed.
294
+ const makeBatchHandlers = (anchorId: string) => {
295
+ const findBatch = (
296
+ segments: MessageSegment[],
297
+ ): ApprovalBatchSegment | undefined =>
298
+ segments.find(
299
+ (s): s is ApprovalBatchSegment =>
300
+ s.type === 'approval_batch' && s.data.approvalRequestId === anchorId,
301
+ )
302
+ const setBatchStatus = (status: 'approved' | 'rejected') => {
303
+ setMessages((prev) => {
304
+ for (let i = prev.length - 1; i >= 0; i--) {
305
+ const m = prev[i]
306
+ if (m.role !== 'assistant' || !Array.isArray(m.content)) continue
307
+ const segments = m.content as MessageSegment[]
308
+ if (!findBatch(segments)) continue
309
+ const next = [...prev]
310
+ next[i] = {
311
+ ...m,
312
+ content: segments.map((s) =>
313
+ s.type === 'approval_batch' && s.data.approvalRequestId === anchorId
314
+ ? { ...s, status }
315
+ : s,
316
+ ),
317
+ }
318
+ return next
319
+ }
320
+ return prev
321
+ })
322
+ }
323
+ const act = (action: 'approve' | 'reject') => async () => {
324
+ // Read the CURRENT row set from state (rows may have been
325
+ // appended after this handler was created).
326
+ let ids: string[] = []
327
+ setMessages((prev) => {
328
+ for (let i = prev.length - 1; i >= 0; i--) {
329
+ const m = prev[i]
330
+ if (m.role !== 'assistant' || !Array.isArray(m.content)) continue
331
+ const batch = findBatch(m.content as MessageSegment[])
332
+ if (batch) {
333
+ ids = batch.data.toolCalls.map((c) => c.toolExecutionRequestId)
334
+ break
335
+ }
336
+ }
337
+ return prev
338
+ })
339
+ // Optimistic status flip so per-row execution loaders show
340
+ // while the sequential confirms run.
341
+ setBatchStatus(action === 'approve' ? 'approved' : 'rejected')
342
+ for (const id of ids) {
343
+ await sendMessage(
344
+ '',
345
+ { approvalAction: { proposalId: id, action } },
346
+ { hidden: true },
347
+ )
348
+ }
349
+ }
350
+ return { onApprove: act('approve'), onReject: act('reject') }
351
+ }
352
+ const liveSegments = [...currentAssistantSegmentsRef.current]
353
+ const lastSeg = liveSegments[liveSegments.length - 1] as any
354
+ if (lastSeg && lastSeg.type === 'approval_batch' && (lastSeg.status ?? 'pending') === 'pending') {
355
+ // Third-or-later proposal of the turn — append a row.
356
+ liveSegments[liveSegments.length - 1] = {
357
+ ...lastSeg,
358
+ data: {
359
+ ...lastSeg.data,
360
+ toolCalls: [...lastSeg.data.toolCalls, toToolCall(seg.data)],
361
+ },
362
+ }
363
+ } else if (
364
+ lastSeg &&
365
+ lastSeg.type === 'approval_request' &&
366
+ (lastSeg.status ?? 'pending') === 'pending'
367
+ ) {
368
+ // Second proposal of the turn — convert card + card → batch.
369
+ const anchorId = String(lastSeg.data?.requestId ?? proposalId)
370
+ liveSegments[liveSegments.length - 1] = {
371
+ type: 'approval_batch',
372
+ status: 'pending',
373
+ data: {
374
+ approvalRequestId: anchorId,
375
+ approvalType: 'chat',
376
+ toolCalls: [toToolCall(lastSeg.data), toToolCall(seg.data)],
377
+ executions: {},
378
+ },
379
+ ...makeBatchHandlers(anchorId),
380
+ } as ApprovalBatchSegment
381
+ } else {
382
+ // Single proposal — the existing per-card flow.
383
+ liveSegments.push({
384
+ ...seg,
385
+ onApprove: wrappedApprove,
386
+ onReject: wrappedReject,
387
+ })
388
+ }
389
+ currentAssistantSegmentsRef.current = liveSegments
390
+ updateLastAssistantMessage([...liveSegments])
265
391
  } else if ((segment as any).type === 'decision_resolved') {
266
392
  // Server-driven decision frame from /api/chat/agent/confirm-tool.
267
393
  //
@@ -305,7 +431,12 @@ export function useChat({
305
431
  willAutoContinue?: boolean
306
432
  }
307
433
  if (!decision.proposalId) continue
308
- // Step 1 — flip the source card's status.
434
+ // Step 1 — flip the source card's status. Two shapes can host
435
+ // the proposal: a standalone `approval_request` card (status
436
+ // flip), or an `approval_batch` row (batch status was flipped
437
+ // optimistically at click time — here we tick the row's
438
+ // per-tool execution icon: check on approved, cross on a
439
+ // failed approve).
309
440
  setMessages((prev) => {
310
441
  for (let i = prev.length - 1; i >= 0; i--) {
311
442
  const m = prev[i]
@@ -314,16 +445,44 @@ export function useChat({
314
445
  const segments = m.content as MessageSegment[]
315
446
  const hasMatch = segments.some(
316
447
  (s) =>
317
- (s as any).type === 'approval_request' &&
318
- (s as any).data?.requestId === decision.proposalId,
448
+ ((s as any).type === 'approval_request' &&
449
+ (s as any).data?.requestId === decision.proposalId) ||
450
+ ((s as any).type === 'approval_batch' &&
451
+ (s as any).data?.toolCalls?.some(
452
+ (c: any) => c?.toolExecutionRequestId === decision.proposalId,
453
+ )),
319
454
  )
320
455
  if (!hasMatch) continue
321
- const flipped = segments.map((s) =>
322
- (s as any).type === 'approval_request' &&
323
- (s as any).data?.requestId === decision.proposalId
324
- ? ({ ...(s as any), status: decision.action } as MessageSegment)
325
- : s,
326
- )
456
+ const flipped = segments.map((s) => {
457
+ if (
458
+ (s as any).type === 'approval_request' &&
459
+ (s as any).data?.requestId === decision.proposalId
460
+ ) {
461
+ return { ...(s as any), status: decision.action } as MessageSegment
462
+ }
463
+ if (
464
+ (s as any).type === 'approval_batch' &&
465
+ (s as any).data?.toolCalls?.some(
466
+ (c: any) => c?.toolExecutionRequestId === decision.proposalId,
467
+ )
468
+ ) {
469
+ const batch = s as any
470
+ return {
471
+ ...batch,
472
+ data: {
473
+ ...batch.data,
474
+ executions: {
475
+ ...(batch.data.executions ?? {}),
476
+ [decision.proposalId as string]: {
477
+ status: 'done',
478
+ success: decision.action === 'approved' ? decision.ok : false,
479
+ },
480
+ },
481
+ },
482
+ } as MessageSegment
483
+ }
484
+ return s
485
+ })
327
486
  const next = [...prev]
328
487
  next[i] = { ...m, content: flipped }
329
488
  return next