flare-chat-core 0.2.1 → 0.2.2

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 +125 -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 +109 -0
  26. package/src/app/components/WorkspaceMainPane.jsx +113 -0
  27. package/src/app/components/WorkspaceSessionPane.jsx +48 -0
  28. package/src/app/components/WorkspaceTopBarSection.jsx +65 -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 +170 -12
  70. package/src/chat-core/messages/buildTimelineItems.knowledge-citation.test.mjs +183 -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
@@ -1,867 +1 @@
1
- /**
2
- * SSE 事件规范化层
3
- */
4
-
5
- /**
6
- * @typedef {'thinking'|'searching'|'generating'|'completed'|string} AgentStatusType
7
- *
8
- * @typedef {Object} AgentStatusPayload
9
- * @property {string} agent
10
- * @property {AgentStatusType} status
11
- * @property {number|null} timestamp
12
- *
13
- * @typedef {Object} ThinkingTracePayload
14
- * @property {string} trace
15
- *
16
- * @typedef {Object} ExecutionSourcePayload
17
- * @property {string} source_type
18
- * @property {string} source_label
19
- * @property {string} title
20
- *
21
- * @typedef {Object} ExecutionTracePayload
22
- * @property {string} session_id
23
- * @property {string} step_id
24
- * @property {string} agent
25
- * @property {string} stage
26
- * @property {string} label
27
- * @property {string} detail
28
- * @property {string} reason
29
- * @property {string} status
30
- * @property {Array<ExecutionSourcePayload>} sources
31
- * @property {number|null} timestamp
32
- *
33
- * @typedef {Object} ContentPayload
34
- * @property {string} chunk
35
- *
36
- * @typedef {Object} TracePayload
37
- * @property {string} trace
38
- * @property {string} summary
39
- * @property {string} agent
40
- *
41
- * @typedef {Object} FieldSource
42
- * @property {number|null} confidence
43
- *
44
- * @typedef {Object} FieldProgressPayload
45
- * @property {Record<string, string>} fields
46
- * @property {Record<string, FieldSource>} sources
47
- * @property {number|null} progress
48
- * @property {Array<string>} collected
49
- * @property {Array<string>} missing
50
- * @property {Object|null} last_field
51
- * @property {string|null} refresh_reason
52
- *
53
- * @typedef {Object} DocPayload
54
- * @property {Object|null} doc
55
- *
56
- * @typedef {Object} CapabilitySuggestionPayload
57
- * @property {string} type
58
- * @property {string} reason
59
- * @property {string} benefit
60
- * @property {number} confidence
61
- * @property {number} estimated_cost
62
- *
63
- * @typedef {Object} ModeArtifactPayload
64
- * @property {string} mode_key
65
- * @property {string|null} project_id
66
- * @property {string} message_excerpt
67
- * @property {string} render_hint
68
- *
69
- * @typedef {Object} FieldDefinition
70
- * @property {string} id
71
- * @property {string} label
72
- * @property {string} priority
73
- *
74
- * @typedef {Object} FieldsUpdatedPayload
75
- * @property {number} total_fields
76
- * @property {number} base_fields
77
- * @property {number} category_fields
78
- * @property {Array<FieldDefinition>} new_fields
79
- * @property {string|null} refresh_reason
80
- *
81
- * @typedef {Object} WorkspaceActivationPayload
82
- * @property {string} function_type
83
- * @property {string} mode
84
- * @property {string} title
85
- * @property {boolean} trace_in_chat
86
- * @property {string} right_panel
87
- * @property {boolean} knowledge_base_available
88
- *
89
- * @typedef {Object} UICardActionPayload
90
- * @property {string} type
91
- * @property {Record<string, unknown>} payload
92
- *
93
- * @typedef {Object} UICardPayload
94
- * @property {string} id
95
- * @property {string} kind
96
- * @property {string} title
97
- * @property {string} description
98
- * @property {boolean} available
99
- * @property {string} status
100
- * @property {string} reason
101
- * @property {UICardActionPayload|null} action
102
- *
103
- * @typedef {Object} UICardsPayload
104
- * @property {string} scope
105
- * @property {Array<UICardPayload>} cards
106
- *
107
- * @typedef {Object} KnowledgeBaseStatePayload
108
- * @property {boolean} enabled
109
- * @property {string} status
110
- * @property {string} label
111
- * @property {string} message
112
- *
113
- * @typedef {Object} CategoryIdentifiedPayload
114
- * @property {string} category
115
- * @property {number|null} confidence
116
- *
117
- * @typedef {Object} SSEErrorPayload
118
- * @property {string} message
119
- */
120
-
121
- export const parseAgentStatus = (raw) => ({
122
- agent: raw?.agent ?? '',
123
- status: raw?.status ?? '',
124
- timestamp: raw?.timestamp ?? null,
125
- });
126
-
127
- export const parseThinkingTrace = (raw) => ({
128
- trace: raw?.trace ?? '',
129
- });
130
-
131
- export const parseExecutionTrace = (raw) => ({
132
- session_id: raw?.session_id ?? '',
133
- step_id: raw?.step_id ?? '',
134
- agent: raw?.agent ?? '',
135
- stage: raw?.stage ?? '',
136
- label: raw?.label ?? '',
137
- detail: raw?.detail ?? raw?.trace ?? '',
138
- reason: raw?.reason ?? '',
139
- status: raw?.status ?? raw?.step_status ?? '',
140
- sources: Array.isArray(raw?.sources) ? raw.sources : [],
141
- timestamp: raw?.timestamp ?? null,
142
- });
143
-
144
- export const parseContent = (raw) => ({
145
- chunk: raw?.content ?? '',
146
- });
147
-
148
- export const parseAck = (raw) => ({
149
- trace_id: String(raw?.trace_id || '').trim(),
150
- session_id: String(raw?.session_id || '').trim(),
151
- request_id: String(raw?.request_id || '').trim(),
152
- mode: String(raw?.mode || '').trim(),
153
- ts: Number.isFinite(Number(raw?.ts)) ? Number(raw.ts) : null,
154
- });
155
-
156
- export const parsePhaseEvent = (raw) => ({
157
- phase: String(raw?.phase || '').trim(),
158
- label: String(raw?.label || '').trim(),
159
- status: String(raw?.status || '').trim(),
160
- progress: Number.isFinite(Number(raw?.progress)) ? Number(raw.progress) : null,
161
- meta: raw?.meta && typeof raw.meta === 'object' && !Array.isArray(raw.meta) ? raw.meta : {},
162
- ts: Number.isFinite(Number(raw?.ts)) ? Number(raw.ts) : null,
163
- });
164
-
165
- export const parsePatch = (raw) => ({
166
- scope: String(raw?.scope || '').trim(),
167
- payload: raw?.payload && typeof raw.payload === 'object' && !Array.isArray(raw.payload) ? raw.payload : {},
168
- ts: Number.isFinite(Number(raw?.ts)) ? Number(raw.ts) : null,
169
- });
170
-
171
- export const parseTextDelta = (raw) => ({
172
- channel: String(raw?.channel || 'assistant').trim() || 'assistant',
173
- delta: String(raw?.delta || ''),
174
- ts: Number.isFinite(Number(raw?.ts)) ? Number(raw.ts) : null,
175
- });
176
-
177
- export const parseTextReplace = (raw) => ({
178
- channel: String(raw?.channel || 'assistant').trim() || 'assistant',
179
- content: String(raw?.content || ''),
180
- ts: Number.isFinite(Number(raw?.ts)) ? Number(raw.ts) : null,
181
- });
182
-
183
- export const parseDone = (raw) => ({
184
- trace_id: String(raw?.trace_id || '').trim(),
185
- session_id: String(raw?.session_id || '').trim(),
186
- request_id: String(raw?.request_id || '').trim(),
187
- status: String(raw?.status || 'done').trim() || 'done',
188
- ts: Number.isFinite(Number(raw?.ts)) ? Number(raw.ts) : null,
189
- });
190
-
191
- export const parseTrace = (raw) => ({
192
- trace: raw?.trace ?? raw?.detail ?? '',
193
- summary: raw?.summary ?? raw?.label ?? '',
194
- agent: raw?.agent ?? '',
195
- });
196
-
197
- function resolveModeEventPayload(raw) {
198
- if (raw?.payload && typeof raw.payload === 'object' && !Array.isArray(raw.payload)) {
199
- return raw.payload;
200
- }
201
-
202
- return raw && typeof raw === 'object' ? raw : {};
203
- }
204
-
205
- function pickFirstArray(payload, keys) {
206
- for (const key of keys) {
207
- const value = payload?.[key];
208
- if (Array.isArray(value)) {
209
- return value;
210
- }
211
- }
212
- return [];
213
- }
214
-
215
- function parseGuidedSession(rawGuidedSession) {
216
- const payload = (rawGuidedSession && typeof rawGuidedSession === 'object' && !Array.isArray(rawGuidedSession))
217
- ? rawGuidedSession
218
- : {};
219
- const questionQueue = (payload.question_queue && typeof payload.question_queue === 'object' && !Array.isArray(payload.question_queue))
220
- ? payload.question_queue
221
- : {};
222
- const summary = (payload.summary && typeof payload.summary === 'object' && !Array.isArray(payload.summary))
223
- ? payload.summary
224
- : {};
225
- const decisionPoint = (payload.decision_point && typeof payload.decision_point === 'object' && !Array.isArray(payload.decision_point))
226
- ? payload.decision_point
227
- : {};
228
- return {
229
- state: payload?.state ?? '',
230
- active: payload?.active === true,
231
- current_question: payload?.current_question && typeof payload.current_question === 'object' && !Array.isArray(payload.current_question)
232
- ? payload.current_question
233
- : null,
234
- question_queue: {
235
- total: Number.isFinite(Number(questionQueue?.total)) ? Number(questionQueue.total) : 0,
236
- answered: Number.isFinite(Number(questionQueue?.answered)) ? Number(questionQueue.answered) : 0,
237
- remaining: Number.isFinite(Number(questionQueue?.remaining)) ? Number(questionQueue.remaining) : 0,
238
- pending_fields: Array.isArray(questionQueue?.pending_fields) ? questionQueue.pending_fields : [],
239
- },
240
- summary: {
241
- text: summary?.text ?? '',
242
- ready: summary?.ready === true,
243
- },
244
- decision_point: {
245
- active: decisionPoint?.active === true,
246
- actions: Array.isArray(decisionPoint?.actions) ? decisionPoint.actions : [],
247
- },
248
- };
249
- }
250
-
251
- export const parseFieldProgress = (raw) => {
252
- const payload = resolveModeEventPayload(raw);
253
- return {
254
- intake_session_id: payload?.intake_session_id ?? '',
255
- intake_session_state: payload?.intake_session_state ?? '',
256
- intake_session_completed: payload?.intake_session_completed === true,
257
- flow_state: payload?.flow_state ?? payload?.collection_phase ?? '',
258
- fields: payload?.fields ?? {},
259
- sources: payload?.sources ?? {},
260
- field_entries: Array.isArray(payload?.field_entries) ? payload.field_entries : [],
261
- field_groups: payload?.field_groups && typeof payload.field_groups === 'object' && !Array.isArray(payload.field_groups)
262
- ? payload.field_groups
263
- : {},
264
- extra_fields: Array.isArray(payload?.extra_fields) ? payload.extra_fields : [],
265
- progress: payload?.progress ?? null,
266
- required_progress: payload?.required_progress ?? null,
267
- recommended_progress: payload?.recommended_progress ?? null,
268
- optional_progress: payload?.optional_progress ?? null,
269
- collected: Array.isArray(payload?.collected) ? payload.collected : [],
270
- missing: Array.isArray(payload?.missing) ? payload.missing : [],
271
- required_collected: Array.isArray(payload?.required_collected) ? payload.required_collected : [],
272
- required_missing: Array.isArray(payload?.required_missing) ? payload.required_missing : [],
273
- recommended_collected: Array.isArray(payload?.recommended_collected) ? payload.recommended_collected : [],
274
- recommended_missing: Array.isArray(payload?.recommended_missing) ? payload.recommended_missing : [],
275
- optional_collected: Array.isArray(payload?.optional_collected) ? payload.optional_collected : [],
276
- optional_missing: Array.isArray(payload?.optional_missing) ? payload.optional_missing : [],
277
- last_field: payload?.last_field ?? null,
278
- refresh_reason: payload?.refresh_reason ?? null,
279
- field_definitions: Array.isArray(payload?.field_definitions) ? payload.field_definitions : [],
280
- required_fields: Array.isArray(payload?.required_fields) ? payload.required_fields : [],
281
- recommended_fields: Array.isArray(payload?.recommended_fields) ? payload.recommended_fields : [],
282
- optional_fields: Array.isArray(payload?.optional_fields) ? payload.optional_fields : [],
283
- intake_core_fields: Array.isArray(payload?.intake_core_fields) ? payload.intake_core_fields : [],
284
- intake_supplementary_fields: Array.isArray(payload?.intake_supplementary_fields) ? payload.intake_supplementary_fields : [],
285
- analysis_enrichment_fields: Array.isArray(payload?.analysis_enrichment_fields) ? payload.analysis_enrichment_fields : [],
286
- field_priorities: payload?.field_priorities && typeof payload.field_priorities === 'object' && !Array.isArray(payload.field_priorities)
287
- ? payload.field_priorities
288
- : {},
289
- current_question: payload?.current_question && typeof payload.current_question === 'object' && !Array.isArray(payload.current_question)
290
- ? payload.current_question
291
- : null,
292
- question_progress: payload?.question_progress && typeof payload.question_progress === 'object' && !Array.isArray(payload.question_progress)
293
- ? payload.question_progress
294
- : { current: 0, total: 0 },
295
- collection_phase: payload?.collection_phase ?? '',
296
- required_missing_count: Number.isFinite(payload?.required_missing_count) ? payload.required_missing_count : null,
297
- required_coverage: Number.isFinite(Number(payload?.required_coverage)) ? Number(payload.required_coverage) : 0,
298
- total_coverage: Number.isFinite(Number(payload?.total_coverage)) ? Number(payload.total_coverage) : 0,
299
- analysis_entry_threshold: Number.isFinite(Number(payload?.analysis_entry_threshold)) ? Number(payload.analysis_entry_threshold) : 0.8,
300
- analysis_entry_eligible: payload?.analysis_entry_eligible === true,
301
- active_collecting: payload?.active_collecting === true,
302
- ready_for_submit: payload?.ready_for_submit === true,
303
- decision_point_active: payload?.decision_point_active === true,
304
- has_active_question: payload?.has_active_question === true,
305
- guided_session: parseGuidedSession(payload?.guided_session),
306
- };
307
- };
308
-
309
- export const parseDoc = (raw) => ({
310
- doc: raw?.doc ?? null,
311
- });
312
-
313
- export const parseCapabilitySuggestion = (raw) => ({
314
- type: raw?.type ?? '',
315
- reason: raw?.reason ?? '',
316
- benefit: raw?.benefit ?? '',
317
- confidence: raw?.confidence ?? 0,
318
- estimated_cost: raw?.estimated_cost ?? 0,
319
- });
320
-
321
- export const parseRequirementDraft = (raw) => {
322
- const payload = resolveModeEventPayload(raw);
323
- const draft = payload?.draft && typeof payload.draft === 'object' && !Array.isArray(payload.draft)
324
- ? payload.draft
325
- : {};
326
-
327
- return {
328
- intake_session_id: payload?.intake_session_id ?? '',
329
- intake_session_state: payload?.intake_session_state ?? '',
330
- intake_session_completed: payload?.intake_session_completed === true,
331
- mode_key: payload?.mode_key ?? '',
332
- project_id: payload?.project_id ?? null,
333
- message_excerpt: payload?.message_excerpt ?? '',
334
- draft: {
335
- ...draft,
336
- fields: Array.isArray(draft.fields) ? draft.fields : [],
337
- },
338
- render_hint: payload?.render_hint ?? 'requirement_draft',
339
- };
340
- };
341
-
342
- export const parseNextActions = (raw) => {
343
- const payload = resolveModeEventPayload(raw);
344
- const actions = Array.isArray(payload?.actions)
345
- ? payload.actions
346
- : (Array.isArray(payload?.next_actions) ? payload.next_actions : []);
347
- return {
348
- intake_session_id: payload?.intake_session_id ?? '',
349
- intake_session_state: payload?.intake_session_state ?? '',
350
- intake_session_completed: payload?.intake_session_completed === true,
351
- mode_key: payload?.mode_key ?? '',
352
- flow_state: payload?.flow_state ?? payload?.collection_phase ?? '',
353
- project_id: payload?.project_id ?? null,
354
- message_excerpt: payload?.message_excerpt ?? '',
355
- actions,
356
- completion_state: payload?.completion_state ?? '',
357
- required_missing: Array.isArray(payload?.required_missing) ? payload.required_missing : [],
358
- recommended_missing: Array.isArray(payload?.recommended_missing) ? payload.recommended_missing : [],
359
- optional_missing: Array.isArray(payload?.optional_missing) ? payload.optional_missing : [],
360
- intake_core_fields: Array.isArray(payload?.intake_core_fields) ? payload.intake_core_fields : [],
361
- intake_supplementary_fields: Array.isArray(payload?.intake_supplementary_fields) ? payload.intake_supplementary_fields : [],
362
- analysis_enrichment_fields: Array.isArray(payload?.analysis_enrichment_fields) ? payload.analysis_enrichment_fields : [],
363
- ready_for_sourcing: payload?.ready_for_sourcing ?? null,
364
- status: payload?.status ?? '',
365
- reason: payload?.reason ?? '',
366
- chooser_required: payload?.chooser_required === true,
367
- blocking_reason: payload?.blocking_reason ?? '',
368
- blocking: payload?.blocking && typeof payload.blocking === 'object' && !Array.isArray(payload.blocking)
369
- ? payload.blocking
370
- : null,
371
- target_field: payload?.target_field ?? '',
372
- degrade_reason: Array.isArray(payload?.degrade_reason) ? payload.degrade_reason : [],
373
- current_question: payload?.current_question && typeof payload.current_question === 'object' && !Array.isArray(payload.current_question)
374
- ? payload.current_question
375
- : null,
376
- question_progress: payload?.question_progress && typeof payload.question_progress === 'object' && !Array.isArray(payload.question_progress)
377
- ? payload.question_progress
378
- : { current: 0, total: 0 },
379
- collection_phase: payload?.collection_phase ?? '',
380
- required_missing_count: Number.isFinite(payload?.required_missing_count) ? payload.required_missing_count : null,
381
- render_hint: payload?.render_hint ?? 'next_actions',
382
- required_coverage: Number.isFinite(Number(payload?.required_coverage)) ? Number(payload.required_coverage) : 0,
383
- total_coverage: Number.isFinite(Number(payload?.total_coverage)) ? Number(payload.total_coverage) : 0,
384
- analysis_entry_threshold: Number.isFinite(Number(payload?.analysis_entry_threshold)) ? Number(payload.analysis_entry_threshold) : 0.8,
385
- analysis_entry_eligible: payload?.analysis_entry_eligible === true,
386
- active_collecting: payload?.active_collecting === true,
387
- ready_for_submit: payload?.ready_for_submit === true,
388
- decision_point_active: payload?.decision_point_active === true,
389
- has_active_question: payload?.has_active_question === true,
390
- guided_session: parseGuidedSession(payload?.guided_session),
391
- };
392
- };
393
-
394
- export const parseSourcingCandidates = (raw) => {
395
- const payload = resolveModeEventPayload(raw);
396
- const missing = pickFirstArray(payload, ['missing', 'base_missing', 'missing_fields', 'required_missing']);
397
- return {
398
- mode_key: payload?.mode_key ?? '',
399
- project_id: payload?.project_id ?? null,
400
- message_excerpt: payload?.message_excerpt ?? '',
401
- missing,
402
- base_fields: Array.isArray(payload?.base_fields) ? payload.base_fields : [],
403
- base_collected: Array.isArray(payload?.base_collected) ? payload.base_collected : [],
404
- base_missing: Array.isArray(payload?.base_missing) ? payload.base_missing : [],
405
- base_total: Number.isFinite(payload?.base_total) ? payload.base_total : 0,
406
- base_progress: Number.isFinite(payload?.base_progress) ? payload.base_progress : null,
407
- base_ready_for_matching: payload?.base_ready_for_matching === true,
408
- mode_state: payload?.mode_state ?? '',
409
- candidate_count: Number.isFinite(payload?.candidate_count) ? payload.candidate_count : null,
410
- is_placeholder: payload?.is_placeholder ?? null,
411
- required_missing: Array.isArray(payload?.required_missing) ? payload.required_missing : [],
412
- recommended_missing: Array.isArray(payload?.recommended_missing) ? payload.recommended_missing : [],
413
- optional_missing: Array.isArray(payload?.optional_missing) ? payload.optional_missing : [],
414
- actions: Array.isArray(payload?.actions) ? payload.actions : [],
415
- candidates: pickFirstArray(payload, ['candidates', 'sourcing_candidates', 'candidate_list', 'results']),
416
- reasoning: Array.isArray(payload?.reasoning) ? payload.reasoning : [],
417
- summary: payload?.sourcing_summary ?? payload?.summary ?? null,
418
- render_hint: payload?.render_hint ?? 'sourcing_candidates',
419
- };
420
- };
421
-
422
- export const parseRiskSummary = (raw) => {
423
- const payload = resolveModeEventPayload(raw);
424
- const risks = pickFirstArray(payload, ['risks', 'risk_items', 'items', 'risk_list']);
425
- const sources = pickFirstArray(payload, ['sources', 'references', 'evidence']);
426
- return {
427
- mode_key: payload?.mode_key ?? '',
428
- project_id: payload?.project_id ?? null,
429
- message_excerpt: payload?.message_excerpt ?? '',
430
- mode_state: payload?.mode_state ?? '',
431
- base_fields: Array.isArray(payload?.base_fields) ? payload.base_fields : [],
432
- base_collected: Array.isArray(payload?.base_collected) ? payload.base_collected : [],
433
- base_missing: Array.isArray(payload?.base_missing) ? payload.base_missing : [],
434
- base_total: Number.isFinite(payload?.base_total) ? payload.base_total : 0,
435
- base_progress: Number.isFinite(payload?.base_progress) ? payload.base_progress : null,
436
- base_ready_for_matching: payload?.base_ready_for_matching === true,
437
- is_placeholder: payload?.is_placeholder ?? null,
438
- actions: Array.isArray(payload?.actions) ? payload.actions : [],
439
- risks,
440
- sources,
441
- summary: payload?.risk_summary ?? payload?.summary ?? null,
442
- render_hint: payload?.render_hint ?? 'risk_summary',
443
- };
444
- };
445
-
446
- export const parseShortlistUpdated = (raw) => {
447
- const payload = resolveModeEventPayload(raw);
448
- const shortlist = pickFirstArray(payload, ['shortlist', 'shortlist_updated', 'selected_candidates', 'items']);
449
- return {
450
- mode_key: payload?.mode_key ?? '',
451
- project_id: payload?.project_id ?? null,
452
- message_excerpt: payload?.message_excerpt ?? '',
453
- mode_state: payload?.mode_state ?? '',
454
- base_fields: Array.isArray(payload?.base_fields) ? payload.base_fields : [],
455
- base_collected: Array.isArray(payload?.base_collected) ? payload.base_collected : [],
456
- base_missing: Array.isArray(payload?.base_missing) ? payload.base_missing : [],
457
- base_total: Number.isFinite(payload?.base_total) ? payload.base_total : 0,
458
- base_progress: Number.isFinite(payload?.base_progress) ? payload.base_progress : null,
459
- base_ready_for_matching: payload?.base_ready_for_matching === true,
460
- shortlist,
461
- status: payload?.status ?? payload?.state ?? '',
462
- reason: payload?.reason ?? payload?.message ?? '',
463
- selected_count: Number.isFinite(payload?.selected_count) ? payload.selected_count : null,
464
- is_placeholder: payload?.is_placeholder ?? null,
465
- actions: Array.isArray(payload?.actions) ? payload.actions : [],
466
- render_hint: payload?.render_hint ?? 'shortlist_updated',
467
- };
468
- };
469
-
470
- export const parseEvaluationReportReady = (raw) => {
471
- const payload = resolveModeEventPayload(raw);
472
- const nestedReport = payload?.evaluation_report_ready
473
- && typeof payload.evaluation_report_ready === 'object'
474
- && !Array.isArray(payload.evaluation_report_ready)
475
- ? payload.evaluation_report_ready
476
- : {};
477
- const reportSource = payload?.report
478
- || nestedReport.report
479
- || nestedReport;
480
- const report = reportSource && typeof reportSource === 'object' && !Array.isArray(reportSource)
481
- ? reportSource
482
- : {};
483
- const compareTable = pickFirstArray(payload, ['compare_table', 'comparison_table', 'compare_rows'])
484
- || pickFirstArray(report, ['compare_table', 'comparison_table', 'compare_rows'])
485
- || pickFirstArray(nestedReport, ['compare_table', 'comparison_table', 'compare_rows']);
486
-
487
- return {
488
- mode_key: payload?.mode_key ?? '',
489
- project_id: payload?.project_id ?? null,
490
- message_excerpt: payload?.message_excerpt ?? '',
491
- mode_state: payload?.mode_state ?? '',
492
- base_fields: Array.isArray(payload?.base_fields) ? payload.base_fields : [],
493
- base_collected: Array.isArray(payload?.base_collected) ? payload.base_collected : [],
494
- base_missing: Array.isArray(payload?.base_missing) ? payload.base_missing : [],
495
- base_total: Number.isFinite(payload?.base_total) ? payload.base_total : 0,
496
- base_progress: Number.isFinite(payload?.base_progress) ? payload.base_progress : null,
497
- base_ready_for_matching: payload?.base_ready_for_matching === true,
498
- is_placeholder: payload?.is_placeholder ?? null,
499
- actions: Array.isArray(payload?.actions) ? payload.actions : [],
500
- report: {
501
- ...report,
502
- sections: Array.isArray(report.sections) ? report.sections : [],
503
- },
504
- compare_table: payload?.compare_table
505
- ?? payload?.comparison_table
506
- ?? payload?.compare_rows
507
- ?? report?.compare_table
508
- ?? report?.comparison_table
509
- ?? report?.compare_rows
510
- ?? nestedReport?.compare_table
511
- ?? nestedReport?.comparison_table
512
- ?? nestedReport?.compare_rows
513
- ?? compareTable,
514
- render_hint: payload?.render_hint ?? 'evaluation_report_ready',
515
- };
516
- };
517
-
518
- export const parsePlanBlock = (raw) => {
519
- const payload = resolveModeEventPayload(raw);
520
- return {
521
- ...payload,
522
- constraints: Array.isArray(payload?.constraints) ? payload.constraints : [],
523
- plan: Array.isArray(payload?.plan) ? payload.plan : [],
524
- acceptance: Array.isArray(payload?.acceptance) ? payload.acceptance : [],
525
- actions: Array.isArray(payload?.actions) ? payload.actions : [],
526
- };
527
- };
528
-
529
- export const parseCanvasState = (raw) => {
530
- const payload = resolveModeEventPayload(raw);
531
- const canvasState = payload?.canvas_state && typeof payload.canvas_state === 'object' && !Array.isArray(payload.canvas_state)
532
- ? payload.canvas_state
533
- : {};
534
- return {
535
- ...payload,
536
- canvas_state: {
537
- ...canvasState,
538
- versions: Array.isArray(canvasState.versions) ? canvasState.versions : [],
539
- },
540
- };
541
- };
542
-
543
- export const parseCanvasRevision = (raw) => {
544
- const payload = resolveModeEventPayload(raw);
545
- return {
546
- ...payload,
547
- actions: Array.isArray(payload?.actions) ? payload.actions : [],
548
- };
549
- };
550
-
551
- export const parseKnowledgeSearch = (raw) => {
552
- const payload = resolveModeEventPayload(raw);
553
- const sourceBreakdown = payload?.source_breakdown && typeof payload.source_breakdown === 'object'
554
- && !Array.isArray(payload.source_breakdown)
555
- ? payload.source_breakdown
556
- : {};
557
- return {
558
- ...payload,
559
- run_id: payload?.run_id ?? '',
560
- query: payload?.query ?? '',
561
- result_count: Number.isFinite(payload?.result_count) ? payload.result_count : null,
562
- source_breakdown: {
563
- local: Number.isFinite(sourceBreakdown.local) ? sourceBreakdown.local : 0,
564
- mcp: Number.isFinite(sourceBreakdown.mcp) ? sourceBreakdown.mcp : 0,
565
- web: Number.isFinite(sourceBreakdown.web) ? sourceBreakdown.web : 0,
566
- },
567
- results: Array.isArray(payload?.results) ? payload.results : [],
568
- };
569
- };
570
-
571
- export const parseKnowledgeCitation = (raw) => {
572
- const payload = resolveModeEventPayload(raw);
573
- return {
574
- ...payload,
575
- run_id: payload?.run_id ?? '',
576
- citations: Array.isArray(payload?.citations) ? payload.citations : [],
577
- };
578
- };
579
-
580
- export const parseOrchestrationStatus = (raw) => {
581
- const payload = resolveModeEventPayload(raw);
582
- const activeWorkItem = payload?.active_work_item && typeof payload.active_work_item === 'object' && !Array.isArray(payload.active_work_item)
583
- ? payload.active_work_item
584
- : null;
585
- return {
586
- trace_id: payload?.trace_id ?? '',
587
- intake_session_id: payload?.intake_session_id ?? '',
588
- intake_session_state: payload?.intake_session_state ?? '',
589
- intake_session_completed: payload?.intake_session_completed === true,
590
- current_stage: payload?.current_stage ?? '',
591
- detected_intent: payload?.detected_intent && typeof payload.detected_intent === 'object' && !Array.isArray(payload.detected_intent)
592
- ? payload.detected_intent
593
- : {},
594
- confirmed_updates: Array.isArray(payload?.confirmed_updates) ? payload.confirmed_updates : [],
595
- open_fields: Array.isArray(payload?.open_fields) ? payload.open_fields : [],
596
- search_actions: Array.isArray(payload?.search_actions) ? payload.search_actions : [],
597
- workspace_updates: payload?.workspace_updates && typeof payload.workspace_updates === 'object' && !Array.isArray(payload.workspace_updates)
598
- ? payload.workspace_updates
599
- : {},
600
- next_action: payload?.next_action && typeof payload.next_action === 'object' && !Array.isArray(payload.next_action)
601
- ? payload.next_action
602
- : {},
603
- next_actions: Array.isArray(payload?.next_actions) ? payload.next_actions : [],
604
- active_collecting: payload?.active_collecting === true,
605
- ready_for_submit: payload?.ready_for_submit === true,
606
- required_coverage: Number.isFinite(Number(payload?.required_coverage)) ? Number(payload.required_coverage) : 0,
607
- total_coverage: Number.isFinite(Number(payload?.total_coverage)) ? Number(payload.total_coverage) : 0,
608
- analysis_entry_threshold: Number.isFinite(Number(payload?.analysis_entry_threshold)) ? Number(payload.analysis_entry_threshold) : 0.8,
609
- analysis_entry_eligible: payload?.analysis_entry_eligible === true,
610
- decision_point_active: payload?.decision_point_active === true,
611
- has_active_question: payload?.has_active_question === true,
612
- guided_session: parseGuidedSession(payload?.guided_session),
613
- active_work_item: activeWorkItem,
614
- active_fields: Array.isArray(payload?.active_fields) ? payload.active_fields : [],
615
- hypotheses: Array.isArray(payload?.hypotheses) ? payload.hypotheses : [],
616
- planner: payload?.planner && typeof payload.planner === 'object' && !Array.isArray(payload.planner)
617
- ? payload.planner
618
- : {},
619
- chooser: payload?.chooser && typeof payload.chooser === 'object' && !Array.isArray(payload.chooser)
620
- ? payload.chooser
621
- : null,
622
- stage_transition: payload?.stage_transition && typeof payload.stage_transition === 'object' && !Array.isArray(payload.stage_transition)
623
- ? payload.stage_transition
624
- : {},
625
- decision_basis: Array.isArray(payload?.decision_basis) ? payload.decision_basis : [],
626
- blocking: payload?.blocking && typeof payload.blocking === 'object' && !Array.isArray(payload.blocking)
627
- ? payload.blocking
628
- : {},
629
- node: payload?.node && typeof payload.node === 'object' && !Array.isArray(payload.node)
630
- ? payload.node
631
- : {},
632
- node_id: payload?.node_id ?? '',
633
- node_state: payload?.node_state ?? '',
634
- node_reason: payload?.node_reason ?? '',
635
- needs_user_choice: payload?.needs_user_choice === true,
636
- conversation_intent_state: payload?.conversation_intent_state ?? '',
637
- session_intent_stage: payload?.session_intent_stage ?? '',
638
- intent_type: payload?.intent_type ?? '',
639
- procurement_relevance: payload?.procurement_relevance === true,
640
- consultative_procurement_qa: payload?.consultative_procurement_qa === true,
641
- escalation_target: payload?.escalation_target ?? '',
642
- intake_candidate: payload?.intake_candidate === true,
643
- clarification_recommended: payload?.clarification_recommended === true,
644
- analysis_ready: payload?.analysis_ready === true,
645
- draft_seeded: payload?.draft_seeded === true,
646
- missing_key_fields: Array.isArray(payload?.missing_key_fields) ? payload.missing_key_fields : [],
647
- recommended_next_step: payload?.recommended_next_step ?? '',
648
- intent_and_escalation_confidence: Number.isFinite(Number(payload?.intent_and_escalation_confidence))
649
- ? Number(payload.intent_and_escalation_confidence)
650
- : 0,
651
- intent_and_escalation_reason: payload?.intent_and_escalation_reason ?? '',
652
- intent_escalation_policy: payload?.intent_escalation_policy && typeof payload.intent_escalation_policy === 'object' && !Array.isArray(payload.intent_escalation_policy)
653
- ? payload.intent_escalation_policy
654
- : {},
655
- intent_escalation_policy_source: payload?.intent_escalation_policy_source ?? '',
656
- degrade_reason: Array.isArray(payload?.degrade_reason) ? payload.degrade_reason : [],
657
- content_policy: payload?.content_policy && typeof payload.content_policy === 'object' && !Array.isArray(payload.content_policy)
658
- ? payload.content_policy
659
- : {},
660
- };
661
- };
662
-
663
- export const parseWorkspaceActivation = (raw) => ({
664
- function_type: raw?.function_type ?? '',
665
- mode: raw?.mode ?? '',
666
- title: raw?.title ?? '',
667
- trace_in_chat: raw?.trace_in_chat === true,
668
- right_panel: raw?.right_panel ?? '',
669
- knowledge_base_available: raw?.knowledge_base_available === true,
670
- });
671
-
672
- export const parseUICards = (raw) => ({
673
- scope: raw?.scope ?? 'conversation',
674
- cards: Array.isArray(raw?.cards) ? raw.cards : [],
675
- });
676
-
677
- export const parseKnowledgeBaseState = (raw) => ({
678
- enabled: raw?.enabled === true,
679
- status: raw?.status ?? '',
680
- label: raw?.label ?? '',
681
- message: raw?.message ?? '',
682
- });
683
-
684
- export const parseCategoryIdentified = (raw) => ({
685
- category: raw?.category ?? raw?.level3 ?? '',
686
- confidence: raw?.confidence ?? null,
687
- });
688
-
689
- export const parseFieldsUpdated = (raw) => ({
690
- total_fields: raw?.total_fields ?? 0,
691
- base_fields: raw?.base_fields ?? 0,
692
- category_fields: raw?.category_fields ?? 0,
693
- new_fields: Array.isArray(raw?.new_fields) ? raw.new_fields : [],
694
- refresh_reason: raw?.refresh_reason ?? null,
695
- });
696
-
697
- export const parseInstanceProfile = (raw) => ({
698
- product_name: raw?.instance_profile?.product_name ?? raw?.product_name ?? '',
699
- brand_tag: raw?.instance_profile?.brand_tag ?? raw?.brand_tag ?? '',
700
- logo_text: raw?.instance_profile?.logo_text ?? raw?.logo_text ?? '',
701
- logo_url: raw?.instance_profile?.logo_url ?? raw?.logo_url ?? '',
702
- ui_labels: raw?.instance_profile?.ui_labels ?? raw?.ui_labels ?? {},
703
- });
704
-
705
- export const parseModeRuntime = (raw) => ({
706
- mode_key: raw?.mode_key ?? '',
707
- mode_state: raw?.mode_state ?? '',
708
- });
709
-
710
- export const parseAgentRuntime = (raw) => ({
711
- trace_id: raw?.trace_id ?? '',
712
- session_id: raw?.session_id ?? '',
713
- intent: raw?.intent ?? '',
714
- function_type: raw?.function_type ?? '',
715
- mode_key: raw?.mode_key ?? '',
716
- mode_state: raw?.mode_state ?? '',
717
- agent_step_count: Number.isFinite(raw?.agent_step_count) ? raw.agent_step_count : 0,
718
- agent_step: Array.isArray(raw?.agent_step) ? raw.agent_step : [],
719
- });
720
-
721
- export const parseSkillRuntime = (raw) => ({
722
- trace_id: raw?.trace_id ?? '',
723
- session_id: raw?.session_id ?? '',
724
- intent: raw?.intent ?? '',
725
- function_type: raw?.function_type ?? '',
726
- mode_key: raw?.mode_key ?? '',
727
- mode_state: raw?.mode_state ?? '',
728
- skill_call_count: Number.isFinite(raw?.skill_call_count) ? raw.skill_call_count : 0,
729
- skill_call: Array.isArray(raw?.skill_call) ? raw.skill_call : [],
730
- });
731
-
732
- export const parseModeSwitchReason = (raw) => ({
733
- mode_key: raw?.mode_key ?? '',
734
- intent: raw?.intent ?? '',
735
- source: raw?.source ?? '',
736
- previous_mode: raw?.previous_mode ?? '',
737
- manual_mode: raw?.manual_mode ?? '',
738
- current_mode: raw?.current_mode ?? '',
739
- intent_override: raw?.intent_override ?? '',
740
- summary: raw?.summary ?? '',
741
- });
742
-
743
- export const parseSSEError = (raw) => ({
744
- message: raw?.message ?? '发生错误',
745
- });
746
-
747
- export const PATCH_SCOPE_AUTHORITATIVE = Object.freeze([
748
- 'session',
749
- 'assistant_reply',
750
- 'primary_flow',
751
- 'checkpoint',
752
- 'intent_state',
753
- 'question',
754
- 'confirmed_fields',
755
- 'missing_fields',
756
- 'next_actions',
757
- 'analysis',
758
- 'observation',
759
- 'capabilities.field_retrieval',
760
- 'errors',
761
- ]);
762
-
763
- export const PATCH_SCOPE_LEGACY_COMPAT = Object.freeze([
764
- 'current_question',
765
- 'recommendation_summary',
766
- ]);
767
-
768
- const PATCH_SCOPE_ALLOWLIST = new Set([
769
- ...PATCH_SCOPE_AUTHORITATIVE,
770
- ...PATCH_SCOPE_LEGACY_COMPAT,
771
- ]);
772
-
773
- function getByScopePath(payload, scope) {
774
- if (!payload || typeof payload !== 'object' || Array.isArray(payload)) {
775
- return undefined;
776
- }
777
- const segments = String(scope || '').split('.').filter(Boolean);
778
- if (!segments.length) {
779
- return undefined;
780
- }
781
- let current = payload;
782
- for (const segment of segments) {
783
- if (!current || typeof current !== 'object' || Array.isArray(current) || !(segment in current)) {
784
- return undefined;
785
- }
786
- current = current[segment];
787
- }
788
- return current;
789
- }
790
-
791
- export const parsePatchEvent = (raw) => {
792
- const envelope = (raw && typeof raw === 'object' && !Array.isArray(raw)) ? raw : {};
793
- const patchScope = Array.isArray(envelope.patch_scope)
794
- ? envelope.patch_scope.map((item) => String(item || '').trim()).filter(Boolean)
795
- : [];
796
- const payload = (envelope.payload && typeof envelope.payload === 'object' && !Array.isArray(envelope.payload))
797
- ? envelope.payload
798
- : {};
799
- const hasPayload = Object.keys(payload).length > 0;
800
- const hasUnknownScope = patchScope.some((item) => !(PATCH_SCOPE_ALLOWLIST.has(item) || item.startsWith('confirmed_fields.')));
801
- const hasScopePayloadMismatch = patchScope.some((scope) => {
802
- const direct = payload[scope];
803
- const byPath = getByScopePath(payload, scope);
804
- const lastSegment = String(scope).split('.').slice(-1)[0];
805
- const leaf = payload[lastSegment];
806
- return direct === undefined && byPath === undefined && leaf === undefined;
807
- });
808
- const legacyPatchScope = patchScope.filter((scope) => PATCH_SCOPE_LEGACY_COMPAT.includes(scope));
809
- const authoritativePatchScope = patchScope.filter(
810
- (scope) => PATCH_SCOPE_AUTHORITATIVE.includes(scope) || scope.startsWith('confirmed_fields.')
811
- );
812
- const invalidReason = (() => {
813
- if (envelope.message_type !== 'patch_event') {
814
- return 'invalid_message_type';
815
- }
816
- if (!['ack', 'patch', 'error', 'final'].includes(String(envelope.event_type || '').trim())) {
817
- return 'invalid_event_type';
818
- }
819
- if (hasPayload && patchScope.length === 0) {
820
- return 'payload_without_patch_scope';
821
- }
822
- if (hasUnknownScope) {
823
- return 'unknown_patch_scope';
824
- }
825
- if (patchScope.length > 0 && hasScopePayloadMismatch) {
826
- return 'patch_scope_payload_mismatch';
827
- }
828
- return '';
829
- })();
830
- return {
831
- contract_version: String(envelope.contract_version || 'flare.v1'),
832
- message_type: 'patch_event',
833
- event_type: String(envelope.event_type || '').trim(),
834
- sequence: Number.isFinite(Number(envelope.sequence)) ? Number(envelope.sequence) : 0,
835
- trace_id: String(envelope.trace_id || '').trim(),
836
- client_request_id: String(envelope.client_request_id || '').trim(),
837
- intake_session_id: String(
838
- payload?.primary_flow?.intake_session_id
839
- || payload?.checkpoint?.intake_session_id
840
- || payload?.next_actions?.intake_session_id
841
- || payload?.question?.intake_session_id
842
- || payload?.intake_session_id
843
- || ''
844
- ).trim(),
845
- intake_session_state: String(
846
- payload?.primary_flow?.intake_session_state
847
- || payload?.next_actions?.intake_session_state
848
- || payload?.intake_session_state
849
- || ''
850
- ).trim(),
851
- intake_session_completed: Boolean(
852
- payload?.primary_flow?.intake_session_completed
853
- || payload?.next_actions?.intake_session_completed
854
- || payload?.intake_session_completed
855
- ),
856
- session: (
857
- envelope.session && typeof envelope.session === 'object' && !Array.isArray(envelope.session)
858
- ) ? envelope.session : { session_id: '', turn_id: '' },
859
- patch_scope: patchScope,
860
- authoritative_patch_scope: authoritativePatchScope,
861
- legacy_patch_scope: legacyPatchScope,
862
- payload,
863
- final: envelope.final === true,
864
- invalid: Boolean(invalidReason),
865
- invalid_reason: invalidReason,
866
- };
867
- };
1
+ export * from '../../contracts/sse-events.js';