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.
- package/README.md +91 -108
- package/bin/antigravity.mjs +81 -52
- package/bin/total-recall.mjs +32 -57
- package/extension/background.js +1 -1
- package/extension/options/options.js +9 -3
- package/extension/sidepanel/sidepanel.js +0 -2
- package/frontend/index.html +4 -3
- package/frontend/package.json +1 -0
- package/frontend/src/App.tsx +109 -152
- package/frontend/src/api.ts +605 -23
- package/frontend/src/components/BrainSelector.tsx +211 -199
- package/frontend/src/components/DocumentEditorModal.tsx +57 -0
- package/frontend/src/components/DocumentTable.tsx +47 -0
- package/frontend/src/components/Graph3D.tsx +120 -71
- package/frontend/src/components/UsageChart.tsx +3 -3
- package/frontend/src/components/brand/BrandMark.tsx +95 -0
- package/frontend/src/index.css +286 -86
- package/frontend/src/pages/ApiKeysPage.tsx +1878 -215
- package/frontend/src/pages/ChatPage.tsx +103 -45
- package/frontend/src/pages/DesignDocsPage.tsx +5 -5
- package/frontend/src/pages/FilesPage.tsx +10 -6
- package/frontend/src/pages/GraphPage.tsx +5 -3
- package/frontend/src/pages/HelpPage.tsx +2 -2
- package/frontend/src/pages/InboxPage.tsx +82 -0
- package/frontend/src/pages/InstructionsPage.tsx +1 -1
- package/frontend/src/pages/IntegrationsPage.tsx +21 -10
- package/frontend/src/pages/LoginPage.tsx +95 -77
- package/frontend/src/pages/MemoryPage.tsx +31 -8
- package/frontend/src/pages/OnboardingPage.tsx +690 -0
- package/frontend/src/pages/OpenWikiPage.tsx +412 -323
- package/frontend/src/pages/SettingsPage.tsx +825 -922
- package/frontend/src/pages/SkillsPage.tsx +272 -147
- package/frontend/src/pages/UsagePage.tsx +23 -31
- package/frontend/src/pages/VaultPage.tsx +223 -0
- package/frontend/src/types.ts +13 -0
- package/package.json +4 -4
- package/scaffold/.agent/skills/total-recall/SKILL.md +43 -4
- package/scaffold/.agent/skills/total-recall/modules/README.md +10 -0
- package/scaffold/.agent/skills/total-recall/modules/agents/README.md +5 -0
- package/scaffold/.agent/skills/total-recall/{skills/tr-cli-agents → modules/agents}/agents.yml +8 -0
- package/scaffold/.agent/skills/total-recall/modules/skill-deploy/README.md +9 -0
- package/scaffold/.agent/skills/total-recall/openwiki/README.md +10 -0
- package/scaffold/.agent/skills/total-recall/openwiki/architecture.md +5 -0
- package/scaffold/.agent/skills/total-recall/openwiki/ide.md +10 -0
- package/scaffold/.agent/skills/total-recall/openwiki/memory.md +11 -0
- package/scaffold/.agent/skills/total-recall/openwiki/secrets.md +7 -0
- package/scaffold/.agent/skills/total-recall/openwiki/skills.md +7 -0
- package/src/cli/brain.mjs +63 -5
- package/src/cli/connect.mjs +11 -19
- package/src/cli/daemon.mjs +5 -4
- package/src/cli/deploy-ui.mjs +8 -3
- package/src/cli/deploy.mjs +84 -27
- package/src/cli/ingest/index.mjs +1 -1
- package/src/cli/init.mjs +63 -34
- package/src/cli/remember.mjs +3 -3
- package/src/cli/secret.mjs +812 -0
- package/src/cli/setup.mjs +34 -7
- package/src/cli/share.mjs +2 -2
- package/src/cli/skill.mjs +396 -35
- package/src/cli/sync.mjs +4 -4
- package/src/cli/task.mjs +287 -0
- package/src/core/clarity-rewriter.mjs +5 -5
- package/src/core/config.mjs +44 -2
- package/src/core/conflict-detector.mjs +5 -5
- package/src/core/crons.mjs +54 -0
- package/src/core/daemon-loop.mjs +87 -235
- package/src/core/dream.mjs +170 -10
- package/src/core/env-import.mjs +456 -0
- package/src/core/fact-seeker.mjs +14 -14
- package/src/core/import-rules.mjs +20 -7
- package/src/core/inference-engine.mjs +3 -3
- package/src/core/migrate.mjs +14 -8
- package/src/core/okf-adapter.mjs +2 -2
- package/src/core/operation-validator.mjs +39 -331
- package/src/core/optimizer.mjs +7 -0
- package/src/core/post-mortem.mjs +2 -2
- package/src/core/project-brain.mjs +349 -0
- package/src/core/provider-catalog.mjs +296 -0
- package/src/core/quick-capture.mjs +1 -1
- package/src/core/remote-vault-sync.mjs +194 -0
- package/src/core/research-queue.mjs +7 -11
- package/src/core/runtime.mjs +33 -9
- package/src/core/scheduler.mjs +59 -5
- package/src/core/schema.mjs +14 -0
- package/src/core/search.mjs +1 -1
- package/src/core/secrets-env-export.mjs +323 -0
- package/src/core/secrets-rotate.mjs +245 -0
- package/src/core/secrets-store.mjs +645 -0
- package/src/core/semantic-index.mjs +3 -3
- package/src/core/skills-registry.mjs +1062 -0
- package/src/core/source-adapters.mjs +4 -1
- package/src/core/ssss-host-extension.mjs +94 -0
- package/src/core/ssss-kernel-bridge.mjs +530 -0
- package/src/core/surface.mjs +14 -0
- package/src/core/task-envelope.mjs +298 -0
- package/src/core/task-executors.mjs +600 -0
- package/src/core/validated-write.mjs +124 -56
- package/src/core/vault.mjs +21 -34
- package/src/core/webauthn-store.mjs +272 -0
- package/src/server/api.mjs +26 -16
- package/src/server/auth.mjs +51 -0
- package/src/server/index.mjs +3 -3
- package/src/server/keys.mjs +30 -4
- package/src/server/rest.mjs +198 -25
- package/src/server/route-manifest.json +156 -0
- package/src/server/routes/_shared.mjs +40 -1
- package/src/server/routes/docs.mjs +260 -0
- package/src/server/routes/memory.mjs +145 -32
- package/src/server/routes/research.mjs +30 -0
- package/src/server/routes/secrets.mjs +470 -0
- package/src/server/routes/share.mjs +2 -2
- package/src/server/routes/skills.mjs +452 -54
- package/src/server/routes/sync.mjs +48 -0
- package/src/server/routes/webauthn.mjs +184 -0
- package/src/server/update-route-manifest.mjs +39 -0
- package/templates/onboarding-interview.md +32 -22
- package/templates/openwiki/README.md +10 -0
- package/templates/openwiki/architecture.md +5 -0
- package/templates/openwiki/ide.md +10 -0
- package/templates/openwiki/memory.md +11 -0
- package/templates/openwiki/secrets.md +7 -0
- package/templates/openwiki/skills.md +7 -0
- package/scaffold/.agent/skills/total-recall/skills/tr-cli-agents/SKILL.md +0 -121
- package/scaffold/.agent/skills/total-recall/skills/tr-cli-agents/references/antigravity.txt +0 -20
- package/scaffold/.agent/skills/total-recall/skills/tr-cli-agents/references/claude.txt +0 -58
- package/scaffold/.agent/skills/total-recall/skills/tr-cli-agents/references/codex.txt +0 -81
- package/scaffold/.agent/skills/total-recall/skills/tr-cli-agents/references/gemini.txt +0 -44
- package/scaffold/.agent/skills/total-recall/skills/tr-cli-agents/scripts/.gitkeep +0 -0
- package/scaffold/.agent/skills/total-recall/skills/tr-cli-agents/subagents/.gitkeep +0 -0
- package/scaffold/.agent/skills/total-recall/skills/tr-research/SKILL.md +0 -338
- package/scaffold/.agent/skills/total-recall/skills/tr-skill/SKILL.md +0 -282
- package/scaffold/.agent/skills/total-recall/skills/tr-skill/assets/skill-template/SKILL.md +0 -20
- package/scaffold/.agent/skills/total-recall/skills/tr-skill/evals/evals.json +0 -20
- package/scaffold/.agent/skills/total-recall/skills/tr-skill/hooks/on-skill-add.sh +0 -14
- package/scaffold/.agent/skills/total-recall/skills/tr-skill/hooks/on-skill-delete.sh +0 -14
- package/scaffold/.agent/skills/total-recall/skills/tr-skill/learnings/.gitkeep +0 -0
- package/scaffold/.agent/skills/total-recall/skills/tr-skill/references/.gitkeep +0 -0
- package/scaffold/.agent/skills/total-recall/skills/tr-skill/scripts/.gitkeep +0 -0
- package/scaffold/.agent/skills/total-recall/skills/tr-skill/scripts/create-skill.sh +0 -65
- package/scaffold/.agent/skills/total-recall/skills/tr-skill/scripts/enforce-skill-optimization.mjs +0 -135
- package/scaffold/.agent/skills/total-recall/skills/tr-skill/scripts/skill-watcher.mjs +0 -64
- package/scaffold/.agent/skills/total-recall/skills/tr-skill/subagents/audit-all-skills.md +0 -62
- package/scaffold/.agent/skills/total-recall/skills/tr-ssss/SKILL.md +0 -298
- package/scaffold/.agent/skills/total-recall/skills/tr-ssss/evals/evals.json +0 -17
- package/scaffold/.agent/skills/total-recall/skills/tr-ssss/references/CHANGELOG.md +0 -124
- package/scaffold/.agent/skills/total-recall/skills/tr-ssss/references/ssss-spec.md +0 -1325
- package/scaffold/.agent/skills/total-recall/skills/tr-ssss/scripts/validate-schema.mjs +0 -232
- package/scaffold/.agent/skills/total-recall/skills/tr-ssss/subagents/audit-ssss-node.md +0 -9
- /package/scaffold/.agent/skills/total-recall/{skills/tr-cli-agents/evals → modules/skill-deploy/scripts}/.gitkeep +0 -0
- /package/scaffold/.agent/skills/total-recall/{skills/tr-skill → modules/skill-deploy}/scripts/find-skills.mjs +0 -0
- /package/scaffold/.agent/skills/total-recall/{skills/tr-skill → modules/skill-deploy}/scripts/install-skill.mjs +0 -0
- /package/scaffold/.agent/skills/total-recall/{skills/tr-skill → modules/skill-deploy}/scripts/scan-skill.mjs +0 -0
|
@@ -110,20 +110,24 @@ export default function ChatPage({ activeBrainId, onBrainChange }: { activeBrain
|
|
|
110
110
|
.then(health => {
|
|
111
111
|
const agents = health.cli_agents && health.cli_agents.length > 0
|
|
112
112
|
? health.cli_agents
|
|
113
|
-
: ['antigravity', 'claude', 'codex'];
|
|
113
|
+
: ['antigravity', 'grok', 'claude', 'codex'];
|
|
114
114
|
setAvailableModels(agents);
|
|
115
115
|
|
|
116
116
|
const saved = localStorage.getItem('selectedModel');
|
|
117
117
|
if (saved && agents.includes(saved)) {
|
|
118
118
|
setSelectedModel(saved);
|
|
119
119
|
} else {
|
|
120
|
-
const defaultAgent = agents.includes('antigravity')
|
|
120
|
+
const defaultAgent = agents.includes('antigravity')
|
|
121
|
+
? 'antigravity'
|
|
122
|
+
: agents.includes('grok')
|
|
123
|
+
? 'grok'
|
|
124
|
+
: agents[0];
|
|
121
125
|
setSelectedModel(defaultAgent);
|
|
122
126
|
localStorage.setItem('selectedModel', defaultAgent);
|
|
123
127
|
}
|
|
124
128
|
})
|
|
125
129
|
.catch(() => {
|
|
126
|
-
const agents = ['antigravity', 'claude', 'codex'];
|
|
130
|
+
const agents = ['antigravity', 'grok', 'claude', 'codex'];
|
|
127
131
|
setAvailableModels(agents);
|
|
128
132
|
const saved = localStorage.getItem('selectedModel');
|
|
129
133
|
if (saved && agents.includes(saved)) {
|
|
@@ -170,11 +174,51 @@ export default function ChatPage({ activeBrainId, onBrainChange }: { activeBrain
|
|
|
170
174
|
}
|
|
171
175
|
}, [])
|
|
172
176
|
|
|
173
|
-
// Reload memory nodes when active selected brain changes
|
|
177
|
+
// Reload memory nodes when active selected brain changes or background SSE triggers
|
|
174
178
|
useEffect(() => {
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
179
|
+
let active = true
|
|
180
|
+
|
|
181
|
+
const loadMemoryNodes = () => {
|
|
182
|
+
listMemory(activeBrainId)
|
|
183
|
+
.then(nodes => {
|
|
184
|
+
if (!active) return
|
|
185
|
+
setAllMemoryNodes(nodes)
|
|
186
|
+
// Ensure selected grounding nodes stay in sync if a node is deleted in another tab/process
|
|
187
|
+
setSelectedGroundingNodes(prev => prev.filter(slug => nodes.some(n => n.slug === slug)))
|
|
188
|
+
})
|
|
189
|
+
.catch(console.error)
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// Initial load
|
|
193
|
+
loadMemoryNodes()
|
|
194
|
+
|
|
195
|
+
// Listen to Cognitive Daemon SSE for memory surface rebuilds
|
|
196
|
+
const sseUrl = "http://localhost:9111/stream"
|
|
197
|
+
const eventSource = new EventSource(sseUrl)
|
|
198
|
+
|
|
199
|
+
eventSource.onmessage = (event) => {
|
|
200
|
+
if (!active) return
|
|
201
|
+
try {
|
|
202
|
+
const logEntry = JSON.parse(event.data)
|
|
203
|
+
if (
|
|
204
|
+
logEntry.sender === 'conflict-detector' ||
|
|
205
|
+
logEntry.sender === 'memory-ingestion' ||
|
|
206
|
+
logEntry.message?.includes('Surface compilation') ||
|
|
207
|
+
logEntry.message?.includes('Brain embeddings rebuilt') ||
|
|
208
|
+
logEntry.message?.includes('Graph indices rebuilt') ||
|
|
209
|
+
logEntry.message?.includes('deleted')
|
|
210
|
+
) {
|
|
211
|
+
loadMemoryNodes()
|
|
212
|
+
}
|
|
213
|
+
} catch (e) {
|
|
214
|
+
// ignore parsing errors from stream
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
return () => {
|
|
219
|
+
active = false
|
|
220
|
+
eventSource.close()
|
|
221
|
+
}
|
|
178
222
|
}, [activeBrainId])
|
|
179
223
|
|
|
180
224
|
// Click outside listener for grounding node selector popover and model selector dropdown
|
|
@@ -501,8 +545,36 @@ export default function ChatPage({ activeBrainId, onBrainChange }: { activeBrain
|
|
|
501
545
|
</aside>
|
|
502
546
|
|
|
503
547
|
{/* Main Chat Area */}
|
|
504
|
-
<div className="chat-container">
|
|
505
|
-
|
|
548
|
+
<div className="chat-container" style={{ position: 'relative', overflow: 'hidden' }}>
|
|
549
|
+
{/* Decorative graph only — never intercepts clicks (was blocking model/provider UI) */}
|
|
550
|
+
<div
|
|
551
|
+
aria-hidden
|
|
552
|
+
style={{
|
|
553
|
+
position: 'absolute',
|
|
554
|
+
top: 0,
|
|
555
|
+
left: 0,
|
|
556
|
+
right: 0,
|
|
557
|
+
bottom: 0,
|
|
558
|
+
zIndex: 0,
|
|
559
|
+
pointerEvents: 'none',
|
|
560
|
+
opacity: messages.length > 0 ? 0.22 : 0.55,
|
|
561
|
+
transition: 'opacity 0.3s ease',
|
|
562
|
+
}}
|
|
563
|
+
>
|
|
564
|
+
<Graph3D
|
|
565
|
+
threads={threads}
|
|
566
|
+
memoryNodes={allMemoryNodes}
|
|
567
|
+
researchItems={researchItems}
|
|
568
|
+
onOpenThread={() => {}}
|
|
569
|
+
onGroundMemoryNode={() => {}}
|
|
570
|
+
selectedGroundingNodes={selectedGroundingNodes}
|
|
571
|
+
interactive={false}
|
|
572
|
+
/>
|
|
573
|
+
</div>
|
|
574
|
+
<header
|
|
575
|
+
className="chat-header animate-fade-in"
|
|
576
|
+
style={{ position: 'relative', zIndex: 40, overflow: 'visible' }}
|
|
577
|
+
>
|
|
506
578
|
<div className="chat-header-title">
|
|
507
579
|
<h2>Chat Session</h2>
|
|
508
580
|
{activeThreadId && (
|
|
@@ -511,13 +583,17 @@ export default function ChatPage({ activeBrainId, onBrainChange }: { activeBrain
|
|
|
511
583
|
</span>
|
|
512
584
|
)}
|
|
513
585
|
</div>
|
|
514
|
-
<div className="chat-header-model-selector" ref={dropdownRef}>
|
|
515
|
-
<span className="selector-label">Model:</span>
|
|
516
|
-
<div className="model-selector-dropdown-wrapper">
|
|
586
|
+
<div className="chat-header-model-selector" ref={dropdownRef} style={{ position: 'relative', zIndex: 50 }}>
|
|
587
|
+
<span className="selector-label">Model / provider:</span>
|
|
588
|
+
<div className="model-selector-dropdown-wrapper" style={{ position: 'relative', zIndex: 50 }}>
|
|
517
589
|
<button
|
|
590
|
+
type="button"
|
|
518
591
|
className={`model-selector-dropdown-btn ${showModelDropdown ? 'active' : ''}`}
|
|
519
|
-
onClick={() =>
|
|
520
|
-
|
|
592
|
+
onClick={(e) => {
|
|
593
|
+
e.stopPropagation()
|
|
594
|
+
setShowModelDropdown((v) => !v)
|
|
595
|
+
}}
|
|
596
|
+
title="Select CLI agent / provider"
|
|
521
597
|
>
|
|
522
598
|
<span style={{ textTransform: 'capitalize' }}>{selectedModel || 'Select Model'}</span>
|
|
523
599
|
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round">
|
|
@@ -525,12 +601,19 @@ export default function ChatPage({ activeBrainId, onBrainChange }: { activeBrain
|
|
|
525
601
|
</svg>
|
|
526
602
|
</button>
|
|
527
603
|
{showModelDropdown && (
|
|
528
|
-
<div
|
|
604
|
+
<div
|
|
605
|
+
className="model-selector-menu glass"
|
|
606
|
+
style={{ zIndex: 200, pointerEvents: 'auto' }}
|
|
607
|
+
role="listbox"
|
|
608
|
+
>
|
|
529
609
|
{availableModels.map(modelName => (
|
|
530
610
|
<div
|
|
531
611
|
key={modelName}
|
|
612
|
+
role="option"
|
|
613
|
+
aria-selected={selectedModel === modelName}
|
|
532
614
|
className={`model-selector-item ${selectedModel === modelName ? 'selected' : ''}`}
|
|
533
|
-
onClick={() => {
|
|
615
|
+
onClick={(e) => {
|
|
616
|
+
e.stopPropagation()
|
|
534
617
|
setSelectedModel(modelName)
|
|
535
618
|
localStorage.setItem('selectedModel', modelName)
|
|
536
619
|
setSelectedSubModel('')
|
|
@@ -542,6 +625,7 @@ export default function ChatPage({ activeBrainId, onBrainChange }: { activeBrain
|
|
|
542
625
|
<span className="model-selector-item-name" style={{ textTransform: 'capitalize' }}>{modelName}</span>
|
|
543
626
|
<span className="model-selector-item-desc">
|
|
544
627
|
{modelName === 'antigravity' && 'Google Antigravity'}
|
|
628
|
+
{modelName === 'grok' && 'xAI Grok Build CLI'}
|
|
545
629
|
{modelName === 'gemini' && 'Google Gemini CLI'}
|
|
546
630
|
{modelName === 'claude' && 'Anthropic Claude CLI'}
|
|
547
631
|
{modelName === 'codex' && 'Codex Coding Agent'}
|
|
@@ -654,7 +738,7 @@ export default function ChatPage({ activeBrainId, onBrainChange }: { activeBrain
|
|
|
654
738
|
style={{
|
|
655
739
|
flexShrink: 0,
|
|
656
740
|
padding: '6px 14px',
|
|
657
|
-
background: 'linear-gradient(135deg, #
|
|
741
|
+
background: 'linear-gradient(135deg, #3b82f6, #93c5fd)',
|
|
658
742
|
color: '#fff',
|
|
659
743
|
borderRadius: 6,
|
|
660
744
|
fontWeight: 500,
|
|
@@ -685,35 +769,9 @@ export default function ChatPage({ activeBrainId, onBrainChange }: { activeBrain
|
|
|
685
769
|
</div>
|
|
686
770
|
)}
|
|
687
771
|
|
|
688
|
-
<div className="chat-messages" style={{ position: 'relative' }}>
|
|
689
|
-
<div style={{
|
|
690
|
-
position: 'absolute',
|
|
691
|
-
top: 0,
|
|
692
|
-
left: 0,
|
|
693
|
-
right: 0,
|
|
694
|
-
bottom: 0,
|
|
695
|
-
zIndex: 1,
|
|
696
|
-
pointerEvents: messages.length > 0 ? 'none' : 'auto',
|
|
697
|
-
opacity: messages.length > 0 ? 0.35 : 1,
|
|
698
|
-
transition: 'opacity 0.3s ease'
|
|
699
|
-
}}>
|
|
700
|
-
<Graph3D
|
|
701
|
-
threads={threads}
|
|
702
|
-
memoryNodes={allMemoryNodes}
|
|
703
|
-
researchItems={researchItems}
|
|
704
|
-
onOpenThread={(threadId) => setActiveThreadId(threadId)}
|
|
705
|
-
onGroundMemoryNode={(slug) => {
|
|
706
|
-
setSelectedGroundingNodes(prev =>
|
|
707
|
-
prev.includes(slug) ? prev.filter(s => s !== slug) : [...prev, slug]
|
|
708
|
-
)
|
|
709
|
-
}}
|
|
710
|
-
selectedGroundingNodes={selectedGroundingNodes}
|
|
711
|
-
/>
|
|
712
|
-
</div>
|
|
713
|
-
|
|
772
|
+
<div className="chat-messages" style={{ position: 'relative', zIndex: 1, pointerEvents: 'auto' }}>
|
|
714
773
|
<div style={{
|
|
715
774
|
position: 'relative',
|
|
716
|
-
zIndex: 2,
|
|
717
775
|
display: 'flex',
|
|
718
776
|
flexDirection: 'column',
|
|
719
777
|
gap: '16px',
|
|
@@ -773,7 +831,7 @@ export default function ChatPage({ activeBrainId, onBrainChange }: { activeBrain
|
|
|
773
831
|
<div ref={messagesEnd} />
|
|
774
832
|
</div>
|
|
775
833
|
</div>
|
|
776
|
-
<div className="chat-input-bar">
|
|
834
|
+
<div className="chat-input-bar" style={{ position: 'relative', zIndex: 30, pointerEvents: 'auto' }}>
|
|
777
835
|
{selectedGroundingNodes.length > 0 && (
|
|
778
836
|
<div className="grounding-pills">
|
|
779
837
|
{selectedGroundingNodes.map(slug => {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useState, useEffect } from 'react';
|
|
1
|
+
import React, { useState, useEffect } from 'react';
|
|
2
2
|
import { fetchDesignDocs, fetchDesignDocContent } from '../api';
|
|
3
3
|
|
|
4
4
|
// ─── Types ──────────────────────────────────────────────────────────────────────
|
|
@@ -42,11 +42,11 @@ const KANBAN_COLUMNS: KanbanColumn[] = [
|
|
|
42
42
|
{
|
|
43
43
|
id: 'in-progress',
|
|
44
44
|
title: 'In Progress',
|
|
45
|
-
accent: '#
|
|
45
|
+
accent: '#3b82f6',
|
|
46
46
|
cards: [
|
|
47
47
|
{ id: 'auto-update', name: 'Auto Update Feature', emoji: '🔄' },
|
|
48
48
|
{ id: 'compaction-cache', name: 'Compaction Cache Bypass', emoji: '⚡' },
|
|
49
|
-
{ id: '
|
|
49
|
+
{ id: 'memory-policy', name: 'Memory Policy Enforcement', emoji: '🛡️' },
|
|
50
50
|
],
|
|
51
51
|
},
|
|
52
52
|
{
|
|
@@ -70,7 +70,7 @@ const KANBAN_COLUMNS: KanbanColumn[] = [
|
|
|
70
70
|
{ id: 'okf-integration', name: 'OKF Integration', emoji: '✅' },
|
|
71
71
|
{ id: 'byom-architecture', name: 'BYOM Architecture', emoji: '✅' },
|
|
72
72
|
{ id: 'cli-performance', name: 'CLI Performance Optimization', emoji: '✅' },
|
|
73
|
-
{ id: 'ssss-
|
|
73
|
+
{ id: 'ssss-portable-memory', name: 'SSSS Portable Memory', emoji: '✅' },
|
|
74
74
|
{ id: 'embedding-pipeline', name: 'Embedding Pipeline v2', emoji: '✅' },
|
|
75
75
|
{ id: 'surface-compiler', name: 'Surface Compiler', emoji: '✅' },
|
|
76
76
|
],
|
|
@@ -91,7 +91,7 @@ const KANBAN_COLUMNS: KanbanColumn[] = [
|
|
|
91
91
|
|
|
92
92
|
// ─── Simple Markdown Renderer ───────────────────────────────────────────────────
|
|
93
93
|
|
|
94
|
-
function renderMarkdown(md: string):
|
|
94
|
+
function renderMarkdown(md: string): React.ReactNode {
|
|
95
95
|
if (!md) return <></>;
|
|
96
96
|
|
|
97
97
|
let html = md
|
|
@@ -35,7 +35,11 @@ export default function FilesPage() {
|
|
|
35
35
|
listSkills()
|
|
36
36
|
])
|
|
37
37
|
setFiles(filesData)
|
|
38
|
-
|
|
38
|
+
// Skills API returns { skills, repos } catalog — flatten for this page
|
|
39
|
+
const flat = Array.isArray(skillsData)
|
|
40
|
+
? skillsData
|
|
41
|
+
: (skillsData?.skills || [])
|
|
42
|
+
setSkills(flat as import('../types').FileNode[])
|
|
39
43
|
setError("")
|
|
40
44
|
} catch (e) {
|
|
41
45
|
setError((e as Error).message)
|
|
@@ -121,10 +125,10 @@ export default function FilesPage() {
|
|
|
121
125
|
setScriptLoading(true)
|
|
122
126
|
try {
|
|
123
127
|
const defaultCode = name.endsWith(".py")
|
|
124
|
-
? "#!/usr/bin/env python3\nprint('Hello from
|
|
128
|
+
? "#!/usr/bin/env python3\nprint('Hello from Total Recall automation!')\n"
|
|
125
129
|
: name.endsWith(".sh")
|
|
126
|
-
? "#!/bin/bash\necho 'Hello from
|
|
127
|
-
: "/**\n *
|
|
130
|
+
? "#!/bin/bash\necho 'Hello from Total Recall automation!'\n"
|
|
131
|
+
: "/**\n * Total Recall skills automation script\n */\nconsole.log(\"Hello from Total Recall automation!\");\n"
|
|
128
132
|
|
|
129
133
|
await saveScript(name, defaultCode)
|
|
130
134
|
setNewScriptName("")
|
|
@@ -143,7 +147,7 @@ export default function FilesPage() {
|
|
|
143
147
|
<div className="page-header" style={{ display: "flex", justifyContent: "space-between", alignItems: "flex-end", marginBottom: 20 }}>
|
|
144
148
|
<div>
|
|
145
149
|
<h1>Storage & Automations</h1>
|
|
146
|
-
<p>Manage
|
|
150
|
+
<p>Manage local files, runtime skills, and automation scripts</p>
|
|
147
151
|
</div>
|
|
148
152
|
|
|
149
153
|
{/* Navigation Tabs */}
|
|
@@ -176,7 +180,7 @@ export default function FilesPage() {
|
|
|
176
180
|
|
|
177
181
|
{activeTab === "files" && (
|
|
178
182
|
<div className="health-log" style={{ background: "rgba(15,23,42,0.45)", backdropFilter: "blur(12px)", border: "1px solid var(--border)", borderRadius: 12, padding: 24 }}>
|
|
179
|
-
<h2 style={{ fontSize: 16, marginBottom: 12, borderBottom: "1px solid var(--border)", paddingBottom: 8, color: "#fff" }}>
|
|
183
|
+
<h2 style={{ fontSize: 16, marginBottom: 12, borderBottom: "1px solid var(--border)", paddingBottom: 8, color: "#fff" }}>Local Files (~/.agent/files/)</h2>
|
|
180
184
|
<table style={{ width: "100%", textAlign: "left", borderCollapse: "collapse" }}>
|
|
181
185
|
<thead>
|
|
182
186
|
<tr>
|
|
@@ -17,7 +17,7 @@ export default function GraphPage({ activeBrainId }: { activeBrainId?: string })
|
|
|
17
17
|
try {
|
|
18
18
|
const [nodes, researchRes, threadsRes] = await Promise.all([
|
|
19
19
|
listMemory(activeBrainId),
|
|
20
|
-
listResearch(
|
|
20
|
+
listResearch().catch(() => ({ items: [] })),
|
|
21
21
|
fetchChatThreads().catch(() => [])
|
|
22
22
|
])
|
|
23
23
|
setMemoryNodes(nodes)
|
|
@@ -35,8 +35,10 @@ export default function GraphPage({ activeBrainId }: { activeBrainId?: string })
|
|
|
35
35
|
return (
|
|
36
36
|
<div className="page" style={{ padding: 0, height: 'calc(100vh - 1px)', display: 'flex', flexDirection: 'column', overflow: 'hidden' }}>
|
|
37
37
|
<div style={{ padding: '16px 24px', background: 'rgba(18, 18, 26, 0.45)', backdropFilter: 'blur(16px)', borderBottom: '1px solid var(--border)', flexShrink: 0 }}>
|
|
38
|
-
<h1 style={{ fontSize: '18px', fontWeight: 700 }}>
|
|
39
|
-
<p style={{ fontSize: '12px', color: 'var(--text-secondary)', marginTop: '2px' }}>
|
|
38
|
+
<h1 style={{ fontSize: '18px', fontWeight: 700, letterSpacing: '-0.02em' }}>Memory Graph</h1>
|
|
39
|
+
<p style={{ fontSize: '12px', color: 'var(--text-secondary)', marginTop: '2px' }}>
|
|
40
|
+
3D vault map — facts (blue), rules (cyan/rose), research (amber), sessions (teal).
|
|
41
|
+
</p>
|
|
40
42
|
</div>
|
|
41
43
|
|
|
42
44
|
<div style={{ flex: 1, position: 'relative', overflow: 'hidden' }}>
|
|
@@ -84,8 +84,8 @@ export default function HelpPage() {
|
|
|
84
84
|
return (
|
|
85
85
|
<div className="page" style={{ padding: '24px', maxWidth: '1100px', margin: '0 auto' }}>
|
|
86
86
|
<div className="page-header" style={{ marginBottom: '24px' }}>
|
|
87
|
-
<h1>
|
|
88
|
-
<p>Offline
|
|
87
|
+
<h1>Help & CLI Reference</h1>
|
|
88
|
+
<p>Offline docs for portable memory: CLI commands, vault layout, dream, tasks, and IDE connect.</p>
|
|
89
89
|
</div>
|
|
90
90
|
|
|
91
91
|
{error && <div className="badge badge-error" style={{ marginBottom: '16px' }}>⚠️ {error}</div>}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { useState, useEffect, useCallback } from "react"
|
|
2
|
+
import { fetchDocs, readDoc, updateDoc, postDecision, type VaultDocument } from "../api"
|
|
3
|
+
import { DocumentTable } from "../components/DocumentTable"
|
|
4
|
+
|
|
5
|
+
export default function InboxPage({ activeBrainId }: { activeBrainId?: string }) {
|
|
6
|
+
const [items, setItems] = useState<VaultDocument[]>([])
|
|
7
|
+
const [loading, setLoading] = useState(true)
|
|
8
|
+
const [error, setError] = useState("")
|
|
9
|
+
|
|
10
|
+
const loadData = useCallback(async () => {
|
|
11
|
+
try {
|
|
12
|
+
setLoading(true)
|
|
13
|
+
const data = await fetchDocs(activeBrainId, { status: "pending_approval" })
|
|
14
|
+
setItems(data.docs)
|
|
15
|
+
} catch (err: unknown) {
|
|
16
|
+
setError(err instanceof Error ? err.message : "Failed to load the approval inbox")
|
|
17
|
+
} finally {
|
|
18
|
+
setLoading(false)
|
|
19
|
+
}
|
|
20
|
+
}, [activeBrainId])
|
|
21
|
+
|
|
22
|
+
useEffect(() => {
|
|
23
|
+
const timer = window.setTimeout(() => { void loadData() }, 0)
|
|
24
|
+
return () => window.clearTimeout(timer)
|
|
25
|
+
}, [loadData])
|
|
26
|
+
|
|
27
|
+
const handleDecision = async (path: string, action: string) => {
|
|
28
|
+
try {
|
|
29
|
+
const doc = await readDoc(path, activeBrainId)
|
|
30
|
+
// Extract proposal ID from path (e.g. proposals/p2.md -> p2)
|
|
31
|
+
const idMatch = path.match(/proposals\/(.+?)\.md$/)
|
|
32
|
+
if (idMatch) {
|
|
33
|
+
const id = idMatch[1]
|
|
34
|
+
// This hits the proxy, which forwards to droplet and runs sync
|
|
35
|
+
await postDecision(id, action)
|
|
36
|
+
} else {
|
|
37
|
+
// Fallback for non-proposal docs (just optimistic update)
|
|
38
|
+
const newBody = doc.raw.replace(/status:.*$/m, `status: ${action}`)
|
|
39
|
+
await updateDoc(path, newBody, activeBrainId)
|
|
40
|
+
}
|
|
41
|
+
loadData()
|
|
42
|
+
} catch (err: unknown) {
|
|
43
|
+
alert("Decision failed: " + (err instanceof Error ? err.message : "Unknown error"))
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return (
|
|
48
|
+
<div style={{ padding: '20px', maxWidth: '1200px', margin: '0 auto', color: 'white' }}>
|
|
49
|
+
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '20px' }}>
|
|
50
|
+
<h1 style={{ margin: 0, fontSize: '24px' }}>Approval Inbox</h1>
|
|
51
|
+
<button onClick={loadData} style={{ padding: '8px 16px', background: 'rgba(255,255,255,0.1)', border: 'none', color: 'white', cursor: 'pointer', borderRadius: '4px' }}>Refresh</button>
|
|
52
|
+
</div>
|
|
53
|
+
|
|
54
|
+
{loading && <p>Loading inbox items...</p>}
|
|
55
|
+
{error && <p style={{ color: 'red' }}>Error: {error}</p>}
|
|
56
|
+
|
|
57
|
+
{!loading && !error && (
|
|
58
|
+
<div style={{ display: 'grid', gridTemplateColumns: '1fr', gap: '30px' }}>
|
|
59
|
+
<div>
|
|
60
|
+
<DocumentTable
|
|
61
|
+
data={items}
|
|
62
|
+
emptyMessage="No items pending approval."
|
|
63
|
+
columns={[
|
|
64
|
+
{ key: 'type', header: 'Type', render: (d) => <span style={{ opacity: 0.6 }}>{d.type}</span> },
|
|
65
|
+
{ key: 'name', header: 'Name', render: (d) => d.name },
|
|
66
|
+
{ key: 'status', header: 'Status', render: (d) => <span style={{ fontSize: '0.8em', background: 'rgba(234,179,8,0.15)', color: '#eab308', padding: '2px 6px', borderRadius: '4px' }}>{d.status || 'pending'}</span> },
|
|
67
|
+
{ key: 'updated', header: 'Last Updated', render: (d) => d.updatedAt ? new Date(d.updatedAt).toLocaleDateString() : '-' },
|
|
68
|
+
{ key: 'actions', header: 'Actions', render: (d) => (
|
|
69
|
+
<>
|
|
70
|
+
<button onClick={() => handleDecision(d.path, 'approved')} style={{ marginRight: '10px', background: '#22c55e', border: 'none', color: 'white', padding: '4px 8px', borderRadius: '4px', cursor: 'pointer' }}>Approve</button>
|
|
71
|
+
<button onClick={() => handleDecision(d.path, 'rejected')} style={{ background: '#e22b22', border: 'none', color: 'white', padding: '4px 8px', borderRadius: '4px', cursor: 'pointer' }}>Reject</button>
|
|
72
|
+
</>
|
|
73
|
+
)
|
|
74
|
+
}
|
|
75
|
+
]}
|
|
76
|
+
/>
|
|
77
|
+
</div>
|
|
78
|
+
</div>
|
|
79
|
+
)}
|
|
80
|
+
</div>
|
|
81
|
+
)
|
|
82
|
+
}
|
|
@@ -268,7 +268,7 @@ export default function InstructionsPage() {
|
|
|
268
268
|
}
|
|
269
269
|
|
|
270
270
|
useEffect(() => {
|
|
271
|
-
// eslint-disable-next-line react-hooks/set-state-in-effect -- legitimate data fetch on mount
|
|
271
|
+
// eslint-disable-next-line react-hooks/set-state-in-effect, react-hooks/exhaustive-deps -- legitimate data fetch on mount
|
|
272
272
|
void loadSurfaces()
|
|
273
273
|
}, [])
|
|
274
274
|
|
|
@@ -45,11 +45,11 @@ const PRESETS: Preset[] = [
|
|
|
45
45
|
snippet: baseUrl => `Read AGENTS.md from the repo root.\nDiscovery: ${baseUrl}/.well-known/total-recall.json`
|
|
46
46
|
},
|
|
47
47
|
{
|
|
48
|
-
id: '
|
|
49
|
-
name: '
|
|
48
|
+
id: 'http-api',
|
|
49
|
+
name: 'HTTP host app',
|
|
50
50
|
surface: 'OpenAI-compatible provider',
|
|
51
51
|
scopes: 'models:read, chat:write, ssss:read',
|
|
52
|
-
command: 'npx total-recall connect
|
|
52
|
+
command: 'npx total-recall connect http-api --brain <brain-url>',
|
|
53
53
|
snippet: baseUrl => `Base URL: ${baseUrl}/v1\nModel: total-recall/gemma4\nModels: ${baseUrl}/v1/models`
|
|
54
54
|
},
|
|
55
55
|
{
|
|
@@ -104,7 +104,7 @@ export default function IntegrationsPage() {
|
|
|
104
104
|
const filteredPresets = useMemo(() => {
|
|
105
105
|
if (activeIdes.length === 0) return PRESETS
|
|
106
106
|
return PRESETS.filter(preset => {
|
|
107
|
-
if (['
|
|
107
|
+
if (['http-api', 'generic'].includes(preset.id)) {
|
|
108
108
|
return true
|
|
109
109
|
}
|
|
110
110
|
return activeIdes.includes(preset.id)
|
|
@@ -364,13 +364,24 @@ export default function IntegrationsPage() {
|
|
|
364
364
|
const snippet = preset.snippet(baseUrl)
|
|
365
365
|
|
|
366
366
|
// Match keys by name (e.g. "Cursor Link", "Claude Code Link", "Codex Link")
|
|
367
|
+
// Guard: some legacy rows stored a non-string name object
|
|
367
368
|
const matchingKey = keys.find(k => {
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
369
|
+
const rawName = k?.name
|
|
370
|
+
const name =
|
|
371
|
+
typeof rawName === 'string'
|
|
372
|
+
? rawName
|
|
373
|
+
: rawName && typeof rawName === 'object' && typeof (rawName as { name?: unknown }).name === 'string'
|
|
374
|
+
? String((rawName as { name: string }).name)
|
|
375
|
+
: ''
|
|
376
|
+
if (!name || k.revoked) return false
|
|
377
|
+
const n = name.toLowerCase()
|
|
378
|
+
if (preset.id === 'cursor') return n.includes('cursor')
|
|
379
|
+
if (preset.id === 'claude-code') return n.includes('claude')
|
|
380
|
+
if (preset.id === 'codex') return n.includes('codex') || n.includes('agents')
|
|
381
|
+
if (preset.id === 'antigravity') return n.includes('antigravity')
|
|
382
|
+
if (preset.id === 'grok') return n.includes('grok') || n.includes('xai')
|
|
383
|
+
if (preset.id === 'http-api') return n.includes('http') || n.includes('host')
|
|
384
|
+
return false
|
|
374
385
|
})
|
|
375
386
|
|
|
376
387
|
let statusBadge = (
|