ltcai 6.4.0 → 6.5.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.
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,31 @@ 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.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.
217
217
  - The release remains local-first: no automatic web/email/calendar ingestion,
218
218
  no package publishing, no production deployment, and no external reranker or
219
219
  embedding API use without explicit opt-in.
220
220
 
221
- Expected artifacts for 6.4.0 release must use exact filenames:
221
+ Expected artifacts for 6.5.0 release must use exact filenames:
222
222
 
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`
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`
228
228
 
229
229
  Do not upload `dist/*`. Package registry publishing remains owner-run.
230
230
 
@@ -243,6 +243,7 @@ Do not upload `dist/*`. Package registry publishing remains owner-run.
243
243
 
244
244
  | Version | Theme |
245
245
  | --- | --- |
246
+ | 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
247
  | 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
248
  | 6.3.1 | Access Runtime / i18n Follow-up: app-factory access-control extraction, focused access runtime tests, and Capture/Review Center i18n coverage |
248
249
  | 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,34 @@
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.5.0] - 2026-06-18
7
+
8
+ > Brain Experience Readiness. Uses the 6.4.0 quality baseline to improve what
9
+ > normal users can understand: how alive the Brain is, where they are in the
10
+ > memory-to-graph journey, and what changed after a useful exchange is saved.
11
+
12
+ ### Added
13
+ - Brain Home readiness strip that classifies the current Brain as waiting for
14
+ its first memory, forming topics, or ready for map exploration.
15
+ - Backend-owned Brain readiness summary from `MemoryService`, exposed through
16
+ `/api/memory/manager` and `/api/memory/brain-quality`, so the UI no longer
17
+ has to infer Brain growth from local fragments.
18
+ - Persistent depth progress rail across the Living Brain surface so the
19
+ memory -> topics -> relationships -> graph journey is visible and reversible.
20
+ - Source-aware memory-save detail under chat feedback, making the recall loop
21
+ explicit after a conversation enriches the Brain.
22
+ - Visual regression assertions for Brain readiness, depth progress, and
23
+ source-aware memory-save feedback.
24
+
25
+ ### Changed
26
+ - Brain overview empty states now guide first-memory use without exposing graph
27
+ internals or administrator language.
28
+ - Brain readiness now uses the same memory, graph, relationship, and source
29
+ health signals that the Memory Manager reports, keeping product UX aligned
30
+ with backend Brain state.
31
+ - Package/runtime/static metadata is synchronized to 6.5.0; package publish and
32
+ deployment remain owner-run only.
33
+
6
34
  ## [6.4.0] - 2026-06-17
7
35
 
8
36
  > Digital Brain Quality Hardening. Tightens workspace-scoped Brain retrieval
@@ -445,6 +445,7 @@ 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", {}),
448
449
  memoryRecall: (query: string, limit = 20) => post("/api/memory/recall", { query, limit }, { matches: [] }),
449
450
  memoryCompact: () => post("/api/memory/compact", {}, {}),
450
451
  memoryRebuild: () => post("/api/memory/rebuild", { target: "vector" }, {}),
@@ -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, 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,6 +20,7 @@ export function BrainConversation({
20
20
  streamRef,
21
21
  memories,
22
22
  concepts,
23
+ readiness,
23
24
  onOpenDepth,
24
25
  onDraftChange,
25
26
  onImageDataChange,
@@ -37,6 +38,7 @@ export function BrainConversation({
37
38
  streamRef: React.RefObject<HTMLDivElement | null>;
38
39
  memories: MemoryFragment[];
39
40
  concepts: KnowledgeConcept[];
41
+ readiness: BrainReadiness;
40
42
  onOpenDepth: (depth: BrainDepth) => void;
41
43
  onDraftChange: (value: string) => void;
42
44
  onImageDataChange: (value: string | null) => void;
@@ -66,6 +68,7 @@ export function BrainConversation({
66
68
  <BrainOverviewPanel
67
69
  memories={memories}
68
70
  concepts={concepts}
71
+ readiness={readiness}
69
72
  onOpenDepth={onOpenDepth}
70
73
  />
71
74
  {messages.length === 0 ? (
@@ -79,7 +82,13 @@ export function BrainConversation({
79
82
  )}
80
83
  </div>
81
84
 
82
- {memoryFeedback ? <div className="brain-save-feedback" role="status">{memoryFeedback}</div> : null}
85
+ {memoryFeedback ? (
86
+ <div className="brain-save-feedback" role="status">
87
+ <DatabaseZap className="h-3.5 w-3.5" />
88
+ <span>{memoryFeedback}</span>
89
+ <small>{t(language, "brain.saved.detail")}</small>
90
+ </div>
91
+ ) : null}
83
92
 
84
93
  <BrainCarePanel language={language} />
85
94
 
@@ -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 { buildMemoryFragments, currentModelName, parseKnowledgeGraph } from "./brainData";
8
+ import { 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
 
@@ -46,6 +46,10 @@ export function BrainHome({
46
46
  () => graphModel.nodes.slice(0, 10),
47
47
  [graphModel.nodes],
48
48
  );
49
+ const brainReadiness = React.useMemo(
50
+ () => buildBrainReadiness(memoriesQ.data?.data, memoryFragments.length, knowledgeConcepts.length),
51
+ [knowledgeConcepts.length, memoriesQ.data, memoryFragments.length],
52
+ );
49
53
  const relationshipThreads = React.useMemo(
50
54
  () => graphModel.edges.slice(0, 10),
51
55
  [graphModel.edges],
@@ -186,6 +190,21 @@ export function BrainHome({
186
190
  <button type="button" className={explorationDepth === 5 ? "is-active" : ""} onClick={() => jumpToDepth(5)}>{t(language, "brain.view.graph")}</button>
187
191
  </div>
188
192
 
193
+ <div className="brain-depth-rail" aria-label={t(language, "brain.depthRail.aria")}>
194
+ {DEPTHS.map((depth) => (
195
+ <button
196
+ key={depth.level}
197
+ type="button"
198
+ className={depth.level <= explorationDepth ? "is-revealed" : ""}
199
+ aria-current={depth.level === explorationDepth ? "step" : undefined}
200
+ onClick={() => jumpToDepth(depth.level)}
201
+ >
202
+ <span>{depth.level}</span>
203
+ <strong>{t(language, `brain.depth.${depth.level}`)}</strong>
204
+ </button>
205
+ ))}
206
+ </div>
207
+
189
208
  <div className="brain-field-layer" aria-hidden={explorationDepth < 2}>
190
209
  <DepthEmergence
191
210
  depth={explorationDepth}
@@ -222,6 +241,7 @@ export function BrainHome({
222
241
  streamRef={streamRef}
223
242
  memories={memoryFragments}
224
243
  concepts={knowledgeConcepts}
244
+ readiness={brainReadiness}
225
245
  onOpenDepth={jumpToDepth}
226
246
  onDraftChange={setDraft}
227
247
  onImageDataChange={setImageData}
@@ -14,7 +14,14 @@ export function BrainMemoryLayer({
14
14
  }) {
15
15
  const language = useAppStore((state) => state.language);
16
16
  const visible = memories.slice(0, depth >= 3 ? 8 : 6);
17
- if (!visible.length) return <div className="memory-fragment is-empty">{t(language, "brain.memory.empty")}</div>;
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
+ }
18
25
 
19
26
  return (
20
27
  <>
@@ -1,15 +1,17 @@
1
1
  import * as React from "react";
2
2
  import { t } from "@/i18n";
3
3
  import { useAppStore } from "@/store/appStore";
4
- import type { BrainDepth, KnowledgeConcept, MemoryFragment } from "./types";
4
+ import type { BrainDepth, BrainReadiness, KnowledgeConcept, MemoryFragment } from "./types";
5
5
 
6
6
  export function BrainOverviewPanel({
7
7
  memories,
8
8
  concepts,
9
+ readiness,
9
10
  onOpenDepth,
10
11
  }: {
11
12
  memories: MemoryFragment[];
12
13
  concepts: KnowledgeConcept[];
14
+ readiness: BrainReadiness;
13
15
  onOpenDepth: (depth: BrainDepth) => void;
14
16
  }) {
15
17
  const language = useAppStore((state) => state.language);
@@ -46,6 +48,18 @@ export function BrainOverviewPanel({
46
48
  onOpen={() => onOpenDepth(3)}
47
49
  />
48
50
  </div>
51
+ <div className="brain-readiness-strip" data-state={readiness.state}>
52
+ <div>
53
+ <span>{t(language, "brain.readiness.kicker")}</span>
54
+ <strong>{t(language, readiness.titleKey)}</strong>
55
+ </div>
56
+ <div className="brain-readiness-meter" aria-label={t(language, "brain.readiness.aria")}>
57
+ <i style={{ width: `${readiness.score}%` }} />
58
+ </div>
59
+ <button type="button" onClick={() => onOpenDepth(readiness.depth)}>
60
+ {t(language, readiness.actionKey)}
61
+ </button>
62
+ </div>
49
63
  </section>
50
64
  );
51
65
  }
@@ -1,5 +1,5 @@
1
1
  import { asArray } from "@/lib/utils";
2
- import type { ApiRecord, KnowledgeConcept, KnowledgeGraphModel, MemoryFragment, RelationshipThread } from "./types";
2
+ import type { ApiRecord, BrainDepth, BrainReadiness, KnowledgeConcept, KnowledgeGraphModel, MemoryFragment, RelationshipThread } from "./types";
3
3
  import { clamp } from "./graphLayout";
4
4
 
5
5
  export function buildMemoryFragments(memoryData: unknown, historyData: unknown): MemoryFragment[] {
@@ -60,6 +60,32 @@ export function currentModelName(data: unknown) {
60
60
  return firstLoaded ? textValue(firstLoaded, ["name", "id", "model_id"], "local mind") : "local mind";
61
61
  }
62
62
 
63
+ export function buildBrainReadiness(memoryData: unknown, fallbackMemoryCount: number, fallbackConceptCount: number): BrainReadiness {
64
+ const memory = isRecord(memoryData) ? memoryData : {};
65
+ const backend = isRecord(memory.brain_readiness) ? memory.brain_readiness : {};
66
+ const backendState = textValue(backend, ["state"]);
67
+ const backendDepth = numberValue(backend, ["depth"]);
68
+ const backendScore = numberValue(backend, ["score"]);
69
+ if ((backendState === "quiet" || backendState === "forming" || backendState === "alive") && isBrainDepth(backendDepth)) {
70
+ const signals = isRecord(backend.signals) ? backend.signals : {};
71
+ return {
72
+ score: clamp(Math.round(backendScore || 0), 0, 100),
73
+ state: backendState,
74
+ depth: backendDepth,
75
+ titleKey: textValue(backend, ["title_key", "titleKey"], `brain.readiness.${backendState}`),
76
+ actionKey: textValue(backend, ["action_key", "actionKey"], readinessActionKey(backendState)),
77
+ source: "memory_service",
78
+ signals: {
79
+ memoryCount: numberValue(signals, ["memory_count", "memoryCount"]),
80
+ conceptCount: numberValue(signals, ["concept_count", "conceptCount"]),
81
+ relationshipCount: numberValue(signals, ["relationship_count", "relationshipCount"]),
82
+ healthySources: numberValue(signals, ["healthy_sources", "healthySources"]),
83
+ },
84
+ };
85
+ }
86
+ return fallbackBrainReadiness(fallbackMemoryCount, fallbackConceptCount);
87
+ }
88
+
63
89
  function uniqueById<T extends { id: string }>(items: T[]) {
64
90
  const seen = new Set<string>();
65
91
  return items.filter((item) => {
@@ -69,6 +95,51 @@ function uniqueById<T extends { id: string }>(items: T[]) {
69
95
  });
70
96
  }
71
97
 
98
+ function fallbackBrainReadiness(memoryCount: number, conceptCount: number): BrainReadiness {
99
+ const score = Math.min(100, Math.round(memoryCount * 12 + conceptCount * 10));
100
+ if (memoryCount < 1) {
101
+ return {
102
+ score: Math.max(12, score),
103
+ state: "quiet",
104
+ depth: 2,
105
+ titleKey: "brain.readiness.quiet",
106
+ actionKey: "brain.readiness.start",
107
+ source: "frontend_fallback",
108
+ signals: { memoryCount, conceptCount, relationshipCount: 0, healthySources: 0 },
109
+ };
110
+ }
111
+ if (conceptCount < 3) {
112
+ return {
113
+ score: Math.max(38, score),
114
+ state: "forming",
115
+ depth: 3,
116
+ titleKey: "brain.readiness.forming",
117
+ actionKey: "brain.readiness.grow",
118
+ source: "frontend_fallback",
119
+ signals: { memoryCount, conceptCount, relationshipCount: 0, healthySources: 0 },
120
+ };
121
+ }
122
+ return {
123
+ score: Math.max(72, score),
124
+ state: "alive",
125
+ depth: 5,
126
+ titleKey: "brain.readiness.alive",
127
+ actionKey: "brain.readiness.map",
128
+ source: "frontend_fallback",
129
+ signals: { memoryCount, conceptCount, relationshipCount: 0, healthySources: 0 },
130
+ };
131
+ }
132
+
133
+ function readinessActionKey(state: "quiet" | "forming" | "alive") {
134
+ if (state === "quiet") return "brain.readiness.start";
135
+ if (state === "forming") return "brain.readiness.grow";
136
+ return "brain.readiness.map";
137
+ }
138
+
139
+ function isBrainDepth(value: number): value is BrainDepth {
140
+ return value >= 1 && value <= 5 && Number.isInteger(value);
141
+ }
142
+
72
143
  export function isRecord(value: unknown): value is ApiRecord {
73
144
  return Boolean(value && typeof value === "object" && !Array.isArray(value));
74
145
  }
@@ -35,6 +35,21 @@ export type KnowledgeGraphModel = {
35
35
  edges: RelationshipThread[];
36
36
  };
37
37
 
38
+ export type BrainReadiness = {
39
+ score: number;
40
+ state: "quiet" | "forming" | "alive";
41
+ depth: BrainDepth;
42
+ titleKey: string;
43
+ actionKey: string;
44
+ source: "memory_service" | "frontend_fallback";
45
+ signals: {
46
+ memoryCount: number;
47
+ conceptCount: number;
48
+ relationshipCount: number;
49
+ healthySources: number;
50
+ };
51
+ };
52
+
38
53
  export const DEPTHS: Array<{ level: BrainDepth; labelKey: string; state: BrainState }> = [
39
54
  { level: 1, labelKey: "brain.depthLabel.1", state: "idle" },
40
55
  { level: 2, labelKey: "brain.depthLabel.2", state: "recalling" },
@@ -31,6 +31,7 @@ export const COPY: Record<Language, TextMap> = {
31
31
  "brain.aria.home": "Lattice Brain",
32
32
  "brain.aria.exploration": "Brain 탐색",
33
33
  "brain.aria.quickViews": "Brain 빠른 보기",
34
+ "brain.depthRail.aria": "Brain 깊이 진행 상태",
34
35
  "brain.aria.conversation": "대화",
35
36
  "brain.aria.ownership": "Brain 소유 보장",
36
37
  "brain.aria.starterPrompts": "시작 프롬프트",
@@ -57,6 +58,7 @@ export const COPY: Record<Language, TextMap> = {
57
58
  "brain.imageAttached": "이미지 첨부됨",
58
59
  "brain.send": "보내기",
59
60
  "brain.saved": "기억에 저장됨 · 연결된 주제 {topics}개 · 관련 기억 {memories}개",
61
+ "brain.saved.detail": "출처와 함께 나중에 다시 불러올 수 있습니다.",
60
62
  "brain.recalled": "기억을 다시 꺼냈습니다: {title}",
61
63
  "brain.overview.kicker": "Brain 한눈에 보기",
62
64
  "brain.overview.title": "기억과 주제를 바로 확인하세요.",
@@ -67,6 +69,15 @@ export const COPY: Record<Language, TextMap> = {
67
69
  "brain.overview.olderEmpty": "대화가 쌓이면 과거 기억이 보입니다.",
68
70
  "brain.overview.topics": "주요 주제",
69
71
  "brain.overview.topicsEmpty": "주제가 형성되는 중입니다.",
72
+ "brain.readiness.kicker": "Brain 준비도",
73
+ "brain.readiness.aria": "Brain 준비도 점수",
74
+ "brain.readiness.quiet": "첫 기억을 기다리고 있습니다.",
75
+ "brain.readiness.forming": "기억이 주제로 자라는 중입니다.",
76
+ "brain.readiness.alive": "기억과 관계가 연결되어 있습니다.",
77
+ "brain.readiness.start": "첫 기억 보기",
78
+ "brain.readiness.grow": "주제 키우기",
79
+ "brain.readiness.map": "지도 보기",
80
+ "brain.memory.empty.kicker": "첫 기억",
70
81
  "brain.memory.empty": "기억이 조용합니다.",
71
82
  "brain.knowledge.empty": "지식이 형성되는 중입니다.",
72
83
  "brain.graph.empty": "아직 맞는 지식이 없습니다.",
@@ -352,6 +363,7 @@ export const COPY: Record<Language, TextMap> = {
352
363
  "brain.aria.home": "Lattice Brain",
353
364
  "brain.aria.exploration": "Brain exploration",
354
365
  "brain.aria.quickViews": "Brain quick views",
366
+ "brain.depthRail.aria": "Brain depth progress",
355
367
  "brain.aria.conversation": "Conversation",
356
368
  "brain.aria.ownership": "Brain ownership guarantees",
357
369
  "brain.aria.starterPrompts": "Starter prompts",
@@ -378,6 +390,7 @@ export const COPY: Record<Language, TextMap> = {
378
390
  "brain.imageAttached": "Image attached",
379
391
  "brain.send": "Send",
380
392
  "brain.saved": "Saved to memory · {topics} linked topics · {memories} related memories",
393
+ "brain.saved.detail": "Available later with source-aware recall.",
381
394
  "brain.recalled": "Recalled this memory: {title}",
382
395
  "brain.overview.kicker": "Brain at a glance",
383
396
  "brain.overview.title": "See memories and topics immediately.",
@@ -388,6 +401,15 @@ export const COPY: Record<Language, TextMap> = {
388
401
  "brain.overview.olderEmpty": "Earlier memories appear as conversations accumulate.",
389
402
  "brain.overview.topics": "Main topics",
390
403
  "brain.overview.topicsEmpty": "Topics are still forming.",
404
+ "brain.readiness.kicker": "Brain readiness",
405
+ "brain.readiness.aria": "Brain readiness score",
406
+ "brain.readiness.quiet": "Waiting for the first memory.",
407
+ "brain.readiness.forming": "Memories are becoming topics.",
408
+ "brain.readiness.alive": "Memories and relationships are connected.",
409
+ "brain.readiness.start": "View first memory",
410
+ "brain.readiness.grow": "Grow topics",
411
+ "brain.readiness.map": "Open map",
412
+ "brain.memory.empty.kicker": "First memory",
391
413
  "brain.memory.empty": "Memory is quiet",
392
414
  "brain.knowledge.empty": "Knowledge is forming",
393
415
  "brain.graph.empty": "No matching knowledge yet",
@@ -1421,7 +1421,65 @@ body {
1421
1421
  line-height: 1.35;
1422
1422
  }
1423
1423
 
1424
+ .brain-readiness-strip {
1425
+ display: grid;
1426
+ grid-template-columns: minmax(0, 1fr) minmax(5.6rem, 0.6fr) auto;
1427
+ align-items: center;
1428
+ gap: 0.7rem;
1429
+ margin-top: 0.75rem;
1430
+ border: 1px solid hsl(var(--brain-core) / 0.28);
1431
+ border-radius: 8px;
1432
+ background: hsl(var(--brain-core) / 0.06);
1433
+ padding: 0.66rem;
1434
+ }
1435
+
1436
+ .brain-readiness-strip span {
1437
+ display: block;
1438
+ color: hsl(var(--fg-muted));
1439
+ font-size: 0.64rem;
1440
+ font-weight: 820;
1441
+ text-transform: uppercase;
1442
+ }
1443
+
1444
+ .brain-readiness-strip strong {
1445
+ display: block;
1446
+ margin-top: 0.12rem;
1447
+ font-size: 0.82rem;
1448
+ line-height: 1.2;
1449
+ }
1450
+
1451
+ .brain-readiness-strip button {
1452
+ min-height: 2rem;
1453
+ border: 1px solid hsl(var(--brain-core) / 0.34);
1454
+ border-radius: 999px;
1455
+ background: hsl(var(--surface) / 0.78);
1456
+ color: hsl(var(--fg));
1457
+ padding: 0 0.72rem;
1458
+ font-size: 0.76rem;
1459
+ font-weight: 760;
1460
+ white-space: nowrap;
1461
+ }
1462
+
1463
+ .brain-readiness-meter {
1464
+ height: 0.42rem;
1465
+ overflow: hidden;
1466
+ border-radius: 999px;
1467
+ background: hsl(var(--bg) / 0.55);
1468
+ }
1469
+
1470
+ .brain-readiness-meter i {
1471
+ display: block;
1472
+ height: 100%;
1473
+ border-radius: inherit;
1474
+ background: linear-gradient(90deg, hsl(var(--memory)), hsl(var(--knowledge)), hsl(var(--brain-core)));
1475
+ transition: width 260ms ease;
1476
+ }
1477
+
1424
1478
  .brain-save-feedback {
1479
+ display: grid;
1480
+ grid-template-columns: auto minmax(0, 1fr);
1481
+ align-items: center;
1482
+ gap: 0.22rem 0.48rem;
1425
1483
  border: 1px solid hsl(var(--memory) / 0.38);
1426
1484
  border-radius: 8px;
1427
1485
  background: hsl(var(--memory) / 0.08);
@@ -1431,6 +1489,13 @@ body {
1431
1489
  font-weight: 720;
1432
1490
  }
1433
1491
 
1492
+ .brain-save-feedback small {
1493
+ grid-column: 2;
1494
+ color: hsl(var(--fg-muted));
1495
+ font-size: 0.72rem;
1496
+ font-weight: 560;
1497
+ }
1498
+
1434
1499
  .brain-message {
1435
1500
  max-width: 78%;
1436
1501
  margin-bottom: 1.1rem;
@@ -1911,6 +1976,59 @@ body {
1911
1976
  outline: none;
1912
1977
  }
1913
1978
 
1979
+ .brain-depth-rail {
1980
+ position: absolute;
1981
+ left: 50%;
1982
+ top: 0.85rem;
1983
+ z-index: 6;
1984
+ display: flex;
1985
+ width: min(92vw, 35rem);
1986
+ transform: translateX(-50%);
1987
+ gap: 0.28rem;
1988
+ border: 1px solid hsl(var(--border) / 0.48);
1989
+ border-radius: 999px;
1990
+ background: hsl(var(--surface-glass));
1991
+ padding: 0.32rem;
1992
+ box-shadow: 0 12px 30px hsl(200 30% 3% / 0.18);
1993
+ backdrop-filter: blur(18px);
1994
+ }
1995
+
1996
+ .brain-depth-rail button {
1997
+ display: inline-grid;
1998
+ min-width: 0;
1999
+ flex: 1 1 0;
2000
+ align-items: center;
2001
+ gap: 0.1rem;
2002
+ border: 0;
2003
+ border-radius: 999px;
2004
+ background: transparent;
2005
+ color: hsl(var(--fg-muted));
2006
+ padding: 0.36rem 0.42rem;
2007
+ text-align: center;
2008
+ }
2009
+
2010
+ .brain-depth-rail span {
2011
+ font-size: 0.58rem;
2012
+ font-weight: 840;
2013
+ }
2014
+
2015
+ .brain-depth-rail strong {
2016
+ overflow: hidden;
2017
+ text-overflow: ellipsis;
2018
+ white-space: nowrap;
2019
+ font-size: 0.66rem;
2020
+ line-height: 1.05;
2021
+ }
2022
+
2023
+ .brain-depth-rail button.is-revealed {
2024
+ background: hsl(var(--brain-core) / 0.1);
2025
+ color: hsl(var(--fg));
2026
+ }
2027
+
2028
+ .brain-depth-rail button[aria-current="step"] {
2029
+ box-shadow: 0 0 0 1px hsl(var(--brain-core) / 0.38) inset;
2030
+ }
2031
+
1914
2032
  .memory-fragment,
1915
2033
  .concept-signal,
1916
2034
  .graph-node {
@@ -2833,6 +2951,16 @@ body {
2833
2951
  .brain-depth-actions button {
2834
2952
  flex: 0 0 auto;
2835
2953
  }
2954
+ .brain-depth-rail {
2955
+ top: 0.35rem;
2956
+ width: min(100vw - 1rem, 25rem);
2957
+ }
2958
+ .brain-depth-rail strong {
2959
+ display: none;
2960
+ }
2961
+ .brain-readiness-strip {
2962
+ grid-template-columns: 1fr;
2963
+ }
2836
2964
  .brain-overview-grid {
2837
2965
  grid-template-columns: 1fr;
2838
2966
  }
@@ -26,7 +26,7 @@ from .storage import (
26
26
  storage_from_env,
27
27
  )
28
28
 
29
- __version__ = "6.4.0"
29
+ __version__ = "6.5.0"
30
30
 
31
31
  __all__ = [
32
32
  "AgentRuntime",