ltcai 6.5.0 → 6.6.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 (39) hide show
  1. package/README.md +22 -20
  2. package/docs/CHANGELOG.md +34 -0
  3. package/frontend/openapi.json +65 -1
  4. package/frontend/src/api/client.ts +1 -0
  5. package/frontend/src/api/openapi.ts +86 -0
  6. package/frontend/src/features/brain/BrainComposer.tsx +20 -1
  7. package/frontend/src/features/brain/BrainConversation.tsx +37 -2
  8. package/frontend/src/features/brain/BrainHome.tsx +41 -1
  9. package/frontend/src/features/brain/BrainOverviewPanel.tsx +62 -1
  10. package/frontend/src/features/brain/brainData.ts +54 -1
  11. package/frontend/src/features/brain/types.ts +37 -0
  12. package/frontend/src/i18n.ts +44 -0
  13. package/frontend/src/styles.css +133 -0
  14. package/lattice_brain/__init__.py +1 -1
  15. package/lattice_brain/graph/ingest.py +41 -6
  16. package/lattice_brain/ingestion.py +1 -0
  17. package/lattice_brain/runtime/multi_agent.py +1 -1
  18. package/latticeai/__init__.py +1 -1
  19. package/latticeai/api/knowledge_graph.py +11 -0
  20. package/latticeai/api/memory.py +14 -0
  21. package/latticeai/app_factory.py +2 -0
  22. package/latticeai/core/marketplace.py +1 -1
  23. package/latticeai/core/workspace_os.py +1 -1
  24. package/latticeai/runtime/chat_wiring.py +2 -0
  25. package/latticeai/runtime/router_registration.py +3 -0
  26. package/latticeai/services/memory_service.py +122 -2
  27. package/latticeai/services/router_context.py +1 -0
  28. package/latticeai/services/upload_service.py +12 -0
  29. package/package.json +1 -1
  30. package/src-tauri/Cargo.lock +1 -1
  31. package/src-tauri/Cargo.toml +1 -1
  32. package/src-tauri/tauri.conf.json +1 -1
  33. package/static/app/asset-manifest.json +5 -5
  34. package/static/app/assets/{index-CdCVz_i4.css → index-C8toxDpv.css} +1 -1
  35. package/static/app/assets/index-CbvcAQ6B.js +16 -0
  36. package/static/app/assets/index-CbvcAQ6B.js.map +1 -0
  37. package/static/app/index.html +2 -2
  38. package/static/app/assets/index-C1J_1441.js +0 -16
  39. package/static/app/assets/index-C1J_1441.js.map +0 -1
package/README.md CHANGED
@@ -200,31 +200,32 @@ See [docs/DEVELOPMENT.md](docs/DEVELOPMENT.md) for developer workflow details.
200
200
 
201
201
  ## Current Release Preparation
202
202
 
203
- The current development target is **6.5.0 Brain Experience Readiness**:
204
-
205
- - Brain Home now gives normal users a readiness signal that explains whether
206
- the Brain is waiting for its first memory, turning memories into topics, or
207
- ready to explore as a connected map. That signal now comes from the backend
208
- Memory Manager summary instead of a frontend-only estimate.
209
- - The Living Brain surface now includes a persistent depth progress rail, so
210
- users can understand and reverse the path from conversation to memory,
211
- topics, relationships, and the full knowledge graph.
212
- - Memory-save feedback now explicitly states that saved context is available
213
- later with source-aware recall, closing the gap between "chat happened" and
214
- "my Brain became more useful."
215
- - The release keeps the 6.4.0 quality hardening intact and focuses on
216
- end-user comprehension rather than adding a new storage or graph schema.
203
+ The current development target is **6.6.0 Brain Proof Runtime**:
204
+
205
+ - Brain Home now shows backend-owned proof that the Brain is a durable context
206
+ store: saved context count, active-model continuity, graph/vector depth, and
207
+ the most recent recallable item.
208
+ - Brain Home now gives first-time users a direct document upload CTA in the
209
+ empty state and composer, backed by workspace-scoped document ingestion.
210
+ - `/api/memory/brain-proof` combines Memory Manager readiness, unified recall,
211
+ graph/vector state, and the active model id so the first screen can say
212
+ "model changed, context stayed" without guessing in the frontend.
213
+ - App-factory interaction wiring now passes active-model state into the Memory
214
+ router through typed runtime context, keeping Brain proof assembly out of the
215
+ monolithic factory body.
216
+ - The release keeps the 6.4.0/6.5.0 Brain readiness and quality hardening intact
217
+ while making the first-five-minute value proposition visible.
217
218
  - The release remains local-first: no automatic web/email/calendar ingestion,
218
219
  no package publishing, no production deployment, and no external reranker or
219
220
  embedding API use without explicit opt-in.
220
221
 
221
- Expected artifacts for 6.5.0 release must use exact filenames:
222
+ Expected artifacts for 6.6.0 release must use exact filenames:
222
223
 
223
- - `dist/ltcai-6.5.0-py3-none-any.whl`
224
- - `dist/ltcai-6.5.0.tar.gz`
225
- - `ltcai-6.5.0.tgz`
226
- - `dist/ltcai-6.5.0.vsix`
227
- - `src-tauri/target/release/bundle/dmg/Lattice AI_6.5.0_aarch64.dmg`
224
+ - `dist/ltcai-6.6.0-py3-none-any.whl`
225
+ - `dist/ltcai-6.6.0.tar.gz`
226
+ - `ltcai-6.6.0.tgz`
227
+ - `dist/ltcai-6.6.0.vsix`
228
+ - `src-tauri/target/release/bundle/dmg/Lattice AI_6.6.0_aarch64.dmg`
228
229
 
229
230
  Do not upload `dist/*`. Package registry publishing remains owner-run.
230
231
 
@@ -243,6 +244,7 @@ Do not upload `dist/*`. Package registry publishing remains owner-run.
243
244
 
244
245
  | Version | Theme |
245
246
  | --- | --- |
247
+ | 6.6.0 | Brain Proof Runtime: backend-owned Brain proof API, model-continuity wiring, first-screen proof that saved context can be recalled across model changes, and direct Brain Home document upload |
246
248
  | 6.5.0 | Brain Experience Readiness: Brain readiness signal, depth progress rail, source-aware memory-save feedback, and visual coverage for the first-memory loop |
247
249
  | 6.4.0 | Digital Brain Quality Hardening: workspace-scoped graph/search/memory reads and mutations, Brain quality primitives, structured context guardrails, and retrieval benchmark coverage |
248
250
  | 6.3.1 | Access Runtime / i18n Follow-up: app-factory access-control extraction, focused access runtime tests, and Capture/Review Center i18n coverage |
package/docs/CHANGELOG.md CHANGED
@@ -3,6 +3,40 @@
3
3
  The top entry is the current release-preparation target. Older entries are
4
4
  historical and may describe behavior as it existed at that release.
5
5
 
6
+ ## [6.6.0] - 2026-06-18
7
+
8
+ > Brain Proof Runtime. Makes the first-five-minute Brain value visible by
9
+ > showing backend-owned evidence that saved context can be recalled and survives
10
+ > model changes.
11
+
12
+ ### Added
13
+ - `/api/memory/brain-proof`, backed by `MemoryService.brain_proof()`, combining
14
+ Brain readiness, durable memory counts, graph/vector depth, active model id,
15
+ and a unified recall sample.
16
+ - Brain Home proof strip for recallable context, model-continuity, and
17
+ knowledge-store state.
18
+ - Recent recall proof card that surfaces the latest memory/item the Brain can
19
+ bring back after a useful exchange.
20
+ - Brain Home document upload CTA in the empty state and composer, giving the
21
+ first session a direct way to grow the Brain with files.
22
+ - Unit coverage for model-independent Brain proof recall behavior.
23
+ - Honest empty-state Brain proof: model-independent capability remains visible,
24
+ but continuity is only marked proven after durable evidence exists.
25
+
26
+ ### Changed
27
+ - Interaction router runtime context now carries an active-model getter into
28
+ the Memory router, reducing app-factory inline wiring while preserving route
29
+ order.
30
+ - Default Brain proof recall seeding is scoped to the current user/workspace
31
+ before falling back to conversation memory.
32
+ - Chat-to-Knowledge-Graph ingestion now carries workspace scope into projected
33
+ graph nodes, and Brain proof conversation counts use scoped conversations so
34
+ another user/workspace cannot make an empty Brain look proven.
35
+ - Direct Knowledge Graph ingest and document upload ingestion now resolve the
36
+ request workspace and project that scope into graph/source/document nodes.
37
+ - Package/runtime/static metadata is synchronized to 6.6.0; package publish and
38
+ deployment remain owner-run only.
39
+
6
40
  ## [6.5.0] - 2026-06-18
7
41
 
8
42
  > Brain Experience Readiness. Uses the 6.4.0 quality baseline to improve what
@@ -3757,7 +3757,7 @@
3757
3757
  },
3758
3758
  "info": {
3759
3759
  "title": "Lattice AI Server (local)",
3760
- "version": "5.6.0"
3760
+ "version": "6.6.0"
3761
3761
  },
3762
3762
  "openapi": "3.1.0",
3763
3763
  "paths": {
@@ -6941,6 +6941,70 @@
6941
6941
  "summary": "Local Agent Status"
6942
6942
  }
6943
6943
  },
6944
+ "/api/memory/brain-proof": {
6945
+ "get": {
6946
+ "operationId": "brain_proof_api_memory_brain_proof_get",
6947
+ "parameters": [
6948
+ {
6949
+ "in": "query",
6950
+ "name": "q",
6951
+ "required": false,
6952
+ "schema": {
6953
+ "default": "",
6954
+ "title": "Q",
6955
+ "type": "string"
6956
+ }
6957
+ },
6958
+ {
6959
+ "in": "query",
6960
+ "name": "limit",
6961
+ "required": false,
6962
+ "schema": {
6963
+ "default": 3,
6964
+ "title": "Limit",
6965
+ "type": "integer"
6966
+ }
6967
+ }
6968
+ ],
6969
+ "responses": {
6970
+ "200": {
6971
+ "content": {
6972
+ "application/json": {
6973
+ "schema": {}
6974
+ }
6975
+ },
6976
+ "description": "Successful Response"
6977
+ },
6978
+ "422": {
6979
+ "content": {
6980
+ "application/json": {
6981
+ "schema": {
6982
+ "$ref": "#/components/schemas/HTTPValidationError"
6983
+ }
6984
+ }
6985
+ },
6986
+ "description": "Validation Error"
6987
+ }
6988
+ },
6989
+ "summary": "Brain Proof"
6990
+ }
6991
+ },
6992
+ "/api/memory/brain-quality": {
6993
+ "get": {
6994
+ "operationId": "brain_quality_summary_api_memory_brain_quality_get",
6995
+ "responses": {
6996
+ "200": {
6997
+ "content": {
6998
+ "application/json": {
6999
+ "schema": {}
7000
+ }
7001
+ },
7002
+ "description": "Successful Response"
7003
+ }
7004
+ },
7005
+ "summary": "Brain Quality Summary"
7006
+ }
7007
+ },
6944
7008
  "/api/memory/clear": {
6945
7009
  "post": {
6946
7010
  "operationId": "memory_clear_api_memory_clear_post",
@@ -446,6 +446,7 @@ export const latticeApi = {
446
446
  browserReadUrl: (url: string) => post("/api/browser/read-url", { url }, {}),
447
447
  memoryManager: () => get("/api/memory/manager", { sources: [], tiers: [], usage: {} }),
448
448
  memoryBrainQuality: () => get("/api/memory/brain-quality", {}),
449
+ memoryBrainProof: (query = "", limit = 3) => get("/api/memory/brain-proof", { proofs: {}, recall: { items: [] }, model_continuity: {}, claims: {} }, { q: query, limit }),
449
450
  memoryRecall: (query: string, limit = 20) => post("/api/memory/recall", { query, limit }, { matches: [] }),
450
451
  memoryCompact: () => post("/api/memory/compact", {}, {}),
451
452
  memoryRebuild: () => post("/api/memory/rebuild", { target: "vector" }, {}),
@@ -1550,6 +1550,40 @@ export interface paths {
1550
1550
  patch?: never;
1551
1551
  trace?: never;
1552
1552
  };
1553
+ "/api/memory/brain-proof": {
1554
+ parameters: {
1555
+ query?: never;
1556
+ header?: never;
1557
+ path?: never;
1558
+ cookie?: never;
1559
+ };
1560
+ /** Brain Proof */
1561
+ get: operations["brain_proof_api_memory_brain_proof_get"];
1562
+ put?: never;
1563
+ post?: never;
1564
+ delete?: never;
1565
+ options?: never;
1566
+ head?: never;
1567
+ patch?: never;
1568
+ trace?: never;
1569
+ };
1570
+ "/api/memory/brain-quality": {
1571
+ parameters: {
1572
+ query?: never;
1573
+ header?: never;
1574
+ path?: never;
1575
+ cookie?: never;
1576
+ };
1577
+ /** Brain Quality Summary */
1578
+ get: operations["brain_quality_summary_api_memory_brain_quality_get"];
1579
+ put?: never;
1580
+ post?: never;
1581
+ delete?: never;
1582
+ options?: never;
1583
+ head?: never;
1584
+ patch?: never;
1585
+ trace?: never;
1586
+ };
1553
1587
  "/api/memory/clear": {
1554
1588
  parameters: {
1555
1589
  query?: never;
@@ -10322,6 +10356,58 @@ export interface operations {
10322
10356
  };
10323
10357
  };
10324
10358
  };
10359
+ brain_proof_api_memory_brain_proof_get: {
10360
+ parameters: {
10361
+ query?: {
10362
+ q?: string;
10363
+ limit?: number;
10364
+ };
10365
+ header?: never;
10366
+ path?: never;
10367
+ cookie?: never;
10368
+ };
10369
+ requestBody?: never;
10370
+ responses: {
10371
+ /** @description Successful Response */
10372
+ 200: {
10373
+ headers: {
10374
+ [name: string]: unknown;
10375
+ };
10376
+ content: {
10377
+ "application/json": unknown;
10378
+ };
10379
+ };
10380
+ /** @description Validation Error */
10381
+ 422: {
10382
+ headers: {
10383
+ [name: string]: unknown;
10384
+ };
10385
+ content: {
10386
+ "application/json": components["schemas"]["HTTPValidationError"];
10387
+ };
10388
+ };
10389
+ };
10390
+ };
10391
+ brain_quality_summary_api_memory_brain_quality_get: {
10392
+ parameters: {
10393
+ query?: never;
10394
+ header?: never;
10395
+ path?: never;
10396
+ cookie?: never;
10397
+ };
10398
+ requestBody?: never;
10399
+ responses: {
10400
+ /** @description Successful Response */
10401
+ 200: {
10402
+ headers: {
10403
+ [name: string]: unknown;
10404
+ };
10405
+ content: {
10406
+ "application/json": unknown;
10407
+ };
10408
+ };
10409
+ };
10410
+ };
10325
10411
  memory_clear_api_memory_clear_post: {
10326
10412
  parameters: {
10327
10413
  query?: never;
@@ -1,5 +1,5 @@
1
1
  import * as React from "react";
2
- import { ImagePlus, Send } from "lucide-react";
2
+ import { FileUp, ImagePlus, Send } from "lucide-react";
3
3
  import { Button } from "@/components/ui/button";
4
4
  import { t, type Language } from "@/i18n";
5
5
 
@@ -8,16 +8,20 @@ export function BrainComposer({
8
8
  draft,
9
9
  streaming,
10
10
  imageData,
11
+ uploadingDocument,
11
12
  onDraftChange,
12
13
  onImageDataChange,
14
+ onUploadDocument,
13
15
  onSend,
14
16
  }: {
15
17
  language: Language;
16
18
  draft: string;
17
19
  streaming: boolean;
18
20
  imageData: string | null;
21
+ uploadingDocument: boolean;
19
22
  onDraftChange: (value: string) => void;
20
23
  onImageDataChange: (value: string | null) => void;
24
+ onUploadDocument: (file: File) => void;
21
25
  onSend: () => void;
22
26
  }) {
23
27
  return (
@@ -34,6 +38,21 @@ export function BrainComposer({
34
38
  placeholder={t(language, "brain.placeholder")}
35
39
  />
36
40
  <div className="brain-composer-actions">
41
+ <label className={`brain-document-input ${uploadingDocument ? "is-disabled" : ""}`}>
42
+ <FileUp className="h-3.5 w-3.5" />
43
+ <span>{uploadingDocument ? t(language, "brain.upload.uploading") : t(language, "brain.upload.ctaShort")}</span>
44
+ <input
45
+ type="file"
46
+ accept=".pdf,.docx,.xlsx,.pptx,.txt,.md,.csv,application/pdf,text/plain,text/markdown,text/csv"
47
+ className="sr-only"
48
+ disabled={uploadingDocument}
49
+ onChange={(event) => {
50
+ const file = event.target.files?.[0];
51
+ event.currentTarget.value = "";
52
+ if (file) onUploadDocument(file);
53
+ }}
54
+ />
55
+ </label>
37
56
  <label className="brain-image-input">
38
57
  <ImagePlus className="h-3.5 w-3.5" />
39
58
  <span>{t(language, "brain.image")}</span>
@@ -2,7 +2,7 @@ import * as React from "react";
2
2
  import { DatabaseZap, ShieldCheck } from "lucide-react";
3
3
  import { LanguageSwitcher } from "@/components/LanguageSwitcher";
4
4
  import { t, type Language } from "@/i18n";
5
- import type { BrainDepth, BrainReadiness, KnowledgeConcept, MemoryFragment, Message } from "./types";
5
+ import type { BrainDepth, BrainProof, BrainReadiness, KnowledgeConcept, MemoryFragment, Message } from "./types";
6
6
  import { BrainCarePanel } from "./BrainCarePanel";
7
7
  import { BrainComposer } from "./BrainComposer";
8
8
  import { BrainOverviewPanel } from "./BrainOverviewPanel";
@@ -21,9 +21,12 @@ export function BrainConversation({
21
21
  memories,
22
22
  concepts,
23
23
  readiness,
24
+ proof,
25
+ uploadingDocument,
24
26
  onOpenDepth,
25
27
  onDraftChange,
26
28
  onImageDataChange,
29
+ onUploadDocument,
27
30
  onSend,
28
31
  }: {
29
32
  language: Language;
@@ -39,9 +42,12 @@ export function BrainConversation({
39
42
  memories: MemoryFragment[];
40
43
  concepts: KnowledgeConcept[];
41
44
  readiness: BrainReadiness;
45
+ proof: BrainProof;
46
+ uploadingDocument: boolean;
42
47
  onOpenDepth: (depth: BrainDepth) => void;
43
48
  onDraftChange: (value: string) => void;
44
49
  onImageDataChange: (value: string | null) => void;
50
+ onUploadDocument: (file: File) => void;
45
51
  onSend: () => void;
46
52
  }) {
47
53
  return (
@@ -69,10 +75,17 @@ export function BrainConversation({
69
75
  memories={memories}
70
76
  concepts={concepts}
71
77
  readiness={readiness}
78
+ proof={proof}
72
79
  onOpenDepth={onOpenDepth}
73
80
  />
74
81
  {messages.length === 0 ? (
75
- <BrainEmptyState language={language} starterPrompts={starterPrompts} onDraftChange={onDraftChange} />
82
+ <BrainEmptyState
83
+ language={language}
84
+ starterPrompts={starterPrompts}
85
+ uploadingDocument={uploadingDocument}
86
+ onDraftChange={onDraftChange}
87
+ onUploadDocument={onUploadDocument}
88
+ />
76
89
  ) : (
77
90
  messages.map((message, index) => (
78
91
  <div key={`${message.role}-${index}`} className={`brain-message ${message.role}`}>
@@ -97,8 +110,10 @@ export function BrainConversation({
97
110
  draft={draft}
98
111
  streaming={streaming}
99
112
  imageData={imageData}
113
+ uploadingDocument={uploadingDocument}
100
114
  onDraftChange={onDraftChange}
101
115
  onImageDataChange={onImageDataChange}
116
+ onUploadDocument={onUploadDocument}
102
117
  onSend={onSend}
103
118
  />
104
119
  </section>
@@ -108,17 +123,37 @@ export function BrainConversation({
108
123
  function BrainEmptyState({
109
124
  language,
110
125
  starterPrompts,
126
+ uploadingDocument,
111
127
  onDraftChange,
128
+ onUploadDocument,
112
129
  }: {
113
130
  language: Language;
114
131
  starterPrompts: string[];
132
+ uploadingDocument: boolean;
115
133
  onDraftChange: (value: string) => void;
134
+ onUploadDocument: (file: File) => void;
116
135
  }) {
117
136
  return (
118
137
  <div className="mind-empty">
119
138
  <div className="mind-empty-kicker">{t(language, "brain.empty.kicker")}</div>
120
139
  <div className="mind-empty-title">{t(language, "brain.empty.title")}</div>
121
140
  <p>{t(language, "brain.empty.body")}</p>
141
+ <label className={`mind-empty-upload ${uploadingDocument ? "is-disabled" : ""}`}>
142
+ <DatabaseZap className="h-3.5 w-3.5" />
143
+ <span>{uploadingDocument ? t(language, "brain.upload.uploading") : t(language, "brain.upload.cta")}</span>
144
+ <input
145
+ type="file"
146
+ accept=".pdf,.docx,.xlsx,.pptx,.txt,.md,.csv,application/pdf,text/plain,text/markdown,text/csv"
147
+ className="sr-only"
148
+ disabled={uploadingDocument}
149
+ onChange={(event) => {
150
+ const file = event.target.files?.[0];
151
+ event.currentTarget.value = "";
152
+ if (file) onUploadDocument(file);
153
+ }}
154
+ />
155
+ </label>
156
+ <small className="mind-empty-upload-hint">{t(language, "brain.upload.hint")}</small>
122
157
  <div className="mind-empty-prompts" aria-label={t(language, "brain.aria.starterPrompts")}>
123
158
  {starterPrompts.map((prompt) => (
124
159
  <button key={prompt} type="button" onClick={() => onDraftChange(prompt)}>
@@ -5,7 +5,7 @@ import { type BrainState, LivingBrain, triggerBrainRecall } from "@/components/L
5
5
  import { useAppStore } from "@/store/appStore";
6
6
  import { t } from "@/i18n";
7
7
  import { BrainConversation } from "./BrainConversation";
8
- import { buildBrainReadiness, buildMemoryFragments, currentModelName, parseKnowledgeGraph } from "./brainData";
8
+ import { buildBrainProof, buildBrainReadiness, buildMemoryFragments, currentModelName, parseKnowledgeGraph } from "./brainData";
9
9
  import { DepthEmergence } from "./DepthEmergence";
10
10
  import { DEPTHS, type BrainDepth, type MemoryFragment, type Message } from "./types";
11
11
 
@@ -29,6 +29,8 @@ export function BrainHome({
29
29
  const [graphSearch, setGraphSearch] = React.useState("");
30
30
  const [selectedGraphId, setSelectedGraphId] = React.useState<string | null>(null);
31
31
  const [memoryFeedback, setMemoryFeedback] = React.useState<string | null>(null);
32
+ const [uploadingDocument, setUploadingDocument] = React.useState(false);
33
+ const [lastRecallQuery, setLastRecallQuery] = React.useState("");
32
34
  const streamRef = React.useRef<HTMLDivElement>(null);
33
35
  const recallTimerRef = React.useRef<number | null>(null);
34
36
 
@@ -36,6 +38,10 @@ export function BrainHome({
36
38
  const historyQ = useQuery({ queryKey: ["chatHistory"], queryFn: latticeApi.chatHistory });
37
39
  const graphQ = useQuery({ queryKey: ["graph"], queryFn: latticeApi.graph });
38
40
  const modelsQ = useQuery({ queryKey: ["models"], queryFn: latticeApi.models });
41
+ const brainProofQ = useQuery({
42
+ queryKey: ["memoryBrainProof", lastRecallQuery],
43
+ queryFn: () => latticeApi.memoryBrainProof(lastRecallQuery, 3),
44
+ });
39
45
 
40
46
  const memoryFragments = React.useMemo(
41
47
  () => buildMemoryFragments(memoriesQ.data?.data, historyQ.data?.data),
@@ -55,6 +61,10 @@ export function BrainHome({
55
61
  [graphModel.edges],
56
62
  );
57
63
  const modelName = React.useMemo(() => currentModelName(modelsQ.data?.data), [modelsQ.data]);
64
+ const brainProof = React.useMemo(
65
+ () => buildBrainProof(brainProofQ.data?.data, modelName),
66
+ [brainProofQ.data, modelName],
67
+ );
58
68
  const currentDepth = DEPTHS[explorationDepth - 1];
59
69
  const starterPrompts = React.useMemo(
60
70
  () => [
@@ -123,12 +133,39 @@ export function BrainHome({
123
133
  });
124
134
  } else {
125
135
  setMemoryFeedback(t(language, "brain.saved", { topics: knowledgeConcepts.length, memories: memoryFragments.length }));
136
+ setLastRecallQuery(text);
126
137
  }
127
138
  } finally {
128
139
  setStreaming(false);
129
140
  void qc.invalidateQueries({ queryKey: ["chatHistory"] });
130
141
  void qc.invalidateQueries({ queryKey: ["memoryManager"] });
131
142
  void qc.invalidateQueries({ queryKey: ["graph"] });
143
+ void qc.invalidateQueries({ queryKey: ["memoryBrainProof"] });
144
+ }
145
+ }
146
+
147
+ async function uploadDocument(file: File) {
148
+ if (uploadingDocument) return;
149
+
150
+ setUploadingDocument(true);
151
+ setMemoryFeedback(t(language, "brain.upload.pending", { name: file.name }));
152
+ onBrainChange("recalling", 0.86);
153
+
154
+ try {
155
+ const result = await latticeApi.uploadDocument(file);
156
+ if (result.error) {
157
+ setMemoryFeedback(t(language, "brain.upload.failed", { reason: result.error }));
158
+ return;
159
+ }
160
+
161
+ setMemoryFeedback(t(language, "brain.upload.saved", { name: file.name }));
162
+ setLastRecallQuery(file.name);
163
+ triggerBrainRecall();
164
+ void qc.invalidateQueries({ queryKey: ["memoryManager"] });
165
+ void qc.invalidateQueries({ queryKey: ["graph"] });
166
+ void qc.invalidateQueries({ queryKey: ["memoryBrainProof"] });
167
+ } finally {
168
+ setUploadingDocument(false);
132
169
  }
133
170
  }
134
171
 
@@ -242,9 +279,12 @@ export function BrainHome({
242
279
  memories={memoryFragments}
243
280
  concepts={knowledgeConcepts}
244
281
  readiness={brainReadiness}
282
+ proof={brainProof}
283
+ uploadingDocument={uploadingDocument}
245
284
  onOpenDepth={jumpToDepth}
246
285
  onDraftChange={setDraft}
247
286
  onImageDataChange={setImageData}
287
+ onUploadDocument={(file) => void uploadDocument(file)}
248
288
  onSend={() => void send()}
249
289
  />
250
290
  </main>
@@ -1,23 +1,31 @@
1
1
  import * as React from "react";
2
+ import { BrainCircuit, DatabaseZap, Repeat2 } from "lucide-react";
2
3
  import { t } from "@/i18n";
3
4
  import { useAppStore } from "@/store/appStore";
4
- import type { BrainDepth, BrainReadiness, KnowledgeConcept, MemoryFragment } from "./types";
5
+ import type { BrainDepth, BrainProof, BrainReadiness, KnowledgeConcept, MemoryFragment } from "./types";
5
6
 
6
7
  export function BrainOverviewPanel({
7
8
  memories,
8
9
  concepts,
9
10
  readiness,
11
+ proof,
10
12
  onOpenDepth,
11
13
  }: {
12
14
  memories: MemoryFragment[];
13
15
  concepts: KnowledgeConcept[];
14
16
  readiness: BrainReadiness;
17
+ proof: BrainProof;
15
18
  onOpenDepth: (depth: BrainDepth) => void;
16
19
  }) {
17
20
  const language = useAppStore((state) => state.language);
18
21
  const recent = memories.slice(0, 3);
19
22
  const older = memories.slice(3, 6);
20
23
  const topics = concepts.slice(0, 4);
24
+ const hasDurableProof = proof.proofs.hasDurableEvidence || proof.modelContinuity.proven;
25
+ const hasRecallProof = proof.recall.items.length > 0;
26
+ const modelProofValue = proof.modelContinuity.proven && proof.claims.keepsContextAcrossModels
27
+ ? proof.modelContinuity.activeModel || t(language, "brain.proof.noModel")
28
+ : t(language, "brain.proof.modelPending");
21
29
 
22
30
  return (
23
31
  <section className="brain-overview-panel" aria-label={t(language, "brain.aria.overview")}>
@@ -60,10 +68,63 @@ export function BrainOverviewPanel({
60
68
  {t(language, readiness.actionKey)}
61
69
  </button>
62
70
  </div>
71
+ <div className="brain-proof-strip" aria-label={t(language, "brain.proof.aria")}>
72
+ <BrainProofPoint
73
+ icon={<DatabaseZap className="h-4 w-4" />}
74
+ label={t(language, "brain.proof.context")}
75
+ value={hasDurableProof
76
+ ? t(language, "brain.proof.contextValue", { count: proof.proofs.durableItems })
77
+ : t(language, "brain.proof.contextEmpty")}
78
+ />
79
+ <BrainProofPoint
80
+ icon={<Repeat2 className="h-4 w-4" />}
81
+ label={t(language, "brain.proof.model")}
82
+ value={modelProofValue}
83
+ />
84
+ <BrainProofPoint
85
+ icon={<BrainCircuit className="h-4 w-4" />}
86
+ label={t(language, "brain.proof.store")}
87
+ value={t(language, "brain.proof.storeValue", {
88
+ topics: proof.proofs.graphConcepts,
89
+ vectors: proof.proofs.vectorItems,
90
+ })}
91
+ />
92
+ </div>
93
+ {hasRecallProof ? (
94
+ <button type="button" className="brain-recall-proof" onClick={() => onOpenDepth(2)}>
95
+ <span>{t(language, "brain.proof.recall")}</span>
96
+ <strong>{proof.recall.items[0].title}</strong>
97
+ <small>{proof.recall.items[0].snippet || proof.recall.query}</small>
98
+ </button>
99
+ ) : (
100
+ <div className="brain-recall-proof is-empty" role="status">
101
+ <span>{t(language, "brain.proof.recallEmpty.kicker")}</span>
102
+ <strong>{t(language, hasDurableProof ? "brain.proof.recallPending" : "brain.proof.recallEmpty.title")}</strong>
103
+ <small>{t(language, hasDurableProof ? "brain.proof.recallPending.detail" : "brain.proof.recallEmpty.detail")}</small>
104
+ </div>
105
+ )}
63
106
  </section>
64
107
  );
65
108
  }
66
109
 
110
+ function BrainProofPoint({
111
+ icon,
112
+ label,
113
+ value,
114
+ }: {
115
+ icon: React.ReactNode;
116
+ label: string;
117
+ value: string;
118
+ }) {
119
+ return (
120
+ <div className="brain-proof-point">
121
+ {icon}
122
+ <span>{label}</span>
123
+ <strong>{value}</strong>
124
+ </div>
125
+ );
126
+ }
127
+
67
128
  function BrainOverviewColumn({
68
129
  title,
69
130
  empty,