ltcai 4.3.1 → 4.4.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 +191 -278
- package/docs/CHANGELOG.md +128 -0
- package/docs/V4_3_2_DEADCODE_AUDIT_REPORT.md +174 -0
- package/docs/V4_3_2_DOCUMENTATION_CLEANUP_REPORT.md +81 -0
- package/docs/V4_3_2_GITHUB_VERCEL_CHECK_REPORT.md +75 -0
- package/docs/V4_3_2_GRAPH_UX_REPORT.md +48 -0
- package/docs/V4_3_2_INDEPENDENT_AUDIT_PACKAGE.md +209 -0
- package/docs/V4_3_2_PRODUCT_POLISH_REPORT.md +57 -0
- package/docs/V4_3_2_SELF_AUDIT_REPORT.md +63 -0
- package/docs/V4_3_2_VALIDATION_REPORT.md +97 -0
- package/docs/V4_3_3_VALIDATION_REPORT.md +46 -0
- package/docs/V4_4_0_EXTRACTION_REPORT.md +239 -0
- package/docs/V4_DIGITAL_BRAIN_RECOVERY.md +18 -19
- package/frontend/openapi.json +1 -1
- package/frontend/src/components/primitives.tsx +92 -10
- package/frontend/src/pages/Act.tsx +11 -9
- package/frontend/src/pages/Ask.tsx +2 -2
- package/frontend/src/pages/Brain.tsx +607 -65
- package/frontend/src/pages/Capture.tsx +11 -7
- package/frontend/src/pages/Library.tsx +3 -3
- package/frontend/src/pages/System.tsx +186 -23
- package/lattice_brain/__init__.py +38 -23
- package/lattice_brain/_kg_common.py +11 -1
- package/lattice_brain/context.py +212 -2
- package/lattice_brain/conversations.py +234 -1
- package/lattice_brain/discovery.py +11 -1
- package/lattice_brain/documents.py +11 -1
- package/lattice_brain/graph/__init__.py +28 -0
- package/lattice_brain/graph/_kg_common.py +1123 -0
- package/lattice_brain/graph/curator.py +473 -0
- package/lattice_brain/graph/discovery.py +1455 -0
- package/lattice_brain/graph/documents.py +218 -0
- package/lattice_brain/graph/identity.py +175 -0
- package/lattice_brain/graph/ingest.py +644 -0
- package/lattice_brain/graph/network.py +205 -0
- package/lattice_brain/graph/projection.py +571 -0
- package/lattice_brain/graph/provenance.py +401 -0
- package/lattice_brain/graph/retrieval.py +1341 -0
- package/lattice_brain/graph/schema.py +640 -0
- package/lattice_brain/graph/store.py +237 -0
- package/lattice_brain/graph/write_master.py +225 -0
- package/lattice_brain/identity.py +11 -13
- package/lattice_brain/ingest.py +11 -1
- package/lattice_brain/ingestion.py +318 -0
- package/lattice_brain/memory.py +100 -1
- package/lattice_brain/network.py +11 -1
- package/lattice_brain/portability.py +431 -0
- package/lattice_brain/projection.py +11 -1
- package/lattice_brain/provenance.py +11 -1
- package/lattice_brain/retrieval.py +11 -1
- package/lattice_brain/runtime/__init__.py +32 -0
- package/lattice_brain/runtime/agent_runtime.py +569 -0
- package/lattice_brain/runtime/hooks.py +754 -0
- package/lattice_brain/runtime/multi_agent.py +795 -0
- package/lattice_brain/schema.py +11 -1
- package/lattice_brain/store.py +10 -2
- package/lattice_brain/workflow.py +461 -0
- package/lattice_brain/write_master.py +11 -1
- package/latticeai/__init__.py +1 -1
- package/latticeai/api/agents.py +2 -2
- package/latticeai/api/browser.py +1 -1
- package/latticeai/api/chat.py +1 -1
- package/latticeai/api/computer_use.py +1 -1
- package/latticeai/api/hooks.py +2 -2
- package/latticeai/api/mcp.py +1 -1
- package/latticeai/api/tools.py +1 -1
- package/latticeai/api/workflow_designer.py +2 -2
- package/latticeai/app_factory.py +4 -4
- package/latticeai/brain/__init__.py +24 -6
- package/latticeai/brain/_kg_common.py +11 -1117
- package/latticeai/brain/context.py +12 -208
- package/latticeai/brain/conversations.py +12 -231
- package/latticeai/brain/discovery.py +13 -1451
- package/latticeai/brain/documents.py +13 -214
- package/latticeai/brain/identity.py +11 -169
- package/latticeai/brain/ingest.py +13 -640
- package/latticeai/brain/memory.py +12 -97
- package/latticeai/brain/network.py +12 -200
- package/latticeai/brain/projection.py +13 -567
- package/latticeai/brain/provenance.py +13 -397
- package/latticeai/brain/retrieval.py +13 -1337
- package/latticeai/brain/schema.py +12 -635
- package/latticeai/brain/store.py +13 -233
- package/latticeai/brain/write_master.py +13 -221
- package/latticeai/core/agent.py +1 -1
- package/latticeai/core/agent_registry.py +2 -2
- package/latticeai/core/builtin_hooks.py +2 -2
- package/latticeai/core/graph_curator.py +6 -468
- package/latticeai/core/hooks.py +6 -749
- package/latticeai/core/marketplace.py +1 -1
- package/latticeai/core/multi_agent.py +6 -790
- package/latticeai/core/workflow_engine.py +6 -456
- package/latticeai/core/workspace_os.py +1 -1
- package/latticeai/services/agent_runtime.py +6 -564
- package/latticeai/services/ingestion.py +6 -313
- package/latticeai/services/kg_portability.py +6 -426
- package/latticeai/services/platform_runtime.py +3 -3
- package/latticeai/services/run_executor.py +1 -1
- package/latticeai/services/upload_service.py +1 -1
- package/p_reinforce.py +1 -1
- package/package.json +3 -6
- package/scripts/build_vercel_static.mjs +77 -0
- package/scripts/bump_version.py +1 -1
- package/scripts/check_markdown_links.mjs +75 -0
- package/scripts/wheel_smoke.py +7 -0
- package/src-tauri/Cargo.lock +1 -1
- package/src-tauri/Cargo.toml +1 -1
- package/src-tauri/src/main.rs +12 -2
- package/src-tauri/tauri.conf.json +1 -1
- package/static/app/asset-manifest.json +5 -5
- package/static/app/assets/index-CHHal8Zl.css +2 -0
- package/static/app/assets/index-pdzil9ac.js +333 -0
- package/static/app/assets/index-pdzil9ac.js.map +1 -0
- package/static/app/index.html +2 -2
- package/latticeai/api/deps.py +0 -15
- package/scripts/capture/README.md +0 -28
- package/scripts/capture/capture_enterprise.js +0 -8
- package/scripts/capture/capture_graph.js +0 -8
- package/scripts/capture/capture_onboarding.js +0 -8
- package/scripts/capture/capture_page.js +0 -43
- package/scripts/capture/capture_release_media.js +0 -125
- package/scripts/capture/capture_skills.js +0 -8
- package/scripts/capture/capture_v340.js +0 -88
- package/scripts/capture/capture_workspace.js +0 -8
- package/scripts/generate_diagrams.py +0 -512
- package/scripts/release-0.3.1.sh +0 -105
- package/scripts/take_screenshots.js +0 -69
- package/static/app/assets/index-BhPuj8rT.js +0 -333
- package/static/app/assets/index-BhPuj8rT.js.map +0 -1
- package/static/app/assets/index-yZswHE3d.css +0 -2
- package/static/css/tokens.3ba22e37.css +0 -260
|
@@ -2,7 +2,7 @@ import * as React from "react";
|
|
|
2
2
|
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
|
3
3
|
import { FolderPlus, Globe2, HardDrive, Upload } from "lucide-react";
|
|
4
4
|
import { latticeApi } from "@/api/client";
|
|
5
|
-
import { ActionButton, DataPanel, EntityList,
|
|
5
|
+
import { ActionButton, DataPanel, EntityList, OperationResult, StructuredView, Tabs } from "@/components/primitives";
|
|
6
6
|
import { Button } from "@/components/ui/button";
|
|
7
7
|
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
|
|
8
8
|
import { Input } from "@/components/ui/input";
|
|
@@ -59,7 +59,11 @@ function FilesPanel() {
|
|
|
59
59
|
<span className="text-sm text-muted-foreground">PDF, DOCX, XLSX, PPTX, TXT, MD, CSV according to backend policy.</span>
|
|
60
60
|
<input type="file" multiple className="sr-only" onChange={(e) => e.target.files && upload.mutate(e.target.files)} />
|
|
61
61
|
</label>
|
|
62
|
-
{upload.data ?
|
|
62
|
+
{upload.data ? (
|
|
63
|
+
<div className="mt-3 space-y-2">
|
|
64
|
+
{upload.data.map((item, index) => <OperationResult key={index} result={item} successLabel="Upload completed" />)}
|
|
65
|
+
</div>
|
|
66
|
+
) : null}
|
|
63
67
|
</CardContent>
|
|
64
68
|
</Card>
|
|
65
69
|
<DataPanel title="Uploaded documents" result={docs.data}>
|
|
@@ -88,7 +92,7 @@ function LocalPanel() {
|
|
|
88
92
|
<CardContent className="space-y-3">
|
|
89
93
|
<Input value={path} onChange={(e) => setPath(e.target.value)} placeholder="/Users/me/Documents/project" />
|
|
90
94
|
<Button disabled={!path.trim() || connect.isPending} onClick={() => connect.mutate()}>Connect and watch</Button>
|
|
91
|
-
{connect.data ? <
|
|
95
|
+
{connect.data ? <OperationResult result={connect.data} successLabel="Folder connection requested" /> : null}
|
|
92
96
|
</CardContent>
|
|
93
97
|
</Card>
|
|
94
98
|
<DataPanel title="Connected sources" result={local.data}>
|
|
@@ -107,7 +111,7 @@ function LocalPanel() {
|
|
|
107
111
|
)}
|
|
108
112
|
</DataPanel>
|
|
109
113
|
<DataPanel title="Local runtime probe" result={agent.data} className="xl:col-span-2">
|
|
110
|
-
{(data) => <
|
|
114
|
+
{(data) => <StructuredView value={data} />}
|
|
111
115
|
</DataPanel>
|
|
112
116
|
</div>
|
|
113
117
|
);
|
|
@@ -127,7 +131,7 @@ function BrowserPanel() {
|
|
|
127
131
|
<Input value={url} onChange={(e) => setUrl(e.target.value)} placeholder="https://example.com/article" />
|
|
128
132
|
<Button disabled={!url.trim() || read.isPending} onClick={() => read.mutate()}>Capture URL</Button>
|
|
129
133
|
</div>
|
|
130
|
-
{read.data ? <
|
|
134
|
+
{read.data ? <OperationResult result={read.data} successLabel="URL capture requested" /> : null}
|
|
131
135
|
</CardContent>
|
|
132
136
|
</Card>
|
|
133
137
|
);
|
|
@@ -139,10 +143,10 @@ function PipelinePanel() {
|
|
|
139
143
|
return (
|
|
140
144
|
<div className="grid gap-4 xl:grid-cols-2">
|
|
141
145
|
<DataPanel title="Index pipeline" result={index.data}>
|
|
142
|
-
{(data) => <
|
|
146
|
+
{(data) => <StructuredView value={data} />}
|
|
143
147
|
</DataPanel>
|
|
144
148
|
<DataPanel title="Graph totals" result={stats.data}>
|
|
145
|
-
{(data) => <
|
|
149
|
+
{(data) => <StructuredView value={data} />}
|
|
146
150
|
</DataPanel>
|
|
147
151
|
<Card className="xl:col-span-2">
|
|
148
152
|
<CardHeader>
|
|
@@ -2,7 +2,7 @@ import * as React from "react";
|
|
|
2
2
|
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
|
3
3
|
import { Boxes, Cpu, PackagePlus, Plug, Puzzle } from "lucide-react";
|
|
4
4
|
import { latticeApi } from "@/api/client";
|
|
5
|
-
import { ActionButton, DataPanel, EntityList,
|
|
5
|
+
import { ActionButton, DataPanel, EntityList, OperationResult, StructuredView, Tabs } from "@/components/primitives";
|
|
6
6
|
import { Badge } from "@/components/ui/badge";
|
|
7
7
|
import { Button } from "@/components/ui/button";
|
|
8
8
|
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
|
|
@@ -82,7 +82,7 @@ function ModelsPanel() {
|
|
|
82
82
|
)}
|
|
83
83
|
</DataPanel>
|
|
84
84
|
<DataPanel title="Embedding provider" result={emb.data}>
|
|
85
|
-
{(data) => <
|
|
85
|
+
{(data) => <StructuredView value={data} />}
|
|
86
86
|
</DataPanel>
|
|
87
87
|
</div>
|
|
88
88
|
);
|
|
@@ -151,7 +151,7 @@ function McpPanel() {
|
|
|
151
151
|
<Input value={query} onChange={(e) => setQuery(e.target.value)} />
|
|
152
152
|
<Button onClick={() => rec.mutate()} disabled={!query.trim() || rec.isPending}>Recommend</Button>
|
|
153
153
|
</div>
|
|
154
|
-
{rec.data ? <
|
|
154
|
+
{rec.data ? <OperationResult result={rec.data} successLabel="Recommendation completed" /> : null}
|
|
155
155
|
</CardContent>
|
|
156
156
|
</Card>
|
|
157
157
|
</div>
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
2
|
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
|
3
|
-
import {
|
|
3
|
+
import { Network, ShieldCheck, UserCircle, Users } from "lucide-react";
|
|
4
4
|
import { latticeApi } from "@/api/client";
|
|
5
|
-
import { ActionButton, DataPanel,
|
|
5
|
+
import { ActionButton, DataPanel, EmptyState, EntityList, KeyValueList, OperationResult, StatGrid, StructuredView, Tabs } from "@/components/primitives";
|
|
6
6
|
import { Badge } from "@/components/ui/badge";
|
|
7
7
|
import { Button } from "@/components/ui/button";
|
|
8
8
|
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
|
|
9
9
|
import { Input } from "@/components/ui/input";
|
|
10
10
|
import { useAppStore } from "@/store/appStore";
|
|
11
|
-
import { asArray, titleize } from "@/lib/utils";
|
|
11
|
+
import { asArray, shortId, titleize } from "@/lib/utils";
|
|
12
12
|
|
|
13
13
|
type SystemTab = "account" | "workspaces" | "snapshots" | "activity" | "network" | "settings" | "admin";
|
|
14
14
|
|
|
@@ -84,11 +84,13 @@ function AccountPanel() {
|
|
|
84
84
|
<Button variant="outline" onClick={() => changePassword.mutate()} disabled={!password || !newPassword || changePassword.isPending}>Change password</Button>
|
|
85
85
|
<ActionButton label="Logout" action={() => latticeApi.logout()} invalidate={["profile"]} />
|
|
86
86
|
</div>
|
|
87
|
-
{[login.data, register.data, saveProfile.data, changePassword.data].filter(Boolean).map((item, i) =>
|
|
87
|
+
{[login.data, register.data, saveProfile.data, changePassword.data].filter(Boolean).map((item, i) => (
|
|
88
|
+
<OperationResult key={i} result={item} successLabel="Account request completed" />
|
|
89
|
+
))}
|
|
88
90
|
</CardContent>
|
|
89
91
|
</Card>
|
|
90
92
|
<DataPanel title="SSO config" result={sso.data} className="xl:col-span-2">
|
|
91
|
-
{(data) => <
|
|
93
|
+
{(data) => <StructuredView value={data} />}
|
|
92
94
|
</DataPanel>
|
|
93
95
|
</div>
|
|
94
96
|
);
|
|
@@ -195,7 +197,7 @@ function SnapshotsPanel() {
|
|
|
195
197
|
<Input value={after} onChange={(e) => setAfter(e.target.value)} placeholder="after id" />
|
|
196
198
|
</div>
|
|
197
199
|
<Button variant="outline" onClick={() => compare.mutate()} disabled={!before || !after || compare.isPending}>Compare</Button>
|
|
198
|
-
{compare.data ? <
|
|
200
|
+
{compare.data ? <OperationResult result={compare.data} successLabel="Snapshot comparison completed" /> : null}
|
|
199
201
|
</CardContent>
|
|
200
202
|
</Card>
|
|
201
203
|
<DataPanel title="Time machine" result={timeline.data} className="xl:col-span-2">
|
|
@@ -214,7 +216,7 @@ function ActivityPanel() {
|
|
|
214
216
|
{(data) => <EntityList items={(data as Record<string, unknown>).events} titleKey="event_type" metaKey="area" limit={14} />}
|
|
215
217
|
</DataPanel>
|
|
216
218
|
<DataPanel title="Presence" result={presence.data}>
|
|
217
|
-
{(data) => <
|
|
219
|
+
{(data) => <PresenceView data={data as Record<string, unknown>} />}
|
|
218
220
|
</DataPanel>
|
|
219
221
|
</div>
|
|
220
222
|
);
|
|
@@ -234,7 +236,7 @@ function NetworkPanel() {
|
|
|
234
236
|
return (
|
|
235
237
|
<div className="grid gap-4 xl:grid-cols-[0.8fr_1.2fr]">
|
|
236
238
|
<DataPanel title="Device identity" result={identity.data}>
|
|
237
|
-
{(data) => <
|
|
239
|
+
{(data) => <DeviceIdentityView data={data as Record<string, unknown>} />}
|
|
238
240
|
</DataPanel>
|
|
239
241
|
<Card>
|
|
240
242
|
<CardHeader>
|
|
@@ -246,7 +248,7 @@ function NetworkPanel() {
|
|
|
246
248
|
<Input value={baseUrl} onChange={(e) => setBaseUrl(e.target.value)} placeholder="http://peer.local:8765" />
|
|
247
249
|
<Input value={publicKey} onChange={(e) => setPublicKey(e.target.value)} placeholder="trusted public key" />
|
|
248
250
|
<Button disabled={!name || !baseUrl || !publicKey || pair.isPending} onClick={() => pair.mutate()}>Pair</Button>
|
|
249
|
-
{pair.data ? <
|
|
251
|
+
{pair.data ? <OperationResult result={pair.data} successLabel="Peer pairing request completed" /> : null}
|
|
250
252
|
</CardContent>
|
|
251
253
|
</Card>
|
|
252
254
|
<DataPanel title="Peers" result={peers.data} className="xl:col-span-2">
|
|
@@ -289,6 +291,7 @@ function SettingsPanel() {
|
|
|
289
291
|
const [restorePath, setRestorePath] = React.useState("");
|
|
290
292
|
const [archivePassphrase, setArchivePassphrase] = React.useState("");
|
|
291
293
|
const [restoreConfirm, setRestoreConfirm] = React.useState(false);
|
|
294
|
+
const [importConfirm, setImportConfirm] = React.useState(false);
|
|
292
295
|
const docker = useMutation({ mutationFn: (consent: boolean) => latticeApi.dockerPostgres({ consent, dry_run: !consent, port: 5432 }) });
|
|
293
296
|
const migration = useMutation({
|
|
294
297
|
mutationFn: () => latticeApi.migratePostgres({ dsn, schema_name: schema || "lattice_brain", dry_run: true }),
|
|
@@ -313,6 +316,16 @@ function SettingsPanel() {
|
|
|
313
316
|
qc.invalidateQueries({ queryKey: ["backupHealth"] });
|
|
314
317
|
},
|
|
315
318
|
});
|
|
319
|
+
const archiveImportDryRun = useMutation({
|
|
320
|
+
mutationFn: () => latticeApi.brainArchiveImport({ path: restorePath, passphrase: archivePassphrase, dry_run: true, confirm: false }),
|
|
321
|
+
});
|
|
322
|
+
const archiveImport = useMutation({
|
|
323
|
+
mutationFn: () => latticeApi.brainArchiveImport({ path: restorePath, passphrase: archivePassphrase, dry_run: false, confirm: importConfirm }),
|
|
324
|
+
onSuccess: () => {
|
|
325
|
+
qc.invalidateQueries({ queryKey: ["brainStorage"] });
|
|
326
|
+
qc.invalidateQueries({ queryKey: ["backupHealth"] });
|
|
327
|
+
},
|
|
328
|
+
});
|
|
316
329
|
return (
|
|
317
330
|
<div className="grid gap-4 xl:grid-cols-3">
|
|
318
331
|
<Card>
|
|
@@ -329,16 +342,16 @@ function SettingsPanel() {
|
|
|
329
342
|
</CardContent>
|
|
330
343
|
</Card>
|
|
331
344
|
<DataPanel title="Server health" result={health.data}>
|
|
332
|
-
{(data) => <
|
|
345
|
+
{(data) => <HealthView data={data as Record<string, unknown>} />}
|
|
333
346
|
</DataPanel>
|
|
334
347
|
<DataPanel title="Host telemetry" result={sys.data}>
|
|
335
|
-
{(data) => <
|
|
348
|
+
{(data) => <StructuredView value={data} />}
|
|
336
349
|
</DataPanel>
|
|
337
350
|
<DataPanel title="Brain storage" result={storage.data} className="xl:col-span-3">
|
|
338
|
-
{(data) => <
|
|
351
|
+
{(data) => <StorageView data={data as Record<string, unknown>} />}
|
|
339
352
|
</DataPanel>
|
|
340
353
|
<DataPanel title="Backup health" result={backupHealth.data} className="xl:col-span-3">
|
|
341
|
-
{(data) => <
|
|
354
|
+
{(data) => <BackupHealthView data={data as Record<string, unknown>} />}
|
|
342
355
|
</DataPanel>
|
|
343
356
|
<Card className="xl:col-span-3">
|
|
344
357
|
<CardHeader>
|
|
@@ -356,14 +369,20 @@ function SettingsPanel() {
|
|
|
356
369
|
<Button variant="outline" onClick={() => archiveInspect.mutate()} disabled={!restorePath || archiveInspect.isPending}>Inspect</Button>
|
|
357
370
|
<Button variant="outline" onClick={() => archiveVerify.mutate()} disabled={!restorePath || !archivePassphrase || archiveVerify.isPending}>Verify</Button>
|
|
358
371
|
<Button variant="outline" onClick={() => archiveDryRun.mutate()} disabled={!restorePath || !archivePassphrase || archiveDryRun.isPending}>Restore dry run</Button>
|
|
372
|
+
<Button variant="outline" onClick={() => archiveImportDryRun.mutate()} disabled={!restorePath || !archivePassphrase || archiveImportDryRun.isPending}>Import dry run</Button>
|
|
359
373
|
<label className="flex items-center gap-2 rounded-md border border-border px-3 py-2 text-sm">
|
|
360
374
|
<input type="checkbox" checked={restoreConfirm} onChange={(e) => setRestoreConfirm(e.target.checked)} />
|
|
361
375
|
Confirm restore
|
|
362
376
|
</label>
|
|
363
377
|
<Button variant="destructive" onClick={() => archiveRestore.mutate()} disabled={!restorePath || !archivePassphrase || !restoreConfirm || archiveRestore.isPending}>Restore</Button>
|
|
378
|
+
<label className="flex items-center gap-2 rounded-md border border-border px-3 py-2 text-sm">
|
|
379
|
+
<input type="checkbox" checked={importConfirm} onChange={(e) => setImportConfirm(e.target.checked)} />
|
|
380
|
+
Confirm import
|
|
381
|
+
</label>
|
|
382
|
+
<Button variant="outline" onClick={() => archiveImport.mutate()} disabled={!restorePath || !archivePassphrase || !importConfirm || archiveImport.isPending}>Import</Button>
|
|
364
383
|
</div>
|
|
365
|
-
{[archiveCreate.data, archiveInspect.data, archiveVerify.data, archiveDryRun.data, archiveRestore.data].filter(Boolean).map((item, i) => (
|
|
366
|
-
<
|
|
384
|
+
{[archiveCreate.data, archiveInspect.data, archiveVerify.data, archiveDryRun.data, archiveRestore.data, archiveImportDryRun.data, archiveImport.data].filter(Boolean).map((item, i) => (
|
|
385
|
+
<OperationResult key={i} result={item} successLabel="Archive request completed" />
|
|
367
386
|
))}
|
|
368
387
|
</CardContent>
|
|
369
388
|
</Card>
|
|
@@ -386,14 +405,14 @@ function SettingsPanel() {
|
|
|
386
405
|
<Button onClick={() => docker.mutate(true)} disabled={!dockerConsent || docker.isPending}>Start Docker</Button>
|
|
387
406
|
<Button variant="outline" onClick={() => migration.mutate()} disabled={!dsn || migration.isPending}>Plan migration</Button>
|
|
388
407
|
</div>
|
|
389
|
-
{docker.data ? <
|
|
390
|
-
{migration.data ? <
|
|
408
|
+
{docker.data ? <OperationResult result={docker.data} successLabel="Docker setup request completed" /> : null}
|
|
409
|
+
{migration.data ? <OperationResult result={migration.data} successLabel="Migration plan completed" /> : null}
|
|
391
410
|
</CardContent>
|
|
392
411
|
</Card>
|
|
393
412
|
<DataPanel title="Computer memory" result={comp.data} className="xl:col-span-3">
|
|
394
413
|
{(data) => (
|
|
395
414
|
<div className="space-y-3">
|
|
396
|
-
<
|
|
415
|
+
<StructuredView value={data} />
|
|
397
416
|
<div className="flex gap-2">
|
|
398
417
|
<ActionButton label="Enable memory" action={() => latticeApi.setComputerMemory(true)} invalidate={["computerMemory"]} />
|
|
399
418
|
<ActionButton label="Disable memory" action={() => latticeApi.setComputerMemory(false)} invalidate={["computerMemory"]} variant="destructive" />
|
|
@@ -405,6 +424,150 @@ function SettingsPanel() {
|
|
|
405
424
|
);
|
|
406
425
|
}
|
|
407
426
|
|
|
427
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
428
|
+
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
function textValue(value: unknown, fallback = "not reported") {
|
|
432
|
+
if (value === null || value === undefined || value === "") return fallback;
|
|
433
|
+
if (typeof value === "boolean") return value ? "enabled" : "disabled";
|
|
434
|
+
return String(value);
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
function PresenceView({ data }: { data: Record<string, unknown> }) {
|
|
438
|
+
const rows = asArray<Record<string, unknown>>(data.presence || data.clients || data);
|
|
439
|
+
if (!rows.length) return <EmptyState title="No active presence" detail="No live collaborators or realtime clients are currently reported." />;
|
|
440
|
+
return <EntityList items={rows} titleKey="user" metaKey="workspace_id" />;
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
function DeviceIdentityView({ data }: { data: Record<string, unknown> }) {
|
|
444
|
+
const publicKey = textValue(data.public_key, "");
|
|
445
|
+
return (
|
|
446
|
+
<div className="space-y-3">
|
|
447
|
+
<div className="flex flex-wrap items-center gap-2">
|
|
448
|
+
<Badge variant="success">local device</Badge>
|
|
449
|
+
<Badge variant="muted">{textValue(data.algorithm, "identity key")}</Badge>
|
|
450
|
+
</div>
|
|
451
|
+
<KeyValueList data={{
|
|
452
|
+
device_id: data.device_id || data.id || "not reported",
|
|
453
|
+
fingerprint: data.fingerprint || "not reported",
|
|
454
|
+
public_key: publicKey ? shortId(publicKey.replace(/\s+/g, " "), 72) : "not reported",
|
|
455
|
+
}} />
|
|
456
|
+
</div>
|
|
457
|
+
);
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
function HealthView({ data }: { data: Record<string, unknown> }) {
|
|
461
|
+
return (
|
|
462
|
+
<div className="space-y-3">
|
|
463
|
+
<StatGrid stats={[
|
|
464
|
+
{ label: "Status", value: data.status || data.ok || "reported" },
|
|
465
|
+
{ label: "Version", value: data.version || "not reported" },
|
|
466
|
+
{ label: "Mode", value: data.mode || data.environment || "local" },
|
|
467
|
+
{ label: "Port", value: data.port || data.backend_port || "configured" },
|
|
468
|
+
]} />
|
|
469
|
+
<StructuredView value={data} />
|
|
470
|
+
</div>
|
|
471
|
+
);
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
function StorageView({ data }: { data: Record<string, unknown> }) {
|
|
475
|
+
const active = isRecord(data.active) ? data.active : data;
|
|
476
|
+
const postgres = isRecord(data.postgres) ? data.postgres : {};
|
|
477
|
+
const backup = isRecord(data.backup_health) ? data.backup_health : {};
|
|
478
|
+
const vector = active.vector_search || active.vector || data.vector_search || data.sqlite_vec;
|
|
479
|
+
const postgresAvailable = Boolean(postgres.available || postgres.connected || postgres.enabled);
|
|
480
|
+
return (
|
|
481
|
+
<div className="space-y-4">
|
|
482
|
+
<StatGrid stats={[
|
|
483
|
+
{ label: "Active engine", value: active.engine || data.engine || "sqlite" },
|
|
484
|
+
{ label: "SQLite default", value: active.engine === "postgres" ? "scale mode" : "enabled" },
|
|
485
|
+
{ label: "Vector search", value: vector || "not reported" },
|
|
486
|
+
{ label: "Postgres", value: postgresAvailable ? "available" : "optional" },
|
|
487
|
+
]} />
|
|
488
|
+
<div className="grid gap-3 md:grid-cols-3">
|
|
489
|
+
<StatusCard title="SQLite" status={active.available === false ? "unavailable" : "default"} detail={textValue(active.reason || active.path || data.path, "Local brain storage is active by default.")} />
|
|
490
|
+
<StatusCard title="Vector search" status={textValue(vector, "reported")} detail={textValue(active.vector_reason || active.sqlite_vec_reason || data.vector_reason, "Uses the configured local vector capability or reports why it is unavailable.")} />
|
|
491
|
+
<StatusCard title="Postgres" status={postgresAvailable ? "available" : "not enabled"} detail={textValue(postgres.reason || postgres.dsn || postgres.status, "Postgres scale mode is opt-in and never required for local use.")} />
|
|
492
|
+
</div>
|
|
493
|
+
{Object.keys(backup).length ? <StructuredView value={{ backup_health: backup }} /> : null}
|
|
494
|
+
</div>
|
|
495
|
+
);
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
function BackupHealthView({ data }: { data: Record<string, unknown> }) {
|
|
499
|
+
return (
|
|
500
|
+
<div className="space-y-3">
|
|
501
|
+
<StatGrid stats={[
|
|
502
|
+
{ label: "Available", value: data.available === false ? "no" : "yes" },
|
|
503
|
+
{ label: "Backups", value: data.count || data.backups || 0 },
|
|
504
|
+
{ label: "Encrypted", value: data.encrypted_archives || 0 },
|
|
505
|
+
{ label: "Zip backups", value: data.zip_backups || 0 },
|
|
506
|
+
]} />
|
|
507
|
+
<KeyValueList data={{
|
|
508
|
+
directory: data.directory || "not reported",
|
|
509
|
+
latest: data.latest || "none reported",
|
|
510
|
+
last_verified: data.last_verified || data.verified_at || "not reported",
|
|
511
|
+
failure: data.error || data.reason || "none reported",
|
|
512
|
+
}} />
|
|
513
|
+
</div>
|
|
514
|
+
);
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
function StatusCard({ title, status, detail }: { title: string; status: string; detail: string }) {
|
|
518
|
+
const variant = /unavailable|failed|denied|disabled|not enabled/i.test(status) ? "warning" : "success";
|
|
519
|
+
return (
|
|
520
|
+
<div className="rounded-md border border-border bg-background p-3">
|
|
521
|
+
<div className="flex items-center justify-between gap-2">
|
|
522
|
+
<div className="font-medium">{title}</div>
|
|
523
|
+
<Badge variant={variant}>{status}</Badge>
|
|
524
|
+
</div>
|
|
525
|
+
<p className="mt-2 text-sm text-muted-foreground">{detail}</p>
|
|
526
|
+
</div>
|
|
527
|
+
);
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
function HardeningView({ data }: { data: Record<string, unknown> }) {
|
|
531
|
+
const startup = isRecord(data.startup) ? data.startup : {};
|
|
532
|
+
const privacy = isRecord(data.privacy) ? data.privacy : {};
|
|
533
|
+
const storage = isRecord(data.storage) ? data.storage : {};
|
|
534
|
+
const backup = isRecord(data.backup) ? data.backup : {};
|
|
535
|
+
const identity = isRecord(data.device_identity) ? data.device_identity : {};
|
|
536
|
+
const permissions = isRecord(data.permissions) ? data.permissions : {};
|
|
537
|
+
return (
|
|
538
|
+
<div className="space-y-3">
|
|
539
|
+
<StatGrid stats={[
|
|
540
|
+
{ label: "Version", value: data.version || "reported" },
|
|
541
|
+
{ label: "Local only", value: privacy.local_only_default ?? startup.local_only_default ?? "reported" },
|
|
542
|
+
{ label: "Storage", value: isRecord(storage.active) ? (storage.active as Record<string, unknown>).engine : "reported" },
|
|
543
|
+
{ label: "Backups", value: backup.count || backup.available || "reported" },
|
|
544
|
+
]} />
|
|
545
|
+
<div className="grid gap-3 md:grid-cols-2">
|
|
546
|
+
<StatusCard title="Startup" status={startup.network_exposed ? "network exposed" : "local-only"} detail={`Host ${textValue(startup.host, "127.0.0.1")} on port ${textValue(startup.port, "configured")}.`} />
|
|
547
|
+
<StatusCard title="Integrations" status={privacy.local_only_default === false ? "review required" : "opt-in"} detail="External integrations remain disabled until the user explicitly enables them." />
|
|
548
|
+
<StatusCard title="Device identity" status={textValue(identity.algorithm || identity.fingerprint, "reported")} detail={textValue(identity.storage, "Stored locally and used for signed bundle exchange.")} />
|
|
549
|
+
<StatusCard title="Permissions" status={permissions.destructive_restore_requires_confirmation === false ? "review required" : "guarded"} detail="Export, import, and destructive restore permissions are surfaced through admin status." />
|
|
550
|
+
</div>
|
|
551
|
+
</div>
|
|
552
|
+
);
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
function SecurityView({ data }: { data: Record<string, unknown> }) {
|
|
556
|
+
const cards = isRecord(data.cards) ? data.cards : {};
|
|
557
|
+
const severities = isRecord(data.severity_counts) ? data.severity_counts : {};
|
|
558
|
+
return (
|
|
559
|
+
<div className="space-y-3">
|
|
560
|
+
<StatGrid stats={[
|
|
561
|
+
{ label: "Events today", value: cards.events_today || 0 },
|
|
562
|
+
{ label: "High risk", value: cards.high_risk_events || severities.high || 0 },
|
|
563
|
+
{ label: "Review", value: cards.review_required || 0 },
|
|
564
|
+
{ label: "Risk rate", value: data.risk_rate || 0 },
|
|
565
|
+
]} />
|
|
566
|
+
<StructuredView value={{ severity_counts: severities, sensitive_fields: data.field_counts || {} }} />
|
|
567
|
+
</div>
|
|
568
|
+
);
|
|
569
|
+
}
|
|
570
|
+
|
|
408
571
|
function AdminPanel() {
|
|
409
572
|
const summary = useQuery({ queryKey: ["adminSummary"], queryFn: latticeApi.adminSummary });
|
|
410
573
|
const users = useQuery({ queryKey: ["adminUsers"], queryFn: latticeApi.adminUsers });
|
|
@@ -419,15 +582,15 @@ function AdminPanel() {
|
|
|
419
582
|
<DataPanel title="Admin summary" result={summary.data}>{(data) => <KeyValueList data={data as Record<string, unknown>} />}</DataPanel>
|
|
420
583
|
<DataPanel title="Users" result={users.data}>{(data) => <EntityList items={data} titleKey="email" metaKey="role" />}</DataPanel>
|
|
421
584
|
<DataPanel title="Audit" result={audit.data}>{(data) => <EntityList items={(data as Record<string, unknown>).recent_events || data} titleKey="act" metaKey="sev" />}</DataPanel>
|
|
422
|
-
<DataPanel title="Roles" result={roles.data}>{(data) => <
|
|
423
|
-
<DataPanel title="Policies" result={policies.data}>{(data) => <
|
|
424
|
-
<DataPanel title="Product hardening" result={hardening.data}>{(data) => <
|
|
425
|
-
<DataPanel title="Security overview" result={security.data}>{(data) => <
|
|
585
|
+
<DataPanel title="Roles" result={roles.data}>{(data) => <EntityList items={(data as Record<string, unknown>).roles || data} titleKey="role" metaKey="members" />}</DataPanel>
|
|
586
|
+
<DataPanel title="Policies" result={policies.data}>{(data) => <EntityList items={(data as Record<string, unknown>).policies || data} titleKey="label" metaKey="enforced" />}</DataPanel>
|
|
587
|
+
<DataPanel title="Product hardening" result={hardening.data}>{(data) => <HardeningView data={data as Record<string, unknown>} />}</DataPanel>
|
|
588
|
+
<DataPanel title="Security overview" result={security.data}>{(data) => <SecurityView data={data as Record<string, unknown>} />}</DataPanel>
|
|
426
589
|
<DataPanel title="Private VPC" result={vpc.data} className="xl:col-span-2">
|
|
427
590
|
{(data) => (
|
|
428
591
|
<div className="space-y-2">
|
|
429
592
|
<Badge variant="muted">Community-disabled features remain honest unavailable states.</Badge>
|
|
430
|
-
<
|
|
593
|
+
<StructuredView value={data} />
|
|
431
594
|
</div>
|
|
432
595
|
)}
|
|
433
596
|
</DataPanel>
|
|
@@ -1,7 +1,14 @@
|
|
|
1
1
|
"""lattice-brain — independent Brain Core package for Lattice AI.
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
Physically hosts the knowledge graph (``lattice_brain.graph``), memory,
|
|
4
|
+
context assembly, conversations, ingestion, agent/hook runtime
|
|
5
|
+
(``lattice_brain.runtime``), workflow engine, portability (backup/restore and
|
|
6
|
+
``.latticebrain`` archives), and the storage abstraction.
|
|
7
|
+
|
|
8
|
+
The package never imports ``latticeai``; FastAPI and the desktop product
|
|
9
|
+
import this package, not the other way around. Heavy graph modules are
|
|
10
|
+
lazy-loaded so storage and archive utilities remain usable without creating
|
|
11
|
+
runtime globals.
|
|
5
12
|
"""
|
|
6
13
|
|
|
7
14
|
from .archive import BrainArchivePaths, EncryptedBrainArchive
|
|
@@ -19,9 +26,10 @@ from .storage import (
|
|
|
19
26
|
storage_from_env,
|
|
20
27
|
)
|
|
21
28
|
|
|
22
|
-
__version__ = "4.
|
|
29
|
+
__version__ = "4.4.0"
|
|
23
30
|
|
|
24
31
|
__all__ = [
|
|
32
|
+
"AgentRuntime",
|
|
25
33
|
"AssembledContext",
|
|
26
34
|
"BrainArchivePaths",
|
|
27
35
|
"BrainCore",
|
|
@@ -33,7 +41,11 @@ __all__ = [
|
|
|
33
41
|
"DockerPostgresPlan",
|
|
34
42
|
"DockerPostgresWizard",
|
|
35
43
|
"EncryptedBrainArchive",
|
|
44
|
+
"IngestionItem",
|
|
45
|
+
"IngestionPipeline",
|
|
46
|
+
"KGPortabilityService",
|
|
36
47
|
"KnowledgeGraphStore",
|
|
48
|
+
"MultiAgentOrchestrator",
|
|
37
49
|
"PostgresConfig",
|
|
38
50
|
"PostgresEngine",
|
|
39
51
|
"SQLiteEngine",
|
|
@@ -41,30 +53,33 @@ __all__ = [
|
|
|
41
53
|
"StorageCapabilities",
|
|
42
54
|
"StorageEngine",
|
|
43
55
|
"StorageUnavailable",
|
|
56
|
+
"WorkflowEngine",
|
|
44
57
|
"storage_from_env",
|
|
45
58
|
"__version__",
|
|
46
59
|
]
|
|
47
60
|
|
|
61
|
+
_LAZY = {
|
|
62
|
+
"AssembledContext": ("context", "AssembledContext"),
|
|
63
|
+
"ContextAssembler": ("context", "ContextAssembler"),
|
|
64
|
+
"ContextSection": ("context", "ContextSection"),
|
|
65
|
+
"ConversationStore": ("conversations", "ConversationStore"),
|
|
66
|
+
"BrainMemory": ("memory", "BrainMemory"),
|
|
67
|
+
"KnowledgeGraphStore": ("graph.store", "KnowledgeGraphStore"),
|
|
68
|
+
"IngestionItem": ("ingestion", "IngestionItem"),
|
|
69
|
+
"IngestionPipeline": ("ingestion", "IngestionPipeline"),
|
|
70
|
+
"KGPortabilityService": ("portability", "KGPortabilityService"),
|
|
71
|
+
"WorkflowEngine": ("workflow", "WorkflowEngine"),
|
|
72
|
+
"AgentRuntime": ("runtime.agent_runtime", "AgentRuntime"),
|
|
73
|
+
"MultiAgentOrchestrator": ("runtime.multi_agent", "MultiAgentOrchestrator"),
|
|
74
|
+
}
|
|
48
75
|
|
|
49
|
-
def __getattr__(name: str):
|
|
50
|
-
if name in {"AssembledContext", "ContextAssembler", "ContextSection"}:
|
|
51
|
-
from .context import AssembledContext, ContextAssembler, ContextSection
|
|
52
|
-
|
|
53
|
-
return {
|
|
54
|
-
"AssembledContext": AssembledContext,
|
|
55
|
-
"ContextAssembler": ContextAssembler,
|
|
56
|
-
"ContextSection": ContextSection,
|
|
57
|
-
}[name]
|
|
58
|
-
if name == "ConversationStore":
|
|
59
|
-
from .conversations import ConversationStore
|
|
60
76
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
from .store import KnowledgeGraphStore
|
|
77
|
+
def __getattr__(name: str):
|
|
78
|
+
target = _LAZY.get(name)
|
|
79
|
+
if target is None:
|
|
80
|
+
raise AttributeError(name)
|
|
81
|
+
module_path, attr = target
|
|
82
|
+
import importlib
|
|
68
83
|
|
|
69
|
-
|
|
70
|
-
|
|
84
|
+
module = importlib.import_module(f".{module_path}", __name__)
|
|
85
|
+
return getattr(module, attr)
|
|
@@ -1 +1,11 @@
|
|
|
1
|
-
|
|
1
|
+
"""Compatibility shim: implementation moved to lattice_brain.graph._kg_common.
|
|
2
|
+
|
|
3
|
+
This module aliases itself to the physical module so identity, singletons,
|
|
4
|
+
and monkeypatching behave as if the old flat path were the real module.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import sys
|
|
8
|
+
|
|
9
|
+
from .graph import _kg_common as _impl
|
|
10
|
+
|
|
11
|
+
sys.modules[__name__] = _impl
|