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
@@ -0,0 +1,86 @@
1
+ import { parseSourcingCandidates } from '../../../contracts/sse-events.js';
2
+ import { resolvePayloadExtra, resolveStreamRefs } from '../build-stream-request.js';
3
+ import { resolvePersistExchangeResult } from '../stream-persist-utils.js';
4
+ import { buildSourcingResponseText } from './sourcing-response-templates.js';
5
+ import { requestSourcingSearchApi } from './sourcing-search-api.js';
6
+
7
+ const SOURCING_REFRESH_COMMANDS = new Set(['换一批', '继续', '继续寻源']);
8
+
9
+ function normalizeText(value) {
10
+ return String(value || '').trim();
11
+ }
12
+
13
+ export function resolveSourcingSearchQuery(content, payloadExtra = {}) {
14
+ const normalizedContent = normalizeText(content);
15
+ const payloadSourcingQuery = normalizeText(payloadExtra?.sourcing_query);
16
+ if (payloadSourcingQuery && SOURCING_REFRESH_COMMANDS.has(normalizedContent)) {
17
+ return payloadSourcingQuery;
18
+ }
19
+ return normalizedContent || payloadSourcingQuery;
20
+ }
21
+
22
+ export async function requestSourcingSearch({
23
+ apiBaseUrl,
24
+ content,
25
+ options,
26
+ scope,
27
+ handlers,
28
+ persistExchange,
29
+ resolvedSessionId,
30
+ }) {
31
+ const payloadExtra = resolvePayloadExtra(options);
32
+ const resolvedQuery = resolveSourcingSearchQuery(content, payloadExtra);
33
+ const streamRefs = resolveStreamRefs(options);
34
+ const sourceIds = Array.isArray(streamRefs.knowledge_refs)
35
+ ? streamRefs.knowledge_refs.map((item) => String(item || '').trim()).filter(Boolean)
36
+ : [];
37
+ const appendMode = payloadExtra.append_mode === true;
38
+ const { payload, results, sourceBreakdown, sourcingPayload } = await requestSourcingSearchApi({
39
+ apiBaseUrl,
40
+ projectId: String(scope?.projectId || '').trim() || 'default',
41
+ userId: String(scope?.userId || '').trim() || 'default',
42
+ sessionId: String(resolvedSessionId || '').trim() || 'default',
43
+ query: resolvedQuery,
44
+ topK: Number.isFinite(Number(payloadExtra.top_k)) ? Number(payloadExtra.top_k) : 8,
45
+ appendMode,
46
+ lockExistingOrder: payloadExtra.lock_existing_order !== false,
47
+ baseResultIds: Array.isArray(payloadExtra.base_result_ids) ? payloadExtra.base_result_ids : [],
48
+ sourceScope: String(payloadExtra.source_scope || 'hybrid').trim() || 'hybrid',
49
+ contextRefs: Array.isArray(streamRefs.context_refs) ? streamRefs.context_refs : [],
50
+ knowledgeRefs: sourceIds,
51
+ sourceIds,
52
+ });
53
+ handlers.onSourcingCandidates?.(parseSourcingCandidates(sourcingPayload));
54
+
55
+ const sourceSummary = `来源分布:本地资料 ${Number(sourceBreakdown.local) || 0} / 外部连接 ${Number(sourceBreakdown.mcp) || 0} / 网页来源 ${Number(sourceBreakdown.web) || 0}`;
56
+ const citableResults = results
57
+ .filter((item) => String(item?.canonical_url || item?.url || '').trim())
58
+ .slice(0, 2);
59
+ const topRefs = citableResults
60
+ .map((item, index) => `- [来源${index + 1}] ${String(item?.source_title || item?.title || '未命名来源')}(${String(item?.canonical_url || item?.url || '')})`)
61
+ .join('\n');
62
+
63
+ const finalContent = buildSourcingResponseText({
64
+ count: results.length,
65
+ sourceBreakdownText: sourceSummary,
66
+ topRefsText: topRefs,
67
+ nextActionText: '继续补充你的目标与约束,我会基于当前上下文递进处理。',
68
+ });
69
+
70
+ if (appendMode) {
71
+ handlers.onComplete?.(finalContent, { sessionId: String(resolvedSessionId || '').trim() });
72
+ return;
73
+ }
74
+
75
+ let finalSessionId = String(resolvedSessionId || '').trim();
76
+ if (typeof persistExchange === 'function') {
77
+ const persistedResult = persistExchange(finalSessionId, content, finalContent, {
78
+ functionType: 'intelligent_sourcing',
79
+ projectId: String(scope?.projectId || '').trim() || null,
80
+ userId: String(scope?.userId || '').trim() || null,
81
+ });
82
+ const persistedSession = resolvePersistExchangeResult(persistedResult, finalSessionId);
83
+ finalSessionId = persistedSession.sessionId;
84
+ }
85
+ handlers.onComplete?.(finalContent, { sessionId: finalSessionId });
86
+ }
@@ -0,0 +1,14 @@
1
+ import test from 'node:test';
2
+ import assert from 'node:assert/strict';
3
+ import { resolveSourcingSearchQuery } from './send-sourcing-search.js';
4
+
5
+ test('resolveSourcingSearchQuery uses payload sourcing_query for refresh commands', () => {
6
+ const query = resolveSourcingSearchQuery('换一批', { sourcing_query: '露营地 上海 亲子' });
7
+ assert.equal(query, '露营地 上海 亲子');
8
+ });
9
+
10
+ test('resolveSourcingSearchQuery keeps direct semantic input', () => {
11
+ const query = resolveSourcingSearchQuery('上海露营地推荐', { sourcing_query: '旧查询' });
12
+ assert.equal(query, '上海露营地推荐');
13
+ });
14
+
@@ -0,0 +1,55 @@
1
+ const SOURCING_RESPONSE_TEMPLATES = {
2
+ sufficient_hits: [
3
+ '已完成寻源:共 {count} 条候选。',
4
+ '{source_breakdown}',
5
+ '{top_refs}',
6
+ '{next_action}',
7
+ ],
8
+ limited_hits: [
9
+ '已完成初步寻源:当前仅命中 {count} 条候选。',
10
+ '{source_breakdown}',
11
+ '{top_refs}',
12
+ '建议:补充更具体的地区/行业/预算关键词后,我可以继续扩展检索。',
13
+ '{next_action}',
14
+ ],
15
+ no_hits: [
16
+ '本轮未命中有效候选。',
17
+ '{source_breakdown}',
18
+ '可能原因:关键词过宽或外部通道未返回有效内容。',
19
+ '建议:补充地区/行业词,或检查外部网络与网关配置后重试。',
20
+ '{next_action}',
21
+ ],
22
+ };
23
+
24
+ function renderTemplate(templateLines, variables = {}) {
25
+ const lines = Array.isArray(templateLines) ? templateLines : [];
26
+ return lines
27
+ .map((line) => String(line || '').replace(/\{([^}]+)\}/g, (_, key) => String(variables[key] || '')))
28
+ .map((line) => line.trim())
29
+ .filter(Boolean)
30
+ .join('\n');
31
+ }
32
+
33
+ export function buildSourcingResponseText({
34
+ count = 0,
35
+ sourceBreakdownText = '',
36
+ topRefsText = '',
37
+ nextActionText = '你可以直接回复序号或补充信息,我来继续整理。',
38
+ } = {}) {
39
+ const normalizedCount = Number.isFinite(Number(count)) ? Number(count) : 0;
40
+ const variables = {
41
+ count: normalizedCount,
42
+ source_breakdown: sourceBreakdownText,
43
+ top_refs: topRefsText || '暂无可展示引用来源。',
44
+ next_action: nextActionText,
45
+ };
46
+ if (normalizedCount <= 0) {
47
+ return renderTemplate(SOURCING_RESPONSE_TEMPLATES.no_hits, variables);
48
+ }
49
+ if (normalizedCount <= 2) {
50
+ return renderTemplate(SOURCING_RESPONSE_TEMPLATES.limited_hits, variables);
51
+ }
52
+ return renderTemplate(SOURCING_RESPONSE_TEMPLATES.sufficient_hits, variables);
53
+ }
54
+
55
+ export { SOURCING_RESPONSE_TEMPLATES };
@@ -0,0 +1,155 @@
1
+ import { resolveEntities } from '../entity-extraction.js';
2
+
3
+ function joinUrl(baseUrl = '', path = '') {
4
+ if (!baseUrl) {
5
+ return path || '';
6
+ }
7
+ if (!path) {
8
+ return baseUrl;
9
+ }
10
+ return `${baseUrl.replace(/\/+$/, '')}/${path.replace(/^\/+/, '')}`;
11
+ }
12
+
13
+ function normalizeSourceType(value) {
14
+ const rawType = String(value || '').trim().toLowerCase();
15
+ if (rawType.includes('web')) {
16
+ return 'web';
17
+ }
18
+ if (rawType.includes('mcp')) {
19
+ return 'mcp';
20
+ }
21
+ return 'local';
22
+ }
23
+
24
+ function normalizeSourceBreakdown(payload = {}) {
25
+ const sourceBreakdown = (
26
+ payload?.source_breakdown
27
+ && typeof payload.source_breakdown === 'object'
28
+ && !Array.isArray(payload.source_breakdown)
29
+ ) ? payload.source_breakdown : {};
30
+ return {
31
+ local: Number(sourceBreakdown.local) || 0,
32
+ mcp: Number(sourceBreakdown.mcp) || 0,
33
+ web: Number(sourceBreakdown.web) || 0,
34
+ };
35
+ }
36
+
37
+ function mapResultToCandidate(item = {}, runId = '', resolvedQuery = '') {
38
+ return {
39
+ id: String(item?.result_id || item?.record_id || item?.title || ''),
40
+ result_id: String(item?.result_id || item?.record_id || item?.title || ''),
41
+ title: String(item?.source_title || item?.title || ''),
42
+ supplier_name: String(item?.source_title || item?.title || ''),
43
+ evidence_kind: 'sourcing',
44
+ match_score: Number(item?.score || 0),
45
+ match_reasons: [String(item?.snippet || '')],
46
+ evidence_refs: [
47
+ {
48
+ url: String(item?.canonical_url || item?.url || ''),
49
+ snippet: String(item?.snippet || ''),
50
+ title: String(item?.source_title || item?.title || ''),
51
+ },
52
+ ],
53
+ source_type: normalizeSourceType(item?.source_type || item?.source),
54
+ source_title: String(item?.source_title || item?.title || ''),
55
+ source_domain: String(item?.source_domain || ''),
56
+ source_meta: (
57
+ item?.source_meta
58
+ && typeof item.source_meta === 'object'
59
+ && !Array.isArray(item.source_meta)
60
+ ) ? item.source_meta : {},
61
+ canonical_url: String(item?.canonical_url || item?.url || ''),
62
+ confidence: item?.confidence ?? '',
63
+ confidence_detail: (
64
+ item?.confidence_detail
65
+ && typeof item.confidence_detail === 'object'
66
+ && !Array.isArray(item.confidence_detail)
67
+ ) ? item.confidence_detail : null,
68
+ summary: String(item?.snippet || ''),
69
+ run_id: runId,
70
+ query: resolvedQuery,
71
+ };
72
+ }
73
+
74
+ export async function requestSourcingSearchApi({
75
+ apiBaseUrl,
76
+ projectId = 'default',
77
+ userId = 'default',
78
+ sessionId = 'default',
79
+ query,
80
+ topK = 8,
81
+ sourceScope = 'hybrid',
82
+ contextRefs = [],
83
+ knowledgeRefs = [],
84
+ sourceIds = [],
85
+ entities = [],
86
+ appendMode = false,
87
+ lockExistingOrder = true,
88
+ baseResultIds = [],
89
+ }) {
90
+ const resolvedEntities = resolveEntities({ text: query, entities });
91
+ const response = await fetch(joinUrl(apiBaseUrl, '/kernel/sourcing/search'), {
92
+ method: 'POST',
93
+ headers: { 'Content-Type': 'application/json' },
94
+ body: JSON.stringify({
95
+ project_id: String(projectId || '').trim() || 'default',
96
+ user_id: String(userId || '').trim() || 'default',
97
+ session_id: String(sessionId || '').trim() || 'default',
98
+ query: String(query || '').trim(),
99
+ top_k: Number.isFinite(Number(topK)) ? Number(topK) : 8,
100
+ append_mode: appendMode === true,
101
+ lock_existing_order: lockExistingOrder !== false,
102
+ base_result_ids: Array.isArray(baseResultIds)
103
+ ? baseResultIds.map((item) => String(item || '').trim()).filter(Boolean)
104
+ : [],
105
+ source_scope: String(sourceScope || 'hybrid').trim() || 'hybrid',
106
+ sourcing_enabled: true,
107
+ context_refs: Array.isArray(contextRefs) ? contextRefs : [],
108
+ knowledge_refs: Array.isArray(knowledgeRefs) ? knowledgeRefs : [],
109
+ source_ids: Array.isArray(sourceIds) ? sourceIds : [],
110
+ entities: resolvedEntities,
111
+ }),
112
+ });
113
+
114
+ if (!response.ok) {
115
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`);
116
+ }
117
+
118
+ const payload = await response.json();
119
+ const runId = String(payload?.retrieval_run_id || '').trim();
120
+ const resolvedQuery = String(payload?.query || query).trim();
121
+ const results = Array.isArray(payload?.results) ? payload.results : [];
122
+ const newResults = Array.isArray(payload?.new_results) ? payload.new_results : [];
123
+ const allResults = Array.isArray(payload?.all_results) ? payload.all_results : [];
124
+ const sourceBreakdown = normalizeSourceBreakdown(payload);
125
+ const sourcingPayload = {
126
+ run_id: runId,
127
+ query: resolvedQuery,
128
+ candidate_count: results.length,
129
+ requested_top_k: Number(payload?.requested_top_k) || Number(topK) || 8,
130
+ returned_count: Number(payload?.returned_count) || results.length,
131
+ has_more: payload?.has_more === true,
132
+ next_top_k: Number(payload?.next_top_k) || Number(topK) || 8,
133
+ append_mode: appendMode === true,
134
+ lock_existing_order: lockExistingOrder !== false,
135
+ candidates: results.map((item) => mapResultToCandidate(item, runId, resolvedQuery)),
136
+ new_results: newResults.map((item) => mapResultToCandidate(item, runId, resolvedQuery)),
137
+ all_results: allResults.map((item) => mapResultToCandidate(item, runId, resolvedQuery)),
138
+ shortlist: Array.isArray(payload?.shortlist) ? payload.shortlist : [],
139
+ source_meta: {
140
+ source_breakdown: sourceBreakdown,
141
+ },
142
+ summary: '',
143
+ };
144
+
145
+ return {
146
+ payload,
147
+ runId,
148
+ resolvedQuery,
149
+ results,
150
+ sourceBreakdown,
151
+ sourcingPayload,
152
+ };
153
+ }
154
+
155
+ export { normalizeSourceType, joinUrl };
@@ -0,0 +1,69 @@
1
+ /**
2
+ * Core runtime mode + debug gating.
3
+ *
4
+ * Env contract:
5
+ * - VITE_FLARE_CHAT_RUNTIME_MODE: optional override, "development" | "production"
6
+ * - VITE_FLARE_CHAT_DEBUG: optional debug switch, "1" | "true" enables debug logs
7
+ *
8
+ * Safety rule:
9
+ * - Debug logs are allowed only when runtime mode is non-production.
10
+ */
11
+
12
+ function toBooleanFlag(value) {
13
+ const normalized = String(value ?? '').trim().toLowerCase();
14
+ return normalized === '1' || normalized === 'true' || normalized === 'yes' || normalized === 'on';
15
+ }
16
+
17
+ function normalizeMode(value) {
18
+ const normalized = String(value ?? '').trim().toLowerCase();
19
+ if (normalized === 'production' || normalized === 'prod') {
20
+ return 'production';
21
+ }
22
+ if (normalized === 'development' || normalized === 'dev') {
23
+ return 'development';
24
+ }
25
+ return '';
26
+ }
27
+
28
+ export function resolveRuntimeMode(envInput) {
29
+ const env = (
30
+ envInput
31
+ && typeof envInput === 'object'
32
+ && !Array.isArray(envInput)
33
+ ) ? envInput : (import.meta?.env || {});
34
+
35
+ const overrideMode = normalizeMode(env.VITE_FLARE_CHAT_RUNTIME_MODE);
36
+ const modeFromVite = normalizeMode(env.MODE);
37
+ const fallbackMode = env.PROD === true ? 'production' : 'development';
38
+ const mode = overrideMode || modeFromVite || fallbackMode;
39
+ const isProduction = mode === 'production';
40
+ const isDevelopment = !isProduction;
41
+ const debugRequested = toBooleanFlag(env.VITE_FLARE_CHAT_DEBUG);
42
+ const debugEnabled = isDevelopment && debugRequested;
43
+
44
+ return {
45
+ mode,
46
+ isProduction,
47
+ isDevelopment,
48
+ debugEnabled,
49
+ };
50
+ }
51
+
52
+ const runtimeMode = resolveRuntimeMode();
53
+
54
+ export function getCoreRuntimeMode() {
55
+ return runtimeMode;
56
+ }
57
+
58
+ export function isCoreDebugEnabled() {
59
+ return runtimeMode.debugEnabled;
60
+ }
61
+
62
+ export function debugCoreLog(...args) {
63
+ if (!runtimeMode.debugEnabled) {
64
+ return;
65
+ }
66
+ console.debug('[flare-chat-core:debug]', ...args);
67
+ }
68
+
69
+ export default getCoreRuntimeMode;
@@ -0,0 +1,24 @@
1
+ export const SESSION_LOADED = 'SESSION_LOADED';
2
+ export const SESSION_RESET = 'SESSION_RESET';
3
+ export const SESSION_ERROR = 'SESSION_ERROR';
4
+ export const TITLE_UPDATED = 'TITLE_UPDATED';
5
+ export const MESSAGES_REFRESHED = 'MESSAGES_REFRESHED';
6
+ export const MESSAGE_APPENDED = 'MESSAGE_APPENDED';
7
+ export const STREAMING_RESET = 'STREAMING_RESET';
8
+ export const STREAMING_CHUNK = 'STREAMING_CHUNK';
9
+ export const STREAMING_REPLACE = 'STREAMING_REPLACE';
10
+ export const AGENT_STATUS_SET = 'AGENT_STATUS_SET';
11
+ export const THINKING_TRACE_SET = 'THINKING_TRACE_SET';
12
+ export const EXECUTION_TRACE_SET = 'EXECUTION_TRACE_SET';
13
+ export const STREAM_KNOWLEDGE_SEARCH_SET = 'STREAM_KNOWLEDGE_SEARCH_SET';
14
+ export const STREAM_SOURCING_CANDIDATES_SET = 'STREAM_SOURCING_CANDIDATES_SET';
15
+ export const STREAM_KNOWLEDGE_CITATION_SET = 'STREAM_KNOWLEDGE_CITATION_SET';
16
+ export const STREAMING_DONE = 'STREAMING_DONE';
17
+ export const DOC_GENERATED = 'DOC_GENERATED';
18
+ export const LEGEND_HINT_SHOWN = 'LEGEND_HINT_SHOWN';
19
+ export const EXECUTION_CARD_UPSERTED = 'EXECUTION_CARD_UPSERTED';
20
+ export const UI_CARDS_SET = 'UI_CARDS_SET';
21
+ export const LAST_USER_MESSAGE_SET = 'LAST_USER_MESSAGE_SET';
22
+ export const UI_CARD_UPDATED = 'UI_CARD_UPDATED';
23
+ export const INSTANCE_PROFILE_SET = 'INSTANCE_PROFILE_SET';
24
+ export const SESSION_PROMOTED = 'SESSION_PROMOTED';