ltcai 8.0.0 → 8.2.0

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 (183) hide show
  1. package/README.md +33 -72
  2. package/docs/CHANGELOG.md +52 -874
  3. package/docs/DEVELOPMENT.md +26 -3
  4. package/docs/LEGACY_COMPATIBILITY.md +3 -1
  5. package/docs/TRUST_MODEL.md +26 -26
  6. package/docs/WHY_LATTICE.md +28 -25
  7. package/docs/kg-schema.md +6 -4
  8. package/lattice_brain/__init__.py +1 -1
  9. package/lattice_brain/embeddings.py +9 -1
  10. package/lattice_brain/graph/schema.py +6 -0
  11. package/lattice_brain/runtime/multi_agent.py +1 -1
  12. package/latticeai/__init__.py +1 -1
  13. package/latticeai/api/computer_use.py +2 -0
  14. package/latticeai/api/memory.py +13 -0
  15. package/latticeai/app_factory.py +43 -162
  16. package/latticeai/brain/__init__.py +1 -1
  17. package/latticeai/core/config.py +10 -0
  18. package/latticeai/core/local_embeddings.py +8 -0
  19. package/latticeai/core/marketplace.py +1 -1
  20. package/latticeai/core/mcp_registry.py +140 -1
  21. package/latticeai/core/tool_registry.py +10 -0
  22. package/latticeai/core/workspace_graph_trace.py +132 -0
  23. package/latticeai/core/workspace_memory.py +75 -0
  24. package/latticeai/core/workspace_os.py +135 -1283
  25. package/latticeai/core/workspace_os_utils.py +132 -0
  26. package/latticeai/core/workspace_permissions.py +99 -0
  27. package/latticeai/core/workspace_plugins.py +97 -0
  28. package/latticeai/core/workspace_runs.py +612 -0
  29. package/latticeai/core/workspace_skills.py +114 -0
  30. package/latticeai/core/workspace_snapshots.py +195 -0
  31. package/latticeai/core/workspace_timeline.py +107 -0
  32. package/latticeai/runtime/audit_runtime.py +64 -0
  33. package/latticeai/runtime/config_runtime.py +4 -0
  34. package/latticeai/runtime/sso_runtime.py +52 -0
  35. package/latticeai/services/architecture_readiness.py +6 -5
  36. package/latticeai/services/memory_service.py +215 -7
  37. package/latticeai/services/model_capability_registry.py +68 -0
  38. package/latticeai/services/model_engines.py +603 -0
  39. package/latticeai/services/model_loading.py +482 -0
  40. package/latticeai/services/model_recommendation.py +5 -0
  41. package/latticeai/services/model_runtime.py +146 -837
  42. package/latticeai/services/product_readiness.py +19 -18
  43. package/package.json +2 -2
  44. package/scripts/build_frontend_assets.mjs +11 -2
  45. package/src-tauri/Cargo.lock +1 -1
  46. package/src-tauri/Cargo.toml +1 -1
  47. package/src-tauri/tauri.conf.json +1 -1
  48. package/static/app/asset-manifest.json +11 -102
  49. package/static/app/assets/Act-D9jIknFd.js +1 -0
  50. package/static/app/assets/Brain-CFOtWbPN.js +321 -0
  51. package/static/app/assets/{Capture-B9Tlhzqr.js → Capture-Q4WYzwr5.js} +1 -2
  52. package/static/app/assets/Library-C5Q2yWee.js +1 -0
  53. package/static/app/assets/System-BLbjdr1_.js +1 -0
  54. package/static/app/assets/core-CwxXejkd.js +1 -2
  55. package/static/app/assets/index-BqammyNu.js +16 -0
  56. package/static/app/assets/index-ty1iGgZu.css +2 -0
  57. package/static/app/assets/primitives-Br8uSfZ4.js +1 -0
  58. package/static/app/assets/{textarea-BZk6ybp5.js → textarea-BnhNs1_X.js} +1 -2
  59. package/static/app/index.html +3 -10
  60. package/static/app/theme-boot.js +8 -0
  61. package/static/sw.js +1 -1
  62. package/tools/__init__.py +2 -1
  63. package/tools/computer.py +21 -0
  64. package/docs/CARRYOVER_AUDIT_v3.6.0.md +0 -61
  65. package/docs/HANDOVER_v3.6.0.md +0 -46
  66. package/docs/RUNTIME_HOOK_COVERAGE_v3.5.0.md +0 -56
  67. package/docs/RUNTIME_HOOK_COVERAGE_v3.6.0.md +0 -49
  68. package/docs/V2_ARCHITECTURE.md +0 -561
  69. package/docs/V3_2_AUDIT.md +0 -82
  70. package/docs/V3_BACKEND_ARCHITECTURE.md +0 -138
  71. package/docs/V3_FRONTEND.md +0 -140
  72. package/docs/V4_1_FRONTEND_ARCHITECTURE_REVIEW.md +0 -65
  73. package/docs/V4_1_FRONTEND_MIGRATION_REPORT.md +0 -70
  74. package/docs/V4_1_VALIDATION_REPORT.md +0 -47
  75. package/docs/V4_2_BRAIN_CORE_ARCHITECTURE.md +0 -97
  76. package/docs/V4_2_STORAGE_MIGRATION_REPORT.md +0 -91
  77. package/docs/V4_2_VALIDATION_REPORT.md +0 -89
  78. package/docs/V4_3_2_DEADCODE_AUDIT_REPORT.md +0 -174
  79. package/docs/V4_3_2_DOCUMENTATION_CLEANUP_REPORT.md +0 -81
  80. package/docs/V4_3_2_GITHUB_VERCEL_CHECK_REPORT.md +0 -75
  81. package/docs/V4_3_2_GRAPH_UX_REPORT.md +0 -48
  82. package/docs/V4_3_2_INDEPENDENT_AUDIT_PACKAGE.md +0 -209
  83. package/docs/V4_3_2_PRODUCT_POLISH_REPORT.md +0 -57
  84. package/docs/V4_3_2_SELF_AUDIT_REPORT.md +0 -63
  85. package/docs/V4_3_2_VALIDATION_REPORT.md +0 -97
  86. package/docs/V4_3_3_VALIDATION_REPORT.md +0 -46
  87. package/docs/V4_3_PORTABILITY_ARCHITECTURE.md +0 -69
  88. package/docs/V4_3_PRIVACY_AUDIT.md +0 -60
  89. package/docs/V4_3_PRODUCT_HARDENING_REPORT.md +0 -53
  90. package/docs/V4_3_VALIDATION_REPORT.md +0 -58
  91. package/docs/V4_4_0_EXTRACTION_REPORT.md +0 -239
  92. package/docs/V4_5_0_GEMMA_RUNTIME_COMPATIBILITY_REPORT.md +0 -49
  93. package/docs/V4_5_0_GRAPH_UX_REPORT.md +0 -34
  94. package/docs/V4_5_0_MODEL_RUNTIME_UX_REPORT.md +0 -40
  95. package/docs/V4_5_0_ONBOARDING_REPORT.md +0 -31
  96. package/docs/V4_5_0_PRODUCT_EXPERIENCE_RECOVERY_REPORT.md +0 -49
  97. package/docs/V4_5_0_VALIDATION_REPORT.md +0 -60
  98. package/docs/V4_5_1_GRAPH_EXPERIENCE_REPORT.md +0 -33
  99. package/docs/V4_5_1_MODEL_EXPERIENCE_REPORT.md +0 -37
  100. package/docs/V4_5_1_NAVIGATION_REPORT.md +0 -37
  101. package/docs/V4_5_1_ONBOARDING_REPORT.md +0 -29
  102. package/docs/V4_5_1_PRODUCT_REIMAGINING_REPORT.md +0 -61
  103. package/docs/V4_5_1_RC_ARTIFACTS.md +0 -44
  104. package/docs/V4_5_1_UX_REPORT.md +0 -45
  105. package/docs/V4_5_1_VALIDATION_REPORT.md +0 -55
  106. package/docs/V4_5_1_VISUAL_DESIGN_REPORT.md +0 -30
  107. package/docs/V4_6_0_LIVING_BRAIN_EXPERIENCE_REPORT.md +0 -72
  108. package/docs/V4_6_1_RELEASE_REFRESH_REPORT.md +0 -42
  109. package/docs/V4_7_0_ADMIN_SEPARATION_REPORT.md +0 -42
  110. package/docs/V4_7_1_ADMIN_OPERATIONS_REPORT.md +0 -49
  111. package/docs/V4_7_2_INTUITIVE_BRAIN_UX_REPORT.md +0 -62
  112. package/docs/V4_BRAIN_ARCHITECTURE.md +0 -322
  113. package/docs/V4_DIGITAL_BRAIN_RECOVERY.md +0 -555
  114. package/docs/V4_IMPLEMENTATION_PLAN.md +0 -470
  115. package/frontend/index.html +0 -24
  116. package/frontend/openapi.json +0 -15425
  117. package/frontend/src/App.tsx +0 -243
  118. package/frontend/src/api/client.ts +0 -580
  119. package/frontend/src/api/openapi.ts +0 -17892
  120. package/frontend/src/components/AdminAccessGate.tsx +0 -70
  121. package/frontend/src/components/FeedbackState.tsx +0 -45
  122. package/frontend/src/components/LanguageSwitcher.tsx +0 -23
  123. package/frontend/src/components/LivingBrain.tsx +0 -220
  124. package/frontend/src/components/ProductFlow.tsx +0 -171
  125. package/frontend/src/components/WorkspaceProfileSwitcher.tsx +0 -176
  126. package/frontend/src/components/onboarding/AnalysisScreen.tsx +0 -135
  127. package/frontend/src/components/onboarding/DownloadConsentPanel.tsx +0 -29
  128. package/frontend/src/components/onboarding/InstallScreen.tsx +0 -263
  129. package/frontend/src/components/onboarding/LanguageChooser.tsx +0 -23
  130. package/frontend/src/components/onboarding/LoginScreen.tsx +0 -131
  131. package/frontend/src/components/onboarding/ProductFlowScreens.tsx +0 -13
  132. package/frontend/src/components/onboarding/RecommendationScreen.tsx +0 -116
  133. package/frontend/src/components/onboarding/recommendationModel.ts +0 -189
  134. package/frontend/src/components/primitives.tsx +0 -392
  135. package/frontend/src/components/ui/badge.tsx +0 -27
  136. package/frontend/src/components/ui/button.tsx +0 -37
  137. package/frontend/src/components/ui/card.tsx +0 -22
  138. package/frontend/src/components/ui/input.tsx +0 -16
  139. package/frontend/src/components/ui/textarea.tsx +0 -16
  140. package/frontend/src/features/admin/AdminConsole.tsx +0 -334
  141. package/frontend/src/features/brain/BrainCarePanel.tsx +0 -254
  142. package/frontend/src/features/brain/BrainComposer.tsx +0 -85
  143. package/frontend/src/features/brain/BrainConversation.tsx +0 -688
  144. package/frontend/src/features/brain/BrainGraphLayer.tsx +0 -365
  145. package/frontend/src/features/brain/BrainHome.tsx +0 -624
  146. package/frontend/src/features/brain/BrainMemoryLayer.tsx +0 -45
  147. package/frontend/src/features/brain/BrainOverviewPanel.tsx +0 -149
  148. package/frontend/src/features/brain/BrainRelationshipLayer.tsx +0 -43
  149. package/frontend/src/features/brain/DepthEmergence.tsx +0 -53
  150. package/frontend/src/features/brain/brainData.ts +0 -246
  151. package/frontend/src/features/brain/graphLayout.ts +0 -37
  152. package/frontend/src/features/brain/types.ts +0 -150
  153. package/frontend/src/features/review/ReviewCard.tsx +0 -100
  154. package/frontend/src/features/review/ReviewInbox.tsx +0 -127
  155. package/frontend/src/features/review/reviewHelpers.ts +0 -69
  156. package/frontend/src/i18n.ts +0 -1303
  157. package/frontend/src/lib/utils.ts +0 -33
  158. package/frontend/src/main.tsx +0 -23
  159. package/frontend/src/pages/Act.tsx +0 -458
  160. package/frontend/src/pages/Ask.tsx +0 -14
  161. package/frontend/src/pages/Brain.tsx +0 -914
  162. package/frontend/src/pages/Capture.tsx +0 -258
  163. package/frontend/src/pages/Library.tsx +0 -486
  164. package/frontend/src/pages/System.tsx +0 -621
  165. package/frontend/src/routes.ts +0 -92
  166. package/frontend/src/store/appStore.ts +0 -94
  167. package/frontend/src/styles.css +0 -6579
  168. package/static/app/assets/Act-DOvf59ru.js +0 -2
  169. package/static/app/assets/Act-DOvf59ru.js.map +0 -1
  170. package/static/app/assets/Brain-C7_0mEiI.js +0 -322
  171. package/static/app/assets/Brain-C7_0mEiI.js.map +0 -1
  172. package/static/app/assets/Capture-B9Tlhzqr.js.map +0 -1
  173. package/static/app/assets/Library-BJPEEm5O.js +0 -2
  174. package/static/app/assets/Library-BJPEEm5O.js.map +0 -1
  175. package/static/app/assets/System-D6t9jo9V.js +0 -2
  176. package/static/app/assets/System-D6t9jo9V.js.map +0 -1
  177. package/static/app/assets/core-CwxXejkd.js.map +0 -1
  178. package/static/app/assets/index-C7g26IF6.css +0 -2
  179. package/static/app/assets/index-DbcEYJQ2.js +0 -17
  180. package/static/app/assets/index-DbcEYJQ2.js.map +0 -1
  181. package/static/app/assets/primitives-CD38lt4n.js +0 -2
  182. package/static/app/assets/primitives-CD38lt4n.js.map +0 -1
  183. package/static/app/assets/textarea-BZk6ybp5.js.map +0 -1
@@ -1,624 +0,0 @@
1
- import * as React from "react";
2
- import { useQuery, useQueryClient } from "@tanstack/react-query";
3
- import { latticeApi } from "@/api/client";
4
- import { type BrainState, LivingBrain, triggerBrainRecall } from "@/components/LivingBrain";
5
- import { useAppStore } from "@/store/appStore";
6
- import { t } from "@/i18n";
7
- import { BrainConversation } from "./BrainConversation";
8
- import { buildBrainProof, buildBrainReadiness, buildMemoryFragments, currentModelName, parseKnowledgeGraph } from "./brainData";
9
- import { DepthEmergence } from "./DepthEmergence";
10
- import {
11
- DEPTHS,
12
- INGESTION_STAGE_ORDER,
13
- type BrainDepth,
14
- type BrainProof,
15
- type EmergenceEvent,
16
- type IngestionPipelineStage,
17
- type IngestionSourceType,
18
- type IngestionState,
19
- type MemoryFragment,
20
- type Message,
21
- type MessageProof,
22
- } from "./types";
23
-
24
- export function BrainHome({
25
- brainState,
26
- intensity,
27
- onBrainChange,
28
- }: {
29
- brainState: BrainState;
30
- intensity: number;
31
- onBrainChange: (state: BrainState, intensity?: number) => void;
32
- }) {
33
- const qc = useQueryClient();
34
- const language = useAppStore((state) => state.language);
35
- const [messages, setMessages] = React.useState<Message[]>([]);
36
- const [draft, setDraft] = React.useState("");
37
- const [imageData, setImageData] = React.useState<string | null>(null);
38
- const [streaming, setStreaming] = React.useState(false);
39
- const [conversationId, setConversationId] = React.useState<string | null>(null);
40
- const [explorationDepth, setExplorationDepth] = React.useState<BrainDepth>(1);
41
- const [graphSearch, setGraphSearch] = React.useState("");
42
- const [selectedGraphId, setSelectedGraphId] = React.useState<string | null>(null);
43
- const [memoryFeedback, setMemoryFeedback] = React.useState<string | null>(null);
44
- const [uploadingDocument, setUploadingDocument] = React.useState(false);
45
- const [lastRecallQuery, setLastRecallQuery] = React.useState("");
46
- const [ingestionStates, setIngestionStates] = React.useState<Record<IngestionSourceType, IngestionState | null>>({
47
- file: null,
48
- folder: null,
49
- note: null,
50
- web: null,
51
- });
52
- const [emergenceEvents, setEmergenceEvents] = React.useState<EmergenceEvent[]>([]);
53
- const streamRef = React.useRef<HTMLDivElement>(null);
54
- const recallTimerRef = React.useRef<number | null>(null);
55
- const stageTimersRef = React.useRef<Record<IngestionSourceType, number[]>>({
56
- file: [],
57
- folder: [],
58
- note: [],
59
- web: [],
60
- });
61
- const pendingBaselineRef = React.useRef<
62
- Partial<Record<IngestionSourceType, { memories: number; entities: number; label: string }>>
63
- >({});
64
- // Source types whose request has resolved and are awaiting count settle to record emergence.
65
- const awaitingEmergenceRef = React.useRef<Set<IngestionSourceType>>(new Set());
66
- const settleTimerRef = React.useRef<number | null>(null);
67
-
68
- const memoriesQ = useQuery({ queryKey: ["memoryManager"], queryFn: latticeApi.memoryManager });
69
- const historyQ = useQuery({ queryKey: ["chatHistory"], queryFn: latticeApi.chatHistory });
70
- const graphQ = useQuery({ queryKey: ["graph"], queryFn: latticeApi.graph });
71
- const modelsQ = useQuery({ queryKey: ["models"], queryFn: latticeApi.models });
72
- const brainProofQ = useQuery({
73
- queryKey: ["memoryBrainProof", lastRecallQuery],
74
- queryFn: () => latticeApi.memoryBrainProof(lastRecallQuery, 3),
75
- });
76
-
77
- const memoryFragments = React.useMemo(
78
- () => buildMemoryFragments(memoriesQ.data?.data, historyQ.data?.data),
79
- [memoriesQ.data, historyQ.data],
80
- );
81
- const graphModel = React.useMemo(() => parseKnowledgeGraph(graphQ.data?.data), [graphQ.data]);
82
- const knowledgeConcepts = React.useMemo(
83
- () => graphModel.nodes.slice(0, 10),
84
- [graphModel.nodes],
85
- );
86
- const brainReadiness = React.useMemo(
87
- () => buildBrainReadiness(memoriesQ.data?.data, memoryFragments.length, knowledgeConcepts.length),
88
- [knowledgeConcepts.length, memoriesQ.data, memoryFragments.length],
89
- );
90
- const relationshipThreads = React.useMemo(
91
- () => graphModel.edges.slice(0, 10),
92
- [graphModel.edges],
93
- );
94
- const modelName = React.useMemo(() => currentModelName(modelsQ.data?.data), [modelsQ.data]);
95
- const brainProof = React.useMemo(
96
- () => buildBrainProof(brainProofQ.data?.data, modelName),
97
- [brainProofQ.data, modelName],
98
- );
99
- const currentDepth = DEPTHS[explorationDepth - 1];
100
- const starterPrompts = React.useMemo(
101
- () => [
102
- t(language, "brain.prompt.remember"),
103
- t(language, "brain.prompt.know"),
104
- t(language, "brain.prompt.plan"),
105
- ],
106
- [language],
107
- );
108
-
109
- React.useEffect(() => {
110
- if (streaming) onBrainChange("thinking", 0.94);
111
- else if (draft.trim().length > 4) onBrainChange("listening", 0.76);
112
- else onBrainChange(currentDepth.state, explorationDepth === 1 ? 0.58 : 0.66 + explorationDepth * 0.06);
113
- }, [streaming, draft, currentDepth.state, explorationDepth, onBrainChange]);
114
-
115
- React.useEffect(() => {
116
- const stream = streamRef.current;
117
- if (stream) stream.scrollTop = stream.scrollHeight;
118
- }, [messages]);
119
-
120
- React.useEffect(() => {
121
- return () => {
122
- if (recallTimerRef.current !== null) window.clearTimeout(recallTimerRef.current);
123
- for (const timers of Object.values(stageTimersRef.current)) {
124
- for (const timer of timers) window.clearTimeout(timer);
125
- }
126
- };
127
- }, []);
128
-
129
- const clearStageTimers = React.useCallback((sourceType: IngestionSourceType) => {
130
- for (const timer of stageTimersRef.current[sourceType]) window.clearTimeout(timer);
131
- stageTimersRef.current[sourceType] = [];
132
- }, []);
133
-
134
- const setStage = React.useCallback((sourceType: IngestionSourceType, stage: IngestionPipelineStage) => {
135
- setIngestionStates((prev) => {
136
- const current = prev[sourceType];
137
- if (!current) return prev;
138
- return {
139
- ...prev,
140
- [sourceType]: {
141
- ...current,
142
- stage,
143
- completedAt: stage === "complete" || stage === "error" ? Date.now() : current.completedAt,
144
- },
145
- };
146
- });
147
- }, []);
148
-
149
- const beginIngestion = React.useCallback(
150
- (sourceType: IngestionSourceType, label: string) => {
151
- clearStageTimers(sourceType);
152
- pendingBaselineRef.current[sourceType] = {
153
- memories: memoryFragments.length,
154
- entities: knowledgeConcepts.length,
155
- label,
156
- };
157
- setIngestionStates((prev) => ({
158
- ...prev,
159
- [sourceType]: {
160
- sourceType,
161
- label,
162
- stage: "preparing",
163
- startedAt: Date.now(),
164
- completedAt: null,
165
- newMemories: 0,
166
- newEntities: 0,
167
- },
168
- }));
169
- // Progressive disclosure of the in-flight pipeline while the request runs.
170
- const interim: IngestionPipelineStage[] = ["parsing", "embedding", "indexing"];
171
- interim.forEach((stage, index) => {
172
- const timer = window.setTimeout(() => setStage(sourceType, stage), 420 * (index + 1));
173
- stageTimersRef.current[sourceType].push(timer);
174
- });
175
- },
176
- [clearStageTimers, knowledgeConcepts.length, memoryFragments.length, setStage],
177
- );
178
-
179
- const resolveEmergence = React.useCallback(
180
- (sourceType: IngestionSourceType, memoryCount: number, entityCount: number) => {
181
- clearStageTimers(sourceType);
182
- const baseline = pendingBaselineRef.current[sourceType];
183
- const label = baseline?.label ?? "";
184
- // Snapshot deltas after invalidation lands; cap at >=0 to avoid noise.
185
- const newMemories = Math.max(0, memoryCount - (baseline?.memories ?? memoryCount));
186
- const newEntities = Math.max(0, entityCount - (baseline?.entities ?? entityCount));
187
- delete pendingBaselineRef.current[sourceType];
188
- setIngestionStates((prev) => {
189
- const current = prev[sourceType];
190
- if (!current) return prev;
191
- return {
192
- ...prev,
193
- [sourceType]: {
194
- ...current,
195
- stage: "complete",
196
- completedAt: Date.now(),
197
- newMemories,
198
- newEntities,
199
- },
200
- };
201
- });
202
- setEmergenceEvents((events) =>
203
- [
204
- {
205
- id: `${sourceType}-${Date.now()}`,
206
- sourceType,
207
- label,
208
- newMemories,
209
- newEntities,
210
- at: Date.now(),
211
- },
212
- ...events,
213
- ].slice(0, 10),
214
- );
215
- },
216
- [clearStageTimers],
217
- );
218
-
219
- // Once a request resolves we wait for the refetched counts to settle, then record the
220
- // real emergence delta. A fallback timer guarantees the panel never hangs on a stale count.
221
- const markAwaitingEmergence = React.useCallback(
222
- (sourceType: IngestionSourceType) => {
223
- awaitingEmergenceRef.current.add(sourceType);
224
- if (settleTimerRef.current !== null) window.clearTimeout(settleTimerRef.current);
225
- settleTimerRef.current = window.setTimeout(() => {
226
- for (const pending of Array.from(awaitingEmergenceRef.current)) {
227
- resolveEmergence(pending, memoryFragments.length, knowledgeConcepts.length);
228
- awaitingEmergenceRef.current.delete(pending);
229
- }
230
- }, 1600);
231
- },
232
- [knowledgeConcepts.length, memoryFragments.length, resolveEmergence],
233
- );
234
-
235
- // Flush awaiting ingestions as soon as the underlying counts change post-invalidation.
236
- React.useEffect(() => {
237
- if (awaitingEmergenceRef.current.size === 0) return;
238
- for (const pending of Array.from(awaitingEmergenceRef.current)) {
239
- const baseline = pendingBaselineRef.current[pending];
240
- if (!baseline) {
241
- awaitingEmergenceRef.current.delete(pending);
242
- continue;
243
- }
244
- if (memoryFragments.length !== baseline.memories || knowledgeConcepts.length !== baseline.entities) {
245
- resolveEmergence(pending, memoryFragments.length, knowledgeConcepts.length);
246
- awaitingEmergenceRef.current.delete(pending);
247
- }
248
- }
249
- }, [memoryFragments.length, knowledgeConcepts.length, resolveEmergence]);
250
-
251
- React.useEffect(() => {
252
- return () => {
253
- if (settleTimerRef.current !== null) window.clearTimeout(settleTimerRef.current);
254
- };
255
- }, []);
256
-
257
- const failIngestion = React.useCallback(
258
- (sourceType: IngestionSourceType, reason: string) => {
259
- clearStageTimers(sourceType);
260
- delete pendingBaselineRef.current[sourceType];
261
- setIngestionStates((prev) => {
262
- const current = prev[sourceType];
263
- if (!current) return prev;
264
- return {
265
- ...prev,
266
- [sourceType]: { ...current, stage: "error", completedAt: Date.now(), error: reason },
267
- };
268
- });
269
- },
270
- [clearStageTimers],
271
- );
272
-
273
- async function send() {
274
- const text = draft.trim();
275
- if (!text || streaming) return;
276
- const activeConversationId = conversationId || `brain-${Date.now()}`;
277
- if (!conversationId) setConversationId(activeConversationId);
278
-
279
- setMessages((items) => [...items, { role: "user", content: text }, { role: "assistant", content: "" }]);
280
- setDraft("");
281
- setImageData(null);
282
- setStreaming(true);
283
- setMemoryFeedback(null);
284
- onBrainChange("thinking", 0.96);
285
-
286
- try {
287
- const result = await latticeApi.streamChat(
288
- { message: text, conversation_id: activeConversationId, image_data: imageData || undefined },
289
- {
290
- onChunk: (_delta, fullText) => {
291
- setMessages((items) => {
292
- const next = [...items];
293
- next[next.length - 1] = { role: "assistant", content: fullText };
294
- return next;
295
- });
296
- },
297
- onTrace: (trace) => {
298
- if (!trace) return;
299
- onBrainChange("recalling", 0.9);
300
- triggerBrainRecall();
301
- if (recallTimerRef.current !== null) window.clearTimeout(recallTimerRef.current);
302
- recallTimerRef.current = window.setTimeout(() => onBrainChange("thinking", 0.9), 900);
303
- },
304
- },
305
- );
306
- if (result.error) {
307
- setMessages((items) => {
308
- const next = [...items];
309
- next[next.length - 1] = { role: "assistant", content: `${t(language, "brain.unavailable")}: ${result.error}` };
310
- return next;
311
- });
312
- } else {
313
- setMemoryFeedback(t(language, "brain.saved", { topics: knowledgeConcepts.length, memories: memoryFragments.length }));
314
- setLastRecallQuery(text);
315
- void attachAnswerProof(text);
316
- }
317
- } finally {
318
- setStreaming(false);
319
- void qc.invalidateQueries({ queryKey: ["chatHistory"] });
320
- void qc.invalidateQueries({ queryKey: ["memoryManager"] });
321
- void qc.invalidateQueries({ queryKey: ["graph"] });
322
- void qc.invalidateQueries({ queryKey: ["memoryBrainProof"] });
323
- }
324
- }
325
-
326
- async function attachAnswerProof(query: string) {
327
- const proofResult = await latticeApi.memoryBrainProof(query, 4);
328
- const proof = buildBrainProof(proofResult.data, modelName);
329
- const messageProof = toMessageProof(proof, query, modelName);
330
- setMessages((items) => {
331
- const next = [...items];
332
- for (let index = next.length - 1; index >= 0; index -= 1) {
333
- if (next[index].role === "assistant") {
334
- next[index] = { ...next[index], proof: messageProof };
335
- break;
336
- }
337
- }
338
- return next;
339
- });
340
- }
341
-
342
- async function uploadDocument(file: File) {
343
- if (uploadingDocument) return;
344
-
345
- setUploadingDocument(true);
346
- setMemoryFeedback(t(language, "brain.upload.pending", { name: file.name }));
347
- onBrainChange("recalling", 0.86);
348
- beginIngestion("file", file.name);
349
-
350
- try {
351
- const result = await latticeApi.uploadDocument(file);
352
- if (result.error) {
353
- setMemoryFeedback(t(language, "brain.upload.failed", { reason: result.error }));
354
- failIngestion("file", result.error);
355
- return;
356
- }
357
-
358
- setMemoryFeedback(t(language, "brain.upload.saved", { name: file.name }));
359
- setLastRecallQuery(file.name);
360
- triggerBrainRecall();
361
- void qc.invalidateQueries({ queryKey: ["memoryManager"] });
362
- void qc.invalidateQueries({ queryKey: ["graph"] });
363
- void qc.invalidateQueries({ queryKey: ["memoryBrainProof"] });
364
- markAwaitingEmergence("file");
365
- } finally {
366
- setUploadingDocument(false);
367
- }
368
- }
369
-
370
- async function connectFolder(path: string) {
371
- const target = path.trim();
372
- if (!target) return;
373
- setMemoryFeedback(t(language, "brain.ingest.folder.pending", { path: target }));
374
- onBrainChange("recalling", 0.84);
375
- beginIngestion("folder", target);
376
- const result = await latticeApi.connectFolder(target);
377
- if (result.error || !result.ok) {
378
- setMemoryFeedback(t(language, "brain.ingest.folder.failed", { reason: result.error || "unavailable" }));
379
- failIngestion("folder", result.error || "unavailable");
380
- return;
381
- }
382
- setMemoryFeedback(t(language, "brain.ingest.folder.saved", { path: target }));
383
- setLastRecallQuery(target);
384
- triggerBrainRecall();
385
- void refreshBrainProof(target);
386
- markAwaitingEmergence("folder");
387
- }
388
-
389
- async function ingestNote(note: string) {
390
- const content = note.trim();
391
- if (!content) return;
392
- setMemoryFeedback(t(language, "brain.ingest.note.pending"));
393
- onBrainChange("recalling", 0.84);
394
- beginIngestion("note", content.slice(0, 80));
395
- const result = await latticeApi.ingestNote(content, content.slice(0, 80));
396
- if (result.error || !result.ok) {
397
- setMemoryFeedback(t(language, "brain.ingest.note.failed", { reason: result.error || "unavailable" }));
398
- failIngestion("note", result.error || "unavailable");
399
- return;
400
- }
401
- setMemoryFeedback(t(language, "brain.ingest.note.saved"));
402
- setLastRecallQuery(content.slice(0, 120));
403
- triggerBrainRecall();
404
- void refreshBrainProof(content.slice(0, 120));
405
- markAwaitingEmergence("note");
406
- }
407
-
408
- async function ingestWeb(url: string) {
409
- const target = url.trim();
410
- if (!target) return;
411
- setMemoryFeedback(t(language, "brain.ingest.web.pending", { url: target }));
412
- onBrainChange("recalling", 0.84);
413
- beginIngestion("web", target);
414
- const result = await latticeApi.browserReadUrl(target);
415
- if (result.error || !result.ok) {
416
- setMemoryFeedback(t(language, "brain.ingest.web.failed", { reason: result.error || "unavailable" }));
417
- failIngestion("web", result.error || "unavailable");
418
- return;
419
- }
420
- setMemoryFeedback(t(language, "brain.ingest.web.saved", { url: target }));
421
- setLastRecallQuery(target);
422
- triggerBrainRecall();
423
- void refreshBrainProof(target);
424
- markAwaitingEmergence("web");
425
- }
426
-
427
- async function refreshBrainProof(query = lastRecallQuery) {
428
- await Promise.all([
429
- qc.invalidateQueries({ queryKey: ["memoryManager"] }),
430
- qc.invalidateQueries({ queryKey: ["graph"] }),
431
- qc.invalidateQueries({ queryKey: ["memoryBrainProof"] }),
432
- ]);
433
- if (query.trim()) {
434
- await attachAnswerProof(query);
435
- }
436
- }
437
-
438
- async function verifyModelContinuity() {
439
- const lastUserMessage = [...messages].reverse().find((message: Message) => message.role === "user");
440
- const query = lastRecallQuery || lastUserMessage?.content || "";
441
- if (!query.trim()) {
442
- setMemoryFeedback(t(language, "brain.modelDemo.needQuestion"));
443
- return;
444
- }
445
- setMemoryFeedback(t(language, "brain.modelDemo.checking", { model: modelName }));
446
- await attachAnswerProof(query);
447
- setLastRecallQuery(query);
448
- setMemoryFeedback(t(language, "brain.modelDemo.done", { model: modelName }));
449
- }
450
-
451
- function deepen() {
452
- setExplorationDepth((depth) => {
453
- const next = Math.min(5, depth + 1) as BrainDepth;
454
- const nextDepth = DEPTHS[next - 1];
455
- onBrainChange(nextDepth.state, 0.66 + next * 0.06);
456
- if (next >= 2) triggerBrainRecall();
457
- return next;
458
- });
459
- }
460
-
461
- function jumpToDepth(next: BrainDepth) {
462
- setExplorationDepth(next);
463
- const nextDepth = DEPTHS[next - 1];
464
- onBrainChange(nextDepth.state, next === 1 ? 0.58 : 0.66 + next * 0.06);
465
- if (next >= 2) triggerBrainRecall();
466
- }
467
-
468
- function surface() {
469
- setExplorationDepth(1);
470
- setSelectedGraphId(null);
471
- setGraphSearch("");
472
- onBrainChange("idle", 0.58);
473
- }
474
-
475
- function recallMemory(fragment: MemoryFragment) {
476
- triggerBrainRecall();
477
- setExplorationDepth((depth) => Math.max(depth, 2) as BrainDepth);
478
- setMessages((items) => [
479
- ...items,
480
- { role: "assistant", content: t(language, "brain.recalled", { title: fragment.title }) },
481
- ]);
482
- }
483
-
484
- return (
485
- <main className="brain-home" aria-label={t(language, "brain.aria.home")}>
486
- <section className="brain-presence" aria-label={t(language, "brain.aria.exploration")}>
487
- <div className="brain-exploration" data-depth={explorationDepth}>
488
- <LivingBrain
489
- state={brainState}
490
- intensity={intensity + explorationDepth * 0.035}
491
- size="large"
492
- depth={explorationDepth}
493
- showLabel={false}
494
- onInteract={deepen}
495
- />
496
-
497
- {explorationDepth > 1 ? (
498
- <>
499
- <BrainDepthRings explorationDepth={explorationDepth} onOpenDepth={jumpToDepth} />
500
-
501
- <div className="brain-depth-badge" aria-live="polite">
502
- <span>{t(language, "brain.level")} {explorationDepth}</span>
503
- <strong>{t(language, `brain.depth.${explorationDepth}`)}</strong>
504
- </div>
505
-
506
- <div className="brain-depth-actions" aria-label={t(language, "brain.aria.quickViews")}>
507
- <button type="button" className={explorationDepth === 2 ? "is-active" : ""} onClick={() => jumpToDepth(2)}>{t(language, "brain.view.memories")}</button>
508
- <button type="button" className={explorationDepth === 3 ? "is-active" : ""} onClick={() => jumpToDepth(3)}>{t(language, "brain.view.topics")}</button>
509
- <button type="button" className={explorationDepth === 4 ? "is-active" : ""} onClick={() => jumpToDepth(4)}>{t(language, "brain.view.relationships")}</button>
510
- <button type="button" className={explorationDepth === 5 ? "is-active" : ""} onClick={() => jumpToDepth(5)}>{t(language, "brain.view.graph")}</button>
511
- </div>
512
-
513
- <div className="brain-depth-rail" aria-label={t(language, "brain.depthRail.aria")}>
514
- {DEPTHS.map((depth) => (
515
- <button
516
- key={depth.level}
517
- type="button"
518
- className={depth.level <= explorationDepth ? "is-revealed" : ""}
519
- aria-current={depth.level === explorationDepth ? "step" : undefined}
520
- onClick={() => jumpToDepth(depth.level)}
521
- >
522
- <span>{depth.level}</span>
523
- <strong>{t(language, `brain.depth.${depth.level}`)}</strong>
524
- </button>
525
- ))}
526
- </div>
527
- </>
528
- ) : null}
529
-
530
- <div className="brain-field-layer" aria-hidden={explorationDepth < 2}>
531
- <DepthEmergence
532
- depth={explorationDepth}
533
- memories={memoryFragments}
534
- concepts={knowledgeConcepts}
535
- relationships={relationshipThreads}
536
- graphModel={graphModel}
537
- graphSearch={graphSearch}
538
- selectedGraphId={selectedGraphId}
539
- onGraphSearch={setGraphSearch}
540
- onSelectGraphNode={setSelectedGraphId}
541
- onRecallMemory={recallMemory}
542
- />
543
- </div>
544
-
545
- {explorationDepth > 1 ? (
546
- <button className="brain-surface-control" type="button" onClick={surface}>
547
- {t(language, "brain.surface")}
548
- </button>
549
- ) : null}
550
- </div>
551
- </section>
552
-
553
- <BrainConversation
554
- language={language}
555
- explorationDepth={explorationDepth}
556
- modelName={modelName}
557
- messages={messages}
558
- starterPrompts={starterPrompts}
559
- memoryFeedback={memoryFeedback}
560
- ingestionStates={ingestionStates}
561
- emergenceEvents={emergenceEvents}
562
- draft={draft}
563
- streaming={streaming}
564
- imageData={imageData}
565
- streamRef={streamRef}
566
- memories={memoryFragments}
567
- concepts={knowledgeConcepts}
568
- readiness={brainReadiness}
569
- proof={brainProof}
570
- uploadingDocument={uploadingDocument}
571
- onOpenDepth={jumpToDepth}
572
- onDraftChange={setDraft}
573
- onImageDataChange={setImageData}
574
- onUploadDocument={(file) => void uploadDocument(file)}
575
- onConnectFolder={(path) => void connectFolder(path)}
576
- onIngestNote={(note) => void ingestNote(note)}
577
- onIngestWeb={(url) => void ingestWeb(url)}
578
- onVerifyModelContinuity={() => void verifyModelContinuity()}
579
- onSend={() => void send()}
580
- />
581
- </main>
582
- );
583
- }
584
-
585
- function BrainDepthRings({
586
- explorationDepth,
587
- onOpenDepth,
588
- }: {
589
- explorationDepth: BrainDepth;
590
- onOpenDepth: (depth: BrainDepth) => void;
591
- }) {
592
- const language = useAppStore((state) => state.language);
593
- return (
594
- <div className="brain-memory-rings" aria-label={t(language, "brain.rings.aria")}>
595
- {DEPTHS.map((depth, index) => (
596
- <button
597
- key={depth.level}
598
- type="button"
599
- className={`brain-memory-ring ring-${depth.level} ${depth.level <= explorationDepth ? "is-revealed" : ""}`}
600
- aria-current={depth.level === explorationDepth ? "step" : undefined}
601
- onClick={() => onOpenDepth(depth.level)}
602
- style={{ "--ring-delay": `${index * 70}ms` } as React.CSSProperties}
603
- >
604
- <span>{depth.level}</span>
605
- <strong>{t(language, `brain.rings.${depth.level}`)}</strong>
606
- </button>
607
- ))}
608
- </div>
609
- );
610
- }
611
-
612
- function toMessageProof(proof: BrainProof, query: string, fallbackModelName: string): MessageProof {
613
- return {
614
- query,
615
- model: proof.modelContinuity.activeModel || fallbackModelName,
616
- provenAcrossModels: proof.modelContinuity.proven && proof.claims.keepsContextAcrossModels,
617
- citations: proof.recall.items.slice(0, 4).map((item) => ({
618
- id: item.id,
619
- source: item.source,
620
- title: item.title,
621
- snippet: item.snippet,
622
- })),
623
- };
624
- }
@@ -1,45 +0,0 @@
1
- import type { BrainDepth, MemoryFragment } from "./types";
2
- import { t } from "@/i18n";
3
- import { useAppStore } from "@/store/appStore";
4
- import { layerStyle, polarPoint } from "./graphLayout";
5
-
6
- export function BrainMemoryLayer({
7
- memories,
8
- depth,
9
- onRecallMemory,
10
- }: {
11
- memories: MemoryFragment[];
12
- depth: BrainDepth;
13
- onRecallMemory: (fragment: MemoryFragment) => void;
14
- }) {
15
- const language = useAppStore((state) => state.language);
16
- const visible = memories.slice(0, depth >= 3 ? 8 : 6);
17
- if (!visible.length) {
18
- return (
19
- <div className="memory-fragment is-empty">
20
- <span>{t(language, "brain.memory.empty.kicker")}</span>
21
- <strong>{t(language, "brain.memory.empty")}</strong>
22
- </div>
23
- );
24
- }
25
-
26
- return (
27
- <>
28
- {visible.map((memory, index) => {
29
- const point = polarPoint(index, visible.length, depth >= 3 ? 39 : 31, depth >= 3 ? 24 : 18, -112);
30
- return (
31
- <button
32
- key={memory.id}
33
- type="button"
34
- className="memory-fragment"
35
- style={layerStyle({ "--x": `${point.x}%`, "--y": `${point.y}%`, "--delay": `${index * 55}ms` })}
36
- onClick={() => onRecallMemory(memory)}
37
- >
38
- <span>{memory.kind}</span>
39
- <strong>{memory.title}</strong>
40
- </button>
41
- );
42
- })}
43
- </>
44
- );
45
- }