total-recall-brain 3.13.0 → 3.13.2

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 (152) hide show
  1. package/README.md +91 -108
  2. package/bin/antigravity.mjs +81 -52
  3. package/bin/total-recall.mjs +32 -57
  4. package/extension/background.js +1 -1
  5. package/extension/options/options.js +9 -3
  6. package/extension/sidepanel/sidepanel.js +0 -2
  7. package/frontend/index.html +4 -3
  8. package/frontend/package.json +1 -0
  9. package/frontend/src/App.tsx +109 -152
  10. package/frontend/src/api.ts +605 -23
  11. package/frontend/src/components/BrainSelector.tsx +211 -199
  12. package/frontend/src/components/DocumentEditorModal.tsx +57 -0
  13. package/frontend/src/components/DocumentTable.tsx +47 -0
  14. package/frontend/src/components/Graph3D.tsx +120 -71
  15. package/frontend/src/components/UsageChart.tsx +3 -3
  16. package/frontend/src/components/brand/BrandMark.tsx +95 -0
  17. package/frontend/src/index.css +286 -86
  18. package/frontend/src/pages/ApiKeysPage.tsx +1878 -215
  19. package/frontend/src/pages/ChatPage.tsx +103 -45
  20. package/frontend/src/pages/DesignDocsPage.tsx +5 -5
  21. package/frontend/src/pages/FilesPage.tsx +10 -6
  22. package/frontend/src/pages/GraphPage.tsx +5 -3
  23. package/frontend/src/pages/HelpPage.tsx +2 -2
  24. package/frontend/src/pages/InboxPage.tsx +82 -0
  25. package/frontend/src/pages/InstructionsPage.tsx +1 -1
  26. package/frontend/src/pages/IntegrationsPage.tsx +21 -10
  27. package/frontend/src/pages/LoginPage.tsx +95 -77
  28. package/frontend/src/pages/MemoryPage.tsx +31 -8
  29. package/frontend/src/pages/OnboardingPage.tsx +690 -0
  30. package/frontend/src/pages/OpenWikiPage.tsx +412 -323
  31. package/frontend/src/pages/SettingsPage.tsx +825 -922
  32. package/frontend/src/pages/SkillsPage.tsx +272 -147
  33. package/frontend/src/pages/UsagePage.tsx +23 -31
  34. package/frontend/src/pages/VaultPage.tsx +223 -0
  35. package/frontend/src/types.ts +13 -0
  36. package/package.json +4 -4
  37. package/scaffold/.agent/skills/total-recall/SKILL.md +43 -4
  38. package/scaffold/.agent/skills/total-recall/modules/README.md +10 -0
  39. package/scaffold/.agent/skills/total-recall/modules/agents/README.md +5 -0
  40. package/scaffold/.agent/skills/total-recall/{skills/tr-cli-agents → modules/agents}/agents.yml +8 -0
  41. package/scaffold/.agent/skills/total-recall/modules/skill-deploy/README.md +9 -0
  42. package/scaffold/.agent/skills/total-recall/openwiki/README.md +10 -0
  43. package/scaffold/.agent/skills/total-recall/openwiki/architecture.md +5 -0
  44. package/scaffold/.agent/skills/total-recall/openwiki/ide.md +10 -0
  45. package/scaffold/.agent/skills/total-recall/openwiki/memory.md +11 -0
  46. package/scaffold/.agent/skills/total-recall/openwiki/secrets.md +7 -0
  47. package/scaffold/.agent/skills/total-recall/openwiki/skills.md +7 -0
  48. package/src/cli/brain.mjs +63 -5
  49. package/src/cli/connect.mjs +11 -19
  50. package/src/cli/daemon.mjs +5 -4
  51. package/src/cli/deploy-ui.mjs +8 -3
  52. package/src/cli/deploy.mjs +84 -27
  53. package/src/cli/ingest/index.mjs +1 -1
  54. package/src/cli/init.mjs +63 -34
  55. package/src/cli/remember.mjs +3 -3
  56. package/src/cli/secret.mjs +812 -0
  57. package/src/cli/setup.mjs +34 -7
  58. package/src/cli/share.mjs +2 -2
  59. package/src/cli/skill.mjs +396 -35
  60. package/src/cli/sync.mjs +4 -4
  61. package/src/cli/task.mjs +287 -0
  62. package/src/core/clarity-rewriter.mjs +5 -5
  63. package/src/core/config.mjs +44 -2
  64. package/src/core/conflict-detector.mjs +5 -5
  65. package/src/core/crons.mjs +54 -0
  66. package/src/core/daemon-loop.mjs +87 -235
  67. package/src/core/dream.mjs +170 -10
  68. package/src/core/env-import.mjs +456 -0
  69. package/src/core/fact-seeker.mjs +14 -14
  70. package/src/core/import-rules.mjs +20 -7
  71. package/src/core/inference-engine.mjs +3 -3
  72. package/src/core/migrate.mjs +14 -8
  73. package/src/core/okf-adapter.mjs +2 -2
  74. package/src/core/operation-validator.mjs +39 -331
  75. package/src/core/optimizer.mjs +7 -0
  76. package/src/core/post-mortem.mjs +2 -2
  77. package/src/core/project-brain.mjs +349 -0
  78. package/src/core/provider-catalog.mjs +296 -0
  79. package/src/core/quick-capture.mjs +1 -1
  80. package/src/core/remote-vault-sync.mjs +194 -0
  81. package/src/core/research-queue.mjs +7 -11
  82. package/src/core/runtime.mjs +33 -9
  83. package/src/core/scheduler.mjs +59 -5
  84. package/src/core/schema.mjs +14 -0
  85. package/src/core/search.mjs +1 -1
  86. package/src/core/secrets-env-export.mjs +323 -0
  87. package/src/core/secrets-rotate.mjs +245 -0
  88. package/src/core/secrets-store.mjs +645 -0
  89. package/src/core/semantic-index.mjs +3 -3
  90. package/src/core/skills-registry.mjs +1062 -0
  91. package/src/core/source-adapters.mjs +4 -1
  92. package/src/core/ssss-host-extension.mjs +94 -0
  93. package/src/core/ssss-kernel-bridge.mjs +530 -0
  94. package/src/core/surface.mjs +14 -0
  95. package/src/core/task-envelope.mjs +298 -0
  96. package/src/core/task-executors.mjs +600 -0
  97. package/src/core/validated-write.mjs +124 -56
  98. package/src/core/vault.mjs +21 -34
  99. package/src/core/webauthn-store.mjs +272 -0
  100. package/src/server/api.mjs +26 -16
  101. package/src/server/auth.mjs +51 -0
  102. package/src/server/index.mjs +3 -3
  103. package/src/server/keys.mjs +30 -4
  104. package/src/server/rest.mjs +198 -25
  105. package/src/server/route-manifest.json +156 -0
  106. package/src/server/routes/_shared.mjs +40 -1
  107. package/src/server/routes/docs.mjs +260 -0
  108. package/src/server/routes/memory.mjs +145 -32
  109. package/src/server/routes/research.mjs +30 -0
  110. package/src/server/routes/secrets.mjs +470 -0
  111. package/src/server/routes/share.mjs +2 -2
  112. package/src/server/routes/skills.mjs +452 -54
  113. package/src/server/routes/sync.mjs +48 -0
  114. package/src/server/routes/webauthn.mjs +184 -0
  115. package/src/server/update-route-manifest.mjs +39 -0
  116. package/templates/onboarding-interview.md +32 -22
  117. package/templates/openwiki/README.md +10 -0
  118. package/templates/openwiki/architecture.md +5 -0
  119. package/templates/openwiki/ide.md +10 -0
  120. package/templates/openwiki/memory.md +11 -0
  121. package/templates/openwiki/secrets.md +7 -0
  122. package/templates/openwiki/skills.md +7 -0
  123. package/scaffold/.agent/skills/total-recall/skills/tr-cli-agents/SKILL.md +0 -121
  124. package/scaffold/.agent/skills/total-recall/skills/tr-cli-agents/references/antigravity.txt +0 -20
  125. package/scaffold/.agent/skills/total-recall/skills/tr-cli-agents/references/claude.txt +0 -58
  126. package/scaffold/.agent/skills/total-recall/skills/tr-cli-agents/references/codex.txt +0 -81
  127. package/scaffold/.agent/skills/total-recall/skills/tr-cli-agents/references/gemini.txt +0 -44
  128. package/scaffold/.agent/skills/total-recall/skills/tr-cli-agents/scripts/.gitkeep +0 -0
  129. package/scaffold/.agent/skills/total-recall/skills/tr-cli-agents/subagents/.gitkeep +0 -0
  130. package/scaffold/.agent/skills/total-recall/skills/tr-research/SKILL.md +0 -338
  131. package/scaffold/.agent/skills/total-recall/skills/tr-skill/SKILL.md +0 -282
  132. package/scaffold/.agent/skills/total-recall/skills/tr-skill/assets/skill-template/SKILL.md +0 -20
  133. package/scaffold/.agent/skills/total-recall/skills/tr-skill/evals/evals.json +0 -20
  134. package/scaffold/.agent/skills/total-recall/skills/tr-skill/hooks/on-skill-add.sh +0 -14
  135. package/scaffold/.agent/skills/total-recall/skills/tr-skill/hooks/on-skill-delete.sh +0 -14
  136. package/scaffold/.agent/skills/total-recall/skills/tr-skill/learnings/.gitkeep +0 -0
  137. package/scaffold/.agent/skills/total-recall/skills/tr-skill/references/.gitkeep +0 -0
  138. package/scaffold/.agent/skills/total-recall/skills/tr-skill/scripts/.gitkeep +0 -0
  139. package/scaffold/.agent/skills/total-recall/skills/tr-skill/scripts/create-skill.sh +0 -65
  140. package/scaffold/.agent/skills/total-recall/skills/tr-skill/scripts/enforce-skill-optimization.mjs +0 -135
  141. package/scaffold/.agent/skills/total-recall/skills/tr-skill/scripts/skill-watcher.mjs +0 -64
  142. package/scaffold/.agent/skills/total-recall/skills/tr-skill/subagents/audit-all-skills.md +0 -62
  143. package/scaffold/.agent/skills/total-recall/skills/tr-ssss/SKILL.md +0 -298
  144. package/scaffold/.agent/skills/total-recall/skills/tr-ssss/evals/evals.json +0 -17
  145. package/scaffold/.agent/skills/total-recall/skills/tr-ssss/references/CHANGELOG.md +0 -124
  146. package/scaffold/.agent/skills/total-recall/skills/tr-ssss/references/ssss-spec.md +0 -1325
  147. package/scaffold/.agent/skills/total-recall/skills/tr-ssss/scripts/validate-schema.mjs +0 -232
  148. package/scaffold/.agent/skills/total-recall/skills/tr-ssss/subagents/audit-ssss-node.md +0 -9
  149. /package/scaffold/.agent/skills/total-recall/{skills/tr-cli-agents/evals → modules/skill-deploy/scripts}/.gitkeep +0 -0
  150. /package/scaffold/.agent/skills/total-recall/{skills/tr-skill → modules/skill-deploy}/scripts/find-skills.mjs +0 -0
  151. /package/scaffold/.agent/skills/total-recall/{skills/tr-skill → modules/skill-deploy}/scripts/install-skill.mjs +0 -0
  152. /package/scaffold/.agent/skills/total-recall/{skills/tr-skill → modules/skill-deploy}/scripts/scan-skill.mjs +0 -0
@@ -1,5 +1,6 @@
1
1
  import { useState, useRef, useEffect } from "react"
2
2
  import { login, changePassword, getAuthStatus, setupPassword } from "../api"
3
+ import BrandMark from "../components/brand/BrandMark"
3
4
 
4
5
  interface Props {
5
6
  onAuthenticated: () => void
@@ -73,7 +74,7 @@ export default function LoginPage({ onAuthenticated }: Props) {
73
74
  if (res.ok) {
74
75
  if (res.requiresPasswordReset) {
75
76
  setNeedsReset(true)
76
- setPassword("") // Clear for security
77
+ setPassword("")
77
78
  } else {
78
79
  onAuthenticated()
79
80
  }
@@ -88,63 +89,66 @@ export default function LoginPage({ onAuthenticated }: Props) {
88
89
  }
89
90
  }
90
91
 
92
+ const disabled = loading || ((isFirstTime || needsReset) ? !newPassword : !password)
93
+
91
94
  return (
92
- <div style={{
93
- minHeight: '100vh',
94
- display: 'flex',
95
- alignItems: 'center',
96
- justifyContent: 'center',
97
- background: 'var(--bg-primary)',
98
- }}>
99
- <div style={{
100
- width: 360,
101
- background: 'var(--bg-secondary)',
102
- border: '1px solid var(--border)',
103
- borderRadius: 16,
104
- padding: '40px 36px',
105
- boxShadow: '0 8px 40px rgba(0,0,0,0.4)',
106
- display: 'flex',
107
- flexDirection: 'column',
108
- gap: 24,
109
- }}>
110
- {/* Brand */}
111
- <div style={{ textAlign: "center" }}>
112
- <div style={{
113
- width: 48,
114
- height: 48,
115
- borderRadius: 12,
116
- background: (needsReset || isFirstTime) ? "linear-gradient(135deg, #f59e0b, #d97706)" : "linear-gradient(135deg, var(--accent), #7c3aed)",
117
- margin: "0 auto 16px",
118
- display: "flex",
119
- alignItems: "center",
120
- justifyContent: "center",
121
- }}>
122
- {(needsReset || isFirstTime) ? (
123
- <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="white" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
95
+ <div className="login-shell">
96
+ <div className="login-card">
97
+ <div style={{ textAlign: "center", display: "flex", flexDirection: "column", alignItems: "center", gap: 16 }}>
98
+ {(needsReset || isFirstTime) ? (
99
+ <div
100
+ style={{
101
+ width: 56,
102
+ height: 56,
103
+ borderRadius: 16,
104
+ background: "linear-gradient(135deg, #f59e0b, #d97706)",
105
+ display: "flex",
106
+ alignItems: "center",
107
+ justifyContent: "center",
108
+ boxShadow: "0 12px 32px rgba(245, 158, 11, 0.35)",
109
+ }}
110
+ >
111
+ <svg width="26" height="26" viewBox="0 0 24 24" fill="none" stroke="white" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
124
112
  <rect x="3" y="11" width="18" height="11" rx="2" ry="2"/>
125
113
  <path d="M7 11V7a5 5 0 0 1 10 0v4"/>
126
114
  </svg>
127
- ) : (
128
- <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="white" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
129
- <path d="M12 2a10 10 0 1 0 10 10H12V2z"/>
130
- <path d="M12 2a10 10 0 0 1 10 10"/>
131
- <circle cx="12" cy="12" r="3"/>
132
- </svg>
133
- )}
115
+ </div>
116
+ ) : (
117
+ <div
118
+ style={{
119
+ width: "100%",
120
+ maxWidth: 280,
121
+ borderRadius: 16,
122
+ padding: "14px 18px",
123
+ background: "linear-gradient(180deg, #f8fafc 0%, #e8eef9 100%)",
124
+ boxShadow:
125
+ "0 1px 0 rgba(255,255,255,0.75) inset, 0 16px 40px rgba(15, 23, 42, 0.45), 0 0 0 1px rgba(148,163,184,0.25)",
126
+ }}
127
+ >
128
+ <BrandMark variant="lockup" height={56} alt="Total Recall" />
129
+ </div>
130
+ )}
131
+ <div>
132
+ <h1 style={{ fontSize: 22, fontWeight: 700, color: "var(--text-primary)", margin: 0, letterSpacing: "-0.03em" }}>
133
+ {needsReset ? "Action Required" : isFirstTime ? "Setup Admin Password" : "Welcome back"}
134
+ </h1>
135
+ <p style={{ fontSize: 13.5, color: "var(--text-secondary)", margin: "8px 0 0", lineHeight: 1.5 }}>
136
+ {needsReset
137
+ ? "You must change your temporary password to continue."
138
+ : isFirstTime
139
+ ? "Choose a strong password to secure your local memory dashboard."
140
+ : "Portable personal memory · any IDE"}
141
+ </p>
134
142
  </div>
135
- <h1 style={{ fontSize: 20, fontWeight: 700, color: "var(--text-primary)", margin: 0 }}>
136
- {needsReset ? "Action Required" : isFirstTime ? "Setup Admin Password" : "Total Recall"}
137
- </h1>
138
- <p style={{ fontSize: 13, color: "var(--text-tertiary)", margin: "4px 0 0" }}>
139
- {needsReset ? "You must change your temporary password to continue." : isFirstTime ? "Choose a strong password to secure your dashboard." : "Sovereign Brain v3.0"}
140
- </p>
141
143
  </div>
142
144
 
143
- {/* Form */}
144
145
  <form onSubmit={handleSubmit} style={{ display: "flex", flexDirection: "column", gap: 16 }}>
145
146
  <input type="text" name="username" autoComplete="username" style={{ display: "none" }} />
146
- <div style={{ display: "flex", flexDirection: "column", gap: 6 }}>
147
- <label htmlFor="login-password" style={{ fontSize: 12, fontWeight: 600, color: "var(--text-secondary)", textTransform: "uppercase", letterSpacing: "0.05em" }}>
147
+ <div style={{ display: "flex", flexDirection: "column", gap: 8 }}>
148
+ <label
149
+ htmlFor="login-password"
150
+ style={{ fontSize: 11, fontWeight: 600, color: "var(--text-tertiary)", textTransform: "uppercase", letterSpacing: "0.08em" }}
151
+ >
148
152
  {(needsReset || isFirstTime) ? "New Password" : "Password"}
149
153
  </label>
150
154
  <input
@@ -161,30 +165,43 @@ export default function LoginPage({ onAuthenticated }: Props) {
161
165
  autoComplete={(isFirstTime || needsReset) ? "new-password" : "current-password"}
162
166
  disabled={loading}
163
167
  style={{
164
- background: "var(--bg-primary)",
165
- border: `1px solid ${error ? "#ef4444" : "var(--border)"}`,
166
- borderRadius: 8,
167
- padding: "10px 14px",
168
+ background: "rgba(7, 11, 20, 0.65)",
169
+ border: `1px solid ${error ? "rgba(248,113,113,0.55)" : "var(--border)"}`,
170
+ borderRadius: 12,
171
+ padding: "12px 16px",
168
172
  color: "var(--text-primary)",
169
173
  fontSize: 14,
170
174
  outline: "none",
171
- transition: "border-color 0.15s",
175
+ transition: "border-color 0.15s, box-shadow 0.15s",
176
+ boxShadow: error ? "0 0 0 3px rgba(248,113,113,0.12)" : "inset 0 1px 2px rgba(0,0,0,0.25)",
177
+ }}
178
+ onFocus={e => {
179
+ if (!error) {
180
+ e.currentTarget.style.borderColor = "var(--border-accent)"
181
+ e.currentTarget.style.boxShadow = "0 0 0 3px var(--accent-glow)"
182
+ }
183
+ }}
184
+ onBlur={e => {
185
+ e.currentTarget.style.borderColor = error ? "rgba(248,113,113,0.55)" : "var(--border)"
186
+ e.currentTarget.style.boxShadow = error ? "0 0 0 3px rgba(248,113,113,0.12)" : "inset 0 1px 2px rgba(0,0,0,0.25)"
172
187
  }}
173
188
  />
174
189
  </div>
175
190
 
176
191
  {error && (
177
- <div style={{
178
- background: "rgba(239,68,68,0.1)",
179
- border: "1px solid rgba(239,68,68,0.3)",
180
- borderRadius: 8,
181
- padding: "10px 14px",
182
- fontSize: 13,
183
- color: "#f87171",
184
- display: "flex",
185
- alignItems: "center",
186
- gap: 8,
187
- }}>
192
+ <div
193
+ style={{
194
+ background: "rgba(248,113,113,0.1)",
195
+ border: "1px solid rgba(248,113,113,0.3)",
196
+ borderRadius: 12,
197
+ padding: "11px 14px",
198
+ fontSize: 13,
199
+ color: "#fca5a5",
200
+ display: "flex",
201
+ alignItems: "center",
202
+ gap: 8,
203
+ }}
204
+ >
188
205
  <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" style={{ flexShrink: 0 }}>
189
206
  <circle cx="12" cy="12" r="10"/><line x1="12" y1="8" x2="12" y2="12"/><line x1="12" y1="16" x2="12.01" y2="16"/>
190
207
  </svg>
@@ -195,20 +212,20 @@ export default function LoginPage({ onAuthenticated }: Props) {
195
212
  <button
196
213
  id="login-submit"
197
214
  type="submit"
198
- disabled={loading || ((isFirstTime || needsReset) ? !newPassword : !password)}
215
+ disabled={disabled}
216
+ className={disabled ? undefined : "btn btn-primary"}
199
217
  style={{
200
- background: (loading || ((isFirstTime || needsReset) ? !newPassword : !password)) ? "var(--bg-tertiary)" : "var(--accent)",
201
- color: (loading || ((isFirstTime || needsReset) ? !newPassword : !password)) ? "var(--text-tertiary)" : "white",
202
- border: "none",
203
- borderRadius: 8,
204
- padding: "11px 0",
218
+ width: "100%",
219
+ justifyContent: "center",
220
+ padding: "12px 0",
205
221
  fontSize: 14,
206
- fontWeight: 600,
207
- cursor: (loading || ((isFirstTime || needsReset) ? !newPassword : !password)) ? "not-allowed" : "pointer",
208
- transition: "background 0.15s, color 0.15s",
222
+ borderRadius: 12,
223
+ border: "none",
224
+ cursor: disabled ? "not-allowed" : "pointer",
225
+ background: disabled ? "var(--bg-tertiary)" : undefined,
226
+ color: disabled ? "var(--text-tertiary)" : undefined,
209
227
  display: "flex",
210
228
  alignItems: "center",
211
- justifyContent: "center",
212
229
  gap: 8,
213
230
  }}
214
231
  >
@@ -224,8 +241,9 @@ export default function LoginPage({ onAuthenticated }: Props) {
224
241
  </form>
225
242
 
226
243
  {(!needsReset && !isFirstTime) && (
227
- <p style={{ textAlign: "center", fontSize: 11, color: "var(--text-tertiary)", margin: 0 }}>
228
- Set password in <code style={{ color: "var(--accent)", fontSize: 11 }}>~/.agent/config/security.yml</code>
244
+ <p style={{ textAlign: "center", fontSize: 11, color: "var(--text-tertiary)", margin: 0, lineHeight: 1.5 }}>
245
+ Local-first vault · credentials in{" "}
246
+ <code style={{ color: "#93c5fd", fontSize: 11 }}>~/.agent/config/security.yml</code>
229
247
  </p>
230
248
  )}
231
249
  </div>
@@ -222,7 +222,7 @@ export default function MemoryPage({ activeBrainId }: { activeBrainId?: string }
222
222
  related: relatedArr,
223
223
  contradicts: contradictsArr,
224
224
  supersedes: supersedesArr
225
- })
225
+ }, activeBrainId)
226
226
  setSelected(updated)
227
227
  setIsEditing(false)
228
228
  fetchNodes()
@@ -260,7 +260,7 @@ export default function MemoryPage({ activeBrainId }: { activeBrainId?: string }
260
260
  related: relatedArr,
261
261
  contradicts: contradictsArr,
262
262
  supersedes: supersedesArr
263
- })
263
+ }, activeBrainId)
264
264
  setIsCreating(false)
265
265
  setSelected(created)
266
266
  setSearchParams({ slug: created.slug })
@@ -277,7 +277,7 @@ export default function MemoryPage({ activeBrainId }: { activeBrainId?: string }
277
277
  setLoading(true)
278
278
  setError("")
279
279
  try {
280
- await deleteMemory(slug)
280
+ await deleteMemory(slug, activeBrainId)
281
281
  setSelected(null)
282
282
  setSearchParams({})
283
283
  fetchNodes()
@@ -339,14 +339,34 @@ export default function MemoryPage({ activeBrainId }: { activeBrainId?: string }
339
339
  useEffect(() => {
340
340
  if (urlSlug) {
341
341
  const loadUrlSlug = async () => {
342
+ // Prefer list payload (already loaded for the active brain) so project-brain
343
+ // nodes render even before a scoped GET completes.
344
+ const fromList = nodes.find((n) => n.slug === urlSlug)
345
+ if (fromList && (fromList.content || fromList.body)) {
346
+ setSelected(fromList)
347
+ setError("")
348
+ return
349
+ }
342
350
  setLoading(true)
343
351
  setError("")
344
352
  try {
345
- const full = await readMemory(urlSlug)
346
- setSelected(full)
353
+ const full = await readMemory(urlSlug, activeBrainId)
354
+ if (full) {
355
+ setSelected(full)
356
+ } else if (fromList) {
357
+ setSelected(fromList)
358
+ } else {
359
+ setError(`Memory node not found: ${urlSlug}`)
360
+ setSelected(null)
361
+ }
347
362
  } catch (e) {
348
- setError(`Failed to load memory node '${urlSlug}': ${(e as Error).message}`)
349
- setSelected(null)
363
+ if (fromList) {
364
+ setSelected(fromList)
365
+ setError("")
366
+ } else {
367
+ setError(`Failed to load memory node '${urlSlug}': ${(e as Error).message}`)
368
+ setSelected(null)
369
+ }
350
370
  } finally {
351
371
  setLoading(false)
352
372
  }
@@ -356,7 +376,7 @@ export default function MemoryPage({ activeBrainId }: { activeBrainId?: string }
356
376
  // eslint-disable-next-line react-hooks/set-state-in-effect -- legitimate reset on url change
357
377
  setSelected(null)
358
378
  }
359
- }, [urlSlug])
379
+ }, [urlSlug, activeBrainId, nodes])
360
380
 
361
381
  // Wire SSE Live Agent Monitor Streaming (Port 9111)
362
382
  useEffect(() => {
@@ -390,6 +410,9 @@ export default function MemoryPage({ activeBrainId }: { activeBrainId?: string }
390
410
  }, [fetchNodes, fetchConflictsList])
391
411
 
392
412
  const handleSelect = (node: MemoryNode) => {
413
+ // Show immediately from list (includes body/content for the vault that listed it)
414
+ setSelected(node)
415
+ setError("")
393
416
  setSearchParams({ slug: node.slug })
394
417
  }
395
418