@flamingo-stack/openframe-frontend-core 0.0.490-1506.3944.1 → 0.0.490
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-5CJR2GZ4.cjs → chunk-2MKUHJ3O.cjs} +31 -31
- package/dist/{chunk-5CJR2GZ4.cjs.map → chunk-2MKUHJ3O.cjs.map} +1 -1
- package/dist/{chunk-S2NHJDKQ.cjs → chunk-3IEVA5AO.cjs} +62 -62
- package/dist/{chunk-S2NHJDKQ.cjs.map → chunk-3IEVA5AO.cjs.map} +1 -1
- package/dist/{chunk-GTWO5NAY.js → chunk-6CFFISUC.js} +2 -2
- package/dist/{chunk-VHG6AX5V.js → chunk-6CJ2K7BD.js} +2 -2
- package/dist/{chunk-4NWARV5I.js → chunk-6QK3ONDD.js} +2 -2
- package/dist/{chunk-YHOIAA2E.cjs → chunk-7I42V2OD.cjs} +60 -60
- package/dist/chunk-7I42V2OD.cjs.map +1 -0
- package/dist/{chunk-T2EGSZFN.js → chunk-AIRNG27G.js} +5 -6
- package/dist/chunk-AIRNG27G.js.map +1 -0
- package/dist/{chunk-NWX7MMF5.cjs → chunk-AW63ET2A.cjs} +15 -15
- package/dist/{chunk-NWX7MMF5.cjs.map → chunk-AW63ET2A.cjs.map} +1 -1
- package/dist/{chunk-TKN5667N.js → chunk-BZMOEWWS.js} +10 -10
- package/dist/chunk-BZMOEWWS.js.map +1 -0
- package/dist/{chunk-DSDZ5T4P.cjs → chunk-D5LQMRPU.cjs} +5 -5
- package/dist/{chunk-DSDZ5T4P.cjs.map → chunk-D5LQMRPU.cjs.map} +1 -1
- package/dist/{chunk-CORIBKHG.js → chunk-EGXCWZYP.js} +2 -2
- package/dist/{chunk-5EYRXSY5.cjs → chunk-EJQUYZXF.cjs} +9 -9
- package/dist/{chunk-5EYRXSY5.cjs.map → chunk-EJQUYZXF.cjs.map} +1 -1
- package/dist/{chunk-P4WTZU6L.cjs → chunk-FY5XAW3H.cjs} +11 -11
- package/dist/{chunk-P4WTZU6L.cjs.map → chunk-FY5XAW3H.cjs.map} +1 -1
- package/dist/{chunk-VQ523ORK.cjs → chunk-GMPYR3MX.cjs} +26 -26
- package/dist/{chunk-VQ523ORK.cjs.map → chunk-GMPYR3MX.cjs.map} +1 -1
- package/dist/{chunk-QSCHCZL3.cjs → chunk-GUTYUMGV.cjs} +7 -7
- package/dist/{chunk-QSCHCZL3.cjs.map → chunk-GUTYUMGV.cjs.map} +1 -1
- package/dist/{chunk-Q6E7VJ4W.cjs → chunk-IEZ3YBAP.cjs} +27 -28
- package/dist/chunk-IEZ3YBAP.cjs.map +1 -0
- package/dist/{chunk-HSACATCD.js → chunk-MPYDX5WX.js} +2 -2
- package/dist/{chunk-RQLBHKXN.js → chunk-NGEFYPY7.js} +2 -2
- package/dist/{chunk-BE6DK7QY.js → chunk-OQIMH2WQ.js} +8985 -11081
- package/dist/chunk-OQIMH2WQ.js.map +1 -0
- package/dist/{chunk-CSYSYJCF.cjs → chunk-UZZT65PQ.cjs} +37 -37
- package/dist/{chunk-CSYSYJCF.cjs.map → chunk-UZZT65PQ.cjs.map} +1 -1
- package/dist/{chunk-YZ2OIYQ3.js → chunk-VDZO6IDR.js} +4 -4
- package/dist/{chunk-UUYRJPGJ.js → chunk-VL2YMDPH.js} +4 -4
- package/dist/{chunk-5LMGFI4J.cjs → chunk-VUXNXWOQ.cjs} +4946 -7042
- package/dist/chunk-VUXNXWOQ.cjs.map +1 -0
- package/dist/{chunk-XQ2B74SI.js → chunk-WLM3F3WU.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/chat-message-enhanced.d.ts.map +1 -1
- package/dist/components/chat/chat-message-list.d.ts.map +1 -1
- package/dist/components/chat/guide-display.d.ts +14 -0
- package/dist/components/chat/guide-display.d.ts.map +1 -0
- package/dist/components/chat/hooks/index.d.ts +1 -0
- package/dist/components/chat/hooks/index.d.ts.map +1 -1
- package/dist/components/chat/hooks/use-chat.d.ts.map +1 -1
- package/dist/components/chat/hooks/use-nats-chat-adapter.d.ts +48 -15
- package/dist/components/chat/hooks/use-nats-chat-adapter.d.ts.map +1 -1
- package/dist/components/chat/hooks/use-realtime-chunk-processor.d.ts +6 -0
- package/dist/components/chat/hooks/use-realtime-chunk-processor.d.ts.map +1 -0
- package/dist/components/chat/hooks/use-sse-chat-adapter.d.ts +57 -10
- package/dist/components/chat/hooks/use-sse-chat-adapter.d.ts.map +1 -1
- package/dist/components/chat/index.cjs +2 -8
- package/dist/components/chat/index.cjs.map +1 -1
- package/dist/components/chat/index.d.ts +1 -1
- package/dist/components/chat/index.d.ts.map +1 -1
- package/dist/components/chat/index.js +15 -21
- package/dist/components/chat/types/api.types.d.ts +81 -1
- package/dist/components/chat/types/api.types.d.ts.map +1 -1
- package/dist/components/chat/types/component.types.d.ts +6 -0
- package/dist/components/chat/types/component.types.d.ts.map +1 -1
- package/dist/components/chat/types/message.types.d.ts +15 -2
- package/dist/components/chat/types/message.types.d.ts.map +1 -1
- package/dist/components/chat/types/processing.types.d.ts +93 -2
- package/dist/components/chat/types/processing.types.d.ts.map +1 -1
- package/dist/components/chat/utils/chunk-parser.d.ts +25 -0
- package/dist/components/chat/utils/chunk-parser.d.ts.map +1 -0
- package/dist/components/chat/utils/extract-incomplete-message-state.d.ts +6 -34
- package/dist/components/chat/utils/extract-incomplete-message-state.d.ts.map +1 -1
- package/dist/components/chat/utils/index.d.ts +3 -2
- package/dist/components/chat/utils/index.d.ts.map +1 -1
- package/dist/components/chat/utils/message-segment-accumulator.d.ts +5 -0
- package/dist/components/chat/utils/message-segment-accumulator.d.ts.map +1 -1
- package/dist/components/chat/utils/process-historical-messages.d.ts +11 -59
- package/dist/components/chat/utils/process-historical-messages.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 +20 -20
- package/dist/components/help-center-pages/index.js +11 -11
- package/dist/components/index.cjs +130 -170
- package/dist/components/index.cjs.map +1 -1
- package/dist/components/index.js +23 -63
- package/dist/components/index.js.map +1 -1
- 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/onboarding-guides/onboarding-guide-detail-view.d.ts.map +1 -1
- package/dist/components/related-content/index.cjs +3 -3
- package/dist/components/related-content/index.js +2 -2
- package/dist/components/shared/product-release/release-detail-page.d.ts.map +1 -1
- package/dist/components/tickets/index.cjs +5 -5
- package/dist/components/tickets/index.js +4 -4
- package/dist/components/ui/index.cjs +2 -42
- package/dist/components/ui/index.cjs.map +1 -1
- package/dist/components/ui/index.d.ts +2 -1
- package/dist/components/ui/index.d.ts.map +1 -1
- package/dist/components/ui/index.js +15 -55
- package/dist/components/ui/rich-markdown-renderer.d.ts +34 -0
- package/dist/components/ui/rich-markdown-renderer.d.ts.map +1 -0
- package/dist/components/ui/simple-markdown-renderer.d.ts +73 -0
- package/dist/components/ui/simple-markdown-renderer.d.ts.map +1 -0
- package/dist/index.cjs +2 -58
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +15 -71
- package/dist/utils/index.cjs +40 -169
- package/dist/utils/index.cjs.map +1 -1
- package/dist/utils/index.d.ts +0 -1
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +41 -162
- package/dist/utils/index.js.map +1 -1
- package/dist/utils/markdown-section-extractor.d.ts.map +1 -1
- package/package.json +1 -7
- package/src/components/chat/__tests__/chat-message-list.test.tsx +5 -293
- package/src/components/chat/chat-message-enhanced.tsx +80 -163
- package/src/components/chat/chat-message-list.tsx +109 -358
- package/src/components/chat/embeddable-chat.tsx +0 -6
- package/src/components/chat/guide-display.tsx +44 -0
- package/src/components/chat/hooks/.index.md +33 -30
- package/src/components/chat/hooks/.use-nats-chat-adapter.md +55 -36
- package/src/components/chat/hooks/.use-realtime-chunk-processor.md +77 -0
- package/src/components/chat/hooks/index.ts +1 -0
- package/src/components/chat/hooks/use-chat.ts +0 -8
- package/src/components/chat/hooks/use-nats-chat-adapter.ts +825 -243
- package/src/components/chat/hooks/use-realtime-chunk-processor.ts +489 -0
- package/src/components/chat/hooks/use-sse-chat-adapter.ts +684 -380
- package/src/components/chat/index.ts +1 -5
- package/src/components/chat/thinking-display.tsx +1 -1
- package/src/components/chat/types/.api.types.md +49 -39
- package/src/components/chat/types/.processing.types.md +58 -29
- package/src/components/chat/types/api.types.ts +73 -6
- package/src/components/chat/types/component.types.ts +9 -0
- package/src/components/chat/types/message.types.ts +17 -1
- package/src/components/chat/types/processing.types.ts +53 -11
- package/src/components/chat/utils/.chunk-parser.md +62 -0
- package/src/components/chat/utils/.index.md +40 -31
- package/src/components/chat/utils/chunk-parser.ts +262 -0
- package/src/components/chat/utils/extract-incomplete-message-state.ts +5 -63
- package/src/components/chat/utils/index.ts +8 -5
- package/src/components/chat/utils/message-segment-accumulator.ts +19 -0
- package/src/components/chat/utils/process-historical-messages.ts +390 -352
- package/src/components/onboarding-guides/onboarding-guide-detail-view.tsx +4 -22
- package/src/components/shared/legal-document/legal-document-page.tsx +1 -1
- package/src/components/shared/product-release/release-detail-page.tsx +4 -19
- package/src/components/ui/.simple-markdown-renderer.md +52 -0
- package/src/components/ui/index.ts +2 -1
- package/src/components/ui/release-changelog-section.tsx +1 -1
- package/src/components/ui/rich-markdown-renderer.tsx +1223 -0
- package/src/components/ui/simple-markdown-renderer.tsx +964 -0
- package/src/components/ui/ticket-info-section.tsx +1 -1
- package/src/stories/ChatMessageEnhanced.stories.tsx +48 -1
- package/src/utils/index.ts +0 -1
- package/src/utils/markdown-section-extractor.ts +56 -41
- package/dist/chat-protocol/decode.d.ts +0 -58
- package/dist/chat-protocol/decode.d.ts.map +0 -1
- package/dist/chat-protocol/encode.d.ts +0 -44
- package/dist/chat-protocol/encode.d.ts.map +0 -1
- package/dist/chat-protocol/env-flag.d.ts +0 -35
- package/dist/chat-protocol/env-flag.d.ts.map +0 -1
- package/dist/chat-protocol/events.d.ts +0 -196
- package/dist/chat-protocol/events.d.ts.map +0 -1
- package/dist/chat-protocol/frames.d.ts +0 -213
- package/dist/chat-protocol/frames.d.ts.map +0 -1
- package/dist/chat-protocol/index.cjs +0 -494
- package/dist/chat-protocol/index.cjs.map +0 -1
- package/dist/chat-protocol/index.d.ts +0 -18
- package/dist/chat-protocol/index.d.ts.map +0 -1
- package/dist/chat-protocol/index.js +0 -478
- package/dist/chat-protocol/index.js.map +0 -1
- package/dist/chat-protocol/ip-normalize.d.ts +0 -44
- package/dist/chat-protocol/ip-normalize.d.ts.map +0 -1
- package/dist/chat-protocol/nats-decoder.d.ts +0 -26
- package/dist/chat-protocol/nats-decoder.d.ts.map +0 -1
- package/dist/chunk-5LMGFI4J.cjs.map +0 -1
- package/dist/chunk-BE6DK7QY.js.map +0 -1
- package/dist/chunk-Q6E7VJ4W.cjs.map +0 -1
- package/dist/chunk-T2EGSZFN.js.map +0 -1
- package/dist/chunk-TKN5667N.js.map +0 -1
- package/dist/chunk-YHOIAA2E.cjs.map +0 -1
- package/dist/components/chat/stream/chat-dialog-store.d.ts +0 -184
- package/dist/components/chat/stream/chat-dialog-store.d.ts.map +0 -1
- package/dist/components/chat/stream/chat-stream-reducer.d.ts +0 -367
- package/dist/components/chat/stream/chat-stream-reducer.d.ts.map +0 -1
- package/dist/components/chat/stream/delta-batcher.d.ts +0 -51
- package/dist/components/chat/stream/delta-batcher.d.ts.map +0 -1
- package/dist/components/chat/stream/index.d.ts +0 -17
- package/dist/components/chat/stream/index.d.ts.map +0 -1
- package/dist/components/chat/stream/message-mutations.d.ts +0 -77
- package/dist/components/chat/stream/message-mutations.d.ts.map +0 -1
- package/dist/components/chat/stream/use-chat-stream-reducer.d.ts +0 -22
- package/dist/components/chat/stream/use-chat-stream-reducer.d.ts.map +0 -1
- package/dist/components/ui/markdown/base-components.d.ts +0 -51
- package/dist/components/ui/markdown/base-components.d.ts.map +0 -1
- package/dist/components/ui/markdown/engine.d.ts +0 -99
- package/dist/components/ui/markdown/engine.d.ts.map +0 -1
- package/dist/components/ui/markdown/heading-ids.d.ts +0 -65
- package/dist/components/ui/markdown/heading-ids.d.ts.map +0 -1
- package/dist/components/ui/markdown/index.d.ts +0 -18
- package/dist/components/ui/markdown/index.d.ts.map +0 -1
- package/dist/components/ui/markdown/mermaid-diagram.d.ts +0 -79
- package/dist/components/ui/markdown/mermaid-diagram.d.ts.map +0 -1
- package/dist/components/ui/markdown/rich/embed-overrides.d.ts +0 -14
- package/dist/components/ui/markdown/rich/embed-overrides.d.ts.map +0 -1
- package/dist/components/ui/markdown/rich/rich-markdown-renderer.d.ts +0 -40
- package/dist/components/ui/markdown/rich/rich-markdown-renderer.d.ts.map +0 -1
- package/dist/components/ui/markdown/rich/shortcodes.d.ts +0 -8
- package/dist/components/ui/markdown/rich/shortcodes.d.ts.map +0 -1
- package/dist/components/ui/markdown/sanitize.d.ts +0 -153
- package/dist/components/ui/markdown/sanitize.d.ts.map +0 -1
- package/dist/components/ui/markdown/simple-markdown-renderer.d.ts +0 -23
- package/dist/components/ui/markdown/simple-markdown-renderer.d.ts.map +0 -1
- package/dist/components/ui/markdown/streaming.d.ts +0 -78
- package/dist/components/ui/markdown/streaming.d.ts.map +0 -1
- package/dist/components/ui/markdown/text-size.d.ts +0 -34
- package/dist/components/ui/markdown/text-size.d.ts.map +0 -1
- package/dist/utils/markdown-fences.d.ts +0 -42
- package/dist/utils/markdown-fences.d.ts.map +0 -1
- package/dist/utils/markdown-heading-id.d.ts +0 -85
- package/dist/utils/markdown-heading-id.d.ts.map +0 -1
- package/src/chat-protocol/__tests__/__snapshots__/nats-decoder-golden.test.ts.snap +0 -307
- package/src/chat-protocol/__tests__/chat-protocol.test.ts +0 -516
- package/src/chat-protocol/__tests__/env-flag.test.ts +0 -52
- package/src/chat-protocol/__tests__/ip-normalize.test.ts +0 -136
- package/src/chat-protocol/__tests__/nats-decoder-golden.test.ts +0 -228
- package/src/chat-protocol/decode.ts +0 -278
- package/src/chat-protocol/encode.ts +0 -71
- package/src/chat-protocol/env-flag.ts +0 -39
- package/src/chat-protocol/events.ts +0 -232
- package/src/chat-protocol/frames.ts +0 -245
- package/src/chat-protocol/index.ts +0 -21
- package/src/chat-protocol/ip-normalize.ts +0 -146
- package/src/chat-protocol/nats-decoder.ts +0 -252
- package/src/components/chat/__tests__/chat-message-streaming-memo.test.tsx +0 -207
- package/src/components/chat/__tests__/chat-pending-turn.test.tsx +0 -209
- package/src/components/chat/hooks/__tests__/__snapshots__/conversation-id-persistence-golden.test.ts.snap +0 -37
- package/src/components/chat/hooks/__tests__/__snapshots__/sse-stream-golden.test.ts.snap +0 -0
- package/src/components/chat/hooks/__tests__/conversation-id-persistence-golden.test.ts +0 -438
- package/src/components/chat/hooks/__tests__/sse-stream-golden.test.ts +0 -318
- package/src/components/chat/stream/__tests__/__snapshots__/chat-stream-reducer-golden.test.ts.snap +0 -940
- package/src/components/chat/stream/__tests__/chat-dialog-store.test.ts +0 -756
- package/src/components/chat/stream/__tests__/chat-stream-reducer-golden.test.ts +0 -460
- package/src/components/chat/stream/__tests__/chat-stream-reducer.test.ts +0 -894
- package/src/components/chat/stream/__tests__/delta-batcher.test.ts +0 -255
- package/src/components/chat/stream/__tests__/use-chat-stream-reducer.test.ts +0 -98
- package/src/components/chat/stream/chat-dialog-store.ts +0 -536
- package/src/components/chat/stream/chat-stream-reducer.ts +0 -1796
- package/src/components/chat/stream/delta-batcher.ts +0 -159
- package/src/components/chat/stream/index.ts +0 -55
- package/src/components/chat/stream/message-mutations.ts +0 -338
- package/src/components/chat/stream/use-chat-stream-reducer.ts +0 -126
- package/src/components/chat/utils/__tests__/__snapshots__/process-historical-messages-golden.test.ts.snap +0 -420
- package/src/components/chat/utils/__tests__/__snapshots__/segment-accumulator-golden.test.ts.snap +0 -605
- package/src/components/chat/utils/__tests__/process-historical-messages-golden.test.ts +0 -317
- package/src/components/chat/utils/__tests__/segment-accumulator-golden.test.ts +0 -270
- package/src/components/ui/__tests__/__snapshots__/markdown-parity.test.tsx.snap +0 -2420
- package/src/components/ui/__tests__/markdown-parity.test.tsx +0 -2528
- package/src/components/ui/markdown/__tests__/mermaid-security.test.ts +0 -115
- package/src/components/ui/markdown/__tests__/mermaid-stale-render.test.tsx +0 -95
- package/src/components/ui/markdown/__tests__/sanitize-invariant.test.ts +0 -309
- package/src/components/ui/markdown/__tests__/streaming.test.tsx +0 -388
- package/src/components/ui/markdown/base-components.tsx +0 -360
- package/src/components/ui/markdown/engine.tsx +0 -315
- package/src/components/ui/markdown/heading-ids.ts +0 -239
- package/src/components/ui/markdown/index.ts +0 -49
- package/src/components/ui/markdown/mermaid-diagram.tsx +0 -291
- package/src/components/ui/markdown/rich/embed-overrides.tsx +0 -199
- package/src/components/ui/markdown/rich/rich-markdown-renderer.tsx +0 -184
- package/src/components/ui/markdown/rich/shortcodes.ts +0 -170
- package/src/components/ui/markdown/sanitize.ts +0 -3499
- package/src/components/ui/markdown/simple-markdown-renderer.tsx +0 -28
- package/src/components/ui/markdown/streaming.ts +0 -362
- package/src/components/ui/markdown/text-size.ts +0 -106
- package/src/utils/markdown-fences.ts +0 -127
- package/src/utils/markdown-heading-id.ts +0 -348
- /package/dist/{chunk-GTWO5NAY.js.map → chunk-6CFFISUC.js.map} +0 -0
- /package/dist/{chunk-VHG6AX5V.js.map → chunk-6CJ2K7BD.js.map} +0 -0
- /package/dist/{chunk-4NWARV5I.js.map → chunk-6QK3ONDD.js.map} +0 -0
- /package/dist/{chunk-CORIBKHG.js.map → chunk-EGXCWZYP.js.map} +0 -0
- /package/dist/{chunk-HSACATCD.js.map → chunk-MPYDX5WX.js.map} +0 -0
- /package/dist/{chunk-RQLBHKXN.js.map → chunk-NGEFYPY7.js.map} +0 -0
- /package/dist/{chunk-YZ2OIYQ3.js.map → chunk-VDZO6IDR.js.map} +0 -0
- /package/dist/{chunk-UUYRJPGJ.js.map → chunk-VL2YMDPH.js.map} +0 -0
- /package/dist/{chunk-XQ2B74SI.js.map → chunk-WLM3F3WU.js.map} +0 -0
|
@@ -1,317 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Phase-0 GOLDEN CHARACTERIZATION TESTS — `processHistoricalMessages`.
|
|
3
|
-
*
|
|
4
|
-
* Pins the CURRENT GraphQL-history → ProcessedMessage[] transformation
|
|
5
|
-
* before the stream-reader unification. A recorded corpus of messageData
|
|
6
|
-
* rows is processed and the FULL output array is snapshotted.
|
|
7
|
-
*
|
|
8
|
-
* NOTE: `process-historical-messages-approvals.test.ts` already guards
|
|
9
|
-
* displayApprovalTypes semantics — this file deliberately does not
|
|
10
|
-
* duplicate it and instead records the full-output shape.
|
|
11
|
-
*/
|
|
12
|
-
|
|
13
|
-
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'
|
|
14
|
-
import { processHistoricalMessages, extractErrorMessages, processHistoricalMessagesWithErrors } from '../process-historical-messages'
|
|
15
|
-
import type { HistoricalMessage } from '../../types'
|
|
16
|
-
|
|
17
|
-
// Deterministic Date.now for the `pending-approvals-${Date.now()}` synthetic
|
|
18
|
-
// message id + its `new Date()` timestamp.
|
|
19
|
-
const FIXED_NOW = new Date('2026-07-20T10:00:00.000Z')
|
|
20
|
-
|
|
21
|
-
beforeEach(() => {
|
|
22
|
-
vi.useFakeTimers()
|
|
23
|
-
vi.setSystemTime(FIXED_NOW)
|
|
24
|
-
})
|
|
25
|
-
|
|
26
|
-
afterEach(() => {
|
|
27
|
-
vi.useRealTimers()
|
|
28
|
-
})
|
|
29
|
-
|
|
30
|
-
/** Recorded corpus — one realistic dialog covering every row shape. */
|
|
31
|
-
const CORPUS: HistoricalMessage[] = [
|
|
32
|
-
// 1. User message with contextItems (CLIENT owner).
|
|
33
|
-
{
|
|
34
|
-
id: 'u1',
|
|
35
|
-
chatType: 'CLIENT_CHAT',
|
|
36
|
-
createdAt: '2026-07-19T09:00:00Z',
|
|
37
|
-
owner: { type: 'CLIENT', userId: 'user-42' },
|
|
38
|
-
lastChunkStreamSeq: 10,
|
|
39
|
-
messageData: [
|
|
40
|
-
{
|
|
41
|
-
type: 'TEXT',
|
|
42
|
-
text: 'diagnose this device please',
|
|
43
|
-
// wire shape: [{ type, id }] — label falls back to id
|
|
44
|
-
contextItems: [
|
|
45
|
-
{ type: 'device', id: 'dev-1' },
|
|
46
|
-
{ type: 'bad' }, // missing id → filtered
|
|
47
|
-
],
|
|
48
|
-
} as any,
|
|
49
|
-
],
|
|
50
|
-
},
|
|
51
|
-
// 2. Assistant: thinking + text (grouped into one bubble).
|
|
52
|
-
{
|
|
53
|
-
id: 'a1',
|
|
54
|
-
chatType: 'CLIENT_CHAT',
|
|
55
|
-
createdAt: '2026-07-19T09:00:05Z',
|
|
56
|
-
owner: { type: 'ASSISTANT', model: 'claude-sonnet-x' },
|
|
57
|
-
lastChunkStreamSeq: 20,
|
|
58
|
-
messageData: [
|
|
59
|
-
{ type: 'THINKING', text: 'Checking device state… ' },
|
|
60
|
-
{ type: 'THINKING', text: 'looks like disk pressure.' },
|
|
61
|
-
{ type: 'TEXT', text: 'The device is low on disk. ' },
|
|
62
|
-
],
|
|
63
|
-
},
|
|
64
|
-
// 3. Assistant continued: EXECUTING + EXECUTED tool pair merges in place.
|
|
65
|
-
{
|
|
66
|
-
id: 'a2',
|
|
67
|
-
chatType: 'CLIENT_CHAT',
|
|
68
|
-
createdAt: '2026-07-19T09:00:10Z',
|
|
69
|
-
owner: { type: 'ASSISTANT' },
|
|
70
|
-
lastChunkStreamSeq: 15, // lower than a1's — MAX (20) must win on the flushed turn
|
|
71
|
-
messageData: [
|
|
72
|
-
{
|
|
73
|
-
type: 'EXECUTING_TOOL',
|
|
74
|
-
integratedToolType: 'TACTICAL_RMM',
|
|
75
|
-
toolFunction: 'run_script',
|
|
76
|
-
title: 'Run cleanup script',
|
|
77
|
-
parameters: { script: 'cleanup.sh' },
|
|
78
|
-
toolExecutionRequestId: 'exec-1',
|
|
79
|
-
},
|
|
80
|
-
{
|
|
81
|
-
type: 'EXECUTED_TOOL',
|
|
82
|
-
integratedToolType: 'TACTICAL_RMM',
|
|
83
|
-
toolFunction: 'run_script',
|
|
84
|
-
parameters: { script: 'cleanup.sh' },
|
|
85
|
-
result: 'Freed 2.3 GB',
|
|
86
|
-
success: true,
|
|
87
|
-
toolExecutionRequestId: 'exec-1',
|
|
88
|
-
},
|
|
89
|
-
{ type: 'TEXT', text: 'Cleanup done.' },
|
|
90
|
-
],
|
|
91
|
-
},
|
|
92
|
-
// 4. ADMIN-owned user message (owner-type author resolution: display name from user).
|
|
93
|
-
{
|
|
94
|
-
id: 'u2',
|
|
95
|
-
chatType: 'CLIENT_CHAT',
|
|
96
|
-
createdAt: '2026-07-19T09:01:00Z',
|
|
97
|
-
owner: {
|
|
98
|
-
type: 'ADMIN',
|
|
99
|
-
user: {
|
|
100
|
-
id: 'admin-7',
|
|
101
|
-
firstName: 'Jane',
|
|
102
|
-
lastName: 'Admin',
|
|
103
|
-
image: { imageUrl: '/avatars/jane.png' },
|
|
104
|
-
},
|
|
105
|
-
},
|
|
106
|
-
messageData: [{ type: 'TEXT', text: 'please restart the broker' }],
|
|
107
|
-
},
|
|
108
|
-
// 5. Assistant: single approval request + result (resolved in-history).
|
|
109
|
-
{
|
|
110
|
-
id: 'a3',
|
|
111
|
-
chatType: 'CLIENT_CHAT',
|
|
112
|
-
createdAt: '2026-07-19T09:01:05Z',
|
|
113
|
-
owner: { type: 'ASSISTANT' },
|
|
114
|
-
lastChunkStreamSeq: 30,
|
|
115
|
-
messageData: [
|
|
116
|
-
{ type: 'TEXT', text: 'I need approval to restart it.' },
|
|
117
|
-
{
|
|
118
|
-
type: 'APPROVAL_REQUEST',
|
|
119
|
-
approvalRequestId: 'req-1',
|
|
120
|
-
approvalType: 'CLIENT',
|
|
121
|
-
command: 'systemctl restart nats',
|
|
122
|
-
explanation: 'Restart the message broker',
|
|
123
|
-
},
|
|
124
|
-
{
|
|
125
|
-
type: 'APPROVAL_RESULT',
|
|
126
|
-
approvalRequestId: 'req-1',
|
|
127
|
-
approved: true,
|
|
128
|
-
approvalType: 'CLIENT',
|
|
129
|
-
resolvedByName: 'Jane Admin',
|
|
130
|
-
},
|
|
131
|
-
],
|
|
132
|
-
},
|
|
133
|
-
// 6. Assistant: batch approval + per-tool executions merged into the batch.
|
|
134
|
-
{
|
|
135
|
-
id: 'a4',
|
|
136
|
-
chatType: 'CLIENT_CHAT',
|
|
137
|
-
createdAt: '2026-07-19T09:02:00Z',
|
|
138
|
-
owner: { type: 'ASSISTANT' },
|
|
139
|
-
messageData: [
|
|
140
|
-
{
|
|
141
|
-
type: 'APPROVAL_REQUEST',
|
|
142
|
-
approvalRequestId: 'batch-1',
|
|
143
|
-
approvalType: 'CLIENT',
|
|
144
|
-
toolCalls: [
|
|
145
|
-
{
|
|
146
|
-
toolExecutionRequestId: 'exec-a',
|
|
147
|
-
toolName: 'create_ticket',
|
|
148
|
-
toolTitle: 'Create ticket',
|
|
149
|
-
requiresApproval: true,
|
|
150
|
-
approvalType: 'CLIENT',
|
|
151
|
-
toolCallArguments: { subject: 'Printer down' },
|
|
152
|
-
},
|
|
153
|
-
{
|
|
154
|
-
toolExecutionRequestId: 'exec-b',
|
|
155
|
-
toolName: 'read_kb',
|
|
156
|
-
requiresApproval: false,
|
|
157
|
-
},
|
|
158
|
-
],
|
|
159
|
-
},
|
|
160
|
-
{
|
|
161
|
-
type: 'APPROVAL_RESULT',
|
|
162
|
-
approvalRequestId: 'batch-1',
|
|
163
|
-
approved: true,
|
|
164
|
-
resolvedByName: 'Alice Client',
|
|
165
|
-
},
|
|
166
|
-
{
|
|
167
|
-
type: 'EXECUTING_TOOL',
|
|
168
|
-
integratedToolType: 'PSA',
|
|
169
|
-
toolFunction: 'create_ticket',
|
|
170
|
-
toolExecutionRequestId: 'exec-a',
|
|
171
|
-
},
|
|
172
|
-
{
|
|
173
|
-
type: 'EXECUTED_TOOL',
|
|
174
|
-
integratedToolType: 'PSA',
|
|
175
|
-
toolFunction: 'create_ticket',
|
|
176
|
-
result: 'Ticket #77 created',
|
|
177
|
-
success: true,
|
|
178
|
-
toolExecutionRequestId: 'exec-a',
|
|
179
|
-
},
|
|
180
|
-
],
|
|
181
|
-
},
|
|
182
|
-
// 7. Standalone SYSTEM message row — flushes the assistant turn and
|
|
183
|
-
// renders as a system-authored row.
|
|
184
|
-
{
|
|
185
|
-
id: 's1',
|
|
186
|
-
chatType: 'CLIENT_CHAT',
|
|
187
|
-
createdAt: '2026-07-19T09:03:00Z',
|
|
188
|
-
owner: { type: 'ASSISTANT' },
|
|
189
|
-
lastChunkStreamSeq: 40,
|
|
190
|
-
messageData: [{ type: 'SYSTEM', text: 'Technician joined the chat' } as any],
|
|
191
|
-
},
|
|
192
|
-
// 8. DIRECT_MESSAGE row. CHARACTERIZATION: DIRECT_MESSAGE is a
|
|
193
|
-
// realtime-only concept — the history processors have NO branch for
|
|
194
|
-
// it. This ADMIN-owned row takes the user-message path, which only
|
|
195
|
-
// renders TEXT data, so it contributes NOTHING to the output (the
|
|
196
|
-
// bubble is lost on reload). Recording current behavior as-is; see
|
|
197
|
-
// unification plan.
|
|
198
|
-
{
|
|
199
|
-
id: 'd1',
|
|
200
|
-
chatType: 'CLIENT_CHAT',
|
|
201
|
-
createdAt: '2026-07-19T09:03:30Z',
|
|
202
|
-
owner: { type: 'ADMIN', user: { id: 'admin-7', firstName: 'Jane' } },
|
|
203
|
-
messageData: [{ type: 'DIRECT_MESSAGE', text: 'Human here, taking over' } as any],
|
|
204
|
-
},
|
|
205
|
-
// 9. Error row inside an assistant turn.
|
|
206
|
-
{
|
|
207
|
-
id: 'a5',
|
|
208
|
-
chatType: 'CLIENT_CHAT',
|
|
209
|
-
createdAt: '2026-07-19T09:04:00Z',
|
|
210
|
-
owner: { type: 'ASSISTANT' },
|
|
211
|
-
messageData: [
|
|
212
|
-
{ type: 'TEXT', text: 'Attempting one more thing… ' },
|
|
213
|
-
{ type: 'ERROR', error: 'Agent crashed', details: '{"error":{"message":"boom"}}' },
|
|
214
|
-
],
|
|
215
|
-
},
|
|
216
|
-
]
|
|
217
|
-
|
|
218
|
-
describe('processHistoricalMessages — golden corpus', () => {
|
|
219
|
-
it('processes the full recorded dialog (snapshot)', () => {
|
|
220
|
-
const result = processHistoricalMessages(CORPUS, {
|
|
221
|
-
assistantName: 'Mingo AI',
|
|
222
|
-
assistantType: 'mingo',
|
|
223
|
-
assistantAvatar: '/avatars/mingo.png',
|
|
224
|
-
})
|
|
225
|
-
expect({
|
|
226
|
-
messages: result.messages,
|
|
227
|
-
escalatedApprovals: Array.from(result.escalatedApprovals.entries()),
|
|
228
|
-
}).toMatchSnapshot()
|
|
229
|
-
})
|
|
230
|
-
|
|
231
|
-
it('unresolved single approval is resurrected as a synthetic pending-approvals bubble', () => {
|
|
232
|
-
const result = processHistoricalMessages([
|
|
233
|
-
{
|
|
234
|
-
id: 'a1',
|
|
235
|
-
createdAt: '2026-07-19T09:00:00Z',
|
|
236
|
-
owner: { type: 'ASSISTANT' },
|
|
237
|
-
messageData: [
|
|
238
|
-
{
|
|
239
|
-
type: 'APPROVAL_REQUEST',
|
|
240
|
-
approvalRequestId: 'req-pending',
|
|
241
|
-
approvalType: 'CLIENT',
|
|
242
|
-
command: 'reboot server',
|
|
243
|
-
},
|
|
244
|
-
],
|
|
245
|
-
},
|
|
246
|
-
])
|
|
247
|
-
// Date.now is frozen → deterministic synthetic id.
|
|
248
|
-
expect(result.messages).toMatchSnapshot()
|
|
249
|
-
})
|
|
250
|
-
|
|
251
|
-
it('owner-type author resolution: CLIENT vs ADMIN vs ADMIN-without-name display names/avatars', () => {
|
|
252
|
-
const rows: HistoricalMessage[] = [
|
|
253
|
-
{
|
|
254
|
-
id: 'u-client',
|
|
255
|
-
createdAt: '2026-07-19T09:00:00Z',
|
|
256
|
-
owner: { type: 'CLIENT', userId: 'user-1' },
|
|
257
|
-
messageData: [{ type: 'TEXT', text: 'from client' }],
|
|
258
|
-
},
|
|
259
|
-
{
|
|
260
|
-
id: 'u-admin-named',
|
|
261
|
-
createdAt: '2026-07-19T09:00:01Z',
|
|
262
|
-
owner: {
|
|
263
|
-
type: 'ADMIN',
|
|
264
|
-
user: { id: 'adm', firstName: 'Jane', lastName: 'Admin', image: { imageUrl: '/a.png' } },
|
|
265
|
-
},
|
|
266
|
-
messageData: [{ type: 'TEXT', text: 'from named admin' }],
|
|
267
|
-
},
|
|
268
|
-
{
|
|
269
|
-
id: 'u-admin-anon',
|
|
270
|
-
createdAt: '2026-07-19T09:00:02Z',
|
|
271
|
-
owner: { type: 'ADMIN' },
|
|
272
|
-
messageData: [{ type: 'TEXT', text: 'from anonymous admin' }],
|
|
273
|
-
},
|
|
274
|
-
]
|
|
275
|
-
expect(processHistoricalMessages(rows).messages).toMatchSnapshot()
|
|
276
|
-
})
|
|
277
|
-
|
|
278
|
-
it('chatTypeFilter drops rows of other chat types', () => {
|
|
279
|
-
const rows: HistoricalMessage[] = [
|
|
280
|
-
{
|
|
281
|
-
id: 'u1',
|
|
282
|
-
chatType: 'CLIENT_CHAT',
|
|
283
|
-
createdAt: '2026-07-19T09:00:00Z',
|
|
284
|
-
owner: { type: 'CLIENT' },
|
|
285
|
-
messageData: [{ type: 'TEXT', text: 'visible' }],
|
|
286
|
-
},
|
|
287
|
-
{
|
|
288
|
-
id: 'u2',
|
|
289
|
-
chatType: 'ADMIN_AI_CHAT',
|
|
290
|
-
createdAt: '2026-07-19T09:00:01Z',
|
|
291
|
-
owner: { type: 'CLIENT' },
|
|
292
|
-
messageData: [{ type: 'TEXT', text: 'filtered out' }],
|
|
293
|
-
},
|
|
294
|
-
]
|
|
295
|
-
expect(
|
|
296
|
-
processHistoricalMessages(rows, { chatTypeFilter: 'CLIENT_CHAT' }).messages,
|
|
297
|
-
).toMatchSnapshot()
|
|
298
|
-
})
|
|
299
|
-
})
|
|
300
|
-
|
|
301
|
-
describe('extractErrorMessages / processHistoricalMessagesWithErrors — golden', () => {
|
|
302
|
-
it('extractErrorMessages surfaces ERROR rows as standalone error-role messages', () => {
|
|
303
|
-
expect(extractErrorMessages(CORPUS, { assistantName: 'Mingo AI', assistantType: 'mingo' })).toMatchSnapshot()
|
|
304
|
-
})
|
|
305
|
-
|
|
306
|
-
it('processHistoricalMessagesWithErrors output parity on the same corpus (snapshot)', () => {
|
|
307
|
-
const result = processHistoricalMessagesWithErrors(CORPUS, {
|
|
308
|
-
assistantName: 'Mingo AI',
|
|
309
|
-
assistantType: 'mingo',
|
|
310
|
-
assistantAvatar: '/avatars/mingo.png',
|
|
311
|
-
})
|
|
312
|
-
expect({
|
|
313
|
-
messages: result.messages,
|
|
314
|
-
escalatedApprovals: Array.from(result.escalatedApprovals.entries()),
|
|
315
|
-
}).toMatchSnapshot()
|
|
316
|
-
})
|
|
317
|
-
})
|
|
@@ -1,270 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Phase-0 GOLDEN CHARACTERIZATION TESTS — `MessageSegmentAccumulator`.
|
|
3
|
-
*
|
|
4
|
-
* Pins the CURRENT segment-accumulation semantics before the stream-reader
|
|
5
|
-
* unification. Each test drives the accumulator through a recorded event
|
|
6
|
-
* sequence and snapshots the final segment array. Behaviors captured here
|
|
7
|
-
* (including the implicit approval-flip on tool execution and the batch
|
|
8
|
-
* no-downgrade rule) are the recorded baseline the future reducer must match.
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
import { describe, it, expect } from 'vitest'
|
|
12
|
-
import { createMessageSegmentAccumulator } from '../message-segment-accumulator'
|
|
13
|
-
import type { ToolExecutionSegment, PendingToolCallData } from '../../types'
|
|
14
|
-
|
|
15
|
-
const executing = (
|
|
16
|
-
execId: string | undefined,
|
|
17
|
-
overrides: Partial<ToolExecutionSegment['data']> = {},
|
|
18
|
-
): ToolExecutionSegment => ({
|
|
19
|
-
type: 'tool_execution',
|
|
20
|
-
data: {
|
|
21
|
-
type: 'EXECUTING_TOOL',
|
|
22
|
-
integratedToolType: 'TACTICAL_RMM',
|
|
23
|
-
toolFunction: 'run_script',
|
|
24
|
-
toolTitle: 'Run cleanup script',
|
|
25
|
-
parameters: { script: 'cleanup.sh' },
|
|
26
|
-
...(execId ? { toolExecutionRequestId: execId } : {}),
|
|
27
|
-
...overrides,
|
|
28
|
-
},
|
|
29
|
-
})
|
|
30
|
-
|
|
31
|
-
const executed = (
|
|
32
|
-
execId: string | undefined,
|
|
33
|
-
overrides: Partial<ToolExecutionSegment['data']> = {},
|
|
34
|
-
): ToolExecutionSegment => ({
|
|
35
|
-
type: 'tool_execution',
|
|
36
|
-
data: {
|
|
37
|
-
type: 'EXECUTED_TOOL',
|
|
38
|
-
integratedToolType: 'TACTICAL_RMM',
|
|
39
|
-
toolFunction: 'run_script',
|
|
40
|
-
// Backend omits toolTitle on EXECUTED_TOOL — the accumulator restores it.
|
|
41
|
-
result: 'Freed 2.3 GB',
|
|
42
|
-
success: true,
|
|
43
|
-
...(execId ? { toolExecutionRequestId: execId } : {}),
|
|
44
|
-
...overrides,
|
|
45
|
-
},
|
|
46
|
-
})
|
|
47
|
-
|
|
48
|
-
const batchToolCalls: PendingToolCallData[] = [
|
|
49
|
-
{
|
|
50
|
-
toolExecutionRequestId: 'exec-a',
|
|
51
|
-
toolName: 'create_ticket',
|
|
52
|
-
toolTitle: 'Create ticket',
|
|
53
|
-
toolExplanation: 'Opens a support ticket',
|
|
54
|
-
requiresApproval: true,
|
|
55
|
-
approvalType: 'CLIENT',
|
|
56
|
-
toolCallArguments: { subject: 'Printer down' },
|
|
57
|
-
},
|
|
58
|
-
{
|
|
59
|
-
toolExecutionRequestId: 'exec-b',
|
|
60
|
-
toolName: 'read_kb',
|
|
61
|
-
requiresApproval: false,
|
|
62
|
-
approvalType: null,
|
|
63
|
-
toolCallArguments: null,
|
|
64
|
-
},
|
|
65
|
-
]
|
|
66
|
-
|
|
67
|
-
describe('MessageSegmentAccumulator — golden sequences', () => {
|
|
68
|
-
it('coalesces consecutive text deltas into one text segment', () => {
|
|
69
|
-
const acc = createMessageSegmentAccumulator()
|
|
70
|
-
acc.appendText('Hello')
|
|
71
|
-
acc.appendText(', ')
|
|
72
|
-
acc.appendText('world')
|
|
73
|
-
expect(acc.getSegments()).toMatchSnapshot()
|
|
74
|
-
})
|
|
75
|
-
|
|
76
|
-
it('coalesces thinking deltas, and text after thinking starts a NEW segment', () => {
|
|
77
|
-
const acc = createMessageSegmentAccumulator()
|
|
78
|
-
acc.appendThinking('Let me think')
|
|
79
|
-
acc.appendThinking('… ok.')
|
|
80
|
-
acc.appendText('Answer: ')
|
|
81
|
-
acc.appendText('42')
|
|
82
|
-
// A late thinking delta after text starts ANOTHER thinking segment
|
|
83
|
-
// (append-to-last only merges into a trailing segment of the same type).
|
|
84
|
-
acc.appendThinking('post-answer thought')
|
|
85
|
-
expect(acc.getSegments()).toMatchSnapshot()
|
|
86
|
-
})
|
|
87
|
-
|
|
88
|
-
it('merges EXECUTING_TOOL → EXECUTED_TOOL in place by toolExecutionRequestId (title restored)', () => {
|
|
89
|
-
const acc = createMessageSegmentAccumulator()
|
|
90
|
-
acc.appendText('Running a cleanup now.')
|
|
91
|
-
acc.addToolExecution(executing('exec-1'))
|
|
92
|
-
acc.addToolExecution(executed('exec-1'))
|
|
93
|
-
expect(acc.getSegments()).toMatchSnapshot()
|
|
94
|
-
})
|
|
95
|
-
|
|
96
|
-
it('pairs by (integratedToolType, toolFunction) when no exec id is present (older backends)', () => {
|
|
97
|
-
const acc = createMessageSegmentAccumulator()
|
|
98
|
-
acc.addToolExecution(executing(undefined))
|
|
99
|
-
acc.addToolExecution(executed(undefined))
|
|
100
|
-
expect(acc.getSegments()).toMatchSnapshot()
|
|
101
|
-
})
|
|
102
|
-
|
|
103
|
-
it('EXECUTED with no prior EXECUTING lands as a standalone completed segment', () => {
|
|
104
|
-
const acc = createMessageSegmentAccumulator()
|
|
105
|
-
acc.addToolExecution(executed('exec-orphan'))
|
|
106
|
-
expect(acc.getSegments()).toMatchSnapshot()
|
|
107
|
-
})
|
|
108
|
-
|
|
109
|
-
it('CHARACTERIZATION: a LATE standalone EXECUTING after its EXECUTED pushes a NEW executing segment (no in-place downgrade, but a duplicate card appears)', () => {
|
|
110
|
-
// The no-downgrade rule is enforced INSIDE approval batches
|
|
111
|
-
// (applyExecutionToBatch); for standalone segments a redelivered
|
|
112
|
-
// EXECUTING after EXECUTED does NOT downgrade the merged segment —
|
|
113
|
-
// it appends a fresh EXECUTING segment. Recording as-is, see
|
|
114
|
-
// unification plan.
|
|
115
|
-
const acc = createMessageSegmentAccumulator()
|
|
116
|
-
acc.addToolExecution(executing('exec-1'))
|
|
117
|
-
acc.addToolExecution(executed('exec-1'))
|
|
118
|
-
acc.addToolExecution(executing('exec-1'))
|
|
119
|
-
expect(acc.getSegments()).toMatchSnapshot()
|
|
120
|
-
})
|
|
121
|
-
|
|
122
|
-
it('single approval request → updateApprovalStatus flips status and stamps resolvedByName only on batches', () => {
|
|
123
|
-
const acc = createMessageSegmentAccumulator({
|
|
124
|
-
onApprove: async () => {},
|
|
125
|
-
onReject: async () => {},
|
|
126
|
-
})
|
|
127
|
-
acc.addApprovalRequest('req-1', 'systemctl restart nats', 'Restart broker', 'CLIENT')
|
|
128
|
-
acc.updateApprovalStatus('req-1', 'approved', 'Jane Admin')
|
|
129
|
-
expect(acc.getSegments()).toMatchSnapshot()
|
|
130
|
-
})
|
|
131
|
-
|
|
132
|
-
it('tracked (pending) approval + processApprovalResult appends a resolved segment', () => {
|
|
133
|
-
const acc = createMessageSegmentAccumulator()
|
|
134
|
-
acc.trackApprovalRequest('req-2', {
|
|
135
|
-
command: 'rm -rf /tmp/cache',
|
|
136
|
-
explanation: 'Clear caches',
|
|
137
|
-
approvalType: 'CLIENT',
|
|
138
|
-
})
|
|
139
|
-
const result = acc.processApprovalResult('req-2', false, 'CLIENT')
|
|
140
|
-
expect({
|
|
141
|
-
segments: acc.getSegments(),
|
|
142
|
-
pendingData: result?.pendingData ?? null,
|
|
143
|
-
hasPendingAfter: acc.hasPendingApprovals(),
|
|
144
|
-
}).toMatchSnapshot()
|
|
145
|
-
})
|
|
146
|
-
|
|
147
|
-
it('batch approval with per-tool executions merged into the batch (no standalone segments)', () => {
|
|
148
|
-
const acc = createMessageSegmentAccumulator()
|
|
149
|
-
acc.addApprovalBatch('batch-1', 'CLIENT', batchToolCalls)
|
|
150
|
-
acc.updateApprovalStatus('batch-1', 'approved', 'Jane Admin')
|
|
151
|
-
// Executions matching the batch's toolExecutionRequestIds merge into
|
|
152
|
-
// `executions` instead of pushing standalone tool_execution segments.
|
|
153
|
-
acc.addToolExecution(executing('exec-a', { toolFunction: 'create_ticket' }))
|
|
154
|
-
acc.addToolExecution(
|
|
155
|
-
executed('exec-a', { toolFunction: 'create_ticket', result: 'Ticket #77 created' }),
|
|
156
|
-
)
|
|
157
|
-
acc.addToolExecution(executing('exec-b', { toolFunction: 'read_kb' }))
|
|
158
|
-
expect(acc.getSegments()).toMatchSnapshot()
|
|
159
|
-
})
|
|
160
|
-
|
|
161
|
-
it('batch no-downgrade rule: a redelivered EXECUTING after EXECUTED keeps the done slot', () => {
|
|
162
|
-
const acc = createMessageSegmentAccumulator()
|
|
163
|
-
acc.addApprovalBatch('batch-1', 'CLIENT', batchToolCalls, 'approved')
|
|
164
|
-
acc.addToolExecution(executed('exec-a', { result: 'done!' }))
|
|
165
|
-
// Late/redelivered EXECUTING for the same exec id — must NOT flip
|
|
166
|
-
// the slot back to 'executing'.
|
|
167
|
-
acc.addToolExecution(executing('exec-a'))
|
|
168
|
-
expect(acc.getSegments()).toMatchSnapshot()
|
|
169
|
-
})
|
|
170
|
-
|
|
171
|
-
it('addApprovalBatch upserts by approvalRequestId (replay path produces ONE batch segment)', () => {
|
|
172
|
-
const acc = createMessageSegmentAccumulator()
|
|
173
|
-
acc.addApprovalBatch('batch-1', 'CLIENT', batchToolCalls, 'pending')
|
|
174
|
-
acc.addApprovalBatch('batch-1', 'CLIENT', batchToolCalls, 'approved', {
|
|
175
|
-
'exec-a': { status: 'done', result: 'ok', success: true },
|
|
176
|
-
}, 'Jane Admin')
|
|
177
|
-
expect(acc.getSegments()).toMatchSnapshot()
|
|
178
|
-
})
|
|
179
|
-
|
|
180
|
-
it('implicit resolvePendingApprovalForExecution: a tool execution flips the LATEST pending single approval (NATS-observer behavior)', () => {
|
|
181
|
-
// An observer (technician mirroring a client chat) may never receive
|
|
182
|
-
// APPROVAL_RESULT — only the tool's EXECUTING/EXECUTED events. The
|
|
183
|
-
// accumulator treats a tool execution as implicit approval of the most
|
|
184
|
-
// recent still-pending single approval gate. Pinning explicitly.
|
|
185
|
-
const acc = createMessageSegmentAccumulator()
|
|
186
|
-
acc.addApprovalRequest('req-old', 'echo old', undefined, 'CLIENT', 'rejected')
|
|
187
|
-
acc.addApprovalRequest('req-1', 'systemctl restart nats', undefined, 'CLIENT', 'pending')
|
|
188
|
-
acc.addToolExecution(executing('exec-9'))
|
|
189
|
-
expect(acc.getSegments()).toMatchSnapshot()
|
|
190
|
-
})
|
|
191
|
-
|
|
192
|
-
it('implicit approval flips ONLY the latest pending gate, earlier pending gates stay pending', () => {
|
|
193
|
-
const acc = createMessageSegmentAccumulator()
|
|
194
|
-
acc.addApprovalRequest('req-1', 'first', undefined, 'CLIENT', 'pending')
|
|
195
|
-
acc.addApprovalRequest('req-2', 'second', undefined, 'CLIENT', 'pending')
|
|
196
|
-
acc.addToolExecution(executing('exec-1'))
|
|
197
|
-
expect(acc.getSegments()).toMatchSnapshot()
|
|
198
|
-
})
|
|
199
|
-
|
|
200
|
-
it('implicit approval does NOT touch approval_batch segments (handled by applyExecutionToBatch only)', () => {
|
|
201
|
-
const acc = createMessageSegmentAccumulator()
|
|
202
|
-
acc.addApprovalBatch('batch-1', 'CLIENT', batchToolCalls, 'pending')
|
|
203
|
-
// exec id NOT in the batch → standalone segment path; batch stays pending.
|
|
204
|
-
acc.addToolExecution(executing('exec-unrelated'))
|
|
205
|
-
expect(acc.getSegments()).toMatchSnapshot()
|
|
206
|
-
})
|
|
207
|
-
|
|
208
|
-
it('context compaction start → complete replaces the started segment in place', () => {
|
|
209
|
-
const acc = createMessageSegmentAccumulator()
|
|
210
|
-
acc.appendText('Before compaction')
|
|
211
|
-
acc.addContextCompaction()
|
|
212
|
-
acc.completeContextCompaction('Summarized 34 messages')
|
|
213
|
-
acc.appendText('After compaction')
|
|
214
|
-
expect(acc.getSegments()).toMatchSnapshot()
|
|
215
|
-
})
|
|
216
|
-
|
|
217
|
-
it('completeContextCompaction with no started segment pushes a completed one', () => {
|
|
218
|
-
const acc = createMessageSegmentAccumulator()
|
|
219
|
-
acc.completeContextCompaction(undefined)
|
|
220
|
-
expect(acc.getSegments()).toMatchSnapshot()
|
|
221
|
-
})
|
|
222
|
-
|
|
223
|
-
it('error segments append verbatim', () => {
|
|
224
|
-
const acc = createMessageSegmentAccumulator()
|
|
225
|
-
acc.appendText('partial answer')
|
|
226
|
-
acc.addError('Agent crashed', 'boom')
|
|
227
|
-
expect(acc.getSegments()).toMatchSnapshot()
|
|
228
|
-
})
|
|
229
|
-
|
|
230
|
-
it('flushPendingApprovals emits pending cards for every tracked-but-unresolved approval', () => {
|
|
231
|
-
const acc = createMessageSegmentAccumulator()
|
|
232
|
-
acc.trackApprovalRequest('req-1', { command: 'cmd-1', approvalType: 'CLIENT' })
|
|
233
|
-
acc.trackApprovalRequest('req-2', { command: 'cmd-2', explanation: 'why', approvalType: 'ADMIN' })
|
|
234
|
-
expect(acc.flushPendingApprovals()).toMatchSnapshot()
|
|
235
|
-
})
|
|
236
|
-
|
|
237
|
-
it('replaySegments reproduces a mixed recorded segment array (idempotent replay)', () => {
|
|
238
|
-
const acc = createMessageSegmentAccumulator()
|
|
239
|
-
acc.appendThinking('hmm')
|
|
240
|
-
acc.appendText('Sure — ')
|
|
241
|
-
acc.addToolExecution(executing('exec-1'))
|
|
242
|
-
acc.addToolExecution(executed('exec-1'))
|
|
243
|
-
acc.addApprovalBatch('batch-1', 'CLIENT', batchToolCalls, 'approved', {
|
|
244
|
-
'exec-a': { status: 'done', result: 'ok', success: true },
|
|
245
|
-
})
|
|
246
|
-
acc.appendText('done.')
|
|
247
|
-
const recorded = acc.getSegments()
|
|
248
|
-
|
|
249
|
-
const replayed = createMessageSegmentAccumulator().replaySegments(recorded)
|
|
250
|
-
expect(replayed).toMatchSnapshot()
|
|
251
|
-
})
|
|
252
|
-
|
|
253
|
-
it('initializeWithState continues an incomplete message across reconnect', () => {
|
|
254
|
-
const first = createMessageSegmentAccumulator()
|
|
255
|
-
first.appendText('partial ')
|
|
256
|
-
first.addToolExecution(executing('exec-1'))
|
|
257
|
-
const state = first.getState()
|
|
258
|
-
|
|
259
|
-
const resumed = createMessageSegmentAccumulator()
|
|
260
|
-
resumed.initializeWithState({
|
|
261
|
-
existingSegments: state.segments,
|
|
262
|
-
pendingApprovals: state.pendingApprovals,
|
|
263
|
-
executingTools: state.executingTools,
|
|
264
|
-
})
|
|
265
|
-
// The EXECUTED for the pre-reconnect EXECUTING merges into it.
|
|
266
|
-
resumed.addToolExecution(executed('exec-1'))
|
|
267
|
-
resumed.appendText('and the rest.')
|
|
268
|
-
expect(resumed.getSegments()).toMatchSnapshot()
|
|
269
|
-
})
|
|
270
|
-
})
|