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
@@ -1,356 +1 @@
1
- import { useRef, useState, useCallback, useEffect } from 'react';
2
- import { SSEClient } from './sse-client.js';
3
- import {
4
- parseAck,
5
- parseAgentStatus,
6
- parseThinkingTrace,
7
- parseExecutionTrace,
8
- parseContent,
9
- parseDone,
10
- parsePatch,
11
- parsePhaseEvent,
12
- parseTextDelta,
13
- parseTextReplace,
14
- parseTrace,
15
- parseFieldProgress,
16
- parseDoc,
17
- parseRequirementDraft,
18
- parseNextActions,
19
- parseSourcingCandidates,
20
- parseRiskSummary,
21
- parseShortlistUpdated,
22
- parseEvaluationReportReady,
23
- parseWorkspaceActivation,
24
- parseUICards,
25
- parseKnowledgeBaseState,
26
- parseCategoryIdentified,
27
- parseCapabilitySuggestion,
28
- parseFieldsUpdated,
29
- parseInstanceProfile,
30
- parseModeRuntime,
31
- parseAgentRuntime,
32
- parseSkillRuntime,
33
- parseModeSwitchReason,
34
- parseKnowledgeSearch,
35
- parseKnowledgeCitation,
36
- parseOrchestrationStatus,
37
- parseCanvasState,
38
- parseCanvasRevision,
39
- parsePlanBlock,
40
- parsePatchEvent,
41
- parseSSEError,
42
- } from './sse-events.js';
43
-
44
- function classifyError(errorMessage) {
45
- if (!errorMessage) return 'unknown';
46
-
47
- const lowerMsg = errorMessage.toLowerCase();
48
- if (lowerMsg.includes('network') || lowerMsg.includes('fetch')) return 'network';
49
- if (lowerMsg.includes('timeout') || lowerMsg.includes('timed out')) return 'timeout';
50
- if (/\b(500|502|503|504)\b/.test(lowerMsg)) return 'api';
51
- return 'unknown';
52
- }
53
-
54
- export function useSSEStream(sessionId) {
55
- const clientRef = useRef(new SSEClient());
56
- const [loading, setLoading] = useState(false);
57
- const [error, setError] = useState(null);
58
- const accumulatedRef = useRef('');
59
- const lastMessageRef = useRef({ content: '', handlers: {} });
60
-
61
- const send = useCallback(async (content, handlers = {}, options = {}) => {
62
- const hasSessionOverride = Object.prototype.hasOwnProperty.call(options, 'sessionIdOverride');
63
- const resolvedSessionId = hasSessionOverride ? options.sessionIdOverride : sessionId;
64
- if (!content.trim()) return;
65
-
66
- lastMessageRef.current = { content, handlers, options };
67
- setError(null);
68
- accumulatedRef.current = '';
69
- setLoading(true);
70
- let latestSessionId = String(resolvedSessionId || '').trim();
71
-
72
- const {
73
- onAck,
74
- onAgentStatus,
75
- onThinkingTrace,
76
- onExecutionTrace,
77
- onContent,
78
- onTextReplace,
79
- onPhaseStart,
80
- onPhaseUpdate,
81
- onPhaseEnd,
82
- onPatch,
83
- onDone,
84
- onTrace,
85
- onWorkspaceActivation,
86
- onUICards,
87
- onKnowledgeBaseState,
88
- onFieldProgress,
89
- onRequirementDraft,
90
- onNextActions,
91
- onSourcingCandidates,
92
- onRiskSummary,
93
- onShortlistUpdated,
94
- onEvaluationReportReady,
95
- onCategoryIdentified,
96
- onFieldsUpdated,
97
- onInstanceProfile,
98
- onModeRuntime,
99
- onAgentRuntime,
100
- onSkillRuntime,
101
- onModeSwitchReason,
102
- onKnowledgeSearch,
103
- onKnowledgeCitation,
104
- onOrchestrationStatus,
105
- onCanvasState,
106
- onCanvasRevision,
107
- onPlanBlock,
108
- onDoc,
109
- onCapabilitySuggestion,
110
- onPatchEvent,
111
- onError,
112
- onComplete,
113
- } = handlers;
114
-
115
- const eventHandler = (event) => {
116
- switch (event.type) {
117
- case 'ack':
118
- onAck?.(parseAck(event.data));
119
- break;
120
- case 'phase.start':
121
- onPhaseStart?.(parsePhaseEvent(event.data));
122
- break;
123
- case 'phase.update':
124
- onPhaseUpdate?.(parsePhaseEvent(event.data));
125
- break;
126
- case 'phase.end':
127
- onPhaseEnd?.(parsePhaseEvent(event.data));
128
- break;
129
- case 'patch':
130
- onPatch?.(parsePatch(event.data));
131
- break;
132
- case 'agent_status':
133
- onAgentStatus?.(parseAgentStatus(event.data));
134
- break;
135
- case 'thinking_trace':
136
- onThinkingTrace?.(parseThinkingTrace(event.data));
137
- break;
138
- case 'execution_trace':
139
- case 'step_started':
140
- case 'step_updated':
141
- case 'step_completed':
142
- case 'step_failed':
143
- onExecutionTrace?.(parseExecutionTrace(event.data));
144
- break;
145
- case 'content': {
146
- const { chunk } = parseContent(event.data);
147
- accumulatedRef.current += chunk;
148
- onContent?.(chunk);
149
- break;
150
- }
151
- case 'text.delta': {
152
- const parsed = parseTextDelta(event.data);
153
- if (parsed.channel === 'assistant' && parsed.delta) {
154
- accumulatedRef.current += parsed.delta;
155
- onContent?.(parsed.delta);
156
- }
157
- break;
158
- }
159
- case 'text.replace': {
160
- const parsed = parseTextReplace(event.data);
161
- if (parsed.channel === 'assistant') {
162
- accumulatedRef.current = parsed.content;
163
- onTextReplace?.(parsed.content);
164
- }
165
- break;
166
- }
167
- case 'done':
168
- onDone?.(parseDone(event.data));
169
- break;
170
- case 'trace':
171
- onTrace?.(parseTrace(event.data));
172
- break;
173
- case 'workspace_activation':
174
- onWorkspaceActivation?.(parseWorkspaceActivation(event.data));
175
- break;
176
- case 'ui_cards':
177
- onUICards?.(parseUICards(event.data));
178
- break;
179
- case 'knowledge_base_state':
180
- onKnowledgeBaseState?.(parseKnowledgeBaseState(event.data));
181
- break;
182
- case 'field_progress':
183
- onFieldProgress?.(parseFieldProgress(event.data));
184
- break;
185
- case 'requirement_draft':
186
- onRequirementDraft?.(parseRequirementDraft(event.data));
187
- break;
188
- case 'next_actions':
189
- onNextActions?.(parseNextActions(event.data));
190
- break;
191
- case 'sourcing_candidates':
192
- onSourcingCandidates?.(parseSourcingCandidates(event.data));
193
- break;
194
- case 'risk_summary':
195
- onRiskSummary?.(parseRiskSummary(event.data));
196
- break;
197
- case 'shortlist_updated':
198
- onShortlistUpdated?.(parseShortlistUpdated(event.data));
199
- break;
200
- case 'evaluation_report_ready':
201
- onEvaluationReportReady?.(parseEvaluationReportReady(event.data));
202
- break;
203
- case 'category_identified':
204
- onCategoryIdentified?.(parseCategoryIdentified(event.data));
205
- break;
206
- case 'fields_updated':
207
- onFieldsUpdated?.(parseFieldsUpdated(event.data));
208
- break;
209
- case 'instance_profile':
210
- onInstanceProfile?.(parseInstanceProfile(event.data));
211
- break;
212
- case 'mode_runtime':
213
- onModeRuntime?.(parseModeRuntime(event.data));
214
- break;
215
- case 'agent_runtime':
216
- onAgentRuntime?.(parseAgentRuntime(event.data));
217
- break;
218
- case 'skill_runtime':
219
- onSkillRuntime?.(parseSkillRuntime(event.data));
220
- break;
221
- case 'mode_switch_reason':
222
- onModeSwitchReason?.(parseModeSwitchReason(event.data));
223
- break;
224
- case 'knowledge_search':
225
- onKnowledgeSearch?.(parseKnowledgeSearch(event.data));
226
- break;
227
- case 'knowledge_citation':
228
- onKnowledgeCitation?.(parseKnowledgeCitation(event.data));
229
- break;
230
- case 'orchestration_status':
231
- onOrchestrationStatus?.(parseOrchestrationStatus(event.data));
232
- break;
233
- case 'canvas_state':
234
- onCanvasState?.(parseCanvasState(event.data));
235
- break;
236
- case 'canvas_revision':
237
- onCanvasRevision?.(parseCanvasRevision(event.data));
238
- break;
239
- case 'plan_block':
240
- onPlanBlock?.(parsePlanBlock(event.data));
241
- break;
242
- case 'doc':
243
- onDoc?.(parseDoc(event.data));
244
- break;
245
- case 'capability_suggestion':
246
- onCapabilitySuggestion?.(parseCapabilitySuggestion(event.data));
247
- break;
248
- case 'patch_event':
249
- {
250
- const parsedPatchEvent = parsePatchEvent(event.data);
251
- const patchSessionId = String(parsedPatchEvent?.session?.session_id || '').trim();
252
- if (patchSessionId) {
253
- latestSessionId = patchSessionId;
254
- }
255
- onPatchEvent?.(parsedPatchEvent);
256
- }
257
- break;
258
- case 'error':
259
- onError?.(parseSSEError(event.data));
260
- break;
261
- default:
262
- console.warn('[SSE] unknown event type:', event.type);
263
- }
264
- };
265
-
266
- const completeHandler = async () => {
267
- setLoading(false);
268
- await onComplete?.(accumulatedRef.current, {
269
- sessionId: latestSessionId || null,
270
- });
271
- accumulatedRef.current = '';
272
- };
273
-
274
- const transportErrorHandler = (errorMessage) => {
275
- setLoading(false);
276
- const errorType = classifyError(errorMessage);
277
- setError({ message: errorMessage, type: errorType });
278
- onError?.({ message: errorMessage });
279
- accumulatedRef.current = '';
280
- };
281
-
282
- const requestOptions = {
283
- sessionId: String(resolvedSessionId || '').trim() || null,
284
- content,
285
- enabledCapabilities: Array.isArray(options.enabledCapabilities) ? options.enabledCapabilities : [],
286
- modeKey: typeof options.modeKey === 'string' ? options.modeKey : '',
287
- manualModeKey: typeof options.manualModeKey === 'string' ? options.manualModeKey : '',
288
- command: typeof options.command === 'string' ? options.command : '',
289
- clientRequestId: typeof options.clientRequestId === 'string' ? options.clientRequestId : '',
290
- contractVersion: typeof options.contractVersion === 'string' ? options.contractVersion : '',
291
- lastTurnId: typeof options.lastTurnId === 'string' ? options.lastTurnId : '',
292
- payloadExtra: (
293
- options.payloadExtra
294
- && typeof options.payloadExtra === 'object'
295
- && !Array.isArray(options.payloadExtra)
296
- )
297
- ? options.payloadExtra
298
- : {},
299
- endpoint: options.endpoint,
300
- baseUrl: options.baseUrl,
301
- url: options.url,
302
- token: typeof options.token === 'string' ? options.token : '',
303
- auth: (
304
- options.auth
305
- && typeof options.auth === 'object'
306
- && !Array.isArray(options.auth)
307
- ) ? options.auth : undefined,
308
- onTiming: typeof options.onTiming === 'function' ? options.onTiming : undefined,
309
- };
310
-
311
- if (!requestOptions.enabledCapabilities.length) {
312
- delete requestOptions.enabledCapabilities;
313
- }
314
- if (!requestOptions.modeKey) delete requestOptions.modeKey;
315
- if (!requestOptions.manualModeKey) delete requestOptions.manualModeKey;
316
- if (!requestOptions.command) delete requestOptions.command;
317
- if (!requestOptions.clientRequestId) delete requestOptions.clientRequestId;
318
- if (!requestOptions.contractVersion) delete requestOptions.contractVersion;
319
- if (!requestOptions.lastTurnId) delete requestOptions.lastTurnId;
320
- if (!Object.keys(requestOptions.payloadExtra || {}).length) delete requestOptions.payloadExtra;
321
-
322
- if (!requestOptions.endpoint) delete requestOptions.endpoint;
323
- if (!requestOptions.baseUrl) delete requestOptions.baseUrl;
324
- if (!requestOptions.url) delete requestOptions.url;
325
- if (!requestOptions.token) delete requestOptions.token;
326
- if (!requestOptions.auth) delete requestOptions.auth;
327
- if (!requestOptions.onTiming) delete requestOptions.onTiming;
328
-
329
- await clientRef.current.sendMessage(
330
- requestOptions,
331
- eventHandler,
332
- completeHandler,
333
- transportErrorHandler
334
- );
335
- }, [sessionId]);
336
-
337
- const retry = useCallback(async () => {
338
- const { content, handlers, options } = lastMessageRef.current;
339
- if (!content) return;
340
- await send(content, handlers, options);
341
- }, [send]);
342
-
343
- const abort = useCallback(() => {
344
- clientRef.current.abort();
345
- setLoading(false);
346
- accumulatedRef.current = '';
347
- }, []);
348
-
349
- useEffect(() => () => {
350
- clientRef.current.abort();
351
- }, []);
352
-
353
- return { send, loading, error, retry, abort };
354
- }
355
-
356
- export default useSSEStream;
1
+ export { useSSEStream, default } from '../../orchestration/useSSEStream.js';
@@ -0,0 +1,46 @@
1
+ import { useCallback } from 'react';
2
+
3
+ /**
4
+ * Stream send lifecycle controller.
5
+ *
6
+ * @param {object} params - Hook params.
7
+ * @param {object} params.stream - Stream adapter with `send`.
8
+ * @param {Function} params.onStarted - Optional started callback.
9
+ * @param {Function} params.onChunk - Optional chunk callback.
10
+ * @param {Function} params.onCompleted - Optional completed callback.
11
+ * @param {Function} params.onFailed - Optional failed callback.
12
+ * @returns {{executeStreamSend: Function}}
13
+ */
14
+ export default function useStreamSendController({
15
+ stream,
16
+ onStarted,
17
+ onChunk,
18
+ onCompleted,
19
+ onFailed,
20
+ } = {}) {
21
+ const executeStreamSend = useCallback(async (content, handlers = {}, options = {}) => {
22
+ if (!stream?.send) {
23
+ throw new Error('stream.send is required');
24
+ }
25
+
26
+ onStarted?.();
27
+
28
+ try {
29
+ await stream.send(content, {
30
+ ...handlers,
31
+ onContent: (chunk, ...args) => {
32
+ onChunk?.(chunk, ...args);
33
+ handlers?.onContent?.(chunk, ...args);
34
+ },
35
+ }, options);
36
+ onCompleted?.();
37
+ } catch (error) {
38
+ onFailed?.(error);
39
+ throw error;
40
+ }
41
+ }, [onChunk, onCompleted, onFailed, onStarted, stream]);
42
+
43
+ return { executeStreamSend };
44
+ }
45
+
46
+ export { useStreamSendController };
@@ -0,0 +1,47 @@
1
+ export function createEmptyContext() {
2
+ return {
3
+ confirmed_fields: {},
4
+ missing_fields: [],
5
+ notes: [],
6
+ evidence: [],
7
+ sourcing_results: [],
8
+ analysis_results: [],
9
+ };
10
+ }
11
+
12
+ function toArray(value) {
13
+ return Array.isArray(value) ? value : [];
14
+ }
15
+
16
+ export function normalizeContext(input = {}) {
17
+ const base = input && typeof input === 'object' ? input : {};
18
+ return {
19
+ confirmed_fields:
20
+ base.confirmed_fields && typeof base.confirmed_fields === 'object' && !Array.isArray(base.confirmed_fields)
21
+ ? { ...base.confirmed_fields }
22
+ : {},
23
+ missing_fields: toArray(base.missing_fields).map((item) => String(item || '').trim()).filter(Boolean),
24
+ notes: toArray(base.notes).map((item) => String(item || '')).filter(Boolean),
25
+ evidence: toArray(base.evidence),
26
+ sourcing_results: toArray(base.sourcing_results),
27
+ analysis_results: toArray(base.analysis_results),
28
+ };
29
+ }
30
+
31
+ export function mergeContextPatch(context = createEmptyContext(), patch = {}) {
32
+ const normalizedContext = normalizeContext(context);
33
+ const normalizedPatch = normalizeContext(patch);
34
+ return {
35
+ confirmed_fields: {
36
+ ...normalizedContext.confirmed_fields,
37
+ ...normalizedPatch.confirmed_fields,
38
+ },
39
+ missing_fields: normalizedPatch.missing_fields.length > 0
40
+ ? normalizedPatch.missing_fields
41
+ : normalizedContext.missing_fields,
42
+ notes: [...normalizedContext.notes, ...normalizedPatch.notes],
43
+ evidence: [...normalizedContext.evidence, ...normalizedPatch.evidence],
44
+ sourcing_results: [...normalizedContext.sourcing_results, ...normalizedPatch.sourcing_results],
45
+ analysis_results: [...normalizedContext.analysis_results, ...normalizedPatch.analysis_results],
46
+ };
47
+ }
@@ -0,0 +1 @@
1
+ export * from './sse-events.js';
@@ -0,0 +1,79 @@
1
+ export const parseAgentStatus = (raw) => ({
2
+ agent: raw?.agent ?? '',
3
+ status: raw?.status ?? '',
4
+ timestamp: raw?.timestamp ?? null,
5
+ });
6
+
7
+ export const parseThinkingTrace = (raw) => ({
8
+ trace: raw?.trace ?? '',
9
+ });
10
+
11
+ export const parseExecutionTrace = (raw) => ({
12
+ session_id: raw?.session_id ?? '',
13
+ step_id: raw?.step_id ?? '',
14
+ agent: raw?.agent ?? '',
15
+ stage: raw?.stage ?? '',
16
+ label: raw?.label ?? '',
17
+ detail: raw?.detail ?? raw?.trace ?? '',
18
+ reason: raw?.reason ?? '',
19
+ status: raw?.status ?? raw?.step_status ?? '',
20
+ sources: Array.isArray(raw?.sources) ? raw.sources : [],
21
+ timestamp: raw?.timestamp ?? null,
22
+ });
23
+
24
+ export const parseContent = (raw) => ({
25
+ chunk: raw?.content ?? '',
26
+ });
27
+
28
+ export const parseAck = (raw) => ({
29
+ trace_id: String(raw?.trace_id || '').trim(),
30
+ session_id: String(raw?.session_id || '').trim(),
31
+ request_id: String(raw?.request_id || '').trim(),
32
+ mode: String(raw?.mode || '').trim(),
33
+ ts: Number.isFinite(Number(raw?.ts)) ? Number(raw.ts) : null,
34
+ });
35
+
36
+ export const parsePhaseEvent = (raw) => ({
37
+ phase: String(raw?.phase || '').trim(),
38
+ label: String(raw?.label || '').trim(),
39
+ status: String(raw?.status || '').trim(),
40
+ progress: Number.isFinite(Number(raw?.progress)) ? Number(raw.progress) : null,
41
+ meta: raw?.meta && typeof raw.meta === 'object' && !Array.isArray(raw.meta) ? raw.meta : {},
42
+ ts: Number.isFinite(Number(raw?.ts)) ? Number(raw.ts) : null,
43
+ });
44
+
45
+ export const parsePatch = (raw) => ({
46
+ scope: String(raw?.scope || '').trim(),
47
+ payload: raw?.payload && typeof raw.payload === 'object' && !Array.isArray(raw.payload) ? raw.payload : {},
48
+ ts: Number.isFinite(Number(raw?.ts)) ? Number(raw.ts) : null,
49
+ });
50
+
51
+ export const parseTextDelta = (raw) => ({
52
+ channel: String(raw?.channel || 'assistant').trim() || 'assistant',
53
+ delta: String(raw?.delta || ''),
54
+ ts: Number.isFinite(Number(raw?.ts)) ? Number(raw.ts) : null,
55
+ });
56
+
57
+ export const parseTextReplace = (raw) => ({
58
+ channel: String(raw?.channel || 'assistant').trim() || 'assistant',
59
+ content: String(raw?.content || ''),
60
+ ts: Number.isFinite(Number(raw?.ts)) ? Number(raw.ts) : null,
61
+ });
62
+
63
+ export const parseDone = (raw) => ({
64
+ trace_id: String(raw?.trace_id || '').trim(),
65
+ session_id: String(raw?.session_id || '').trim(),
66
+ request_id: String(raw?.request_id || '').trim(),
67
+ status: String(raw?.status || 'done').trim() || 'done',
68
+ ts: Number.isFinite(Number(raw?.ts)) ? Number(raw.ts) : null,
69
+ });
70
+
71
+ export const parseTrace = (raw) => ({
72
+ trace: raw?.trace ?? raw?.detail ?? '',
73
+ summary: raw?.summary ?? raw?.label ?? '',
74
+ agent: raw?.agent ?? '',
75
+ });
76
+
77
+ export const parseSSEError = (raw) => ({
78
+ message: raw?.message ?? '发生错误',
79
+ });
@@ -0,0 +1,3 @@
1
+ export * from './parsers-intake.js';
2
+ export * from './parsers-sourcing.js';
3
+ export * from './parsers-runtime.js';
@@ -0,0 +1,143 @@
1
+ export function resolveModeEventPayload(raw) {
2
+ if (raw?.payload && typeof raw.payload === 'object' && !Array.isArray(raw.payload)) {
3
+ return raw.payload;
4
+ }
5
+
6
+ return raw && typeof raw === 'object' ? raw : {};
7
+ }
8
+
9
+ export function pickFirstArray(payload, keys) {
10
+ for (const key of keys) {
11
+ const value = payload?.[key];
12
+ if (Array.isArray(value)) {
13
+ return value;
14
+ }
15
+ }
16
+ return [];
17
+ }
18
+
19
+ export function isPlainObject(value) {
20
+ return Boolean(value && typeof value === 'object' && !Array.isArray(value));
21
+ }
22
+
23
+ export function ensureActionObject(value) {
24
+ return isPlainObject(value) ? value : {};
25
+ }
26
+
27
+ export function pickFirstString(candidates = []) {
28
+ for (const candidate of candidates) {
29
+ const text = String(candidate ?? '').trim();
30
+ if (text) {
31
+ return text;
32
+ }
33
+ }
34
+ return '';
35
+ }
36
+
37
+ export function normalizeActionPayload(rawPayload) {
38
+ if (isPlainObject(rawPayload)) {
39
+ return rawPayload;
40
+ }
41
+ return {};
42
+ }
43
+
44
+ export function normalizeAction(rawAction, index = 0) {
45
+ const action = ensureActionObject(rawAction);
46
+ const actionKey = pickFirstString([
47
+ action?.action_key,
48
+ action?.actionKey,
49
+ action?.key,
50
+ action?.id,
51
+ ]);
52
+ const label = pickFirstString([
53
+ action?.label,
54
+ action?.title,
55
+ action?.name,
56
+ actionKey,
57
+ 'action',
58
+ ]);
59
+ const reason = pickFirstString([
60
+ action?.reason,
61
+ action?.description,
62
+ ]);
63
+ const ownerModule = pickFirstString([
64
+ action?.owner_module,
65
+ action?.ownerModule,
66
+ ]);
67
+ const policyId = pickFirstString([
68
+ action?.policy_id,
69
+ action?.policyId,
70
+ ]);
71
+ const payload = normalizeActionPayload(action?.payload);
72
+
73
+ return {
74
+ ...action,
75
+ action_key: actionKey,
76
+ label,
77
+ reason,
78
+ payload,
79
+ owner_module: ownerModule,
80
+ policy_id: policyId,
81
+ action_index: Number.isFinite(Number(action?.action_index))
82
+ ? Number(action.action_index)
83
+ : index,
84
+ };
85
+ }
86
+
87
+ export function normalizeActionList(rawActions) {
88
+ if (!Array.isArray(rawActions)) {
89
+ return [];
90
+ }
91
+ const normalized = [];
92
+ const seenActionKeys = new Set();
93
+
94
+ rawActions.forEach((rawAction, index) => {
95
+ const action = normalizeAction(rawAction, index);
96
+ const actionKey = String(action?.action_key || '').trim();
97
+ if (actionKey) {
98
+ if (seenActionKeys.has(actionKey)) {
99
+ return;
100
+ }
101
+ seenActionKeys.add(actionKey);
102
+ }
103
+ normalized.push(action);
104
+ });
105
+
106
+ return normalized;
107
+ }
108
+
109
+ export function parseGuidedSession(rawGuidedSession) {
110
+ const payload = (rawGuidedSession && typeof rawGuidedSession === 'object' && !Array.isArray(rawGuidedSession))
111
+ ? rawGuidedSession
112
+ : {};
113
+ const questionQueue = (payload.question_queue && typeof payload.question_queue === 'object' && !Array.isArray(payload.question_queue))
114
+ ? payload.question_queue
115
+ : {};
116
+ const summary = (payload.summary && typeof payload.summary === 'object' && !Array.isArray(payload.summary))
117
+ ? payload.summary
118
+ : {};
119
+ const decisionPoint = (payload.decision_point && typeof payload.decision_point === 'object' && !Array.isArray(payload.decision_point))
120
+ ? payload.decision_point
121
+ : {};
122
+ return {
123
+ state: payload?.state ?? '',
124
+ active: payload?.active === true,
125
+ current_question: payload?.current_question && typeof payload.current_question === 'object' && !Array.isArray(payload.current_question)
126
+ ? payload.current_question
127
+ : null,
128
+ question_queue: {
129
+ total: Number.isFinite(Number(questionQueue?.total)) ? Number(questionQueue.total) : 0,
130
+ answered: Number.isFinite(Number(questionQueue?.answered)) ? Number(questionQueue.answered) : 0,
131
+ remaining: Number.isFinite(Number(questionQueue?.remaining)) ? Number(questionQueue.remaining) : 0,
132
+ pending_fields: Array.isArray(questionQueue?.pending_fields) ? questionQueue.pending_fields : [],
133
+ },
134
+ summary: {
135
+ text: summary?.text ?? '',
136
+ ready: summary?.ready === true,
137
+ },
138
+ decision_point: {
139
+ active: decisionPoint?.active === true,
140
+ actions: Array.isArray(decisionPoint?.actions) ? decisionPoint.actions : [],
141
+ },
142
+ };
143
+ }