ltcai 6.4.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 (40) hide show
  1. package/README.md +31 -28
  2. package/docs/CHANGELOG.md +62 -0
  3. package/frontend/openapi.json +65 -1
  4. package/frontend/src/api/client.ts +2 -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 +48 -4
  8. package/frontend/src/features/brain/BrainHome.tsx +61 -1
  9. package/frontend/src/features/brain/BrainMemoryLayer.tsx +8 -1
  10. package/frontend/src/features/brain/BrainOverviewPanel.tsx +76 -1
  11. package/frontend/src/features/brain/brainData.ts +125 -1
  12. package/frontend/src/features/brain/types.ts +52 -0
  13. package/frontend/src/i18n.ts +66 -0
  14. package/frontend/src/styles.css +261 -0
  15. package/lattice_brain/__init__.py +1 -1
  16. package/lattice_brain/graph/ingest.py +41 -6
  17. package/lattice_brain/ingestion.py +1 -0
  18. package/lattice_brain/runtime/multi_agent.py +1 -1
  19. package/latticeai/__init__.py +1 -1
  20. package/latticeai/api/knowledge_graph.py +11 -0
  21. package/latticeai/api/memory.py +20 -0
  22. package/latticeai/app_factory.py +2 -0
  23. package/latticeai/core/marketplace.py +1 -1
  24. package/latticeai/core/workspace_os.py +1 -1
  25. package/latticeai/runtime/chat_wiring.py +2 -0
  26. package/latticeai/runtime/router_registration.py +3 -0
  27. package/latticeai/services/memory_service.py +188 -2
  28. package/latticeai/services/router_context.py +1 -0
  29. package/latticeai/services/upload_service.py +12 -0
  30. package/package.json +1 -1
  31. package/src-tauri/Cargo.lock +1 -1
  32. package/src-tauri/Cargo.toml +1 -1
  33. package/src-tauri/tauri.conf.json +1 -1
  34. package/static/app/asset-manifest.json +5 -5
  35. package/static/app/assets/{index-Div5vMlq.css → index-C8toxDpv.css} +1 -1
  36. package/static/app/assets/index-CbvcAQ6B.js +16 -0
  37. package/static/app/assets/index-CbvcAQ6B.js.map +1 -0
  38. package/static/app/index.html +2 -2
  39. package/static/app/assets/index-t1jx1BR9.js +0 -16
  40. package/static/app/assets/index-t1jx1BR9.js.map +0 -1
package/README.md CHANGED
@@ -69,42 +69,42 @@ You need Lattice AI when:
69
69
  Choose the owner of the Brain. The profile is not a SaaS account by default; it
70
70
  is the local identity for the knowledge you keep.
71
71
 
72
- ![Login](output/release/v6.4.0/screenshots/01-login.png)
72
+ ![Login](output/release/v6.5.0/screenshots/01-login.png)
73
73
 
74
74
  ### 2. Environment Analysis
75
75
 
76
76
  See what kind of local AI experience this computer can support before choosing a
77
77
  model.
78
78
 
79
- ![Environment Analysis](output/release/v6.4.0/screenshots/02-environment-analysis.png)
79
+ ![Environment Analysis](output/release/v6.5.0/screenshots/02-environment-analysis.png)
80
80
 
81
81
  ### 3. Recommended Models
82
82
 
83
83
  Start with a short list: safest recommendation, faster model, stronger model.
84
84
  Advanced details stay available without overwhelming first-time users.
85
85
 
86
- ![Recommended Models](output/release/v6.4.0/screenshots/03-recommended-models.png)
86
+ ![Recommended Models](output/release/v6.5.0/screenshots/03-recommended-models.png)
87
87
 
88
88
  ### 4. Install And Load
89
89
 
90
90
  Download and load only after consent. Lattice explains model size, local
91
91
  execution, and network use before work starts.
92
92
 
93
- ![Install and Load](output/release/v6.4.0/screenshots/04-install-load-progress.png)
93
+ ![Install and Load](output/release/v6.5.0/screenshots/04-install-load-progress.png)
94
94
 
95
95
  ### 5. Brain Chat
96
96
 
97
97
  Talk normally. Useful decisions and context become memory, then appear later as
98
98
  topics, relationships, and graph structure.
99
99
 
100
- ![Brain Chat Home](output/release/v6.4.0/screenshots/05-brain-chat-home.png)
100
+ ![Brain Chat Home](output/release/v6.5.0/screenshots/05-brain-chat-home.png)
101
101
 
102
102
  ### 6. Review Center
103
103
 
104
104
  Automation results are staged for review before they become durable decisions.
105
105
  Snooze, unsnooze, run now, approve, and dismiss actions stay explicit.
106
106
 
107
- ![Review Center](output/release/v6.4.0/screenshots/13-review-center.png)
107
+ ![Review Center](output/release/v6.5.0/screenshots/13-review-center.png)
108
108
 
109
109
  ## Brain Depths
110
110
 
@@ -120,10 +120,10 @@ The user travels inward from everyday memory to deeper structure:
120
120
 
121
121
  Walkthrough:
122
122
 
123
- ![v6.4.0 Living Brain walkthrough](output/release/v6.4.0/gifs/v6.4.0-living-brain-walkthrough.gif)
123
+ ![v6.5.0 Living Brain walkthrough](output/release/v6.5.0/gifs/v6.5.0-living-brain-walkthrough.gif)
124
124
 
125
125
  Screenshot index and capture notes:
126
- [output/release/v6.4.0/SCREENSHOT_INDEX.md](output/release/v6.4.0/SCREENSHOT_INDEX.md)
126
+ [output/release/v6.5.0/SCREENSHOT_INDEX.md](output/release/v6.5.0/SCREENSHOT_INDEX.md)
127
127
 
128
128
  ## Install
129
129
 
@@ -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.4.0 Digital Brain Quality Hardening**:
204
-
205
- - `lattice_brain.quality` adds a non-destructive Brain quality layer for
206
- embedding fallback labelling, drift/re-index planning, BM25 lexical scoring,
207
- hybrid fusion, reranker fallback contracts, memory candidate quality,
208
- graph-edge confidence/evidence scoring, structured context guardrails, and
209
- retrieval benchmark metrics.
210
- - Graph/search API reads now carry workspace scope through graph, node,
211
- neighborhood, relationship, keyword, vector, graph, and hybrid search paths.
212
- - Memory Manager prune, compact, and clear operations are scoped to the
213
- caller's workspace and owner boundary; unscoped graph clearing from Memory
214
- Manager is blocked until a workspace-safe graph delete path exists.
215
- - Digital Brain quality documentation now records the 6.4.0 baseline, risk
216
- register, validation checklist, and intentionally deferred work.
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.4.0 release must use exact filenames:
222
+ Expected artifacts for 6.6.0 release must use exact filenames:
222
223
 
223
- - `dist/ltcai-6.4.0-py3-none-any.whl`
224
- - `dist/ltcai-6.4.0.tar.gz`
225
- - `ltcai-6.4.0.tgz`
226
- - `dist/ltcai-6.4.0.vsix`
227
- - `src-tauri/target/release/bundle/dmg/Lattice AI_6.4.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,8 @@ 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 |
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 |
246
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 |
247
250
  | 6.3.1 | Access Runtime / i18n Follow-up: app-factory access-control extraction, focused access runtime tests, and Capture/Review Center i18n coverage |
248
251
  | 6.3.0 | Product Hardening Completion: Brain archive/provenance/ingestion UX polish, Review Center Run Now contract hardening, local model runtime status, app-factory review wiring, shim smoke, i18n guard, and exact release artifacts |
package/docs/CHANGELOG.md CHANGED
@@ -3,6 +3,68 @@
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
+
40
+ ## [6.5.0] - 2026-06-18
41
+
42
+ > Brain Experience Readiness. Uses the 6.4.0 quality baseline to improve what
43
+ > normal users can understand: how alive the Brain is, where they are in the
44
+ > memory-to-graph journey, and what changed after a useful exchange is saved.
45
+
46
+ ### Added
47
+ - Brain Home readiness strip that classifies the current Brain as waiting for
48
+ its first memory, forming topics, or ready for map exploration.
49
+ - Backend-owned Brain readiness summary from `MemoryService`, exposed through
50
+ `/api/memory/manager` and `/api/memory/brain-quality`, so the UI no longer
51
+ has to infer Brain growth from local fragments.
52
+ - Persistent depth progress rail across the Living Brain surface so the
53
+ memory -> topics -> relationships -> graph journey is visible and reversible.
54
+ - Source-aware memory-save detail under chat feedback, making the recall loop
55
+ explicit after a conversation enriches the Brain.
56
+ - Visual regression assertions for Brain readiness, depth progress, and
57
+ source-aware memory-save feedback.
58
+
59
+ ### Changed
60
+ - Brain overview empty states now guide first-memory use without exposing graph
61
+ internals or administrator language.
62
+ - Brain readiness now uses the same memory, graph, relationship, and source
63
+ health signals that the Memory Manager reports, keeping product UX aligned
64
+ with backend Brain state.
65
+ - Package/runtime/static metadata is synchronized to 6.5.0; package publish and
66
+ deployment remain owner-run only.
67
+
6
68
  ## [6.4.0] - 2026-06-17
7
69
 
8
70
  > Digital Brain Quality Hardening. Tightens workspace-scoped Brain retrieval
@@ -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",
@@ -445,6 +445,8 @@ export const latticeApi = {
445
445
  },
446
446
  browserReadUrl: (url: string) => post("/api/browser/read-url", { url }, {}),
447
447
  memoryManager: () => get("/api/memory/manager", { sources: [], tiers: [], usage: {} }),
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 }),
448
450
  memoryRecall: (query: string, limit = 20) => post("/api/memory/recall", { query, limit }, { matches: [] }),
449
451
  memoryCompact: () => post("/api/memory/compact", {}, {}),
450
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>
@@ -1,8 +1,8 @@
1
1
  import * as React from "react";
2
- import { ShieldCheck } from "lucide-react";
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, 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";
@@ -20,9 +20,13 @@ export function BrainConversation({
20
20
  streamRef,
21
21
  memories,
22
22
  concepts,
23
+ readiness,
24
+ proof,
25
+ uploadingDocument,
23
26
  onOpenDepth,
24
27
  onDraftChange,
25
28
  onImageDataChange,
29
+ onUploadDocument,
26
30
  onSend,
27
31
  }: {
28
32
  language: Language;
@@ -37,9 +41,13 @@ export function BrainConversation({
37
41
  streamRef: React.RefObject<HTMLDivElement | null>;
38
42
  memories: MemoryFragment[];
39
43
  concepts: KnowledgeConcept[];
44
+ readiness: BrainReadiness;
45
+ proof: BrainProof;
46
+ uploadingDocument: boolean;
40
47
  onOpenDepth: (depth: BrainDepth) => void;
41
48
  onDraftChange: (value: string) => void;
42
49
  onImageDataChange: (value: string | null) => void;
50
+ onUploadDocument: (file: File) => void;
43
51
  onSend: () => void;
44
52
  }) {
45
53
  return (
@@ -66,10 +74,18 @@ export function BrainConversation({
66
74
  <BrainOverviewPanel
67
75
  memories={memories}
68
76
  concepts={concepts}
77
+ readiness={readiness}
78
+ proof={proof}
69
79
  onOpenDepth={onOpenDepth}
70
80
  />
71
81
  {messages.length === 0 ? (
72
- <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
+ />
73
89
  ) : (
74
90
  messages.map((message, index) => (
75
91
  <div key={`${message.role}-${index}`} className={`brain-message ${message.role}`}>
@@ -79,7 +95,13 @@ export function BrainConversation({
79
95
  )}
80
96
  </div>
81
97
 
82
- {memoryFeedback ? <div className="brain-save-feedback" role="status">{memoryFeedback}</div> : null}
98
+ {memoryFeedback ? (
99
+ <div className="brain-save-feedback" role="status">
100
+ <DatabaseZap className="h-3.5 w-3.5" />
101
+ <span>{memoryFeedback}</span>
102
+ <small>{t(language, "brain.saved.detail")}</small>
103
+ </div>
104
+ ) : null}
83
105
 
84
106
  <BrainCarePanel language={language} />
85
107
 
@@ -88,8 +110,10 @@ export function BrainConversation({
88
110
  draft={draft}
89
111
  streaming={streaming}
90
112
  imageData={imageData}
113
+ uploadingDocument={uploadingDocument}
91
114
  onDraftChange={onDraftChange}
92
115
  onImageDataChange={onImageDataChange}
116
+ onUploadDocument={onUploadDocument}
93
117
  onSend={onSend}
94
118
  />
95
119
  </section>
@@ -99,17 +123,37 @@ export function BrainConversation({
99
123
  function BrainEmptyState({
100
124
  language,
101
125
  starterPrompts,
126
+ uploadingDocument,
102
127
  onDraftChange,
128
+ onUploadDocument,
103
129
  }: {
104
130
  language: Language;
105
131
  starterPrompts: string[];
132
+ uploadingDocument: boolean;
106
133
  onDraftChange: (value: string) => void;
134
+ onUploadDocument: (file: File) => void;
107
135
  }) {
108
136
  return (
109
137
  <div className="mind-empty">
110
138
  <div className="mind-empty-kicker">{t(language, "brain.empty.kicker")}</div>
111
139
  <div className="mind-empty-title">{t(language, "brain.empty.title")}</div>
112
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>
113
157
  <div className="mind-empty-prompts" aria-label={t(language, "brain.aria.starterPrompts")}>
114
158
  {starterPrompts.map((prompt) => (
115
159
  <button key={prompt} type="button" onClick={() => onDraftChange(prompt)}>