flare-chat-core 0.2.1 → 0.2.3

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 (120) hide show
  1. package/README.md +28 -0
  2. package/docs/CAPABILITY-INVENTORY.md +42 -0
  3. package/docs/CHAT-CORE-BOUNDARY.md +47 -0
  4. package/docs/CORE-APP-REALIGNMENT-WORKLOAD-2026-04-18.md +86 -0
  5. package/docs/SSOT-CHAT-CORE-BOUNDARY.md +73 -0
  6. package/docs/SSOT-CHAT-CORE-DATAFLOW.md +97 -0
  7. package/index.html +12 -0
  8. package/package.json +24 -2
  9. package/src/adapters/index.js +6 -0
  10. package/src/adapters/message-api.adapter.js +59 -0
  11. package/src/adapters/session-api.adapter.js +133 -0
  12. package/src/adapters/session-message-api.http.js +161 -0
  13. package/src/adapters/session-message-api.js +34 -0
  14. package/src/adapters/session-message-api.normalize-source-record.test.mjs +180 -0
  15. package/src/adapters/session-message-api.normalizers.js +153 -0
  16. package/src/adapters/source-api.adapter.js +135 -0
  17. package/src/adapters/sse-client.js +244 -0
  18. package/src/adapters/sse-event-dispatcher.js +121 -0
  19. package/src/app/App.jsx +11 -0
  20. package/src/app/AppProviders.jsx +12 -0
  21. package/src/app/ChatWorkspaceScreen.jsx +33 -0
  22. package/src/app/WorkspaceLayout.jsx +190 -0
  23. package/src/app/components/AppCanvasPanel.jsx +64 -0
  24. package/src/app/components/TriggerThresholdPopoverContent.jsx +122 -0
  25. package/src/app/components/WorkspaceBodySection.jsx +156 -0
  26. package/src/app/components/WorkspaceMainPane.jsx +121 -0
  27. package/src/app/components/WorkspaceSessionPane.jsx +70 -0
  28. package/src/app/components/WorkspaceTopBarSection.jsx +71 -0
  29. package/src/app/core-chat-entry/ComposerSectionNode.jsx +241 -0
  30. package/src/app/core-chat-entry/attachmentSendRefs.js +154 -0
  31. package/src/app/core-chat-entry/attachmentSendRefs.test.mjs +101 -0
  32. package/src/app/core-chat-entry/composerActionRouter.js +26 -0
  33. package/src/app/core-chat-entry/constants.js +108 -0
  34. package/src/app/core-chat-entry/selectors.js +28 -0
  35. package/src/app/core-chat-entry/useAppActionErrorGuards.js +68 -0
  36. package/src/app/core-chat-entry/useChatCorePipelines.js +110 -0
  37. package/src/app/core-chat-entry/useComposerModeSuggestion.js +89 -0
  38. package/src/app/core-chat-entry/useDevCapabilityStatusNote.js +22 -0
  39. package/src/app/core-chat-entry/useProjectNameEditing.js +41 -0
  40. package/src/app/core-chat-entry/useProjectSourceUpload.js +341 -0
  41. package/src/app/core-chat-entry/useRealApiReadinessGate.js +103 -0
  42. package/src/app/core-chat-entry/useUnavailableActionError.js +29 -0
  43. package/src/app/core-chat-entry/useWorkspaceCanvasController.jsx +177 -0
  44. package/src/app/core-chat-entry/useWorkspaceCanvasProjection.jsx +171 -0
  45. package/src/app/core-chat-entry/useWorkspaceComposerController.jsx +199 -0
  46. package/src/app/core-chat-entry/useWorkspaceController.jsx +226 -0
  47. package/src/app/core-chat-entry/useWorkspacePanels.js +55 -0
  48. package/src/app/hooks/useComposerAttachmentSync.js +223 -0
  49. package/src/app/hooks/useComposerChooserHandlers.js +52 -0
  50. package/src/app/hooks/useSendWithContextRefs.js +140 -0
  51. package/src/app/hooks/useSendWithContextRefs.test.mjs +29 -0
  52. package/src/app/hooks/useUserThresholdProfile.js +121 -0
  53. package/src/app/index.js +1 -0
  54. package/src/app/selectors/assistantTextSelector.js +73 -0
  55. package/src/app/selectors/canvasEvidenceSummarySelector.js +28 -0
  56. package/src/app/selectors/canvasReportTemplateSelector.js +28 -0
  57. package/src/app/selectors/canvasTabsSelector.js +58 -0
  58. package/src/app/selectors/evidenceProjectionSelector.js +175 -0
  59. package/src/app/selectors/evidenceProjectionSelector.test.mjs +107 -0
  60. package/src/app/selectors/modeSuggestionSelector.js +50 -0
  61. package/src/chat-core/app/mockRuntime.js +291 -0
  62. package/src/chat-core/app/useAppStream.js +187 -0
  63. package/src/chat-core/app/useAppStream.refs.test.mjs +44 -0
  64. package/src/chat-core/app/useAppStream.request-body.test.mjs +116 -0
  65. package/src/chat-core/app/useCoreChatApp.js +115 -0
  66. package/src/chat-core/facade/useBasicConversationFacade.js +280 -0
  67. package/src/chat-core/index.js +9 -1
  68. package/src/chat-core/messages/buildTimelineItems.analysis-route.test.mjs +36 -0
  69. package/src/chat-core/messages/buildTimelineItems.js +139 -13
  70. package/src/chat-core/messages/buildTimelineItems.knowledge-citation.test.mjs +182 -0
  71. package/src/chat-core/messages/contextUsageDefaults.js +3 -0
  72. package/src/chat-core/messages/contextUsageViewModel.js +147 -0
  73. package/src/chat-core/messages/contextUsageViewModel.test.mjs +74 -0
  74. package/src/chat-core/messages/useContextUsageViewModel.js +41 -0
  75. package/src/chat-core/orchestration/useBasicSendHandler.js +55 -0
  76. package/src/chat-core/pipelines/build-action-request.js +46 -0
  77. package/src/chat-core/pipelines/build-stream-request.js +74 -0
  78. package/src/chat-core/pipelines/entity-extraction.js +159 -0
  79. package/src/chat-core/pipelines/preprocess-message.js +16 -0
  80. package/src/chat-core/pipelines/stream-persist-utils.js +32 -0
  81. package/src/chat-core/pipelines/transport/send-mock-stream.js +86 -0
  82. package/src/chat-core/pipelines/transport/send-real-stream.js +330 -0
  83. package/src/chat-core/pipelines/transport/send-real-stream.test.mjs +27 -0
  84. package/src/chat-core/pipelines/transport/send-sourcing-search.js +86 -0
  85. package/src/chat-core/pipelines/transport/send-sourcing-search.test.mjs +14 -0
  86. package/src/chat-core/pipelines/transport/sourcing-response-templates.js +55 -0
  87. package/src/chat-core/pipelines/transport/sourcing-search-api.js +155 -0
  88. package/src/chat-core/runtime/runtimeMode.js +69 -0
  89. package/src/chat-core/session/chatSessionActionTypes.js +24 -0
  90. package/src/chat-core/session/chatSessionReducer.js +352 -0
  91. package/src/chat-core/session/chatSessionReducer.streaming-done.test.mjs +39 -0
  92. package/src/chat-core/session/index.js +2 -0
  93. package/src/chat-core/session/sessionActionsMessages.js +44 -0
  94. package/src/chat-core/session/sessionActionsSessionCrud.js +131 -0
  95. package/src/chat-core/session/sessionActionsStreaming.js +80 -0
  96. package/src/chat-core/session/sessionActionsUiState.js +51 -0
  97. package/src/chat-core/session/useChatSessionReducer.js +62 -455
  98. package/src/chat-core/session/useSessionListController.js +67 -0
  99. package/src/chat-core/stream/sse-client.js +1 -244
  100. package/src/chat-core/stream/sse-event-dispatcher.js +1 -0
  101. package/src/chat-core/stream/sse-events.js +1 -867
  102. package/src/chat-core/stream/useSSEStream.js +1 -356
  103. package/src/chat-core/stream/useStreamSendController.js +46 -0
  104. package/src/contracts/context-ssot.js +47 -0
  105. package/src/contracts/index.js +1 -0
  106. package/src/contracts/sse-events/base-parsers.js +79 -0
  107. package/src/contracts/sse-events/domain-parsers.js +3 -0
  108. package/src/contracts/sse-events/internal-normalizers.js +143 -0
  109. package/src/contracts/sse-events/parsers-intake.js +235 -0
  110. package/src/contracts/sse-events/parsers-runtime.js +37 -0
  111. package/src/contracts/sse-events/parsers-sourcing.js +179 -0
  112. package/src/contracts/sse-events/patch-event-parser.js +121 -0
  113. package/src/contracts/sse-events/runtime-parsers.js +79 -0
  114. package/src/contracts/sse-events.js +4 -0
  115. package/src/index.js +5 -0
  116. package/src/main.jsx +28 -0
  117. package/src/orchestration/index.js +6 -0
  118. package/src/orchestration/useSSEStream.js +221 -0
  119. package/src/state/index.js +4 -0
  120. package/vite.config.js +36 -0
@@ -0,0 +1,235 @@
1
+ import {
2
+ normalizeAction,
3
+ normalizeActionList,
4
+ parseGuidedSession,
5
+ resolveModeEventPayload,
6
+ } from './internal-normalizers.js';
7
+
8
+ export const parseFieldProgress = (raw) => {
9
+ const payload = resolveModeEventPayload(raw);
10
+ return {
11
+ intake_session_id: payload?.intake_session_id ?? '',
12
+ intake_session_state: payload?.intake_session_state ?? '',
13
+ intake_session_completed: payload?.intake_session_completed === true,
14
+ flow_state: payload?.flow_state ?? payload?.collection_phase ?? '',
15
+ fields: payload?.fields ?? {},
16
+ sources: payload?.sources ?? {},
17
+ field_entries: Array.isArray(payload?.field_entries) ? payload.field_entries : [],
18
+ field_groups: payload?.field_groups && typeof payload.field_groups === 'object' && !Array.isArray(payload.field_groups)
19
+ ? payload.field_groups
20
+ : {},
21
+ extra_fields: Array.isArray(payload?.extra_fields) ? payload.extra_fields : [],
22
+ progress: payload?.progress ?? null,
23
+ required_progress: payload?.required_progress ?? null,
24
+ recommended_progress: payload?.recommended_progress ?? null,
25
+ optional_progress: payload?.optional_progress ?? null,
26
+ collected: Array.isArray(payload?.collected) ? payload.collected : [],
27
+ missing: Array.isArray(payload?.missing) ? payload.missing : [],
28
+ required_collected: Array.isArray(payload?.required_collected) ? payload.required_collected : [],
29
+ required_missing: Array.isArray(payload?.required_missing) ? payload.required_missing : [],
30
+ recommended_collected: Array.isArray(payload?.recommended_collected) ? payload.recommended_collected : [],
31
+ recommended_missing: Array.isArray(payload?.recommended_missing) ? payload.recommended_missing : [],
32
+ optional_collected: Array.isArray(payload?.optional_collected) ? payload.optional_collected : [],
33
+ optional_missing: Array.isArray(payload?.optional_missing) ? payload.optional_missing : [],
34
+ last_field: payload?.last_field ?? null,
35
+ refresh_reason: payload?.refresh_reason ?? null,
36
+ field_definitions: Array.isArray(payload?.field_definitions) ? payload.field_definitions : [],
37
+ required_fields: Array.isArray(payload?.required_fields) ? payload.required_fields : [],
38
+ recommended_fields: Array.isArray(payload?.recommended_fields) ? payload.recommended_fields : [],
39
+ optional_fields: Array.isArray(payload?.optional_fields) ? payload.optional_fields : [],
40
+ intake_core_fields: Array.isArray(payload?.intake_core_fields) ? payload.intake_core_fields : [],
41
+ intake_supplementary_fields: Array.isArray(payload?.intake_supplementary_fields) ? payload.intake_supplementary_fields : [],
42
+ analysis_enrichment_fields: Array.isArray(payload?.analysis_enrichment_fields) ? payload.analysis_enrichment_fields : [],
43
+ field_priorities: payload?.field_priorities && typeof payload.field_priorities === 'object' && !Array.isArray(payload.field_priorities)
44
+ ? payload.field_priorities
45
+ : {},
46
+ current_question: payload?.current_question && typeof payload.current_question === 'object' && !Array.isArray(payload.current_question)
47
+ ? payload.current_question
48
+ : null,
49
+ question_progress: payload?.question_progress && typeof payload.question_progress === 'object' && !Array.isArray(payload.question_progress)
50
+ ? payload.question_progress
51
+ : { current: 0, total: 0 },
52
+ collection_phase: payload?.collection_phase ?? '',
53
+ required_missing_count: Number.isFinite(payload?.required_missing_count) ? payload.required_missing_count : null,
54
+ required_coverage: Number.isFinite(Number(payload?.required_coverage)) ? Number(payload.required_coverage) : 0,
55
+ total_coverage: Number.isFinite(Number(payload?.total_coverage)) ? Number(payload.total_coverage) : 0,
56
+ analysis_entry_threshold: Number.isFinite(Number(payload?.analysis_entry_threshold)) ? Number(payload.analysis_entry_threshold) : 0.8,
57
+ analysis_entry_eligible: payload?.analysis_entry_eligible === true,
58
+ active_collecting: payload?.active_collecting === true,
59
+ ready_for_submit: payload?.ready_for_submit === true,
60
+ decision_point_active: payload?.decision_point_active === true,
61
+ has_active_question: payload?.has_active_question === true,
62
+ guided_session: parseGuidedSession(payload?.guided_session),
63
+ };
64
+ };
65
+
66
+ export const parseDoc = (raw) => ({
67
+ doc: raw?.doc ?? null,
68
+ });
69
+
70
+ export const parseCapabilitySuggestion = (raw) => ({
71
+ type: raw?.type ?? '',
72
+ reason: raw?.reason ?? '',
73
+ benefit: raw?.benefit ?? '',
74
+ confidence: raw?.confidence ?? 0,
75
+ estimated_cost: raw?.estimated_cost ?? 0,
76
+ });
77
+
78
+ export const parseRequirementDraft = (raw) => {
79
+ const payload = resolveModeEventPayload(raw);
80
+ const draft = payload?.draft && typeof payload.draft === 'object' && !Array.isArray(payload.draft)
81
+ ? payload.draft
82
+ : {};
83
+
84
+ return {
85
+ intake_session_id: payload?.intake_session_id ?? '',
86
+ intake_session_state: payload?.intake_session_state ?? '',
87
+ intake_session_completed: payload?.intake_session_completed === true,
88
+ mode_key: payload?.mode_key ?? '',
89
+ project_id: payload?.project_id ?? null,
90
+ message_excerpt: payload?.message_excerpt ?? '',
91
+ draft: {
92
+ ...draft,
93
+ fields: Array.isArray(draft.fields) ? draft.fields : [],
94
+ },
95
+ render_hint: payload?.render_hint ?? 'requirement_draft',
96
+ };
97
+ };
98
+
99
+ export const parseNextActions = (raw) => {
100
+ const payload = resolveModeEventPayload(raw);
101
+ const rawActions = Array.isArray(payload?.actions)
102
+ ? payload.actions
103
+ : (Array.isArray(payload?.next_actions) ? payload.next_actions : []);
104
+ const actions = normalizeActionList(rawActions);
105
+ return {
106
+ intake_session_id: payload?.intake_session_id ?? '',
107
+ intake_session_state: payload?.intake_session_state ?? '',
108
+ intake_session_completed: payload?.intake_session_completed === true,
109
+ mode_key: payload?.mode_key ?? '',
110
+ flow_state: payload?.flow_state ?? payload?.collection_phase ?? '',
111
+ project_id: payload?.project_id ?? null,
112
+ message_excerpt: payload?.message_excerpt ?? '',
113
+ actions,
114
+ completion_state: payload?.completion_state ?? '',
115
+ required_missing: Array.isArray(payload?.required_missing) ? payload.required_missing : [],
116
+ recommended_missing: Array.isArray(payload?.recommended_missing) ? payload.recommended_missing : [],
117
+ optional_missing: Array.isArray(payload?.optional_missing) ? payload.optional_missing : [],
118
+ intake_core_fields: Array.isArray(payload?.intake_core_fields) ? payload.intake_core_fields : [],
119
+ intake_supplementary_fields: Array.isArray(payload?.intake_supplementary_fields) ? payload.intake_supplementary_fields : [],
120
+ analysis_enrichment_fields: Array.isArray(payload?.analysis_enrichment_fields) ? payload.analysis_enrichment_fields : [],
121
+ ready_for_sourcing: payload?.ready_for_sourcing ?? null,
122
+ status: payload?.status ?? '',
123
+ reason: payload?.reason ?? '',
124
+ chooser_required: payload?.chooser_required === true,
125
+ blocking_reason: payload?.blocking_reason ?? '',
126
+ blocking: payload?.blocking && typeof payload.blocking === 'object' && !Array.isArray(payload.blocking)
127
+ ? payload.blocking
128
+ : null,
129
+ target_field: payload?.target_field ?? '',
130
+ degrade_reason: Array.isArray(payload?.degrade_reason) ? payload.degrade_reason : [],
131
+ current_question: payload?.current_question && typeof payload.current_question === 'object' && !Array.isArray(payload.current_question)
132
+ ? payload.current_question
133
+ : null,
134
+ question_progress: payload?.question_progress && typeof payload.question_progress === 'object' && !Array.isArray(payload.question_progress)
135
+ ? payload.question_progress
136
+ : { current: 0, total: 0 },
137
+ collection_phase: payload?.collection_phase ?? '',
138
+ required_missing_count: Number.isFinite(payload?.required_missing_count) ? payload.required_missing_count : null,
139
+ render_hint: payload?.render_hint ?? 'next_actions',
140
+ required_coverage: Number.isFinite(Number(payload?.required_coverage)) ? Number(payload.required_coverage) : 0,
141
+ total_coverage: Number.isFinite(Number(payload?.total_coverage)) ? Number(payload.total_coverage) : 0,
142
+ analysis_entry_threshold: Number.isFinite(Number(payload?.analysis_entry_threshold)) ? Number(payload.analysis_entry_threshold) : 0.8,
143
+ analysis_entry_eligible: payload?.analysis_entry_eligible === true,
144
+ active_collecting: payload?.active_collecting === true,
145
+ ready_for_submit: payload?.ready_for_submit === true,
146
+ decision_point_active: payload?.decision_point_active === true,
147
+ has_active_question: payload?.has_active_question === true,
148
+ guided_session: parseGuidedSession(payload?.guided_session),
149
+ };
150
+ };
151
+
152
+ export const parseOrchestrationStatus = (raw) => {
153
+ const payload = resolveModeEventPayload(raw);
154
+ const activeWorkItem = payload?.active_work_item && typeof payload.active_work_item === 'object' && !Array.isArray(payload.active_work_item)
155
+ ? payload.active_work_item
156
+ : null;
157
+ const nextAction = normalizeAction(payload?.next_action, 0);
158
+ return {
159
+ trace_id: payload?.trace_id ?? '',
160
+ intake_session_id: payload?.intake_session_id ?? '',
161
+ intake_session_state: payload?.intake_session_state ?? '',
162
+ intake_session_completed: payload?.intake_session_completed === true,
163
+ current_stage: payload?.current_stage ?? '',
164
+ detected_intent: payload?.detected_intent && typeof payload.detected_intent === 'object' && !Array.isArray(payload.detected_intent)
165
+ ? payload.detected_intent
166
+ : {},
167
+ confirmed_updates: Array.isArray(payload?.confirmed_updates) ? payload.confirmed_updates : [],
168
+ open_fields: Array.isArray(payload?.open_fields) ? payload.open_fields : [],
169
+ search_actions: Array.isArray(payload?.search_actions) ? payload.search_actions : [],
170
+ workspace_updates: payload?.workspace_updates && typeof payload.workspace_updates === 'object' && !Array.isArray(payload.workspace_updates)
171
+ ? payload.workspace_updates
172
+ : {},
173
+ next_action: nextAction,
174
+ next_actions: normalizeActionList(payload?.next_actions),
175
+ active_collecting: payload?.active_collecting === true,
176
+ ready_for_submit: payload?.ready_for_submit === true,
177
+ required_coverage: Number.isFinite(Number(payload?.required_coverage)) ? Number(payload.required_coverage) : 0,
178
+ total_coverage: Number.isFinite(Number(payload?.total_coverage)) ? Number(payload.total_coverage) : 0,
179
+ analysis_entry_threshold: Number.isFinite(Number(payload?.analysis_entry_threshold)) ? Number(payload.analysis_entry_threshold) : 0.8,
180
+ analysis_entry_eligible: payload?.analysis_entry_eligible === true,
181
+ decision_point_active: payload?.decision_point_active === true,
182
+ has_active_question: payload?.has_active_question === true,
183
+ guided_session: parseGuidedSession(payload?.guided_session),
184
+ active_work_item: activeWorkItem,
185
+ active_fields: Array.isArray(payload?.active_fields) ? payload.active_fields : [],
186
+ hypotheses: Array.isArray(payload?.hypotheses) ? payload.hypotheses : [],
187
+ planner: payload?.planner && typeof payload.planner === 'object' && !Array.isArray(payload.planner)
188
+ ? payload.planner
189
+ : {},
190
+ chooser: payload?.chooser && typeof payload.chooser === 'object' && !Array.isArray(payload.chooser)
191
+ ? payload.chooser
192
+ : null,
193
+ stage_transition: payload?.stage_transition && typeof payload.stage_transition === 'object' && !Array.isArray(payload.stage_transition)
194
+ ? payload.stage_transition
195
+ : {},
196
+ decision_basis: Array.isArray(payload?.decision_basis) ? payload.decision_basis : [],
197
+ blocking: payload?.blocking && typeof payload.blocking === 'object' && !Array.isArray(payload.blocking)
198
+ ? payload.blocking
199
+ : {},
200
+ node: payload?.node && typeof payload.node === 'object' && !Array.isArray(payload.node)
201
+ ? payload.node
202
+ : {},
203
+ node_id: payload?.node_id ?? '',
204
+ node_state: payload?.node_state ?? '',
205
+ node_reason: payload?.node_reason ?? '',
206
+ needs_user_choice: payload?.needs_user_choice === true,
207
+ conversation_intent_state: payload?.conversation_intent_state ?? '',
208
+ session_intent_stage: payload?.session_intent_stage ?? '',
209
+ intent_type: payload?.intent_type ?? '',
210
+ procurement_relevance: payload?.procurement_relevance === true,
211
+ consultative_procurement_qa: payload?.consultative_procurement_qa === true,
212
+ escalation_target: payload?.escalation_target ?? '',
213
+ intake_candidate: payload?.intake_candidate === true,
214
+ clarification_recommended: payload?.clarification_recommended === true,
215
+ analysis_ready: payload?.analysis_ready === true,
216
+ draft_seeded: payload?.draft_seeded === true,
217
+ missing_key_fields: Array.isArray(payload?.missing_key_fields) ? payload.missing_key_fields : [],
218
+ recommended_next_step: payload?.recommended_next_step ?? '',
219
+ intent_and_escalation_confidence: Number.isFinite(Number(payload?.intent_and_escalation_confidence))
220
+ ? Number(payload.intent_and_escalation_confidence)
221
+ : 0,
222
+ intent_and_escalation_reason: payload?.intent_and_escalation_reason ?? '',
223
+ intent_escalation_policy: payload?.intent_escalation_policy && typeof payload.intent_escalation_policy === 'object' && !Array.isArray(payload.intent_escalation_policy)
224
+ ? payload.intent_escalation_policy
225
+ : {},
226
+ intent_escalation_policy_source: payload?.intent_escalation_policy_source ?? '',
227
+ analysis_route: payload?.analysis_route && typeof payload.analysis_route === 'object' && !Array.isArray(payload.analysis_route)
228
+ ? payload.analysis_route
229
+ : {},
230
+ degrade_reason: Array.isArray(payload?.degrade_reason) ? payload.degrade_reason : [],
231
+ content_policy: payload?.content_policy && typeof payload.content_policy === 'object' && !Array.isArray(payload.content_policy)
232
+ ? payload.content_policy
233
+ : {},
234
+ };
235
+ };
@@ -0,0 +1,37 @@
1
+ import {
2
+ normalizeActionList,
3
+ resolveModeEventPayload,
4
+ } from './internal-normalizers.js';
5
+
6
+ export const parsePlanBlock = (raw) => {
7
+ const payload = resolveModeEventPayload(raw);
8
+ return {
9
+ ...payload,
10
+ constraints: Array.isArray(payload?.constraints) ? payload.constraints : [],
11
+ plan: Array.isArray(payload?.plan) ? payload.plan : [],
12
+ acceptance: Array.isArray(payload?.acceptance) ? payload.acceptance : [],
13
+ actions: normalizeActionList(payload?.actions),
14
+ };
15
+ };
16
+
17
+ export const parseCanvasState = (raw) => {
18
+ const payload = resolveModeEventPayload(raw);
19
+ const canvasState = payload?.canvas_state && typeof payload.canvas_state === 'object' && !Array.isArray(payload.canvas_state)
20
+ ? payload.canvas_state
21
+ : {};
22
+ return {
23
+ ...payload,
24
+ canvas_state: {
25
+ ...canvasState,
26
+ versions: Array.isArray(canvasState.versions) ? canvasState.versions : [],
27
+ },
28
+ };
29
+ };
30
+
31
+ export const parseCanvasRevision = (raw) => {
32
+ const payload = resolveModeEventPayload(raw);
33
+ return {
34
+ ...payload,
35
+ actions: normalizeActionList(payload?.actions),
36
+ };
37
+ };
@@ -0,0 +1,179 @@
1
+ import {
2
+ normalizeActionList,
3
+ pickFirstArray,
4
+ resolveModeEventPayload,
5
+ } from './internal-normalizers.js';
6
+
7
+ export const parseSourcingCandidates = (raw) => {
8
+ const payload = resolveModeEventPayload(raw);
9
+ const missing = pickFirstArray(payload, ['missing', 'base_missing', 'missing_fields', 'required_missing']);
10
+ return {
11
+ mode_key: payload?.mode_key ?? '',
12
+ project_id: payload?.project_id ?? null,
13
+ message_excerpt: payload?.message_excerpt ?? '',
14
+ missing,
15
+ base_fields: Array.isArray(payload?.base_fields) ? payload.base_fields : [],
16
+ base_collected: Array.isArray(payload?.base_collected) ? payload.base_collected : [],
17
+ base_missing: Array.isArray(payload?.base_missing) ? payload.base_missing : [],
18
+ base_total: Number.isFinite(payload?.base_total) ? payload.base_total : 0,
19
+ base_progress: Number.isFinite(payload?.base_progress) ? payload.base_progress : null,
20
+ base_ready_for_matching: payload?.base_ready_for_matching === true,
21
+ mode_state: payload?.mode_state ?? '',
22
+ requested_top_k: Number.isFinite(Number(payload?.requested_top_k)) ? Number(payload.requested_top_k) : 8,
23
+ returned_count: Number.isFinite(Number(payload?.returned_count)) ? Number(payload.returned_count) : null,
24
+ has_more: payload?.has_more === true,
25
+ next_top_k: Number.isFinite(Number(payload?.next_top_k)) ? Number(payload.next_top_k) : null,
26
+ append_mode: payload?.append_mode === true,
27
+ lock_existing_order: payload?.lock_existing_order !== false,
28
+ candidate_count: Number.isFinite(payload?.candidate_count) ? payload.candidate_count : null,
29
+ is_placeholder: payload?.is_placeholder ?? null,
30
+ required_missing: Array.isArray(payload?.required_missing) ? payload.required_missing : [],
31
+ recommended_missing: Array.isArray(payload?.recommended_missing) ? payload.recommended_missing : [],
32
+ optional_missing: Array.isArray(payload?.optional_missing) ? payload.optional_missing : [],
33
+ actions: normalizeActionList(payload?.actions),
34
+ candidates: pickFirstArray(payload, ['candidates', 'sourcing_candidates', 'candidate_list', 'results']),
35
+ new_results: pickFirstArray(payload, ['new_results']),
36
+ all_results: pickFirstArray(payload, ['all_results']),
37
+ source_meta: payload?.source_meta && typeof payload.source_meta === 'object' && !Array.isArray(payload.source_meta)
38
+ ? payload.source_meta
39
+ : {},
40
+ reasoning: Array.isArray(payload?.reasoning) ? payload.reasoning : [],
41
+ summary: payload?.sourcing_summary ?? payload?.summary ?? null,
42
+ render_hint: payload?.render_hint ?? 'sourcing_candidates',
43
+ };
44
+ };
45
+
46
+ export const parseRiskSummary = (raw) => {
47
+ const payload = resolveModeEventPayload(raw);
48
+ const risks = pickFirstArray(payload, ['risks', 'risk_items', 'items', 'risk_list']);
49
+ const sources = pickFirstArray(payload, ['sources', 'references', 'evidence']);
50
+ return {
51
+ mode_key: payload?.mode_key ?? '',
52
+ project_id: payload?.project_id ?? null,
53
+ message_excerpt: payload?.message_excerpt ?? '',
54
+ mode_state: payload?.mode_state ?? '',
55
+ base_fields: Array.isArray(payload?.base_fields) ? payload.base_fields : [],
56
+ base_collected: Array.isArray(payload?.base_collected) ? payload.base_collected : [],
57
+ base_missing: Array.isArray(payload?.base_missing) ? payload.base_missing : [],
58
+ base_total: Number.isFinite(payload?.base_total) ? payload.base_total : 0,
59
+ base_progress: Number.isFinite(payload?.base_progress) ? payload.base_progress : null,
60
+ base_ready_for_matching: payload?.base_ready_for_matching === true,
61
+ is_placeholder: payload?.is_placeholder ?? null,
62
+ actions: normalizeActionList(payload?.actions),
63
+ risks,
64
+ sources,
65
+ source_meta: payload?.source_meta && typeof payload.source_meta === 'object' && !Array.isArray(payload.source_meta)
66
+ ? payload.source_meta
67
+ : {},
68
+ summary: payload?.risk_summary ?? payload?.summary ?? null,
69
+ render_hint: payload?.render_hint ?? 'risk_summary',
70
+ };
71
+ };
72
+
73
+ export const parseShortlistUpdated = (raw) => {
74
+ const payload = resolveModeEventPayload(raw);
75
+ const shortlist = pickFirstArray(payload, ['shortlist', 'shortlist_updated', 'selected_candidates', 'items']);
76
+ return {
77
+ mode_key: payload?.mode_key ?? '',
78
+ project_id: payload?.project_id ?? null,
79
+ message_excerpt: payload?.message_excerpt ?? '',
80
+ mode_state: payload?.mode_state ?? '',
81
+ base_fields: Array.isArray(payload?.base_fields) ? payload.base_fields : [],
82
+ base_collected: Array.isArray(payload?.base_collected) ? payload.base_collected : [],
83
+ base_missing: Array.isArray(payload?.base_missing) ? payload.base_missing : [],
84
+ base_total: Number.isFinite(payload?.base_total) ? payload.base_total : 0,
85
+ base_progress: Number.isFinite(payload?.base_progress) ? payload.base_progress : null,
86
+ base_ready_for_matching: payload?.base_ready_for_matching === true,
87
+ shortlist,
88
+ status: payload?.status ?? payload?.state ?? '',
89
+ reason: payload?.reason ?? payload?.message ?? '',
90
+ selected_count: Number.isFinite(payload?.selected_count) ? payload.selected_count : null,
91
+ is_placeholder: payload?.is_placeholder ?? null,
92
+ actions: normalizeActionList(payload?.actions),
93
+ source_meta: payload?.source_meta && typeof payload.source_meta === 'object' && !Array.isArray(payload.source_meta)
94
+ ? payload.source_meta
95
+ : {},
96
+ render_hint: payload?.render_hint ?? 'shortlist_updated',
97
+ };
98
+ };
99
+
100
+ export const parseEvaluationReportReady = (raw) => {
101
+ const payload = resolveModeEventPayload(raw);
102
+ const nestedReport = payload?.evaluation_report_ready
103
+ && typeof payload.evaluation_report_ready === 'object'
104
+ && !Array.isArray(payload.evaluation_report_ready)
105
+ ? payload.evaluation_report_ready
106
+ : {};
107
+ const reportSource = payload?.report
108
+ || nestedReport.report
109
+ || nestedReport;
110
+ const report = reportSource && typeof reportSource === 'object' && !Array.isArray(reportSource)
111
+ ? reportSource
112
+ : {};
113
+ const compareTable = pickFirstArray(payload, ['compare_table', 'comparison_table', 'compare_rows'])
114
+ || pickFirstArray(report, ['compare_table', 'comparison_table', 'compare_rows'])
115
+ || pickFirstArray(nestedReport, ['compare_table', 'comparison_table', 'compare_rows']);
116
+
117
+ return {
118
+ mode_key: payload?.mode_key ?? '',
119
+ project_id: payload?.project_id ?? null,
120
+ message_excerpt: payload?.message_excerpt ?? '',
121
+ mode_state: payload?.mode_state ?? '',
122
+ base_fields: Array.isArray(payload?.base_fields) ? payload.base_fields : [],
123
+ base_fields: Array.isArray(payload?.base_fields) ? payload.base_fields : [],
124
+ base_collected: Array.isArray(payload?.base_collected) ? payload.base_collected : [],
125
+ base_missing: Array.isArray(payload?.base_missing) ? payload.base_missing : [],
126
+ base_total: Number.isFinite(payload?.base_total) ? payload.base_total : 0,
127
+ base_progress: Number.isFinite(payload?.base_progress) ? payload.base_progress : null,
128
+ base_ready_for_matching: payload?.base_ready_for_matching === true,
129
+ is_placeholder: payload?.is_placeholder ?? null,
130
+ actions: normalizeActionList(payload?.actions),
131
+ report: {
132
+ ...report,
133
+ sections: Array.isArray(report.sections) ? report.sections : [],
134
+ },
135
+ compare_table: payload?.compare_table
136
+ ?? payload?.comparison_table
137
+ ?? payload?.compare_rows
138
+ ?? report?.compare_table
139
+ ?? report?.comparison_table
140
+ ?? report?.compare_rows
141
+ ?? nestedReport?.compare_table
142
+ ?? nestedReport?.comparison_table
143
+ ?? nestedReport?.compare_rows
144
+ ?? compareTable,
145
+ source_meta: payload?.source_meta && typeof payload.source_meta === 'object' && !Array.isArray(payload.source_meta)
146
+ ? payload.source_meta
147
+ : {},
148
+ render_hint: payload?.render_hint ?? 'evaluation_report_ready',
149
+ };
150
+ };
151
+
152
+ export const parseKnowledgeSearch = (raw) => {
153
+ const payload = resolveModeEventPayload(raw);
154
+ const sourceBreakdown = payload?.source_breakdown && typeof payload.source_breakdown === 'object'
155
+ && !Array.isArray(payload.source_breakdown)
156
+ ? payload.source_breakdown
157
+ : {};
158
+ return {
159
+ ...payload,
160
+ run_id: payload?.run_id ?? '',
161
+ query: payload?.query ?? '',
162
+ result_count: Number.isFinite(payload?.result_count) ? payload.result_count : null,
163
+ source_breakdown: {
164
+ local: Number.isFinite(sourceBreakdown.local) ? sourceBreakdown.local : 0,
165
+ mcp: Number.isFinite(sourceBreakdown.mcp) ? sourceBreakdown.mcp : 0,
166
+ web: Number.isFinite(sourceBreakdown.web) ? sourceBreakdown.web : 0,
167
+ },
168
+ results: Array.isArray(payload?.results) ? payload.results : [],
169
+ };
170
+ };
171
+
172
+ export const parseKnowledgeCitation = (raw) => {
173
+ const payload = resolveModeEventPayload(raw);
174
+ return {
175
+ ...payload,
176
+ run_id: payload?.run_id ?? '',
177
+ citations: Array.isArray(payload?.citations) ? payload.citations : [],
178
+ };
179
+ };
@@ -0,0 +1,121 @@
1
+ export const PATCH_SCOPE_AUTHORITATIVE = Object.freeze([
2
+ 'session',
3
+ 'assistant_reply',
4
+ 'primary_flow',
5
+ 'checkpoint',
6
+ 'intent_state',
7
+ 'question',
8
+ 'confirmed_fields',
9
+ 'missing_fields',
10
+ 'next_actions',
11
+ 'analysis',
12
+ 'observation',
13
+ 'capabilities.field_retrieval',
14
+ 'errors',
15
+ ]);
16
+
17
+ export const PATCH_SCOPE_LEGACY_COMPAT = Object.freeze([
18
+ 'current_question',
19
+ 'recommendation_summary',
20
+ ]);
21
+
22
+ const PATCH_SCOPE_ALLOWLIST = new Set([
23
+ ...PATCH_SCOPE_AUTHORITATIVE,
24
+ ...PATCH_SCOPE_LEGACY_COMPAT,
25
+ ]);
26
+
27
+ function getByScopePath(payload, scope) {
28
+ if (!payload || typeof payload !== 'object' || Array.isArray(payload)) {
29
+ return undefined;
30
+ }
31
+ const segments = String(scope || '').split('.').filter(Boolean);
32
+ if (!segments.length) {
33
+ return undefined;
34
+ }
35
+ let current = payload;
36
+ for (const segment of segments) {
37
+ if (!current || typeof current !== 'object' || Array.isArray(current) || !(segment in current)) {
38
+ return undefined;
39
+ }
40
+ current = current[segment];
41
+ }
42
+ return current;
43
+ }
44
+
45
+ export const parsePatchEvent = (raw) => {
46
+ const envelope = (raw && typeof raw === 'object' && !Array.isArray(raw)) ? raw : {};
47
+ const patchScope = Array.isArray(envelope.patch_scope)
48
+ ? envelope.patch_scope.map((item) => String(item || '').trim()).filter(Boolean)
49
+ : [];
50
+ const payload = (envelope.payload && typeof envelope.payload === 'object' && !Array.isArray(envelope.payload))
51
+ ? envelope.payload
52
+ : {};
53
+ const hasPayload = Object.keys(payload).length > 0;
54
+ const hasUnknownScope = patchScope.some((item) => !(PATCH_SCOPE_ALLOWLIST.has(item) || item.startsWith('confirmed_fields.')));
55
+ const hasScopePayloadMismatch = patchScope.some((scope) => {
56
+ const direct = payload[scope];
57
+ const byPath = getByScopePath(payload, scope);
58
+ const lastSegment = String(scope).split('.').slice(-1)[0];
59
+ const leaf = payload[lastSegment];
60
+ return direct === undefined && byPath === undefined && leaf === undefined;
61
+ });
62
+ const legacyPatchScope = patchScope.filter((scope) => PATCH_SCOPE_LEGACY_COMPAT.includes(scope));
63
+ const authoritativePatchScope = patchScope.filter(
64
+ (scope) => PATCH_SCOPE_AUTHORITATIVE.includes(scope) || scope.startsWith('confirmed_fields.')
65
+ );
66
+ const invalidReason = (() => {
67
+ if (envelope.message_type !== 'patch_event') {
68
+ return 'invalid_message_type';
69
+ }
70
+ if (!['ack', 'patch', 'error', 'final'].includes(String(envelope.event_type || '').trim())) {
71
+ return 'invalid_event_type';
72
+ }
73
+ if (hasPayload && patchScope.length === 0) {
74
+ return 'payload_without_patch_scope';
75
+ }
76
+ if (hasUnknownScope) {
77
+ return 'unknown_patch_scope';
78
+ }
79
+ if (patchScope.length > 0 && hasScopePayloadMismatch) {
80
+ return 'patch_scope_payload_mismatch';
81
+ }
82
+ return '';
83
+ })();
84
+ return {
85
+ contract_version: String(envelope.contract_version || 'flare.v1'),
86
+ message_type: 'patch_event',
87
+ event_type: String(envelope.event_type || '').trim(),
88
+ sequence: Number.isFinite(Number(envelope.sequence)) ? Number(envelope.sequence) : 0,
89
+ trace_id: String(envelope.trace_id || '').trim(),
90
+ client_request_id: String(envelope.client_request_id || '').trim(),
91
+ intake_session_id: String(
92
+ payload?.primary_flow?.intake_session_id
93
+ || payload?.checkpoint?.intake_session_id
94
+ || payload?.next_actions?.intake_session_id
95
+ || payload?.question?.intake_session_id
96
+ || payload?.intake_session_id
97
+ || ''
98
+ ).trim(),
99
+ intake_session_state: String(
100
+ payload?.primary_flow?.intake_session_state
101
+ || payload?.next_actions?.intake_session_state
102
+ || payload?.intake_session_state
103
+ || ''
104
+ ).trim(),
105
+ intake_session_completed: Boolean(
106
+ payload?.primary_flow?.intake_session_completed
107
+ || payload?.next_actions?.intake_session_completed
108
+ || payload?.intake_session_completed
109
+ ),
110
+ session: (
111
+ envelope.session && typeof envelope.session === 'object' && !Array.isArray(envelope.session)
112
+ ) ? envelope.session : { session_id: '', turn_id: '' },
113
+ patch_scope: patchScope,
114
+ authoritative_patch_scope: authoritativePatchScope,
115
+ legacy_patch_scope: legacyPatchScope,
116
+ payload,
117
+ final: envelope.final === true,
118
+ invalid: Boolean(invalidReason),
119
+ invalid_reason: invalidReason,
120
+ };
121
+ };
@@ -0,0 +1,79 @@
1
+ export const parseWorkspaceActivation = (raw) => ({
2
+ function_type: raw?.function_type ?? '',
3
+ mode: raw?.mode ?? '',
4
+ title: raw?.title ?? '',
5
+ trace_in_chat: raw?.trace_in_chat === true,
6
+ right_panel: raw?.right_panel ?? '',
7
+ knowledge_base_available: raw?.knowledge_base_available === true,
8
+ });
9
+
10
+ export const parseUICards = (raw) => ({
11
+ scope: raw?.scope ?? 'conversation',
12
+ cards: Array.isArray(raw?.cards) ? raw.cards : [],
13
+ });
14
+
15
+ export const parseKnowledgeBaseState = (raw) => ({
16
+ enabled: raw?.enabled === true,
17
+ status: raw?.status ?? '',
18
+ label: raw?.label ?? '',
19
+ message: raw?.message ?? '',
20
+ });
21
+
22
+ export const parseCategoryIdentified = (raw) => ({
23
+ category: raw?.category ?? raw?.level3 ?? '',
24
+ confidence: raw?.confidence ?? null,
25
+ });
26
+
27
+ export const parseFieldsUpdated = (raw) => ({
28
+ total_fields: raw?.total_fields ?? 0,
29
+ base_fields: raw?.base_fields ?? 0,
30
+ category_fields: raw?.category_fields ?? 0,
31
+ new_fields: Array.isArray(raw?.new_fields) ? raw.new_fields : [],
32
+ refresh_reason: raw?.refresh_reason ?? null,
33
+ });
34
+
35
+ export const parseInstanceProfile = (raw) => ({
36
+ product_name: raw?.instance_profile?.product_name ?? raw?.product_name ?? '',
37
+ brand_tag: raw?.instance_profile?.brand_tag ?? raw?.brand_tag ?? '',
38
+ logo_text: raw?.instance_profile?.logo_text ?? raw?.logo_text ?? '',
39
+ logo_url: raw?.instance_profile?.logo_url ?? raw?.logo_url ?? '',
40
+ ui_labels: raw?.instance_profile?.ui_labels ?? raw?.ui_labels ?? {},
41
+ });
42
+
43
+ export const parseModeRuntime = (raw) => ({
44
+ mode_key: raw?.mode_key ?? '',
45
+ mode_state: raw?.mode_state ?? '',
46
+ });
47
+
48
+ export const parseAgentRuntime = (raw) => ({
49
+ trace_id: raw?.trace_id ?? '',
50
+ session_id: raw?.session_id ?? '',
51
+ intent: raw?.intent ?? '',
52
+ function_type: raw?.function_type ?? '',
53
+ mode_key: raw?.mode_key ?? '',
54
+ mode_state: raw?.mode_state ?? '',
55
+ agent_step_count: Number.isFinite(raw?.agent_step_count) ? raw.agent_step_count : 0,
56
+ agent_step: Array.isArray(raw?.agent_step) ? raw.agent_step : [],
57
+ });
58
+
59
+ export const parseSkillRuntime = (raw) => ({
60
+ trace_id: raw?.trace_id ?? '',
61
+ session_id: raw?.session_id ?? '',
62
+ intent: raw?.intent ?? '',
63
+ function_type: raw?.function_type ?? '',
64
+ mode_key: raw?.mode_key ?? '',
65
+ mode_state: raw?.mode_state ?? '',
66
+ skill_call_count: Number.isFinite(raw?.skill_call_count) ? raw.skill_call_count : 0,
67
+ skill_call: Array.isArray(raw?.skill_call) ? raw.skill_call : [],
68
+ });
69
+
70
+ export const parseModeSwitchReason = (raw) => ({
71
+ mode_key: raw?.mode_key ?? '',
72
+ intent: raw?.intent ?? '',
73
+ source: raw?.source ?? '',
74
+ previous_mode: raw?.previous_mode ?? '',
75
+ manual_mode: raw?.manual_mode ?? '',
76
+ current_mode: raw?.current_mode ?? '',
77
+ intent_override: raw?.intent_override ?? '',
78
+ summary: raw?.summary ?? '',
79
+ });
@@ -0,0 +1,4 @@
1
+ export * from './sse-events/base-parsers.js';
2
+ export * from './sse-events/runtime-parsers.js';
3
+ export * from './sse-events/domain-parsers.js';
4
+ export * from './sse-events/patch-event-parser.js';
package/src/index.js CHANGED
@@ -1 +1,6 @@
1
1
  export * from './chat-core/index.js';
2
+ export * from './app/index.js';
3
+ export * from './state/index.js';
4
+ export * from './orchestration/index.js';
5
+ export * from './adapters/index.js';
6
+ export * from './contracts/index.js';