@flamingo-stack/openframe-frontend-core 0.0.288 → 0.0.289
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-6CSW5TMS.cjs → chunk-6NL7TTDR.cjs} +23 -23
- package/dist/{chunk-6CSW5TMS.cjs.map → chunk-6NL7TTDR.cjs.map} +1 -1
- package/dist/{chunk-TY2EB7VK.js → chunk-7YLTJXMQ.js} +2 -2
- package/dist/{chunk-ZYLQMCHW.js → chunk-BZR546EB.js} +2 -2
- package/dist/{chunk-JQ4I743L.cjs → chunk-DFAMTCC4.cjs} +5 -5
- package/dist/{chunk-JQ4I743L.cjs.map → chunk-DFAMTCC4.cjs.map} +1 -1
- package/dist/{chunk-RG6FNZUA.cjs → chunk-HIABEYRE.cjs} +3 -3
- package/dist/{chunk-RG6FNZUA.cjs.map → chunk-HIABEYRE.cjs.map} +1 -1
- package/dist/{chunk-OXC72UIP.cjs → chunk-KLXCXNLW.cjs} +7 -5
- package/dist/chunk-KLXCXNLW.cjs.map +1 -0
- package/dist/{chunk-6AW25OS6.cjs → chunk-LZQ4HSOR.cjs} +25 -25
- package/dist/{chunk-6AW25OS6.cjs.map → chunk-LZQ4HSOR.cjs.map} +1 -1
- package/dist/{chunk-DVUFNTI2.cjs → chunk-OV3ZCU6X.cjs} +4 -4
- package/dist/{chunk-DVUFNTI2.cjs.map → chunk-OV3ZCU6X.cjs.map} +1 -1
- package/dist/{chunk-EFYXPR43.js → chunk-P2SO7ADJ.js} +7 -5
- package/dist/{chunk-EFYXPR43.js.map → chunk-P2SO7ADJ.js.map} +1 -1
- package/dist/{chunk-GJDXIVEQ.js → chunk-PYHCHGM5.js} +2 -2
- package/dist/{chunk-RWCA2ZQK.js → chunk-Q6S6DCVP.js} +2 -2
- package/dist/{chunk-AISIZLZP.js → chunk-UBFYGWFP.js} +2 -2
- package/dist/components/chat/hooks/use-realtime-chunk-processor.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/types/api.types.d.ts +11 -4
- package/dist/components/chat/types/api.types.d.ts.map +1 -1
- package/dist/components/chat/utils/history-merge.d.ts +5 -1
- package/dist/components/chat/utils/history-merge.d.ts.map +1 -1
- package/dist/components/contact/index.cjs +3 -3
- package/dist/components/contact/index.js +2 -2
- 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/index.cjs +46 -46
- package/dist/components/index.js +5 -5
- package/dist/components/navigation/index.cjs +2 -2
- package/dist/components/navigation/index.js +1 -1
- package/dist/components/related-content/index.cjs +3 -3
- package/dist/components/related-content/index.js +2 -2
- package/dist/components/tickets/index.cjs +45 -45
- package/dist/components/tickets/index.js +3 -3
- 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/hooks/use-realtime-chunk-processor.ts +3 -1
- package/src/components/chat/types/api.types.ts +9 -6
- package/src/components/chat/utils/__tests__/history-merge.test.ts +62 -0
- package/src/components/chat/utils/history-merge.ts +5 -1
- package/dist/chunk-OXC72UIP.cjs.map +0 -1
- /package/dist/{chunk-TY2EB7VK.js.map → chunk-7YLTJXMQ.js.map} +0 -0
- /package/dist/{chunk-ZYLQMCHW.js.map → chunk-BZR546EB.js.map} +0 -0
- /package/dist/{chunk-GJDXIVEQ.js.map → chunk-PYHCHGM5.js.map} +0 -0
- /package/dist/{chunk-RWCA2ZQK.js.map → chunk-Q6S6DCVP.js.map} +0 -0
- /package/dist/{chunk-AISIZLZP.js.map → chunk-UBFYGWFP.js.map} +0 -0
|
@@ -164,14 +164,17 @@ export interface RealtimeChunkCallbacks {
|
|
|
164
164
|
onSegmentsUpdate?: (segments: MessageSegment[], metadata?: SegmentsUpdateMetadata) => void
|
|
165
165
|
/** Called when an error is received */
|
|
166
166
|
onError?: (error: string, details?: string) => void
|
|
167
|
-
/** Called when a user message request is received (echo)
|
|
168
|
-
|
|
167
|
+
/** Called when a user message request is received (echo). `streamSeq` (when
|
|
168
|
+
* the transport carries one) lets hosts stamp the synthetic so the history
|
|
169
|
+
* merge can dedup it against its persisted twin by sequence. */
|
|
170
|
+
onUserMessage?: (text: string, metadata?: { ownerType?: string; displayName?: string; userId?: string; streamSeq?: number }) => void
|
|
169
171
|
/** Called when TOKEN_USAGE chunk is received with token stats */
|
|
170
172
|
onTokenUsage?: (data: TokenUsageData) => void
|
|
171
|
-
/** Called when a direct message is received (immediately displayed)
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
173
|
+
/** Called when a direct message is received (immediately displayed). Carries
|
|
174
|
+
* `streamSeq` for the same per-message dedup as `onUserMessage`. */
|
|
175
|
+
onDirectMessage?: (text: string, metadata?: { ownerType?: string; displayName?: string; userId?: string; streamSeq?: number }) => void
|
|
176
|
+
/** Called when a system message is received (e.g. "User joined the chat"). */
|
|
177
|
+
onSystemMessage?: (text: string, metadata?: { streamSeq?: number }) => void
|
|
175
178
|
/** Callback for approval actions */
|
|
176
179
|
onApprove?: (requestId?: string) => Promise<void> | void
|
|
177
180
|
/** Callback for rejection actions */
|
|
@@ -363,6 +363,68 @@ describe('mergeHistoryWithRealtime', () => {
|
|
|
363
363
|
})
|
|
364
364
|
expect(ids(merged)).toEqual([U0.id, A0.id, U1.id, A1.id])
|
|
365
365
|
})
|
|
366
|
+
|
|
367
|
+
// The reported reconnect bug: a replayed direct/system message re-mints a
|
|
368
|
+
// synthetic with a FRESH timestamp (wall-clock can't catch it), so dedup
|
|
369
|
+
// must be by seq. Direct/system are role 'user' rows persisted with a
|
|
370
|
+
// lastChunkStreamSeq equal to the realtime chunk's streamSeq.
|
|
371
|
+
it('drops a replayed direct-message synthetic once history persisted it', () => {
|
|
372
|
+
const histDirect: TestMessage = { id: 'aaaa0009', role: 'user', content: 'ping from tech', timestamp: t(2100) }
|
|
373
|
+
const replayedDirect: TestMessage = {
|
|
374
|
+
id: 'direct-9999-r',
|
|
375
|
+
role: 'user',
|
|
376
|
+
content: 'ping from tech',
|
|
377
|
+
timestamp: t(9999), // re-minted on reconnect replay — newer than the snapshot
|
|
378
|
+
streamSeq: 80,
|
|
379
|
+
}
|
|
380
|
+
const merged = mergeHistoryWithRealtime<TestMessage>({
|
|
381
|
+
processedHistory: [U0, A0, histDirect],
|
|
382
|
+
rawHistoryIds: new Set([U0.id, A0.id, histDirect.id]),
|
|
383
|
+
existingMessages: [U0, A0, histDirect, replayedDirect],
|
|
384
|
+
streamingMessageId: null,
|
|
385
|
+
historyFetchedAt: 5000,
|
|
386
|
+
historyMaxStreamSeq: 80,
|
|
387
|
+
})
|
|
388
|
+
expect(ids(merged)).toEqual([U0.id, A0.id, histDirect.id])
|
|
389
|
+
})
|
|
390
|
+
|
|
391
|
+
it('keeps a live direct message history has not persisted yet (no loss)', () => {
|
|
392
|
+
const liveDirect: TestMessage = {
|
|
393
|
+
id: 'direct-3000-x',
|
|
394
|
+
role: 'user',
|
|
395
|
+
content: 'new tech msg',
|
|
396
|
+
timestamp: t(3000),
|
|
397
|
+
streamSeq: 90,
|
|
398
|
+
}
|
|
399
|
+
const merged = mergeHistoryWithRealtime<TestMessage>({
|
|
400
|
+
processedHistory: [U0, A0],
|
|
401
|
+
existingMessages: [U0, A0, liveDirect],
|
|
402
|
+
streamingMessageId: null,
|
|
403
|
+
historyFetchedAt: 5000,
|
|
404
|
+
historyMaxStreamSeq: 80, // history hasn't reached seq 90 yet
|
|
405
|
+
})
|
|
406
|
+
expect(ids(merged)).toContain(liveDirect.id)
|
|
407
|
+
})
|
|
408
|
+
|
|
409
|
+
it('dedups a system-message synthetic by seq coverage', () => {
|
|
410
|
+
const histSystem: TestMessage = { id: 'aaaa0010', role: 'user', content: 'User joined', timestamp: t(2100) }
|
|
411
|
+
const synSystem: TestMessage = {
|
|
412
|
+
id: 'system-9999-r',
|
|
413
|
+
role: 'user',
|
|
414
|
+
content: 'User joined',
|
|
415
|
+
timestamp: t(9999),
|
|
416
|
+
streamSeq: 70,
|
|
417
|
+
}
|
|
418
|
+
const merged = mergeHistoryWithRealtime<TestMessage>({
|
|
419
|
+
processedHistory: [U0, A0, histSystem],
|
|
420
|
+
rawHistoryIds: new Set([U0.id, A0.id, histSystem.id]),
|
|
421
|
+
existingMessages: [U0, A0, histSystem, synSystem],
|
|
422
|
+
streamingMessageId: null,
|
|
423
|
+
historyFetchedAt: 5000,
|
|
424
|
+
historyMaxStreamSeq: 80,
|
|
425
|
+
})
|
|
426
|
+
expect(ids(merged)).toEqual([U0.id, A0.id, histSystem.id])
|
|
427
|
+
})
|
|
366
428
|
})
|
|
367
429
|
})
|
|
368
430
|
|
|
@@ -36,14 +36,18 @@ export interface MergeableChatMessage {
|
|
|
36
36
|
|
|
37
37
|
/** Ids minted client-side by realtime chunk processors
|
|
38
38
|
* (`assistant-<ts>-…` placeholder bubbles, `user-<ts>-…` peer messages,
|
|
39
|
+
* `direct-<ts>-…` technician direct messages, `system-<ts>-…` system notices,
|
|
39
40
|
* `error-<ts>` stream errors). They never match the Mongo ObjectIds history
|
|
40
41
|
* returns for the same turns. This is the cross-host contract every minting
|
|
41
42
|
* site (lib `use-chat`, Mingo / tickets chunk processors, openframe-chat)
|
|
42
43
|
* must keep matching — exported so it lives in exactly one place.
|
|
44
|
+
* `direct-`/`system-` are persisted (as ADMIN/SYSTEM history rows) and so are
|
|
45
|
+
* replayed by JetStream on reconnect; without them here a replayed direct
|
|
46
|
+
* message renders twice (its persisted twin + the fresh synthetic).
|
|
43
47
|
* `welcome-` and `optimistic-` ids are intentionally NOT listed: welcome
|
|
44
48
|
* bubbles are never persisted server-side, and optimistic user messages are
|
|
45
49
|
* deduped by content below. */
|
|
46
|
-
export const SYNTHETIC_REALTIME_ID_PREFIXES = ['assistant-', 'user-', 'error-'] as const
|
|
50
|
+
export const SYNTHETIC_REALTIME_ID_PREFIXES = ['assistant-', 'user-', 'direct-', 'system-', 'error-'] as const
|
|
47
51
|
|
|
48
52
|
function isSyntheticRealtimeId(id: string): boolean {
|
|
49
53
|
return SYNTHETIC_REALTIME_ID_PREFIXES.some((prefix) => id.startsWith(prefix))
|