ltcai 8.0.0 → 8.1.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 (103) hide show
  1. package/README.md +32 -50
  2. package/docs/CHANGELOG.md +14 -0
  3. package/docs/V3_FRONTEND.md +11 -5
  4. package/lattice_brain/__init__.py +1 -1
  5. package/lattice_brain/embeddings.py +9 -1
  6. package/lattice_brain/runtime/multi_agent.py +1 -1
  7. package/latticeai/__init__.py +1 -1
  8. package/latticeai/app_factory.py +13 -134
  9. package/latticeai/core/local_embeddings.py +8 -0
  10. package/latticeai/core/marketplace.py +1 -1
  11. package/latticeai/core/mcp_registry.py +140 -1
  12. package/latticeai/core/workspace_os.py +1 -1
  13. package/latticeai/services/architecture_readiness.py +6 -5
  14. package/latticeai/services/model_runtime.py +36 -2
  15. package/latticeai/services/product_readiness.py +18 -18
  16. package/package.json +2 -2
  17. package/scripts/build_frontend_assets.mjs +11 -2
  18. package/src-tauri/Cargo.lock +1 -1
  19. package/src-tauri/Cargo.toml +1 -1
  20. package/src-tauri/tauri.conf.json +1 -1
  21. package/static/app/asset-manifest.json +11 -102
  22. package/static/app/assets/Act-BOO66G-c.js +1 -0
  23. package/static/app/assets/Brain-C6lEYiD7.js +321 -0
  24. package/static/app/assets/{Capture-B9Tlhzqr.js → Capture-TATXBRDw.js} +1 -2
  25. package/static/app/assets/Library-DK4FIp8a.js +1 -0
  26. package/static/app/assets/System-Bgs6Ql7x.js +1 -0
  27. package/static/app/assets/core-CwxXejkd.js +1 -2
  28. package/static/app/assets/index-Bvv79nre.js +16 -0
  29. package/static/app/assets/index-Dslqglia.css +2 -0
  30. package/static/app/assets/primitives-B70WOra0.js +1 -0
  31. package/static/app/assets/{textarea-BZk6ybp5.js → textarea-Czrd9gwM.js} +1 -2
  32. package/static/app/index.html +3 -10
  33. package/static/app/theme-boot.js +8 -0
  34. package/static/sw.js +1 -1
  35. package/frontend/index.html +0 -24
  36. package/frontend/openapi.json +0 -15425
  37. package/frontend/src/App.tsx +0 -243
  38. package/frontend/src/api/client.ts +0 -580
  39. package/frontend/src/api/openapi.ts +0 -17892
  40. package/frontend/src/components/AdminAccessGate.tsx +0 -70
  41. package/frontend/src/components/FeedbackState.tsx +0 -45
  42. package/frontend/src/components/LanguageSwitcher.tsx +0 -23
  43. package/frontend/src/components/LivingBrain.tsx +0 -220
  44. package/frontend/src/components/ProductFlow.tsx +0 -171
  45. package/frontend/src/components/WorkspaceProfileSwitcher.tsx +0 -176
  46. package/frontend/src/components/onboarding/AnalysisScreen.tsx +0 -135
  47. package/frontend/src/components/onboarding/DownloadConsentPanel.tsx +0 -29
  48. package/frontend/src/components/onboarding/InstallScreen.tsx +0 -263
  49. package/frontend/src/components/onboarding/LanguageChooser.tsx +0 -23
  50. package/frontend/src/components/onboarding/LoginScreen.tsx +0 -131
  51. package/frontend/src/components/onboarding/ProductFlowScreens.tsx +0 -13
  52. package/frontend/src/components/onboarding/RecommendationScreen.tsx +0 -116
  53. package/frontend/src/components/onboarding/recommendationModel.ts +0 -189
  54. package/frontend/src/components/primitives.tsx +0 -392
  55. package/frontend/src/components/ui/badge.tsx +0 -27
  56. package/frontend/src/components/ui/button.tsx +0 -37
  57. package/frontend/src/components/ui/card.tsx +0 -22
  58. package/frontend/src/components/ui/input.tsx +0 -16
  59. package/frontend/src/components/ui/textarea.tsx +0 -16
  60. package/frontend/src/features/admin/AdminConsole.tsx +0 -334
  61. package/frontend/src/features/brain/BrainCarePanel.tsx +0 -254
  62. package/frontend/src/features/brain/BrainComposer.tsx +0 -85
  63. package/frontend/src/features/brain/BrainConversation.tsx +0 -688
  64. package/frontend/src/features/brain/BrainGraphLayer.tsx +0 -365
  65. package/frontend/src/features/brain/BrainHome.tsx +0 -624
  66. package/frontend/src/features/brain/BrainMemoryLayer.tsx +0 -45
  67. package/frontend/src/features/brain/BrainOverviewPanel.tsx +0 -149
  68. package/frontend/src/features/brain/BrainRelationshipLayer.tsx +0 -43
  69. package/frontend/src/features/brain/DepthEmergence.tsx +0 -53
  70. package/frontend/src/features/brain/brainData.ts +0 -246
  71. package/frontend/src/features/brain/graphLayout.ts +0 -37
  72. package/frontend/src/features/brain/types.ts +0 -150
  73. package/frontend/src/features/review/ReviewCard.tsx +0 -100
  74. package/frontend/src/features/review/ReviewInbox.tsx +0 -127
  75. package/frontend/src/features/review/reviewHelpers.ts +0 -69
  76. package/frontend/src/i18n.ts +0 -1303
  77. package/frontend/src/lib/utils.ts +0 -33
  78. package/frontend/src/main.tsx +0 -23
  79. package/frontend/src/pages/Act.tsx +0 -458
  80. package/frontend/src/pages/Ask.tsx +0 -14
  81. package/frontend/src/pages/Brain.tsx +0 -914
  82. package/frontend/src/pages/Capture.tsx +0 -258
  83. package/frontend/src/pages/Library.tsx +0 -486
  84. package/frontend/src/pages/System.tsx +0 -621
  85. package/frontend/src/routes.ts +0 -92
  86. package/frontend/src/store/appStore.ts +0 -94
  87. package/frontend/src/styles.css +0 -6579
  88. package/static/app/assets/Act-DOvf59ru.js +0 -2
  89. package/static/app/assets/Act-DOvf59ru.js.map +0 -1
  90. package/static/app/assets/Brain-C7_0mEiI.js +0 -322
  91. package/static/app/assets/Brain-C7_0mEiI.js.map +0 -1
  92. package/static/app/assets/Capture-B9Tlhzqr.js.map +0 -1
  93. package/static/app/assets/Library-BJPEEm5O.js +0 -2
  94. package/static/app/assets/Library-BJPEEm5O.js.map +0 -1
  95. package/static/app/assets/System-D6t9jo9V.js +0 -2
  96. package/static/app/assets/System-D6t9jo9V.js.map +0 -1
  97. package/static/app/assets/core-CwxXejkd.js.map +0 -1
  98. package/static/app/assets/index-C7g26IF6.css +0 -2
  99. package/static/app/assets/index-DbcEYJQ2.js +0 -17
  100. package/static/app/assets/index-DbcEYJQ2.js.map +0 -1
  101. package/static/app/assets/primitives-CD38lt4n.js +0 -2
  102. package/static/app/assets/primitives-CD38lt4n.js.map +0 -1
  103. package/static/app/assets/textarea-BZk6ybp5.js.map +0 -1
@@ -1,365 +0,0 @@
1
- import * as React from "react";
2
- import { Filter, RotateCcw, Search, X } from "lucide-react";
3
- import { t } from "@/i18n";
4
- import { useAppStore } from "@/store/appStore";
5
- import type { KnowledgeConcept, KnowledgeGraphModel } from "./types";
6
- import { clamp, computeGraphNeighbors, layerStyle, layoutGraphNodes, polarPoint } from "./graphLayout";
7
-
8
- export function BrainKnowledgeLayer({ concepts, depth }: { concepts: KnowledgeConcept[]; depth: number }) {
9
- const language = useAppStore((state) => state.language);
10
- const visible = concepts.slice(0, depth >= 4 ? 10 : 7);
11
- if (!visible.length) return <div className="concept-signal is-empty">{t(language, "brain.knowledge.empty")}</div>;
12
-
13
- return (
14
- <>
15
- {visible.map((concept, index) => {
16
- const point = polarPoint(index, visible.length, 24, 15, -70);
17
- return (
18
- <button
19
- key={concept.id}
20
- type="button"
21
- className="concept-signal"
22
- style={layerStyle({ "--x": `${point.x}%`, "--y": `${point.y}%`, "--delay": `${index * 45}ms` })}
23
- title={concept.summary || concept.type}
24
- >
25
- <span>{concept.type}</span>
26
- {concept.label}
27
- </button>
28
- );
29
- })}
30
- </>
31
- );
32
- }
33
-
34
- const DAY_MS = 24 * 60 * 60 * 1000;
35
- // Time-exploration presets (days back). null == all time.
36
- const TIME_WINDOWS: Array<{ days: number | null }> = [{ days: 7 }, { days: 30 }, { days: 90 }, { days: null }];
37
- // A node added within this window of "now" is treated as recent for highlighting.
38
- const RECENT_WINDOW_MS = 7 * DAY_MS;
39
-
40
- // Highlight occurrences of `query` inside `label` by wrapping matches in <mark>.
41
- function highlightMatch(label: string, query: string): React.ReactNode {
42
- if (!query) return label;
43
- const lower = label.toLowerCase();
44
- const target = query.toLowerCase();
45
- const parts: React.ReactNode[] = [];
46
- let cursor = 0;
47
- let matchIndex = lower.indexOf(target, cursor);
48
- let key = 0;
49
- while (matchIndex !== -1 && target.length > 0) {
50
- if (matchIndex > cursor) parts.push(label.slice(cursor, matchIndex));
51
- parts.push(<mark key={`m-${key++}`}>{label.slice(matchIndex, matchIndex + target.length)}</mark>);
52
- cursor = matchIndex + target.length;
53
- matchIndex = lower.indexOf(target, cursor);
54
- }
55
- if (cursor < label.length) parts.push(label.slice(cursor));
56
- return parts.length ? parts : label;
57
- }
58
-
59
- export function BrainGraphLayer({
60
- model,
61
- search,
62
- selectedId,
63
- onSearch,
64
- onSelect,
65
- }: {
66
- model: KnowledgeGraphModel;
67
- search: string;
68
- selectedId: string | null;
69
- onSearch: (value: string) => void;
70
- onSelect: (id: string | null) => void;
71
- }) {
72
- const language = useAppStore((state) => state.language);
73
- const query = search.trim().toLowerCase();
74
-
75
- // Control state local to the graph view so existing lifted search/selection
76
- // behavior (and its reset in surface()) is preserved untouched.
77
- const [activeTypes, setActiveTypes] = React.useState<Set<string>>(() => new Set());
78
- const [timeDays, setTimeDays] = React.useState<number | null>(null);
79
- const [typeaheadOpen, setTypeaheadOpen] = React.useState(false);
80
-
81
- // Distinct entity types available in the graph (for the type filter chips).
82
- const availableTypes = React.useMemo(() => {
83
- const seen = new Set<string>();
84
- for (const node of model.nodes) seen.add(node.type);
85
- return Array.from(seen).sort((a, b) => a.localeCompare(b));
86
- }, [model.nodes]);
87
-
88
- // Drop type selections that no longer exist after a graph refresh.
89
- React.useEffect(() => {
90
- setActiveTypes((prev) => {
91
- if (prev.size === 0) return prev;
92
- const next = new Set<string>();
93
- for (const type of prev) if (availableTypes.includes(type)) next.add(type);
94
- return next.size === prev.size ? prev : next;
95
- });
96
- }, [availableTypes]);
97
-
98
- const hasTimestamps = React.useMemo(() => model.nodes.some((node) => typeof node.createdAt === "number"), [model.nodes]);
99
- const timeCutoff = timeDays === null ? null : Date.now() - timeDays * DAY_MS;
100
-
101
- const filtersActive = activeTypes.size > 0 || timeDays !== null;
102
-
103
- const visibleNodes = React.useMemo(() => {
104
- const filtered = model.nodes.filter((node) => {
105
- if (activeTypes.size > 0 && !activeTypes.has(node.type)) return false;
106
- if (timeCutoff !== null && typeof node.createdAt === "number" && node.createdAt < timeCutoff) return false;
107
- if (!query) return true;
108
- return `${node.label} ${node.type} ${node.summary}`.toLowerCase().includes(query);
109
- });
110
- return filtered.slice(0, 18);
111
- }, [model.nodes, query, activeTypes, timeCutoff]);
112
-
113
- // Typeahead suggestions: label-prefix/substring matches ranked by importance.
114
- const suggestions = React.useMemo(() => {
115
- if (!query) return [];
116
- return model.nodes
117
- .filter((node) => node.label.toLowerCase().includes(query) || node.type.toLowerCase().includes(query))
118
- .slice(0, 6);
119
- }, [model.nodes, query]);
120
-
121
- const layout = React.useMemo(() => layoutGraphNodes(visibleNodes, 38, 24), [visibleNodes]);
122
- const positionById = React.useMemo(() => new Map(layout.map((item) => [item.node.id, item])), [layout]);
123
- const visibleEdges = React.useMemo(
124
- () => model.edges.filter((edge) => positionById.has(edge.source) && positionById.has(edge.target)).slice(0, 36),
125
- [model.edges, positionById],
126
- );
127
- const selected = visibleNodes.find((node) => node.id === selectedId) || visibleNodes[0] || null;
128
-
129
- // 1-hop neighbor set for the focused node, drives focus/dim + edge highlight.
130
- const neighborIds = React.useMemo(
131
- () => (selectedId ? computeGraphNeighbors(selectedId, model.edges) : new Set<string>()),
132
- [selectedId, model.edges],
133
- );
134
- const focusActive = Boolean(selectedId) && neighborIds.size > 0;
135
-
136
- const matchedIds = React.useMemo(() => {
137
- if (!query) return new Set<string>();
138
- return new Set(visibleNodes.filter((node) => node.label.toLowerCase().includes(query)).map((node) => node.id));
139
- }, [visibleNodes, query]);
140
-
141
- function focusState(nodeId: string): "true" | "false" | undefined {
142
- if (!focusActive) return undefined;
143
- if (nodeId === selectedId || neighborIds.has(nodeId)) return "true";
144
- return "false";
145
- }
146
-
147
- return (
148
- <section className="mind-core-graph" data-testid="emergent-knowledge-graph" aria-label={t(language, "brain.aria.graph")}>
149
- <div className="brain-graph-head">
150
- <div>
151
- <span>{t(language, "brain.level")} 5</span>
152
- <strong>{t(language, "brain.depth.5")}</strong>
153
- </div>
154
- <div className="brain-graph-search-wrap">
155
- <label className="brain-graph-search">
156
- <Search className="h-3.5 w-3.5" />
157
- <input
158
- value={search}
159
- onChange={(event) => {
160
- onSearch(event.target.value);
161
- setTypeaheadOpen(true);
162
- }}
163
- onFocus={() => setTypeaheadOpen(true)}
164
- onBlur={() => window.setTimeout(() => setTypeaheadOpen(false), 120)}
165
- placeholder={t(language, "brain.graph.search")}
166
- aria-label={t(language, "brain.graph.searchAria")}
167
- role="combobox"
168
- aria-expanded={typeaheadOpen && Boolean(query)}
169
- aria-autocomplete="list"
170
- aria-controls="brain-graph-typeahead"
171
- />
172
- {search ? (
173
- <button
174
- type="button"
175
- className="brain-graph-search-clear"
176
- onClick={() => onSearch("")}
177
- aria-label={t(language, "brain.graph.focus.clear")}
178
- >
179
- <X className="h-3 w-3" />
180
- </button>
181
- ) : null}
182
- </label>
183
- {typeaheadOpen && query ? (
184
- <ul
185
- id="brain-graph-typeahead"
186
- className="brain-graph-typeahead"
187
- role="listbox"
188
- aria-label={t(language, "brain.graph.search.typeaheadAria")}
189
- >
190
- {suggestions.length ? (
191
- suggestions.map((node) => (
192
- <li key={node.id} role="option" aria-selected={node.id === selectedId}>
193
- <button
194
- type="button"
195
- onMouseDown={(event) => {
196
- event.preventDefault();
197
- onSearch(node.label);
198
- onSelect(node.id);
199
- setTypeaheadOpen(false);
200
- }}
201
- >
202
- <span>{node.type}</span>
203
- <strong>{highlightMatch(node.label, query)}</strong>
204
- </button>
205
- </li>
206
- ))
207
- ) : (
208
- <li className="is-empty" aria-disabled>
209
- {t(language, "brain.graph.search.noMatches")}
210
- </li>
211
- )}
212
- </ul>
213
- ) : null}
214
- </div>
215
- </div>
216
-
217
- <div className="brain-graph-control-panel" role="group" aria-label={t(language, "brain.graph.control.panel")}>
218
- <div className="brain-graph-filter-row">
219
- <span className="brain-graph-control-label">
220
- <Filter className="h-3 w-3" aria-hidden />
221
- {t(language, "brain.graph.control.types")}
222
- </span>
223
- <div className="brain-graph-type-chips" role="group" aria-label={t(language, "brain.graph.control.types")}>
224
- <button
225
- type="button"
226
- className={`brain-graph-chip ${activeTypes.size === 0 ? "is-active" : ""}`}
227
- aria-pressed={activeTypes.size === 0}
228
- onClick={() => setActiveTypes(new Set())}
229
- >
230
- {t(language, "brain.graph.control.allTypes")}
231
- </button>
232
- {availableTypes.map((type) => {
233
- const active = activeTypes.has(type);
234
- return (
235
- <button
236
- key={type}
237
- type="button"
238
- className={`brain-graph-chip ${active ? "is-active" : ""}`}
239
- aria-pressed={active}
240
- aria-label={t(language, "brain.graph.control.toggleType", { type })}
241
- onClick={() =>
242
- setActiveTypes((prev) => {
243
- const next = new Set(prev);
244
- if (next.has(type)) next.delete(type);
245
- else next.add(type);
246
- return next;
247
- })
248
- }
249
- >
250
- {type}
251
- </button>
252
- );
253
- })}
254
- </div>
255
- </div>
256
-
257
- <div className="brain-graph-filter-row">
258
- <span className="brain-graph-control-label">{t(language, "brain.graph.control.time")}</span>
259
- <div className="brain-graph-time-chips" role="group" aria-label={t(language, "brain.graph.control.dateRange")}>
260
- {TIME_WINDOWS.map((window) => {
261
- const active = timeDays === window.days;
262
- const label =
263
- window.days === null
264
- ? t(language, "brain.graph.control.allTime")
265
- : t(language, "brain.graph.control.recentWindow", { days: window.days });
266
- return (
267
- <button
268
- key={window.days ?? "all"}
269
- type="button"
270
- className={`brain-graph-chip ${active ? "is-active" : ""}`}
271
- aria-pressed={active}
272
- disabled={window.days !== null && !hasTimestamps}
273
- onClick={() => setTimeDays(window.days)}
274
- >
275
- {label}
276
- </button>
277
- );
278
- })}
279
- </div>
280
- {filtersActive ? (
281
- <button
282
- type="button"
283
- className="brain-graph-reset"
284
- onClick={() => {
285
- setActiveTypes(new Set());
286
- setTimeDays(null);
287
- }}
288
- >
289
- <RotateCcw className="h-3 w-3" aria-hidden />
290
- {t(language, "brain.graph.control.resetFilters")}
291
- </button>
292
- ) : null}
293
- </div>
294
- </div>
295
-
296
- {visibleNodes.length ? (
297
- <div className="brain-graph-canvas" data-focus-active={focusActive ? "true" : "false"}>
298
- <svg className="brain-graph-edges" viewBox="0 0 100 100" aria-hidden>
299
- {visibleEdges.map((edge, index) => {
300
- const source = positionById.get(edge.source);
301
- const target = positionById.get(edge.target);
302
- if (!source || !target) return null;
303
- const touchesFocus =
304
- focusActive && (edge.source === selectedId || edge.target === selectedId);
305
- const touchesMatch = matchedIds.has(edge.source) || matchedIds.has(edge.target);
306
- const highlight = touchesFocus || (query ? touchesMatch : false);
307
- return (
308
- <line
309
- key={`${edge.id}-${index}`}
310
- x1={source.x}
311
- y1={source.y}
312
- x2={target.x}
313
- y2={target.y}
314
- data-highlight={
315
- focusActive || query ? (highlight ? "true" : "false") : undefined
316
- }
317
- style={{ "--weight": String(clamp(edge.weight, 0.4, 2.8)) } as React.CSSProperties}
318
- />
319
- );
320
- })}
321
- </svg>
322
- {layout.map(({ node, x, y }, index) => {
323
- const isRecent =
324
- typeof node.createdAt === "number" && Date.now() - node.createdAt <= RECENT_WINDOW_MS;
325
- return (
326
- <button
327
- key={node.id}
328
- type="button"
329
- className={`graph-node ${selected?.id === node.id ? "is-selected" : ""} ${matchedIds.has(node.id) ? "is-match" : ""}`}
330
- data-focus={focusState(node.id)}
331
- data-recent={isRecent ? "true" : undefined}
332
- style={layerStyle({ "--x": `${x}%`, "--y": `${y}%`, "--delay": `${index * 35}ms` })}
333
- onClick={() => onSelect(selectedId === node.id ? null : node.id)}
334
- >
335
- <span>{node.type}</span>
336
- {highlightMatch(node.label, query)}
337
- </button>
338
- );
339
- })}
340
- </div>
341
- ) : (
342
- <div className="brain-graph-empty">{t(language, "brain.graph.empty")}</div>
343
- )}
344
-
345
- <div className="brain-graph-focus">
346
- {selected ? (
347
- <>
348
- <span>{selected.type}</span>
349
- <strong>{selected.label}</strong>
350
- <p>{selected.summary || t(language, "brain.graph.summaryFallback")}</p>
351
- {focusActive ? (
352
- <p className="brain-graph-focus-meta" role="status">
353
- {t(language, "brain.graph.focus.neighbors", { count: neighborIds.size })}
354
- </p>
355
- ) : (
356
- <p>{t(language, "brain.graph.focused")}</p>
357
- )}
358
- </>
359
- ) : (
360
- <p>{t(language, "brain.graph.emptyFocus")}</p>
361
- )}
362
- </div>
363
- </section>
364
- );
365
- }