@flamingo-stack/openframe-frontend-core 0.0.508 → 0.0.509

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 (306) hide show
  1. package/dist/chat-protocol/decode.d.ts +58 -0
  2. package/dist/chat-protocol/decode.d.ts.map +1 -0
  3. package/dist/chat-protocol/encode.d.ts +44 -0
  4. package/dist/chat-protocol/encode.d.ts.map +1 -0
  5. package/dist/chat-protocol/env-flag.d.ts +35 -0
  6. package/dist/chat-protocol/env-flag.d.ts.map +1 -0
  7. package/dist/chat-protocol/events.d.ts +216 -0
  8. package/dist/chat-protocol/events.d.ts.map +1 -0
  9. package/dist/chat-protocol/frames.d.ts +213 -0
  10. package/dist/chat-protocol/frames.d.ts.map +1 -0
  11. package/dist/chat-protocol/index.cjs +506 -0
  12. package/dist/chat-protocol/index.cjs.map +1 -0
  13. package/dist/chat-protocol/index.d.ts +18 -0
  14. package/dist/chat-protocol/index.d.ts.map +1 -0
  15. package/dist/chat-protocol/index.js +490 -0
  16. package/dist/chat-protocol/index.js.map +1 -0
  17. package/dist/chat-protocol/ip-normalize.d.ts +44 -0
  18. package/dist/chat-protocol/ip-normalize.d.ts.map +1 -0
  19. package/dist/chat-protocol/nats-decoder.d.ts +26 -0
  20. package/dist/chat-protocol/nats-decoder.d.ts.map +1 -0
  21. package/dist/{chunk-3SQ5KXHQ.cjs → chunk-2T4GTV25.cjs} +9 -9
  22. package/dist/{chunk-3SQ5KXHQ.cjs.map → chunk-2T4GTV25.cjs.map} +1 -1
  23. package/dist/{chunk-CWAOV2FK.cjs → chunk-3CGZPAGR.cjs} +3 -3
  24. package/dist/{chunk-CWAOV2FK.cjs.map → chunk-3CGZPAGR.cjs.map} +1 -1
  25. package/dist/{chunk-TLMJHMXJ.js → chunk-3QSR22IC.js} +12 -12
  26. package/dist/chunk-3QSR22IC.js.map +1 -0
  27. package/dist/{chunk-TWYNR4TZ.cjs → chunk-4G5TLHJD.cjs} +7141 -4799
  28. package/dist/chunk-4G5TLHJD.cjs.map +1 -0
  29. package/dist/{chunk-ADPMHWOE.js → chunk-72XAID7Y.js} +4 -4
  30. package/dist/{chunk-PAGKRNWK.js → chunk-7WZHBQ4J.js} +2 -2
  31. package/dist/{chunk-OCEKO5CW.js → chunk-AEELJC4N.js} +12106 -9764
  32. package/dist/chunk-AEELJC4N.js.map +1 -0
  33. package/dist/{chunk-Z42BGM6Q.cjs → chunk-BT2WWDFP.cjs} +5 -5
  34. package/dist/{chunk-Z42BGM6Q.cjs.map → chunk-BT2WWDFP.cjs.map} +1 -1
  35. package/dist/{chunk-7LEL3RIX.cjs → chunk-C7OY3PQP.cjs} +11 -11
  36. package/dist/{chunk-7LEL3RIX.cjs.map → chunk-C7OY3PQP.cjs.map} +1 -1
  37. package/dist/{chunk-RHGGEKPQ.cjs → chunk-CIOLOQKQ.cjs} +4 -4
  38. package/dist/{chunk-RHGGEKPQ.cjs.map → chunk-CIOLOQKQ.cjs.map} +1 -1
  39. package/dist/{chunk-TSZHM74B.js → chunk-DC2TKS7C.js} +2 -2
  40. package/dist/{chunk-M2GOR3XQ.cjs → chunk-DYPYR7FR.cjs} +87 -87
  41. package/dist/{chunk-M2GOR3XQ.cjs.map → chunk-DYPYR7FR.cjs.map} +1 -1
  42. package/dist/{chunk-I64ABCDX.js → chunk-I2B6X77L.js} +2 -2
  43. package/dist/{chunk-NIZDKTGL.cjs → chunk-K5MSCMDK.cjs} +37 -37
  44. package/dist/{chunk-NIZDKTGL.cjs.map → chunk-K5MSCMDK.cjs.map} +1 -1
  45. package/dist/{chunk-DTYRYB2N.js → chunk-K6QTZ7CZ.js} +2 -2
  46. package/dist/{chunk-4LTMXDUS.js → chunk-KBN7PMFT.js} +2 -2
  47. package/dist/{chunk-TBMV7I5N.js → chunk-KXZO2WZX.js} +2 -2
  48. package/dist/{chunk-QTYZMP6D.cjs → chunk-LBAKLT6T.cjs} +62 -62
  49. package/dist/chunk-LBAKLT6T.cjs.map +1 -0
  50. package/dist/{chunk-KEBYLU3U.js → chunk-LFUPI7VX.js} +2 -2
  51. package/dist/{chunk-LDTR4IVZ.cjs → chunk-M37PBG4N.cjs} +31 -31
  52. package/dist/{chunk-LDTR4IVZ.cjs.map → chunk-M37PBG4N.cjs.map} +1 -1
  53. package/dist/{chunk-WA7RR64F.js → chunk-PO5O654H.js} +6 -5
  54. package/dist/chunk-PO5O654H.js.map +1 -0
  55. package/dist/{chunk-WK4N5VBX.cjs → chunk-QJJISLPA.cjs} +28 -27
  56. package/dist/chunk-QJJISLPA.cjs.map +1 -0
  57. package/dist/{chunk-2F44PLTV.cjs → chunk-T7MCSA55.cjs} +26 -26
  58. package/dist/{chunk-2F44PLTV.cjs.map → chunk-T7MCSA55.cjs.map} +1 -1
  59. package/dist/{chunk-WJQHJD7J.js → chunk-TWSKTJHW.js} +2 -2
  60. package/dist/{chunk-ZSQHZYCO.cjs → chunk-UQDMJ6DH.cjs} +13 -13
  61. package/dist/{chunk-ZSQHZYCO.cjs.map → chunk-UQDMJ6DH.cjs.map} +1 -1
  62. package/dist/{chunk-TZRUCD56.js → chunk-V3ZAAFE3.js} +5 -5
  63. package/dist/{chunk-LRGHJPET.js → chunk-XGIFOHKE.js} +2 -2
  64. package/dist/{chunk-POOMO3PA.cjs → chunk-YQEPRYT5.cjs} +7 -7
  65. package/dist/{chunk-POOMO3PA.cjs.map → chunk-YQEPRYT5.cjs.map} +1 -1
  66. package/dist/components/case-studies/index.cjs +8 -8
  67. package/dist/components/case-studies/index.js +2 -2
  68. package/dist/components/chat/chat-message-enhanced.d.ts.map +1 -1
  69. package/dist/components/chat/chat-message-list.d.ts.map +1 -1
  70. package/dist/components/chat/hooks/index.d.ts +0 -1
  71. package/dist/components/chat/hooks/index.d.ts.map +1 -1
  72. package/dist/components/chat/hooks/use-chat.d.ts.map +1 -1
  73. package/dist/components/chat/hooks/use-chunk-catchup.d.ts.map +1 -1
  74. package/dist/components/chat/hooks/use-nats-chat-adapter.d.ts +15 -48
  75. package/dist/components/chat/hooks/use-nats-chat-adapter.d.ts.map +1 -1
  76. package/dist/components/chat/hooks/use-sse-chat-adapter.d.ts +10 -57
  77. package/dist/components/chat/hooks/use-sse-chat-adapter.d.ts.map +1 -1
  78. package/dist/components/chat/index.cjs +10 -2
  79. package/dist/components/chat/index.cjs.map +1 -1
  80. package/dist/components/chat/index.d.ts +1 -0
  81. package/dist/components/chat/index.d.ts.map +1 -1
  82. package/dist/components/chat/index.js +21 -13
  83. package/dist/components/chat/stream/chat-dialog-store.d.ts +184 -0
  84. package/dist/components/chat/stream/chat-dialog-store.d.ts.map +1 -0
  85. package/dist/components/chat/stream/chat-stream-reducer.d.ts +380 -0
  86. package/dist/components/chat/stream/chat-stream-reducer.d.ts.map +1 -0
  87. package/dist/components/chat/stream/delta-batcher.d.ts +51 -0
  88. package/dist/components/chat/stream/delta-batcher.d.ts.map +1 -0
  89. package/dist/components/chat/stream/index.d.ts +17 -0
  90. package/dist/components/chat/stream/index.d.ts.map +1 -0
  91. package/dist/components/chat/stream/message-mutations.d.ts +77 -0
  92. package/dist/components/chat/stream/message-mutations.d.ts.map +1 -0
  93. package/dist/components/chat/stream/use-chat-stream-reducer.d.ts +22 -0
  94. package/dist/components/chat/stream/use-chat-stream-reducer.d.ts.map +1 -0
  95. package/dist/components/chat/types/api.types.d.ts +1 -81
  96. package/dist/components/chat/types/api.types.d.ts.map +1 -1
  97. package/dist/components/chat/types/processing.types.d.ts +2 -93
  98. package/dist/components/chat/types/processing.types.d.ts.map +1 -1
  99. package/dist/components/chat/types/unified-chat-state.types.d.ts +15 -0
  100. package/dist/components/chat/types/unified-chat-state.types.d.ts.map +1 -1
  101. package/dist/components/chat/utils/extract-incomplete-message-state.d.ts +34 -6
  102. package/dist/components/chat/utils/extract-incomplete-message-state.d.ts.map +1 -1
  103. package/dist/components/chat/utils/history-merge.d.ts.map +1 -1
  104. package/dist/components/chat/utils/index.d.ts +2 -3
  105. package/dist/components/chat/utils/index.d.ts.map +1 -1
  106. package/dist/components/chat/utils/process-historical-messages.d.ts +59 -11
  107. package/dist/components/chat/utils/process-historical-messages.d.ts.map +1 -1
  108. package/dist/components/contact/index.cjs +3 -3
  109. package/dist/components/contact/index.js +2 -2
  110. package/dist/components/docs/index.cjs +5 -5
  111. package/dist/components/docs/index.js +4 -4
  112. package/dist/components/embeds/index.cjs +3 -3
  113. package/dist/components/embeds/index.js +2 -2
  114. package/dist/components/faq/index.cjs +3 -3
  115. package/dist/components/faq/index.js +2 -2
  116. package/dist/components/features/index.cjs +2 -2
  117. package/dist/components/features/index.js +1 -1
  118. package/dist/components/help-center-pages/index.cjs +22 -22
  119. package/dist/components/help-center-pages/index.js +13 -13
  120. package/dist/components/index.cjs +174 -132
  121. package/dist/components/index.cjs.map +1 -1
  122. package/dist/components/index.js +65 -23
  123. package/dist/components/index.js.map +1 -1
  124. package/dist/components/meeting-scheduler/index.cjs +34 -34
  125. package/dist/components/meeting-scheduler/index.js +3 -3
  126. package/dist/components/navigation/index.cjs +2 -2
  127. package/dist/components/navigation/index.js +1 -1
  128. package/dist/components/onboarding-guides/index.cjs +5 -5
  129. package/dist/components/onboarding-guides/index.js +4 -4
  130. package/dist/components/onboarding-guides/onboarding-guide-detail-view.d.ts.map +1 -1
  131. package/dist/components/related-content/index.cjs +3 -3
  132. package/dist/components/related-content/index.js +2 -2
  133. package/dist/components/shared/product-release/release-detail-page.d.ts.map +1 -1
  134. package/dist/components/tickets/index.cjs +6 -6
  135. package/dist/components/tickets/index.js +5 -5
  136. package/dist/components/ui/index.cjs +44 -2
  137. package/dist/components/ui/index.cjs.map +1 -1
  138. package/dist/components/ui/index.d.ts +1 -2
  139. package/dist/components/ui/index.d.ts.map +1 -1
  140. package/dist/components/ui/index.js +55 -13
  141. package/dist/components/ui/markdown/base-components.d.ts +51 -0
  142. package/dist/components/ui/markdown/base-components.d.ts.map +1 -0
  143. package/dist/components/ui/markdown/engine.d.ts +99 -0
  144. package/dist/components/ui/markdown/engine.d.ts.map +1 -0
  145. package/dist/components/ui/markdown/heading-ids.d.ts +65 -0
  146. package/dist/components/ui/markdown/heading-ids.d.ts.map +1 -0
  147. package/dist/components/ui/markdown/index.d.ts +18 -0
  148. package/dist/components/ui/markdown/index.d.ts.map +1 -0
  149. package/dist/components/ui/markdown/mermaid-diagram.d.ts +79 -0
  150. package/dist/components/ui/markdown/mermaid-diagram.d.ts.map +1 -0
  151. package/dist/components/ui/markdown/rich/embed-overrides.d.ts +14 -0
  152. package/dist/components/ui/markdown/rich/embed-overrides.d.ts.map +1 -0
  153. package/dist/components/ui/markdown/rich/rich-markdown-renderer.d.ts +40 -0
  154. package/dist/components/ui/markdown/rich/rich-markdown-renderer.d.ts.map +1 -0
  155. package/dist/components/ui/markdown/rich/shortcodes.d.ts +8 -0
  156. package/dist/components/ui/markdown/rich/shortcodes.d.ts.map +1 -0
  157. package/dist/components/ui/markdown/sanitize.d.ts +153 -0
  158. package/dist/components/ui/markdown/sanitize.d.ts.map +1 -0
  159. package/dist/components/ui/markdown/simple-markdown-renderer.d.ts +23 -0
  160. package/dist/components/ui/markdown/simple-markdown-renderer.d.ts.map +1 -0
  161. package/dist/components/ui/markdown/streaming.d.ts +78 -0
  162. package/dist/components/ui/markdown/streaming.d.ts.map +1 -0
  163. package/dist/components/ui/markdown/text-size.d.ts +34 -0
  164. package/dist/components/ui/markdown/text-size.d.ts.map +1 -0
  165. package/dist/index.cjs +60 -2
  166. package/dist/index.cjs.map +1 -1
  167. package/dist/index.js +71 -13
  168. package/dist/utils/index.cjs +169 -40
  169. package/dist/utils/index.cjs.map +1 -1
  170. package/dist/utils/index.d.ts +1 -0
  171. package/dist/utils/index.d.ts.map +1 -1
  172. package/dist/utils/index.js +162 -41
  173. package/dist/utils/index.js.map +1 -1
  174. package/dist/utils/markdown-fences.d.ts +42 -0
  175. package/dist/utils/markdown-fences.d.ts.map +1 -0
  176. package/dist/utils/markdown-heading-id.d.ts +85 -0
  177. package/dist/utils/markdown-heading-id.d.ts.map +1 -0
  178. package/dist/utils/markdown-section-extractor.d.ts.map +1 -1
  179. package/package.json +7 -1
  180. package/src/chat-protocol/__tests__/__snapshots__/nats-decoder-golden.test.ts.snap +319 -0
  181. package/src/chat-protocol/__tests__/chat-protocol.test.ts +516 -0
  182. package/src/chat-protocol/__tests__/env-flag.test.ts +52 -0
  183. package/src/chat-protocol/__tests__/ip-normalize.test.ts +136 -0
  184. package/src/chat-protocol/__tests__/nats-decoder-golden.test.ts +237 -0
  185. package/src/chat-protocol/decode.ts +278 -0
  186. package/src/chat-protocol/encode.ts +71 -0
  187. package/src/chat-protocol/env-flag.ts +39 -0
  188. package/src/chat-protocol/events.ts +254 -0
  189. package/src/chat-protocol/frames.ts +245 -0
  190. package/src/chat-protocol/index.ts +21 -0
  191. package/src/chat-protocol/ip-normalize.ts +146 -0
  192. package/src/chat-protocol/nats-decoder.ts +264 -0
  193. package/src/components/chat/.chat-message-list.md +10 -6
  194. package/src/components/chat/__tests__/chat-message-list.test.tsx +450 -9
  195. package/src/components/chat/__tests__/chat-message-streaming-memo.test.tsx +207 -0
  196. package/src/components/chat/__tests__/chat-pending-turn.test.tsx +250 -0
  197. package/src/components/chat/chat-message-enhanced.tsx +122 -20
  198. package/src/components/chat/chat-message-list.tsx +515 -133
  199. package/src/components/chat/embeddable-chat.tsx +6 -0
  200. package/src/components/chat/hooks/.index.md +30 -33
  201. package/src/components/chat/hooks/.use-nats-chat-adapter.md +36 -55
  202. package/src/components/chat/hooks/__tests__/__snapshots__/conversation-id-persistence-golden.test.ts.snap +37 -0
  203. package/src/components/chat/hooks/__tests__/__snapshots__/sse-stream-golden.test.ts.snap +0 -0
  204. package/src/components/chat/hooks/__tests__/chunk-catchup-dialog-staleness.test.ts +92 -0
  205. package/src/components/chat/hooks/__tests__/conversation-id-persistence-golden.test.ts +438 -0
  206. package/src/components/chat/hooks/__tests__/sse-stream-golden.test.ts +318 -0
  207. package/src/components/chat/hooks/index.ts +0 -1
  208. package/src/components/chat/hooks/use-chat.ts +8 -0
  209. package/src/components/chat/hooks/use-chunk-catchup.ts +26 -1
  210. package/src/components/chat/hooks/use-nats-chat-adapter.ts +254 -825
  211. package/src/components/chat/hooks/use-sse-chat-adapter.ts +380 -684
  212. package/src/components/chat/index.ts +5 -0
  213. package/src/components/chat/stream/__tests__/__snapshots__/chat-stream-reducer-golden.test.ts.snap +944 -0
  214. package/src/components/chat/stream/__tests__/chat-dialog-store.test.ts +806 -0
  215. package/src/components/chat/stream/__tests__/chat-stream-reducer-golden.test.ts +467 -0
  216. package/src/components/chat/stream/__tests__/chat-stream-reducer.test.ts +1330 -0
  217. package/src/components/chat/stream/__tests__/delta-batcher.test.ts +255 -0
  218. package/src/components/chat/stream/__tests__/use-chat-stream-reducer.test.ts +105 -0
  219. package/src/components/chat/stream/chat-dialog-store.ts +536 -0
  220. package/src/components/chat/stream/chat-stream-reducer.ts +1928 -0
  221. package/src/components/chat/stream/delta-batcher.ts +168 -0
  222. package/src/components/chat/stream/index.ts +55 -0
  223. package/src/components/chat/stream/message-mutations.ts +340 -0
  224. package/src/components/chat/stream/use-chat-stream-reducer.ts +126 -0
  225. package/src/components/chat/thinking-display.tsx +1 -1
  226. package/src/components/chat/types/.api.types.md +39 -49
  227. package/src/components/chat/types/.processing.types.md +29 -58
  228. package/src/components/chat/types/.unified-chat-state.types.md +2 -2
  229. package/src/components/chat/types/api.types.ts +6 -73
  230. package/src/components/chat/types/processing.types.ts +11 -53
  231. package/src/components/chat/types/unified-chat-state.types.ts +16 -0
  232. package/src/components/chat/utils/.extract-incomplete-message-state.md +21 -3
  233. package/src/components/chat/utils/.history-merge.md +44 -3
  234. package/src/components/chat/utils/.index.md +31 -40
  235. package/src/components/chat/utils/__tests__/__snapshots__/process-historical-messages-golden.test.ts.snap +420 -0
  236. package/src/components/chat/utils/__tests__/__snapshots__/segment-accumulator-golden.test.ts.snap +605 -0
  237. package/src/components/chat/utils/__tests__/extract-incomplete-message-state.test.ts +106 -0
  238. package/src/components/chat/utils/__tests__/history-merge.test.ts +160 -0
  239. package/src/components/chat/utils/__tests__/process-historical-messages-approvals.test.ts +61 -0
  240. package/src/components/chat/utils/__tests__/process-historical-messages-golden.test.ts +394 -0
  241. package/src/components/chat/utils/__tests__/segment-accumulator-golden.test.ts +270 -0
  242. package/src/components/chat/utils/extract-incomplete-message-state.ts +93 -6
  243. package/src/components/chat/utils/history-merge.ts +85 -5
  244. package/src/components/chat/utils/index.ts +5 -8
  245. package/src/components/chat/utils/process-historical-messages.ts +361 -385
  246. package/src/components/onboarding-guides/onboarding-guide-detail-view.tsx +22 -4
  247. package/src/components/shared/legal-document/legal-document-page.tsx +1 -1
  248. package/src/components/shared/product-release/release-detail-page.tsx +19 -4
  249. package/src/components/ui/__tests__/__snapshots__/markdown-parity.test.tsx.snap +2420 -0
  250. package/src/components/ui/__tests__/markdown-parity.test.tsx +2540 -0
  251. package/src/components/ui/index.ts +1 -2
  252. package/src/components/ui/markdown/__tests__/mermaid-security.test.ts +176 -0
  253. package/src/components/ui/markdown/__tests__/mermaid-stale-render.test.tsx +151 -0
  254. package/src/components/ui/markdown/__tests__/sanitize-invariant.test.ts +354 -0
  255. package/src/components/ui/markdown/__tests__/sanitize-render.test.tsx +118 -0
  256. package/src/components/ui/markdown/__tests__/streaming.test.tsx +436 -0
  257. package/src/components/ui/markdown/base-components.tsx +360 -0
  258. package/src/components/ui/markdown/engine.tsx +315 -0
  259. package/src/components/ui/markdown/heading-ids.ts +239 -0
  260. package/src/components/ui/markdown/index.ts +49 -0
  261. package/src/components/ui/markdown/mermaid-diagram.tsx +323 -0
  262. package/src/components/ui/markdown/rich/embed-overrides.tsx +199 -0
  263. package/src/components/ui/markdown/rich/rich-markdown-renderer.tsx +184 -0
  264. package/src/components/ui/markdown/rich/shortcodes.ts +170 -0
  265. package/src/components/ui/markdown/sanitize.ts +3584 -0
  266. package/src/components/ui/markdown/simple-markdown-renderer.tsx +28 -0
  267. package/src/components/ui/markdown/streaming.ts +390 -0
  268. package/src/components/ui/markdown/text-size.ts +106 -0
  269. package/src/components/ui/release-changelog-section.tsx +1 -1
  270. package/src/components/ui/ticket-info-section.tsx +1 -1
  271. package/src/utils/index.ts +1 -0
  272. package/src/utils/markdown-fences.ts +127 -0
  273. package/src/utils/markdown-heading-id.ts +348 -0
  274. package/src/utils/markdown-section-extractor.ts +41 -56
  275. package/dist/chunk-OCEKO5CW.js.map +0 -1
  276. package/dist/chunk-QTYZMP6D.cjs.map +0 -1
  277. package/dist/chunk-TLMJHMXJ.js.map +0 -1
  278. package/dist/chunk-TWYNR4TZ.cjs.map +0 -1
  279. package/dist/chunk-WA7RR64F.js.map +0 -1
  280. package/dist/chunk-WK4N5VBX.cjs.map +0 -1
  281. package/dist/components/chat/hooks/use-realtime-chunk-processor.d.ts +0 -6
  282. package/dist/components/chat/hooks/use-realtime-chunk-processor.d.ts.map +0 -1
  283. package/dist/components/chat/utils/chunk-parser.d.ts +0 -25
  284. package/dist/components/chat/utils/chunk-parser.d.ts.map +0 -1
  285. package/dist/components/ui/rich-markdown-renderer.d.ts +0 -34
  286. package/dist/components/ui/rich-markdown-renderer.d.ts.map +0 -1
  287. package/dist/components/ui/simple-markdown-renderer.d.ts +0 -73
  288. package/dist/components/ui/simple-markdown-renderer.d.ts.map +0 -1
  289. package/src/components/chat/hooks/.use-realtime-chunk-processor.md +0 -77
  290. package/src/components/chat/hooks/use-realtime-chunk-processor.ts +0 -489
  291. package/src/components/chat/utils/.chunk-parser.md +0 -62
  292. package/src/components/chat/utils/chunk-parser.ts +0 -262
  293. package/src/components/ui/.simple-markdown-renderer.md +0 -52
  294. package/src/components/ui/rich-markdown-renderer.tsx +0 -1223
  295. package/src/components/ui/simple-markdown-renderer.tsx +0 -964
  296. /package/dist/{chunk-ADPMHWOE.js.map → chunk-72XAID7Y.js.map} +0 -0
  297. /package/dist/{chunk-PAGKRNWK.js.map → chunk-7WZHBQ4J.js.map} +0 -0
  298. /package/dist/{chunk-TSZHM74B.js.map → chunk-DC2TKS7C.js.map} +0 -0
  299. /package/dist/{chunk-I64ABCDX.js.map → chunk-I2B6X77L.js.map} +0 -0
  300. /package/dist/{chunk-DTYRYB2N.js.map → chunk-K6QTZ7CZ.js.map} +0 -0
  301. /package/dist/{chunk-4LTMXDUS.js.map → chunk-KBN7PMFT.js.map} +0 -0
  302. /package/dist/{chunk-TBMV7I5N.js.map → chunk-KXZO2WZX.js.map} +0 -0
  303. /package/dist/{chunk-KEBYLU3U.js.map → chunk-LFUPI7VX.js.map} +0 -0
  304. /package/dist/{chunk-WJQHJD7J.js.map → chunk-TWSKTJHW.js.map} +0 -0
  305. /package/dist/{chunk-TZRUCD56.js.map → chunk-V3ZAAFE3.js.map} +0 -0
  306. /package/dist/{chunk-LRGHJPET.js.map → chunk-XGIFOHKE.js.map} +0 -0
@@ -0,0 +1,605 @@
1
+ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2
+
3
+ exports[`MessageSegmentAccumulator — golden sequences > CHARACTERIZATION: a LATE standalone EXECUTING after its EXECUTED pushes a NEW executing segment (no in-place downgrade, but a duplicate card appears) 1`] = `
4
+ [
5
+ {
6
+ "data": {
7
+ "integratedToolType": "TACTICAL_RMM",
8
+ "parameters": {
9
+ "script": "cleanup.sh",
10
+ },
11
+ "result": "Freed 2.3 GB",
12
+ "success": true,
13
+ "toolExecutionRequestId": "exec-1",
14
+ "toolExplanation": undefined,
15
+ "toolFunction": "run_script",
16
+ "toolTitle": "Run cleanup script",
17
+ "type": "EXECUTED_TOOL",
18
+ },
19
+ "type": "tool_execution",
20
+ },
21
+ {
22
+ "data": {
23
+ "integratedToolType": "TACTICAL_RMM",
24
+ "parameters": {
25
+ "script": "cleanup.sh",
26
+ },
27
+ "toolExecutionRequestId": "exec-1",
28
+ "toolFunction": "run_script",
29
+ "toolTitle": "Run cleanup script",
30
+ "type": "EXECUTING_TOOL",
31
+ },
32
+ "type": "tool_execution",
33
+ },
34
+ ]
35
+ `;
36
+
37
+ exports[`MessageSegmentAccumulator — golden sequences > EXECUTED with no prior EXECUTING lands as a standalone completed segment 1`] = `
38
+ [
39
+ {
40
+ "data": {
41
+ "integratedToolType": "TACTICAL_RMM",
42
+ "parameters": undefined,
43
+ "result": "Freed 2.3 GB",
44
+ "success": true,
45
+ "toolExecutionRequestId": "exec-orphan",
46
+ "toolExplanation": undefined,
47
+ "toolFunction": "run_script",
48
+ "toolTitle": undefined,
49
+ "type": "EXECUTED_TOOL",
50
+ },
51
+ "type": "tool_execution",
52
+ },
53
+ ]
54
+ `;
55
+
56
+ exports[`MessageSegmentAccumulator — golden sequences > addApprovalBatch upserts by approvalRequestId (replay path produces ONE batch segment) 1`] = `
57
+ [
58
+ {
59
+ "data": {
60
+ "approvalRequestId": "batch-1",
61
+ "approvalType": "CLIENT",
62
+ "executions": {
63
+ "exec-a": {
64
+ "result": "ok",
65
+ "status": "done",
66
+ "success": true,
67
+ },
68
+ },
69
+ "toolCalls": [
70
+ {
71
+ "approvalType": "CLIENT",
72
+ "requiresApproval": true,
73
+ "toolCallArguments": {
74
+ "subject": "Printer down",
75
+ },
76
+ "toolExecutionRequestId": "exec-a",
77
+ "toolExplanation": "Opens a support ticket",
78
+ "toolName": "create_ticket",
79
+ "toolTitle": "Create ticket",
80
+ },
81
+ {
82
+ "approvalType": null,
83
+ "requiresApproval": false,
84
+ "toolCallArguments": null,
85
+ "toolExecutionRequestId": "exec-b",
86
+ "toolName": "read_kb",
87
+ },
88
+ ],
89
+ },
90
+ "onApprove": undefined,
91
+ "onReject": undefined,
92
+ "resolvedByName": "Jane Admin",
93
+ "status": "approved",
94
+ "type": "approval_batch",
95
+ },
96
+ ]
97
+ `;
98
+
99
+ exports[`MessageSegmentAccumulator — golden sequences > batch approval with per-tool executions merged into the batch (no standalone segments) 1`] = `
100
+ [
101
+ {
102
+ "data": {
103
+ "approvalRequestId": "batch-1",
104
+ "approvalType": "CLIENT",
105
+ "executions": {
106
+ "exec-a": {
107
+ "result": "Ticket #77 created",
108
+ "status": "done",
109
+ "success": true,
110
+ },
111
+ "exec-b": {
112
+ "result": undefined,
113
+ "status": "executing",
114
+ "success": undefined,
115
+ },
116
+ },
117
+ "toolCalls": [
118
+ {
119
+ "approvalType": "CLIENT",
120
+ "requiresApproval": true,
121
+ "toolCallArguments": {
122
+ "subject": "Printer down",
123
+ },
124
+ "toolExecutionRequestId": "exec-a",
125
+ "toolExplanation": "Opens a support ticket",
126
+ "toolName": "create_ticket",
127
+ "toolTitle": "Create ticket",
128
+ },
129
+ {
130
+ "approvalType": null,
131
+ "requiresApproval": false,
132
+ "toolCallArguments": null,
133
+ "toolExecutionRequestId": "exec-b",
134
+ "toolName": "read_kb",
135
+ },
136
+ ],
137
+ },
138
+ "onApprove": undefined,
139
+ "onReject": undefined,
140
+ "resolvedByName": "Jane Admin",
141
+ "status": "approved",
142
+ "type": "approval_batch",
143
+ },
144
+ ]
145
+ `;
146
+
147
+ exports[`MessageSegmentAccumulator — golden sequences > batch no-downgrade rule: a redelivered EXECUTING after EXECUTED keeps the done slot 1`] = `
148
+ [
149
+ {
150
+ "data": {
151
+ "approvalRequestId": "batch-1",
152
+ "approvalType": "CLIENT",
153
+ "executions": {
154
+ "exec-a": {
155
+ "result": "done!",
156
+ "status": "done",
157
+ "success": true,
158
+ },
159
+ },
160
+ "toolCalls": [
161
+ {
162
+ "approvalType": "CLIENT",
163
+ "requiresApproval": true,
164
+ "toolCallArguments": {
165
+ "subject": "Printer down",
166
+ },
167
+ "toolExecutionRequestId": "exec-a",
168
+ "toolExplanation": "Opens a support ticket",
169
+ "toolName": "create_ticket",
170
+ "toolTitle": "Create ticket",
171
+ },
172
+ {
173
+ "approvalType": null,
174
+ "requiresApproval": false,
175
+ "toolCallArguments": null,
176
+ "toolExecutionRequestId": "exec-b",
177
+ "toolName": "read_kb",
178
+ },
179
+ ],
180
+ },
181
+ "onApprove": undefined,
182
+ "onReject": undefined,
183
+ "resolvedByName": undefined,
184
+ "status": "approved",
185
+ "type": "approval_batch",
186
+ },
187
+ ]
188
+ `;
189
+
190
+ exports[`MessageSegmentAccumulator — golden sequences > coalesces consecutive text deltas into one text segment 1`] = `
191
+ [
192
+ {
193
+ "text": "Hello, world",
194
+ "type": "text",
195
+ },
196
+ ]
197
+ `;
198
+
199
+ exports[`MessageSegmentAccumulator — golden sequences > coalesces thinking deltas, and text after thinking starts a NEW segment 1`] = `
200
+ [
201
+ {
202
+ "text": "Let me think… ok.",
203
+ "type": "thinking",
204
+ },
205
+ {
206
+ "text": "Answer: 42",
207
+ "type": "text",
208
+ },
209
+ {
210
+ "text": "post-answer thought",
211
+ "type": "thinking",
212
+ },
213
+ ]
214
+ `;
215
+
216
+ exports[`MessageSegmentAccumulator — golden sequences > completeContextCompaction with no started segment pushes a completed one 1`] = `
217
+ [
218
+ {
219
+ "status": "completed",
220
+ "summary": undefined,
221
+ "type": "context_compaction",
222
+ },
223
+ ]
224
+ `;
225
+
226
+ exports[`MessageSegmentAccumulator — golden sequences > context compaction start → complete replaces the started segment in place 1`] = `
227
+ [
228
+ {
229
+ "text": "Before compaction",
230
+ "type": "text",
231
+ },
232
+ {
233
+ "status": "completed",
234
+ "summary": "Summarized 34 messages",
235
+ "type": "context_compaction",
236
+ },
237
+ {
238
+ "text": "After compaction",
239
+ "type": "text",
240
+ },
241
+ ]
242
+ `;
243
+
244
+ exports[`MessageSegmentAccumulator — golden sequences > error segments append verbatim 1`] = `
245
+ [
246
+ {
247
+ "text": "partial answer",
248
+ "type": "text",
249
+ },
250
+ {
251
+ "details": "boom",
252
+ "title": "Agent crashed",
253
+ "type": "error",
254
+ },
255
+ ]
256
+ `;
257
+
258
+ exports[`MessageSegmentAccumulator — golden sequences > flushPendingApprovals emits pending cards for every tracked-but-unresolved approval 1`] = `
259
+ [
260
+ {
261
+ "data": {
262
+ "approvalType": "CLIENT",
263
+ "command": "cmd-1",
264
+ "explanation": undefined,
265
+ "requestId": "req-1",
266
+ },
267
+ "onApprove": undefined,
268
+ "onReject": undefined,
269
+ "status": "pending",
270
+ "type": "approval_request",
271
+ },
272
+ {
273
+ "data": {
274
+ "approvalType": "ADMIN",
275
+ "command": "cmd-2",
276
+ "explanation": "why",
277
+ "requestId": "req-2",
278
+ },
279
+ "onApprove": undefined,
280
+ "onReject": undefined,
281
+ "status": "pending",
282
+ "type": "approval_request",
283
+ },
284
+ ]
285
+ `;
286
+
287
+ exports[`MessageSegmentAccumulator — golden sequences > implicit approval does NOT touch approval_batch segments (handled by applyExecutionToBatch only) 1`] = `
288
+ [
289
+ {
290
+ "data": {
291
+ "approvalRequestId": "batch-1",
292
+ "approvalType": "CLIENT",
293
+ "toolCalls": [
294
+ {
295
+ "approvalType": "CLIENT",
296
+ "requiresApproval": true,
297
+ "toolCallArguments": {
298
+ "subject": "Printer down",
299
+ },
300
+ "toolExecutionRequestId": "exec-a",
301
+ "toolExplanation": "Opens a support ticket",
302
+ "toolName": "create_ticket",
303
+ "toolTitle": "Create ticket",
304
+ },
305
+ {
306
+ "approvalType": null,
307
+ "requiresApproval": false,
308
+ "toolCallArguments": null,
309
+ "toolExecutionRequestId": "exec-b",
310
+ "toolName": "read_kb",
311
+ },
312
+ ],
313
+ },
314
+ "onApprove": undefined,
315
+ "onReject": undefined,
316
+ "resolvedByName": undefined,
317
+ "status": "pending",
318
+ "type": "approval_batch",
319
+ },
320
+ {
321
+ "data": {
322
+ "integratedToolType": "TACTICAL_RMM",
323
+ "parameters": {
324
+ "script": "cleanup.sh",
325
+ },
326
+ "toolExecutionRequestId": "exec-unrelated",
327
+ "toolFunction": "run_script",
328
+ "toolTitle": "Run cleanup script",
329
+ "type": "EXECUTING_TOOL",
330
+ },
331
+ "type": "tool_execution",
332
+ },
333
+ ]
334
+ `;
335
+
336
+ exports[`MessageSegmentAccumulator — golden sequences > implicit approval flips ONLY the latest pending gate, earlier pending gates stay pending 1`] = `
337
+ [
338
+ {
339
+ "data": {
340
+ "approvalType": "CLIENT",
341
+ "command": "first",
342
+ "explanation": undefined,
343
+ "requestId": "req-1",
344
+ },
345
+ "onApprove": undefined,
346
+ "onReject": undefined,
347
+ "status": "pending",
348
+ "type": "approval_request",
349
+ },
350
+ {
351
+ "data": {
352
+ "approvalType": "CLIENT",
353
+ "command": "second",
354
+ "explanation": undefined,
355
+ "requestId": "req-2",
356
+ },
357
+ "onApprove": undefined,
358
+ "onReject": undefined,
359
+ "status": "approved",
360
+ "type": "approval_request",
361
+ },
362
+ {
363
+ "data": {
364
+ "integratedToolType": "TACTICAL_RMM",
365
+ "parameters": {
366
+ "script": "cleanup.sh",
367
+ },
368
+ "toolExecutionRequestId": "exec-1",
369
+ "toolFunction": "run_script",
370
+ "toolTitle": "Run cleanup script",
371
+ "type": "EXECUTING_TOOL",
372
+ },
373
+ "type": "tool_execution",
374
+ },
375
+ ]
376
+ `;
377
+
378
+ exports[`MessageSegmentAccumulator — golden sequences > implicit resolvePendingApprovalForExecution: a tool execution flips the LATEST pending single approval (NATS-observer behavior) 1`] = `
379
+ [
380
+ {
381
+ "data": {
382
+ "approvalType": "CLIENT",
383
+ "command": "echo old",
384
+ "explanation": undefined,
385
+ "requestId": "req-old",
386
+ },
387
+ "onApprove": undefined,
388
+ "onReject": undefined,
389
+ "status": "rejected",
390
+ "type": "approval_request",
391
+ },
392
+ {
393
+ "data": {
394
+ "approvalType": "CLIENT",
395
+ "command": "systemctl restart nats",
396
+ "explanation": undefined,
397
+ "requestId": "req-1",
398
+ },
399
+ "onApprove": undefined,
400
+ "onReject": undefined,
401
+ "status": "approved",
402
+ "type": "approval_request",
403
+ },
404
+ {
405
+ "data": {
406
+ "integratedToolType": "TACTICAL_RMM",
407
+ "parameters": {
408
+ "script": "cleanup.sh",
409
+ },
410
+ "toolExecutionRequestId": "exec-9",
411
+ "toolFunction": "run_script",
412
+ "toolTitle": "Run cleanup script",
413
+ "type": "EXECUTING_TOOL",
414
+ },
415
+ "type": "tool_execution",
416
+ },
417
+ ]
418
+ `;
419
+
420
+ exports[`MessageSegmentAccumulator — golden sequences > initializeWithState continues an incomplete message across reconnect 1`] = `
421
+ [
422
+ {
423
+ "text": "partial ",
424
+ "type": "text",
425
+ },
426
+ {
427
+ "data": {
428
+ "integratedToolType": "TACTICAL_RMM",
429
+ "parameters": {
430
+ "script": "cleanup.sh",
431
+ },
432
+ "result": "Freed 2.3 GB",
433
+ "success": true,
434
+ "toolExecutionRequestId": "exec-1",
435
+ "toolExplanation": undefined,
436
+ "toolFunction": "run_script",
437
+ "toolTitle": "Run cleanup script",
438
+ "type": "EXECUTED_TOOL",
439
+ },
440
+ "type": "tool_execution",
441
+ },
442
+ {
443
+ "text": "and the rest.",
444
+ "type": "text",
445
+ },
446
+ ]
447
+ `;
448
+
449
+ exports[`MessageSegmentAccumulator — golden sequences > merges EXECUTING_TOOL → EXECUTED_TOOL in place by toolExecutionRequestId (title restored) 1`] = `
450
+ [
451
+ {
452
+ "text": "Running a cleanup now.",
453
+ "type": "text",
454
+ },
455
+ {
456
+ "data": {
457
+ "integratedToolType": "TACTICAL_RMM",
458
+ "parameters": {
459
+ "script": "cleanup.sh",
460
+ },
461
+ "result": "Freed 2.3 GB",
462
+ "success": true,
463
+ "toolExecutionRequestId": "exec-1",
464
+ "toolExplanation": undefined,
465
+ "toolFunction": "run_script",
466
+ "toolTitle": "Run cleanup script",
467
+ "type": "EXECUTED_TOOL",
468
+ },
469
+ "type": "tool_execution",
470
+ },
471
+ ]
472
+ `;
473
+
474
+ exports[`MessageSegmentAccumulator — golden sequences > pairs by (integratedToolType, toolFunction) when no exec id is present (older backends) 1`] = `
475
+ [
476
+ {
477
+ "data": {
478
+ "integratedToolType": "TACTICAL_RMM",
479
+ "parameters": {
480
+ "script": "cleanup.sh",
481
+ },
482
+ "result": "Freed 2.3 GB",
483
+ "success": true,
484
+ "toolExplanation": undefined,
485
+ "toolFunction": "run_script",
486
+ "toolTitle": "Run cleanup script",
487
+ "type": "EXECUTED_TOOL",
488
+ },
489
+ "type": "tool_execution",
490
+ },
491
+ ]
492
+ `;
493
+
494
+ exports[`MessageSegmentAccumulator — golden sequences > replaySegments reproduces a mixed recorded segment array (idempotent replay) 1`] = `
495
+ [
496
+ {
497
+ "text": "hmm",
498
+ "type": "thinking",
499
+ },
500
+ {
501
+ "text": "Sure — ",
502
+ "type": "text",
503
+ },
504
+ {
505
+ "data": {
506
+ "integratedToolType": "TACTICAL_RMM",
507
+ "parameters": {
508
+ "script": "cleanup.sh",
509
+ },
510
+ "result": "Freed 2.3 GB",
511
+ "success": true,
512
+ "toolExecutionRequestId": "exec-1",
513
+ "toolExplanation": undefined,
514
+ "toolFunction": "run_script",
515
+ "toolTitle": "Run cleanup script",
516
+ "type": "EXECUTED_TOOL",
517
+ },
518
+ "type": "tool_execution",
519
+ },
520
+ {
521
+ "data": {
522
+ "approvalRequestId": "batch-1",
523
+ "approvalType": "CLIENT",
524
+ "executions": {
525
+ "exec-a": {
526
+ "result": "ok",
527
+ "status": "done",
528
+ "success": true,
529
+ },
530
+ },
531
+ "toolCalls": [
532
+ {
533
+ "approvalType": "CLIENT",
534
+ "requiresApproval": true,
535
+ "toolCallArguments": {
536
+ "subject": "Printer down",
537
+ },
538
+ "toolExecutionRequestId": "exec-a",
539
+ "toolExplanation": "Opens a support ticket",
540
+ "toolName": "create_ticket",
541
+ "toolTitle": "Create ticket",
542
+ },
543
+ {
544
+ "approvalType": null,
545
+ "requiresApproval": false,
546
+ "toolCallArguments": null,
547
+ "toolExecutionRequestId": "exec-b",
548
+ "toolName": "read_kb",
549
+ },
550
+ ],
551
+ },
552
+ "onApprove": undefined,
553
+ "onReject": undefined,
554
+ "resolvedByName": undefined,
555
+ "status": "approved",
556
+ "type": "approval_batch",
557
+ },
558
+ {
559
+ "text": "done.",
560
+ "type": "text",
561
+ },
562
+ ]
563
+ `;
564
+
565
+ exports[`MessageSegmentAccumulator — golden sequences > single approval request → updateApprovalStatus flips status and stamps resolvedByName only on batches 1`] = `
566
+ [
567
+ {
568
+ "data": {
569
+ "approvalType": "CLIENT",
570
+ "command": "systemctl restart nats",
571
+ "explanation": "Restart broker",
572
+ "requestId": "req-1",
573
+ },
574
+ "onApprove": [Function],
575
+ "onReject": [Function],
576
+ "status": "approved",
577
+ "type": "approval_request",
578
+ },
579
+ ]
580
+ `;
581
+
582
+ exports[`MessageSegmentAccumulator — golden sequences > tracked (pending) approval + processApprovalResult appends a resolved segment 1`] = `
583
+ {
584
+ "hasPendingAfter": false,
585
+ "pendingData": {
586
+ "approvalType": "CLIENT",
587
+ "command": "rm -rf /tmp/cache",
588
+ "explanation": "Clear caches",
589
+ },
590
+ "segments": [
591
+ {
592
+ "data": {
593
+ "approvalType": "CLIENT",
594
+ "command": "rm -rf /tmp/cache",
595
+ "explanation": "Clear caches",
596
+ "requestId": "req-2",
597
+ },
598
+ "onApprove": undefined,
599
+ "onReject": undefined,
600
+ "status": "rejected",
601
+ "type": "approval_request",
602
+ },
603
+ ],
604
+ }
605
+ `;
@@ -0,0 +1,106 @@
1
+ import { describe, expect, it } from 'vitest'
2
+ import type { MessageSegment, ProcessedMessage } from '../../types'
3
+ import { extractIncompleteTailState } from '../extract-incomplete-message-state'
4
+
5
+ /**
6
+ * `agentBusy` is the restore-time counterpart of a live `onAgentBusy`: after a
7
+ * reload nothing on the wire re-establishes the activity indicator, so it has
8
+ * to be derived from the persisted tail. The distinction it draws is the whole
9
+ * point — an unfinished turn can be unfinished because the AGENT is working, or
10
+ * because it is BLOCKED ON THE USER, and only the first should spin.
11
+ */
12
+
13
+ const assistant = (content: MessageSegment[]): ProcessedMessage =>
14
+ ({ id: 'a1', role: 'assistant', content }) as unknown as ProcessedMessage
15
+
16
+ const user = (): ProcessedMessage => ({ id: 'u1', role: 'user', content: 'go' }) as unknown as ProcessedMessage
17
+
18
+ const batch = (
19
+ status: string | undefined,
20
+ executions?: Record<string, { status: string }>,
21
+ ): MessageSegment =>
22
+ ({
23
+ type: 'approval_batch',
24
+ status,
25
+ data: {
26
+ approvalRequestId: 'req-1',
27
+ toolCalls: [{ toolExecutionRequestId: 'exec-1' }, { toolExecutionRequestId: 'exec-2' }],
28
+ executions,
29
+ },
30
+ }) as unknown as MessageSegment
31
+
32
+ const request = (status: string | undefined): MessageSegment =>
33
+ ({
34
+ type: 'approval_request',
35
+ status,
36
+ data: { requestId: 'req-2', command: 'rm -rf /tmp/cache', approvalType: 'CLIENT' },
37
+ }) as unknown as MessageSegment
38
+
39
+ const executing = (): MessageSegment =>
40
+ ({
41
+ type: 'tool_execution',
42
+ data: {
43
+ type: 'EXECUTING_TOOL',
44
+ integratedToolType: 'openframe',
45
+ toolFunction: 'search_machines',
46
+ toolExecutionRequestId: 'exec-9',
47
+ },
48
+ }) as unknown as MessageSegment
49
+
50
+ describe('extractIncompleteTailState → agentBusy', () => {
51
+ it('is set for an APPROVED batch whose tool calls are still running', () => {
52
+ // The reported case: the operator approves a batch, reloads, and the thread
53
+ // renders as if nothing were happening. A batch records its runs INSIDE the
54
+ // segment, so `executingTools` is empty here — gating the indicator on that
55
+ // map missed the entire approval path.
56
+ const extras = extractIncompleteTailState([user(), assistant([batch('approved')])])
57
+ expect(extras?.agentBusy).toBe(true)
58
+ })
59
+
60
+ it('is NOT set while the batch is still awaiting the user', () => {
61
+ const extras = extractIncompleteTailState([user(), assistant([batch('pending')])])
62
+ expect(extras).toBeDefined()
63
+ expect(extras?.agentBusy).toBeUndefined()
64
+ })
65
+
66
+ it('is NOT set once every call in the batch is done', () => {
67
+ const extras = extractIncompleteTailState([
68
+ user(),
69
+ assistant([batch('approved', { 'exec-1': { status: 'done' }, 'exec-2': { status: 'done' } })]),
70
+ ])
71
+ expect(extras?.agentBusy).toBeUndefined()
72
+ })
73
+
74
+ it('is set for a trailing APPROVED single request (nothing produced since)', () => {
75
+ const extras = extractIncompleteTailState([user(), assistant([request('approved')])])
76
+ expect(extras?.agentBusy).toBe(true)
77
+ })
78
+
79
+ it('is NOT set when work already followed the approved request', () => {
80
+ // Position is the only signal for a single request: work after it means the
81
+ // agent came back. Here that work is itself finished.
82
+ const extras = extractIncompleteTailState([
83
+ user(),
84
+ assistant([request('approved'), { type: 'text', text: 'done, 2 endpoints online' }]),
85
+ ])
86
+ expect(extras).toBeUndefined()
87
+ })
88
+
89
+ it('is set for a tool left mid-flight', () => {
90
+ const extras = extractIncompleteTailState([user(), assistant([executing()])])
91
+ expect(extras?.agentBusy).toBe(true)
92
+ })
93
+
94
+ it('is set for an open compaction (the agent’s own work)', () => {
95
+ const extras = extractIncompleteTailState([
96
+ user(),
97
+ assistant([{ type: 'context_compaction', status: 'started' } as unknown as MessageSegment]),
98
+ ])
99
+ expect(extras?.agentBusy).toBe(true)
100
+ })
101
+
102
+ it('reports nothing for a finished tail', () => {
103
+ const extras = extractIncompleteTailState([user(), assistant([{ type: 'text', text: 'all good' }])])
104
+ expect(extras).toBeUndefined()
105
+ })
106
+ })