@flamingo-stack/openframe-frontend-core 0.0.579 → 0.0.580-1958.5358.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (176) hide show
  1. package/dist/chat-protocol/decode.d.ts.map +1 -1
  2. package/dist/chat-protocol/events.d.ts +1 -47
  3. package/dist/chat-protocol/events.d.ts.map +1 -1
  4. package/dist/chat-protocol/index.cjs +0 -51
  5. package/dist/chat-protocol/index.cjs.map +1 -1
  6. package/dist/chat-protocol/index.js +1 -48
  7. package/dist/chat-protocol/index.js.map +1 -1
  8. package/dist/chat-protocol/leading-frames.d.ts +3 -9
  9. package/dist/chat-protocol/leading-frames.d.ts.map +1 -1
  10. package/dist/chat-protocol/nats-decoder.d.ts +0 -41
  11. package/dist/chat-protocol/nats-decoder.d.ts.map +1 -1
  12. package/dist/{chunk-OGZAFM6M.cjs → chunk-26NL6WZU.cjs} +5 -5
  13. package/dist/{chunk-OGZAFM6M.cjs.map → chunk-26NL6WZU.cjs.map} +1 -1
  14. package/dist/{chunk-QCP2KV7P.js → chunk-4SYELUXP.js} +3 -3
  15. package/dist/{chunk-4HGRXFQP.cjs → chunk-6PLACYFA.cjs} +3 -3
  16. package/dist/{chunk-4HGRXFQP.cjs.map → chunk-6PLACYFA.cjs.map} +1 -1
  17. package/dist/{chunk-FEVLXRTC.cjs → chunk-7LGVUXWE.cjs} +344 -282
  18. package/dist/chunk-7LGVUXWE.cjs.map +1 -0
  19. package/dist/{chunk-AKPLEWMH.js → chunk-A4BZ2CP4.js} +2 -2
  20. package/dist/{chunk-HHJVIHVR.cjs → chunk-COSV5FC2.cjs} +69 -69
  21. package/dist/{chunk-HHJVIHVR.cjs.map → chunk-COSV5FC2.cjs.map} +1 -1
  22. package/dist/{chunk-4WSKSNXL.js → chunk-CX62MVCV.js} +6 -6
  23. package/dist/{chunk-O2FTVZOD.js → chunk-DZAPMQ6U.js} +3 -3
  24. package/dist/{chunk-YW4WBNYV.cjs → chunk-EXUKW6GT.cjs} +24 -24
  25. package/dist/{chunk-YW4WBNYV.cjs.map → chunk-EXUKW6GT.cjs.map} +1 -1
  26. package/dist/{chunk-PWOYEUIQ.js → chunk-FN4PUTHZ.js} +5 -5
  27. package/dist/{chunk-L2CF2QZW.js → chunk-JDP5BGVO.js} +5 -5
  28. package/dist/{chunk-5Y6TPFW7.js → chunk-JFVT6XRN.js} +5 -5
  29. package/dist/{chunk-BCJQ7CJE.cjs → chunk-JHLZTU7R.cjs} +9 -9
  30. package/dist/{chunk-BCJQ7CJE.cjs.map → chunk-JHLZTU7R.cjs.map} +1 -1
  31. package/dist/{chunk-D44S2QLI.js → chunk-OSIIXSYG.js} +3 -3
  32. package/dist/{chunk-DB3TV4CW.js → chunk-PGWXVNYS.js} +3 -3
  33. package/dist/{chunk-H54EPP3P.cjs → chunk-PP3LCLWI.cjs} +4 -4
  34. package/dist/{chunk-H54EPP3P.cjs.map → chunk-PP3LCLWI.cjs.map} +1 -1
  35. package/dist/{chunk-M5GWCMYE.cjs → chunk-QIDD3LTS.cjs} +32 -32
  36. package/dist/{chunk-M5GWCMYE.cjs.map → chunk-QIDD3LTS.cjs.map} +1 -1
  37. package/dist/{chunk-3AX3ZEY3.js → chunk-QZSNH7XU.js} +1317 -1480
  38. package/dist/chunk-QZSNH7XU.js.map +1 -0
  39. package/dist/{chunk-37XJYOAY.cjs → chunk-RF65N4QA.cjs} +10 -10
  40. package/dist/{chunk-37XJYOAY.cjs.map → chunk-RF65N4QA.cjs.map} +1 -1
  41. package/dist/{chunk-36TJYCF3.cjs → chunk-S5BS2CZA.cjs} +21 -21
  42. package/dist/{chunk-36TJYCF3.cjs.map → chunk-S5BS2CZA.cjs.map} +1 -1
  43. package/dist/{chunk-QGJYWLF4.js → chunk-SN6SL4HR.js} +2 -2
  44. package/dist/{chunk-4FJSN2XW.cjs → chunk-TDM5FQZG.cjs} +10 -10
  45. package/dist/{chunk-4FJSN2XW.cjs.map → chunk-TDM5FQZG.cjs.map} +1 -1
  46. package/dist/{chunk-HGA74X35.js → chunk-TXNAZKWW.js} +3 -3
  47. package/dist/{chunk-IFYQ7DHV.cjs → chunk-UNVW4BVF.cjs} +43 -43
  48. package/dist/{chunk-IFYQ7DHV.cjs.map → chunk-UNVW4BVF.cjs.map} +1 -1
  49. package/dist/{chunk-CP5EYMNL.js → chunk-V5RWTIRS.js} +202 -140
  50. package/dist/{chunk-CP5EYMNL.js.map → chunk-V5RWTIRS.js.map} +1 -1
  51. package/dist/{chunk-RKGOVTXZ.cjs → chunk-VYQNM75R.cjs} +167 -330
  52. package/dist/chunk-VYQNM75R.cjs.map +1 -0
  53. package/dist/{chunk-PMENIZXI.js → chunk-WEUWTEQL.js} +4 -4
  54. package/dist/{chunk-H4BPHGU5.js → chunk-WW435OOF.js} +2 -2
  55. package/dist/{chunk-4LZ7B5H5.cjs → chunk-YB75SLEH.cjs} +3 -3
  56. package/dist/{chunk-4LZ7B5H5.cjs.map → chunk-YB75SLEH.cjs.map} +1 -1
  57. package/dist/{chunk-A6C5HOD7.cjs → chunk-YLYU6DDT.cjs} +15 -15
  58. package/dist/{chunk-A6C5HOD7.cjs.map → chunk-YLYU6DDT.cjs.map} +1 -1
  59. package/dist/{chunk-BW4WDUSI.js → chunk-ZNCYSM6R.js} +2 -2
  60. package/dist/{chunk-NPVDKOGD.cjs → chunk-ZQ2RBAXO.cjs} +22 -22
  61. package/dist/{chunk-NPVDKOGD.cjs.map → chunk-ZQ2RBAXO.cjs.map} +1 -1
  62. package/dist/components/case-studies/index.cjs +9 -9
  63. package/dist/components/case-studies/index.js +3 -3
  64. package/dist/components/chat/chat-message-enhanced.d.ts.map +1 -1
  65. package/dist/components/chat/index.cjs +2 -10
  66. package/dist/components/chat/index.cjs.map +1 -1
  67. package/dist/components/chat/index.d.ts +0 -1
  68. package/dist/components/chat/index.d.ts.map +1 -1
  69. package/dist/components/chat/index.js +1 -9
  70. package/dist/components/chat/stream/chat-stream-reducer.d.ts +0 -9
  71. package/dist/components/chat/stream/chat-stream-reducer.d.ts.map +1 -1
  72. package/dist/components/chat/stream/delta-batcher.d.ts +3 -3
  73. package/dist/components/chat/stream/delta-batcher.d.ts.map +1 -1
  74. package/dist/components/chat/stream/message-mutations.d.ts.map +1 -1
  75. package/dist/components/chat/types/component.types.d.ts +0 -6
  76. package/dist/components/chat/types/component.types.d.ts.map +1 -1
  77. package/dist/components/chat/types/message.types.d.ts +2 -30
  78. package/dist/components/chat/types/message.types.d.ts.map +1 -1
  79. package/dist/components/chat/types/network.types.d.ts +1 -3
  80. package/dist/components/chat/types/network.types.d.ts.map +1 -1
  81. package/dist/components/chat/types/processing.types.d.ts +1 -4
  82. package/dist/components/chat/types/processing.types.d.ts.map +1 -1
  83. package/dist/components/chat/utils/approval-display.d.ts +8 -36
  84. package/dist/components/chat/utils/approval-display.d.ts.map +1 -1
  85. package/dist/components/chat/utils/extract-incomplete-message-state.d.ts.map +1 -1
  86. package/dist/components/chat/utils/index.d.ts +1 -1
  87. package/dist/components/chat/utils/index.d.ts.map +1 -1
  88. package/dist/components/chat/utils/message-segment-accumulator.d.ts +4 -16
  89. package/dist/components/chat/utils/message-segment-accumulator.d.ts.map +1 -1
  90. package/dist/components/chat/utils/process-historical-messages.d.ts.map +1 -1
  91. package/dist/components/chat/utils/resolve-fetched-card-href.d.ts +1 -1
  92. package/dist/components/contact/index.cjs +4 -4
  93. package/dist/components/contact/index.js +3 -3
  94. package/dist/components/docs/index.cjs +7 -7
  95. package/dist/components/docs/index.js +6 -6
  96. package/dist/components/embeds/index.cjs +4 -4
  97. package/dist/components/embeds/index.js +3 -3
  98. package/dist/components/faq/index.cjs +4 -4
  99. package/dist/components/faq/index.js +3 -3
  100. package/dist/components/features/index.cjs +3 -3
  101. package/dist/components/features/index.js +2 -2
  102. package/dist/components/features/push-button-selector.d.ts +29 -1
  103. package/dist/components/features/push-button-selector.d.ts.map +1 -1
  104. package/dist/components/help-center-pages/index.cjs +23 -23
  105. package/dist/components/help-center-pages/index.js +14 -14
  106. package/dist/components/index.cjs +121 -129
  107. package/dist/components/index.cjs.map +1 -1
  108. package/dist/components/index.js +13 -21
  109. package/dist/components/index.js.map +1 -1
  110. package/dist/components/meeting-scheduler/index.cjs +21 -21
  111. package/dist/components/meeting-scheduler/index.js +2 -2
  112. package/dist/components/navigation/index.cjs +6 -6
  113. package/dist/components/navigation/index.js +5 -5
  114. package/dist/components/onboarding-guides/index.cjs +6 -6
  115. package/dist/components/onboarding-guides/index.js +5 -5
  116. package/dist/components/tickets/index.cjs +7 -7
  117. package/dist/components/tickets/index.js +6 -6
  118. package/dist/components/ui/index.cjs +3 -11
  119. package/dist/components/ui/index.cjs.map +1 -1
  120. package/dist/components/ui/index.js +2 -10
  121. package/dist/index.cjs +7 -15
  122. package/dist/index.cjs.map +1 -1
  123. package/dist/index.js +6 -14
  124. package/dist/index.js.map +1 -1
  125. package/dist/utils/index.cjs.map +1 -1
  126. package/dist/utils/index.js.map +1 -1
  127. package/package.json +1 -1
  128. package/src/chat-protocol/__tests__/__snapshots__/nats-decoder-golden.test.ts.snap +0 -80
  129. package/src/chat-protocol/__tests__/nats-decoder-golden.test.ts +1 -118
  130. package/src/chat-protocol/decode.ts +3 -4
  131. package/src/chat-protocol/events.ts +0 -51
  132. package/src/chat-protocol/leading-frames.ts +3 -9
  133. package/src/chat-protocol/nats-decoder.ts +1 -110
  134. package/src/components/chat/__tests__/chat-pending-turn.test.tsx +0 -19
  135. package/src/components/chat/chat-message-enhanced.tsx +1 -13
  136. package/src/components/chat/index.ts +0 -1
  137. package/src/components/chat/stream/__tests__/chat-stream-reducer.test.ts +0 -90
  138. package/src/components/chat/stream/chat-dialog-store.ts +1 -1
  139. package/src/components/chat/stream/chat-stream-reducer.ts +11 -49
  140. package/src/components/chat/stream/delta-batcher.ts +4 -9
  141. package/src/components/chat/stream/message-mutations.ts +0 -2
  142. package/src/components/chat/types/component.types.ts +0 -9
  143. package/src/components/chat/types/message.types.ts +0 -32
  144. package/src/components/chat/types/network.types.ts +1 -3
  145. package/src/components/chat/types/processing.types.ts +1 -4
  146. package/src/components/chat/utils/__tests__/process-historical-messages-approvals.test.ts +0 -93
  147. package/src/components/chat/utils/__tests__/process-historical-messages-golden.test.ts +1 -78
  148. package/src/components/chat/utils/approval-display.ts +8 -53
  149. package/src/components/chat/utils/extract-incomplete-message-state.ts +0 -1
  150. package/src/components/chat/utils/index.ts +1 -6
  151. package/src/components/chat/utils/message-segment-accumulator.ts +3 -37
  152. package/src/components/chat/utils/process-historical-messages.ts +7 -44
  153. package/src/components/chat/utils/resolve-fetched-card-href.ts +1 -1
  154. package/src/components/features/.push-button-selector.md +1 -1
  155. package/src/components/features/push-button-selector.tsx +130 -6
  156. package/src/stories/ChatMessageEnhanced.stories.tsx +3 -7
  157. package/dist/chunk-3AX3ZEY3.js.map +0 -1
  158. package/dist/chunk-FEVLXRTC.cjs.map +0 -1
  159. package/dist/chunk-RKGOVTXZ.cjs.map +0 -1
  160. package/dist/components/chat/guide-display.d.ts +0 -14
  161. package/dist/components/chat/guide-display.d.ts.map +0 -1
  162. package/src/components/chat/guide-display.tsx +0 -42
  163. /package/dist/{chunk-QCP2KV7P.js.map → chunk-4SYELUXP.js.map} +0 -0
  164. /package/dist/{chunk-AKPLEWMH.js.map → chunk-A4BZ2CP4.js.map} +0 -0
  165. /package/dist/{chunk-4WSKSNXL.js.map → chunk-CX62MVCV.js.map} +0 -0
  166. /package/dist/{chunk-O2FTVZOD.js.map → chunk-DZAPMQ6U.js.map} +0 -0
  167. /package/dist/{chunk-PWOYEUIQ.js.map → chunk-FN4PUTHZ.js.map} +0 -0
  168. /package/dist/{chunk-L2CF2QZW.js.map → chunk-JDP5BGVO.js.map} +0 -0
  169. /package/dist/{chunk-5Y6TPFW7.js.map → chunk-JFVT6XRN.js.map} +0 -0
  170. /package/dist/{chunk-D44S2QLI.js.map → chunk-OSIIXSYG.js.map} +0 -0
  171. /package/dist/{chunk-DB3TV4CW.js.map → chunk-PGWXVNYS.js.map} +0 -0
  172. /package/dist/{chunk-QGJYWLF4.js.map → chunk-SN6SL4HR.js.map} +0 -0
  173. /package/dist/{chunk-HGA74X35.js.map → chunk-TXNAZKWW.js.map} +0 -0
  174. /package/dist/{chunk-PMENIZXI.js.map → chunk-WEUWTEQL.js.map} +0 -0
  175. /package/dist/{chunk-H4BPHGU5.js.map → chunk-WW435OOF.js.map} +0 -0
  176. /package/dist/{chunk-BW4WDUSI.js.map → chunk-ZNCYSM6R.js.map} +0 -0
@@ -207,7 +207,7 @@ const EMPTY_STATE: ChatReducerState = Object.freeze({
207
207
  dialogTokenUsage: null,
208
208
  liveModel: null,
209
209
  approvalStatuses: Object.freeze({}) as ChatReducerState['approvalStatuses'],
210
- }) as ChatReducerState;
210
+ });
211
211
 
212
212
  /** Shared frozen empty list for a key with no send in flight. */
213
213
  const EMPTY_PENDING_ECHOES: readonly PendingEcho[] = Object.freeze([]);
@@ -51,7 +51,7 @@
51
51
  */
52
52
 
53
53
  import { escapeThinkingTags } from '../../../chat-protocol/decode';
54
- import { errorDetailsMessage, isGuideOrigin } from '../../../chat-protocol/events';
54
+ import { errorDetailsMessage } from '../../../chat-protocol/events';
55
55
  import {
56
56
  CHAT_OWNER_ADMIN,
57
57
  type ChatStreamEvent,
@@ -77,7 +77,7 @@ import type {
77
77
  UnifiedChatMessage,
78
78
  UnifiedUsageBreakdown,
79
79
  } from '../types/unified-chat-state.types';
80
- import { approvalDisplaysInline, guideApprovalOrigin } from '../utils/approval-display';
80
+ import { approvalDisplaysInline } from '../utils/approval-display';
81
81
  import {
82
82
  type MessageSegmentAccumulator,
83
83
  createMessageSegmentAccumulator,
@@ -161,15 +161,6 @@ export interface ChatReducerState {
161
161
  contextWindowMaxTokens: number | null;
162
162
  } | null;
163
163
  approvalStatuses: Record<string, ChatApprovalStatus>;
164
- /**
165
- * Hub conversation id for the Product Guide half of this dialog, learned from
166
- * a guide metadata frame. The hub mints it and requires it back on every
167
- * confirm-tool call, so a host resolving a guide approval card reads it from
168
- * here. Null until a guide turn has streamed — an approval that arrives from
169
- * history (after a reload) has no live frame to learn it from, which is why
170
- * the agent should also expose it per dialog.
171
- */
172
- guideConversationId: string | null;
173
164
  }
174
165
 
175
166
  /** Escalated-approval bookkeeping entry (mirrors the legacy processor). */
@@ -539,7 +530,6 @@ export function createChatStreamReducer(options: ChatStreamReducerOptions = {}):
539
530
  let streamingPhase: StreamingPhase = 'idle';
540
531
  let dialogTokenUsage: DialogTokenUsage | null = null;
541
532
  let liveModel: ChatReducerState['liveModel'] = null;
542
- let guideConversationId: string | null = null;
543
533
  let approvalStatuses: Record<string, ChatApprovalStatus> = {
544
534
  ...(options.approvalStatuses ?? {}),
545
535
  };
@@ -701,7 +691,6 @@ export function createChatStreamReducer(options: ChatStreamReducerOptions = {}):
701
691
  dialogTokenUsage,
702
692
  liveModel,
703
693
  approvalStatuses,
704
- guideConversationId,
705
694
  };
706
695
  }
707
696
  return stateCache;
@@ -996,17 +985,6 @@ export function createChatStreamReducer(options: ChatStreamReducerOptions = {}):
996
985
  }
997
986
 
998
987
  case 'metadata': {
999
- // A guide metadata event carries ONLY the hub's conversation id (the
1000
- // decoder strips the rest): record it and stop. Falling through would
1001
- // rebuild `liveModel` from an event with no model in it and blank the
1002
- // dialog's model badge mid-answer.
1003
- if (isGuideOrigin(event)) {
1004
- if (typeof event.conversationId === 'string' && event.conversationId) {
1005
- guideConversationId = event.conversationId;
1006
- invalidate();
1007
- }
1008
- break;
1009
- }
1010
988
  // Legacy `parseChunkToAction` action shape, reconstructed for the
1011
989
  // callback contract.
1012
990
  emit('onMetadata', {
@@ -1025,21 +1003,13 @@ export function createChatStreamReducer(options: ChatStreamReducerOptions = {}):
1025
1003
  break;
1026
1004
  }
1027
1005
 
1028
- // The three APPEND-ONLY body streams share one shape: accumulate the
1029
- // verbatim slice into the trailing segment of the matching kind (the
1030
- // accumulator coalesces), then emit. `guide-delta` is NATS-only today
1031
- // but rides the identical path so a guide body streams, coalesces and
1032
- // continues post-MESSAGE_END exactly like answer text.
1006
+ // Both APPEND-ONLY body streams share one shape: accumulate the verbatim
1007
+ // slice into the trailing segment of the matching kind (the accumulator
1008
+ // coalesces), then emit.
1033
1009
  case 'text-delta':
1034
- case 'thinking-delta':
1035
- case 'guide-delta': {
1036
- const kind = event.type === 'text-delta' ? 'text' : event.type === 'thinking-delta' ? 'thinking' : 'guide';
1037
- const segments =
1038
- kind === 'text'
1039
- ? accumulator.appendText(event.text)
1040
- : kind === 'thinking'
1041
- ? accumulator.appendThinking(event.text)
1042
- : accumulator.appendGuide(event.text);
1010
+ case 'thinking-delta': {
1011
+ const kind = event.type === 'text-delta' ? 'text' : 'thinking';
1012
+ const segments = kind === 'text' ? accumulator.appendText(event.text) : accumulator.appendThinking(event.text);
1043
1013
  // Append-mode only for *true* post-stream continuation (after a
1044
1014
  // MESSAGE_END we actually saw). Cold-start chunks (no prior
1045
1015
  // MESSAGE_START) emit cumulative segments so the consumer can spawn
@@ -1115,10 +1085,8 @@ export function createChatStreamReducer(options: ChatStreamReducerOptions = {}):
1115
1085
  const approvalType = event.approvalType ?? 'USER';
1116
1086
  const toolCalls = event.toolCalls;
1117
1087
  // Where this card renders is ONE rule, shared with the SSE kernel and
1118
- // the history replay (`approval-display`) — a Product Guide proposal
1119
- // that renders inline live must not move on the next page load.
1120
- const guideOrigin = guideApprovalOrigin(event);
1121
- const displayInline = (type: string) => approvalDisplaysInline(event, type, displayApprovalTypes);
1088
+ // the history replay (`approval-display`).
1089
+ const displayInline = (type: string) => approvalDisplaysInline(type, displayApprovalTypes);
1122
1090
 
1123
1091
  if (toolCalls && toolCalls.length > 0) {
1124
1092
  // ── Batch form ──
@@ -1150,7 +1118,6 @@ export function createChatStreamReducer(options: ChatStreamReducerOptions = {}):
1150
1118
  status,
1151
1119
  undefined,
1152
1120
  undefined,
1153
- guideOrigin,
1154
1121
  );
1155
1122
  applyAccumulated(before, segments);
1156
1123
  break;
@@ -1170,7 +1137,6 @@ export function createChatStreamReducer(options: ChatStreamReducerOptions = {}):
1170
1137
  approvalType,
1171
1138
  status,
1172
1139
  undefined,
1173
- guideOrigin,
1174
1140
  );
1175
1141
  }
1176
1142
  applyAccumulated(before, segments);
@@ -1189,10 +1155,8 @@ export function createChatStreamReducer(options: ChatStreamReducerOptions = {}):
1189
1155
  explanation,
1190
1156
  approvalType,
1191
1157
  status,
1192
- // SSE-shaped cards (a re-streamed Product Guide proposal) carry
1193
- // their body as structured rows rather than prose.
1158
+ // SSE-shaped cards carry their body as structured rows rather than prose.
1194
1159
  event.fields,
1195
- guideOrigin,
1196
1160
  );
1197
1161
  applyAccumulated(before, segments);
1198
1162
  } else {
@@ -1704,7 +1668,6 @@ export function createChatStreamReducer(options: ChatStreamReducerOptions = {}):
1704
1668
  streamingPhase = 'idle';
1705
1669
  dialogTokenUsage = null;
1706
1670
  liveModel = null;
1707
- guideConversationId = null;
1708
1671
  approvalStatuses = {};
1709
1672
  accumulator.reset();
1710
1673
  pendingEscalated.clear();
@@ -1752,7 +1715,6 @@ export function createChatStreamReducer(options: ChatStreamReducerOptions = {}):
1752
1715
  streamingPhase = 'idle';
1753
1716
  dialogTokenUsage = null;
1754
1717
  liveModel = null;
1755
- guideConversationId = null;
1756
1718
  accumulator.reset();
1757
1719
  pendingEscalated.clear();
1758
1720
  isInStream = false;
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * createDeltaBatcher — framework-free, append-only batching of the
3
- * `text-delta` / `thinking-delta` / `guide-delta` events.
3
+ * `text-delta` / `thinking-delta` events.
4
4
  *
5
5
  * WHY THIS IS SHARED (and not a hook-private detail): every host that drives a
6
6
  * `ChatStreamReducer` from a raw transport needs the SAME batching, and a
@@ -23,19 +23,14 @@
23
23
  * deltas never land on the wrong dialog/side.
24
24
  */
25
25
 
26
- import type {
27
- ChatStreamEvent,
28
- GuideDeltaEvent,
29
- TextDeltaEvent,
30
- ThinkingDeltaEvent,
31
- } from '../../../chat-protocol/events';
26
+ import type { ChatStreamEvent, TextDeltaEvent, ThinkingDeltaEvent } from '../../../chat-protocol/events';
32
27
 
33
28
  export const DELTA_FLUSH_FALLBACK_MS = 50;
34
29
 
35
- export type DeltaEvent = TextDeltaEvent | ThinkingDeltaEvent | GuideDeltaEvent;
30
+ export type DeltaEvent = TextDeltaEvent | ThinkingDeltaEvent;
36
31
 
37
32
  export function isDeltaEvent(event: ChatStreamEvent): event is DeltaEvent {
38
- return event.type === 'text-delta' || event.type === 'thinking-delta' || event.type === 'guide-delta';
33
+ return event.type === 'text-delta' || event.type === 'thinking-delta';
39
34
  }
40
35
 
41
36
  export interface CreateDeltaBatcherOptions<K> {
@@ -126,8 +126,6 @@ export function appendToTrailingAssistant(
126
126
  merged[merged.length - 1] = { type: 'text', text: tail.text + seg.text };
127
127
  } else if (seg.type === 'thinking' && tail?.type === 'thinking') {
128
128
  merged[merged.length - 1] = { type: 'thinking', text: tail.text + seg.text };
129
- } else if (seg.type === 'guide' && tail?.type === 'guide') {
130
- merged[merged.length - 1] = { type: 'guide', text: tail.text + seg.text };
131
129
  } else if (key) {
132
130
  // Block deltas must be IDEMPOTENT: an emit can be seen twice (live plus
133
131
  // the JetStream catch-up replay over hydrated history), so upsert on the
@@ -578,15 +578,6 @@ export interface ThinkingDisplayProps extends HTMLAttributes<HTMLDivElement> {
578
578
  isStreaming?: boolean;
579
579
  }
580
580
 
581
- // ========== Guide Display Props ==========
582
-
583
- export interface GuideDisplayProps extends HTMLAttributes<HTMLDivElement> {
584
- /** Rendered guide body. `ChatMessageEnhanced` passes the SAME markdown
585
- * output a text segment gets, so `[card://]` cards and mention chips
586
- * behave identically inside a guide. */
587
- children?: ReactNode;
588
- }
589
-
590
581
  // ========== Ask Display Props ==========
591
582
 
592
583
  export interface AskDisplayProps extends Omit<HTMLAttributes<HTMLDivElement>, 'onSelect'> {
@@ -10,7 +10,6 @@ import type { AssistantType, AuthorType, ChatApprovalStatus, MessageOwner } from
10
10
  export const MESSAGE_TYPE = {
11
11
  TEXT: 'TEXT',
12
12
  THINKING: 'THINKING',
13
- GUIDE: 'GUIDE',
14
13
  ASK: 'ASK',
15
14
  EXECUTING_TOOL: 'EXECUTING_TOOL',
16
15
  EXECUTED_TOOL: 'EXECUTED_TOOL',
@@ -114,12 +113,6 @@ export interface ApprovalRequestData {
114
113
  requestId?: string;
115
114
  approvalRequestId?: string;
116
115
  approvalType?: string;
117
- /** `'guide'` when the card came from a Product Guide frame (see `GuideOrigin`
118
- * in `chat-protocol/events`). Carried onto the segment because hosts route
119
- * and filter approvals by where they came from: mingo lifts its OWN pending
120
- * cards into a sticky footer, and a guide card must stay inline in the turn
121
- * the way it does in the hub's chat. */
122
- origin?: 'guide';
123
116
  }
124
117
 
125
118
  export interface ApprovalResultData {
@@ -227,11 +220,6 @@ export interface ApprovalBatchData {
227
220
  * batch itself is approved.
228
221
  */
229
222
  executions?: Record<string, ApprovalBatchExecutionState>;
230
- /** `'guide'` when the batch came from a Product Guide `approval_batch` frame.
231
- * Same contract and the same reasons as `ApprovalRequestData.origin` — the
232
- * hub groups multiple proposals into one card, and that card resolves
233
- * through the hub exactly like a single one. */
234
- origin?: 'guide';
235
223
  }
236
224
 
237
225
  /** Approve/reject handler stamped onto approval segments. MAY resolve a
@@ -254,15 +242,6 @@ export type ThinkingSegment = {
254
242
  text: string;
255
243
  };
256
244
 
257
- /** Guide answer body — the assistant's how-to/documentation reply, rendered as
258
- * a titled "OpenFrame Guide" card instead of a bare paragraph. `text` is
259
- * markdown, streamed in fragments like a `text` segment and coalesced by the
260
- * accumulator. */
261
- export type GuideSegment = {
262
- type: 'guide';
263
- text: string;
264
- };
265
-
266
245
  /** One reading the assistant offers in an `ask` card. `label` is BOTH the row's
267
246
  * headline and the exact text sent back when the row is picked — the backend's
268
247
  * guide classifier resolves the user's next message against the labels it
@@ -380,7 +359,6 @@ export type ContextCompactionSegment = {
380
359
  export type MessageSegment =
381
360
  | TextSegment
382
361
  | ThinkingSegment
383
- | GuideSegment
384
362
  | AskSegment
385
363
  | ToolExecutionSegment
386
364
  | ApprovalRequestSegment
@@ -413,15 +391,6 @@ export interface ThinkingMessageData extends MessageDataBase {
413
391
  text?: string;
414
392
  }
415
393
 
416
- export interface GuideMessageData extends MessageDataBase {
417
- type: 'GUIDE';
418
- text?: string;
419
- /** Persisted Product Guide frame (GraphQL `GuideData.payload`, a JSON scalar)
420
- * for rows that carry a card instead of answer text — replayed through the
421
- * live path's `guideFrameEvent`. */
422
- payload?: Record<string, unknown>;
423
- }
424
-
425
394
  /** Persisted `ASK` row (GraphQL `AskData`). `text` is the intro sentence, which
426
395
  * history replays as a text segment ahead of the card — same split the live
427
396
  * `ASK` chunk carries. */
@@ -563,7 +532,6 @@ export interface ContextCompactionEndMessageData extends MessageDataBase {
563
532
  export type MessageData =
564
533
  | TextMessageData
565
534
  | ThinkingMessageData
566
- | GuideMessageData
567
535
  | AskMessageData
568
536
  | ExecutingToolMessageData
569
537
  | ExecutedToolMessageData
@@ -63,9 +63,7 @@ export interface ChunkData {
63
63
  /** Batch-approval rows. Coerced by `normalizeToolCalls` in the NATS decoder
64
64
  * — the wire shape is per-backend, so entries stay opaque here. */
65
65
  toolCalls?: unknown[];
66
- /** On GUIDE chunks: a Product Guide frame the agent re-streamed verbatim from
67
- * the hub, instead of the answer text `text` carries. Decoded by
68
- * `decodeNatsChunk` through the shared leading-frame table. */
66
+ /** Opaque per-chunk payload some backends attach beside `text`. */
69
67
  payload?: Record<string, unknown>;
70
68
  modelName?: string;
71
69
  providerName?: string;
@@ -18,12 +18,9 @@ export interface PendingApproval {
18
18
  command: string;
19
19
  explanation?: string;
20
20
  approvalType: string;
21
- /** Structured label/value rows — a Product Guide card is almost entirely
21
+ /** Structured label/value rows — a card can be almost entirely
22
22
  * these, so a path that forgets them renders a bare title. */
23
23
  fields?: ApprovalRequestField[];
24
- /** Where the card came from; `'guide'` keeps it inline and routes its
25
- * buttons to the hub (see `ApprovalRequestData.origin`). */
26
- origin?: 'guide';
27
24
  }
28
25
 
29
26
  export interface AccumulatorState {
@@ -9,7 +9,6 @@
9
9
  */
10
10
 
11
11
  import { describe, it, expect } from 'vitest';
12
- import { decodeNatsChunk } from '../../../../chat-protocol/nats-decoder';
13
12
  import { createChatStreamReducer } from '../../stream/chat-stream-reducer';
14
13
  import type { HistoricalMessage, MessageSegment } from '../../types';
15
14
  import { processHistoricalMessagesWithErrors } from '../process-historical-messages';
@@ -151,95 +150,3 @@ describe('displayApprovalTypes — history vs realtime default divergence', () =
151
150
  expect(live.escalated).toHaveLength(0);
152
151
  });
153
152
  });
154
-
155
- /**
156
- * A Product Guide card must survive a reload UNCHANGED.
157
- *
158
- * It arrives as a hub frame the agent re-streams into the dialog, so it is
159
- * typed by TOOL and resolves through the hub's confirm route — the tier gate
160
- * these tests are about simply does not apply to it. When the rule lived only
161
- * in the realtime kernel, the same card rendered inline while streaming and
162
- * then, on the next page load, was tier-gated: lifted out of its turn into the
163
- * consumer's sticky pending-approvals envelope, stripped of the `fields` that
164
- * are its entire body, and stripped of the `origin` that routes its buttons —
165
- * so clicking Approve hit the agent's endpoint and 404'd.
166
- */
167
- describe('Product Guide approvals — history and realtime agree', () => {
168
- const guideProposalMessage = (id: string): HistoricalMessage => ({
169
- id,
170
- createdAt: '2026-07-17T12:00:00Z',
171
- owner: { type: 'ASSISTANT' },
172
- messageData: [
173
- {
174
- type: 'GUIDE',
175
- payload: {
176
- kind: 'approval_request',
177
- proposalId: 'prop-1',
178
- toolName: 'create_ticket',
179
- title: 'Open a support ticket',
180
- fields: [{ label: 'Subject', value: 'Agent will not enroll' }],
181
- },
182
- },
183
- ],
184
- });
185
-
186
- // The narrowest possible tier list: it excludes the guide's tool name, so a
187
- // card that still renders proves the gate was bypassed, not merely satisfied.
188
- const NARROW_TIERS = ['CLIENT'];
189
-
190
- it('renders inline on reload despite a tier list that excludes it', () => {
191
- const { messages, escalatedApprovals } = processHistoricalMessagesWithErrors([guideProposalMessage('m1')], {
192
- displayApprovalTypes: NARROW_TIERS,
193
- });
194
- const rendered = messages.flatMap(m => approvalSegments(m.content));
195
- expect(rendered).toHaveLength(1);
196
- expect(escalatedApprovals.size).toBe(0);
197
- });
198
-
199
- it('keeps the card in its own turn, not in the sticky pending-approvals envelope', () => {
200
- const { messages } = processHistoricalMessagesWithErrors([guideProposalMessage('m1')], {
201
- displayApprovalTypes: NARROW_TIERS,
202
- });
203
- expect(messages.some(m => String(m.id).startsWith('pending-approvals-'))).toBe(false);
204
- const turn = messages.find(m => m.id === 'm1');
205
- expect(approvalSegments(turn?.content)).toHaveLength(1);
206
- });
207
-
208
- it('carries the same fields and origin as the live stream', () => {
209
- /** The single card's `data`, narrowed off the segment union. */
210
- const cardData = (segments: MessageSegment[]) => {
211
- const card = segments.find(s => s.type === 'approval_request');
212
- return card?.type === 'approval_request' ? card.data : undefined;
213
- };
214
-
215
- const { messages } = processHistoricalMessagesWithErrors([guideProposalMessage('m1')], {
216
- displayApprovalTypes: NARROW_TIERS,
217
- });
218
- const replayed = cardData(messages.flatMap(m => approvalSegments(m.content)));
219
-
220
- const live = createChatStreamReducer({ transport: 'nats', displayApprovalTypes: NARROW_TIERS });
221
- live.apply({ type: 'turn-start' });
222
- const guideChunk = decodeNatsChunk({
223
- type: 'GUIDE',
224
- payload: {
225
- kind: 'approval_request',
226
- proposalId: 'prop-1',
227
- toolName: 'create_ticket',
228
- title: 'Open a support ticket',
229
- fields: [{ label: 'Subject', value: 'Agent will not enroll' }],
230
- },
231
- });
232
- // A decoder that declines this frame is the failure the test exists to
233
- // catch — say so rather than applying `undefined` to the reducer.
234
- if (!guideChunk) throw new Error('decodeNatsChunk did not decode the GUIDE approval_request frame');
235
- live.apply(guideChunk);
236
- const streamed = cardData(live.state.messages.flatMap(m => approvalSegments(m.segments)));
237
-
238
- // Same card either way — down to the rows that ARE its body, and the marker
239
- // that decides which backend its buttons talk to.
240
- expect(replayed).toEqual(streamed);
241
- expect(replayed?.origin).toBe('guide');
242
- expect(replayed?.approvalType).toBe('create_ticket');
243
- expect(replayed?.fields).toEqual([{ label: 'Subject', value: 'Agent will not enroll' }]);
244
- });
245
- });
@@ -318,86 +318,9 @@ describe('extractErrorMessages / processHistoricalMessagesWithErrors — golden'
318
318
  });
319
319
  });
320
320
 
321
- // ─── GUIDE rows (#1583) ────────────────────────────────────────────────────
322
- //
323
- // The realtime path decodes a `GUIDE` chunk into a `guide-delta` event; the
324
- // history path must decode the persisted `GUIDE` row into the SAME event so a
325
- // reloaded dialog renders the guide card instead of silently dropping it.
326
- // Explicit assertions rather than a corpus snapshot — the point is the exact
327
- // coalescing shape, not the whole-dialog record.
328
-
329
- describe('processHistoricalMessages — GUIDE rows', () => {
330
- const guideDialog: HistoricalMessage[] = [
331
- {
332
- id: 'u1',
333
- chatType: 'CLIENT_CHAT',
334
- createdAt: '2026-07-19T09:00:00Z',
335
- owner: { type: 'CLIENT', userId: 'user-42' },
336
- messageData: [{ type: 'TEXT', text: 'how do I enroll a device?' }],
337
- },
338
- {
339
- id: 'a1',
340
- chatType: 'CLIENT_CHAT',
341
- createdAt: '2026-07-19T09:00:05Z',
342
- owner: { type: 'ASSISTANT', model: 'claude-sonnet-x' },
343
- messageData: [
344
- { type: 'GUIDE', text: '## Enroll a device\n' },
345
- { type: 'GUIDE', text: '1. Open Settings' },
346
- ],
347
- },
348
- ];
349
-
350
- it('replays consecutive GUIDE rows into ONE coalesced guide segment', () => {
351
- const { messages } = processHistoricalMessages(guideDialog, {
352
- assistantName: 'Mingo AI',
353
- assistantType: 'mingo',
354
- });
355
- expect(messages).toHaveLength(2);
356
- expect(messages[1]).toMatchObject({
357
- role: 'assistant',
358
- content: [{ type: 'guide', text: '## Enroll a device\n1. Open Settings' }],
359
- });
360
- });
361
-
362
- it('keeps GUIDE and TEXT as separate segments in wire order', () => {
363
- const mixed: HistoricalMessage[] = [
364
- {
365
- id: 'a1',
366
- chatType: 'CLIENT_CHAT',
367
- createdAt: '2026-07-19T09:00:05Z',
368
- owner: { type: 'ASSISTANT' },
369
- messageData: [
370
- { type: 'TEXT', text: 'Here is the walkthrough.' },
371
- { type: 'GUIDE', text: '## Steps' },
372
- { type: 'TEXT', text: 'Anything else?' },
373
- ],
374
- },
375
- ];
376
- const { messages } = processHistoricalMessages(mixed);
377
- expect(messages[0].content).toEqual([
378
- { type: 'text', text: 'Here is the walkthrough.' },
379
- { type: 'guide', text: '## Steps' },
380
- { type: 'text', text: 'Anything else?' },
381
- ]);
382
- });
383
-
384
- it('a GUIDE row with no text decodes to nothing (no empty card)', () => {
385
- const empty: HistoricalMessage[] = [
386
- {
387
- id: 'a1',
388
- chatType: 'CLIENT_CHAT',
389
- createdAt: '2026-07-19T09:00:05Z',
390
- owner: { type: 'ASSISTANT' },
391
- messageData: [{ type: 'GUIDE' }],
392
- },
393
- ];
394
- expect(processHistoricalMessages(empty).messages).toEqual([]);
395
- });
396
- });
397
-
398
321
  // ─── ASK rows ──────────────────────────────────────────────────────────────
399
322
  //
400
- // Same contract as GUIDE above: history must replay a persisted `ASK` row into
323
+ // History must replay a persisted `ASK` row into
401
324
  // the SAME shape the live `ASK` chunk produces — the intro sentence as answer
402
325
  // text, then the card — or a reloaded dialog loses the clarification the user
403
326
  // was about to answer.
@@ -4,65 +4,20 @@
4
4
  *
5
5
  * There are three such paths and they must agree: the NATS kernel (live chunks),
6
6
  * the SSE kernel, and the history replay that rebuilds a dialog after a reload.
7
- * When the rule lived only in the live kernel, a Product Guide card rendered
8
- * inline while streaming and then moved into the escalation treatment on the
9
- * next page load — same card, two behaviors, and its buttons pointed at the
10
- * wrong backend once it moved.
11
- *
12
- * The rule itself: a card is gated on approval TIER (`displayApprovalTypes`,
13
- * the agent's own privilege model) UNLESS it came from the Product Guide, whose
14
- * proposals are typed by TOOL and are resolved through the hub's confirm route.
15
- * A hub proposal has no tier to gate on, so tier-gating one means escalating it
16
- * to a human who has no way to act on it.
7
+ * When the rule lived only in the live kernel, a card could render inline while
8
+ * streaming and then move into the escalation treatment on the next page load —
9
+ * same card, two behaviors.
17
10
  */
18
11
 
19
- import type { GuideOrigin } from '../../../chat-protocol/events';
20
- import { GUIDE_ORIGIN, isGuideOrigin } from '../../../chat-protocol/events';
21
- import type { MessageSegment } from '../types/message.types';
22
-
23
- /** Anything carrying the optional `origin` marker: a stream event, or the
24
- * `data` of a rendered segment. */
25
- interface OriginBearing {
26
- origin?: GuideOrigin | string;
27
- }
28
-
29
- /** True when this approval was minted by the Product Guide (the hub) rather
30
- * than by the agent's own tool-approval tiering. */
31
- export function isGuideApproval(source: OriginBearing | null | undefined): boolean {
32
- return isGuideOrigin(source);
33
- }
34
-
35
- /** The marker to stamp onto a built segment, or undefined for agent approvals.
36
- * Keeps `origin` out of segment data unless it means something. */
37
- export function guideApprovalOrigin(source: OriginBearing | null | undefined): GuideOrigin | undefined {
38
- return isGuideApproval(source) ? GUIDE_ORIGIN : undefined;
39
- }
40
-
41
12
  /**
42
13
  * Does this approval render inline in its turn (true), or take the consumer's
43
14
  * escalation treatment (false)?
44
15
  *
45
- * `displayApprovalTypes` undefined means "no tier gate configured" and every
46
- * agent approval displays — the pre-existing history-replay default, preserved
47
- * here so both kernels can share one predicate.
16
+ * A card is gated on approval TIER (`displayApprovalTypes`, the agent's own
17
+ * privilege model). `undefined` means "no tier gate configured" and every
18
+ * approval displays — the pre-existing history-replay default, preserved here
19
+ * so both kernels can share one predicate.
48
20
  */
49
- export function approvalDisplaysInline(
50
- source: OriginBearing | null | undefined,
51
- approvalType: string,
52
- displayApprovalTypes?: readonly string[],
53
- ): boolean {
54
- if (isGuideApproval(source)) return true;
21
+ export function approvalDisplaysInline(approvalType: string, displayApprovalTypes?: readonly string[]): boolean {
55
22
  return !displayApprovalTypes || displayApprovalTypes.includes(approvalType);
56
23
  }
57
-
58
- /**
59
- * True for a rendered segment that holds a Product Guide card (single or batch).
60
- *
61
- * Consumers use it to keep such a card in the message flow, where the hub's own
62
- * chat puts it, instead of lifting it into their sticky pending-approvals
63
- * footer — and to route its buttons to the hub rather than to the agent.
64
- */
65
- export function isGuideApprovalSegment(segment: MessageSegment): boolean {
66
- if (segment.type !== 'approval_request' && segment.type !== 'approval_batch') return false;
67
- return isGuideApproval(segment.data);
68
- }
@@ -136,7 +136,6 @@ export function extractIncompleteMessageState(
136
136
  // backend has already abandoned.
137
137
  case 'text':
138
138
  case 'thinking':
139
- case 'guide':
140
139
  case 'ask':
141
140
  case 'ticket_escalated':
142
141
  case 'ticket_event':
@@ -16,12 +16,7 @@ export {
16
16
 
17
17
  // Where an approval card renders — one rule for both live kernels, the history
18
18
  // replay, and hosts that sort cards between their own footer and the flow.
19
- export {
20
- isGuideApproval,
21
- guideApprovalOrigin,
22
- approvalDisplaysInline,
23
- isGuideApprovalSegment,
24
- } from './approval-display';
19
+ export { approvalDisplaysInline } from './approval-display';
25
20
 
26
21
  // Historical message processing
27
22
  export {