ltcai 4.2.0 → 4.3.1

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 (51) hide show
  1. package/README.md +28 -21
  2. package/bin/ltcai.js +6 -2
  3. package/docs/CHANGELOG.md +72 -0
  4. package/docs/V4_3_PORTABILITY_ARCHITECTURE.md +69 -0
  5. package/docs/V4_3_PRIVACY_AUDIT.md +60 -0
  6. package/docs/V4_3_PRODUCT_HARDENING_REPORT.md +53 -0
  7. package/docs/V4_3_VALIDATION_REPORT.md +58 -0
  8. package/docs/V4_DIGITAL_BRAIN_RECOVERY.md +19 -25
  9. package/frontend/openapi.json +213 -1
  10. package/frontend/src/App.tsx +15 -1
  11. package/frontend/src/api/client.ts +26 -1
  12. package/frontend/src/api/openapi.ts +268 -0
  13. package/frontend/src/pages/Act.tsx +63 -2
  14. package/frontend/src/pages/Library.tsx +9 -3
  15. package/frontend/src/pages/System.tsx +58 -0
  16. package/lattice_brain/__init__.py +1 -1
  17. package/lattice_brain/archive.py +360 -47
  18. package/lattice_brain/storage/sqlite.py +15 -2
  19. package/latticeai/__init__.py +1 -1
  20. package/latticeai/api/admin.py +11 -0
  21. package/latticeai/api/agents.py +3 -1
  22. package/latticeai/api/models.py +66 -18
  23. package/latticeai/api/portability.py +59 -2
  24. package/latticeai/app_factory.py +9 -0
  25. package/latticeai/brain/projection.py +12 -2
  26. package/latticeai/brain/retrieval.py +10 -0
  27. package/latticeai/brain/store.py +6 -1
  28. package/latticeai/core/config.py +4 -2
  29. package/latticeai/core/marketplace.py +1 -1
  30. package/latticeai/core/multi_agent.py +1 -1
  31. package/latticeai/core/product_hardening.py +218 -0
  32. package/latticeai/core/workspace_os.py +1 -1
  33. package/latticeai/services/agent_runtime.py +52 -12
  34. package/latticeai/services/kg_portability.py +147 -4
  35. package/latticeai/services/model_runtime.py +83 -2
  36. package/ltcai_cli.py +16 -4
  37. package/package.json +5 -4
  38. package/requirements.txt +17 -0
  39. package/scripts/clean_release_artifacts.mjs +27 -0
  40. package/scripts/lint_frontend.mjs +5 -0
  41. package/scripts/validate_release_artifacts.py +10 -0
  42. package/src-tauri/Cargo.lock +1 -1
  43. package/src-tauri/Cargo.toml +1 -1
  44. package/src-tauri/src/main.rs +356 -24
  45. package/src-tauri/tauri.conf.json +20 -1
  46. package/static/app/asset-manifest.json +5 -5
  47. package/static/app/assets/{index-C_HAkbAg.js → index-BhPuj8rT.js} +45 -45
  48. package/static/app/assets/index-BhPuj8rT.js.map +1 -0
  49. package/static/app/assets/{index-CDjiH_se.css → index-yZswHE3d.css} +1 -1
  50. package/static/app/index.html +2 -2
  51. package/static/app/assets/index-C_HAkbAg.js.map +0 -1
@@ -731,8 +731,42 @@
731
731
  "title": "EncryptedArchiveRequest",
732
732
  "type": "object"
733
733
  },
734
+ "EncryptedInspectRequest": {
735
+ "properties": {
736
+ "passphrase": {
737
+ "anyOf": [
738
+ {
739
+ "type": "string"
740
+ },
741
+ {
742
+ "type": "null"
743
+ }
744
+ ],
745
+ "title": "Passphrase"
746
+ },
747
+ "path": {
748
+ "title": "Path",
749
+ "type": "string"
750
+ }
751
+ },
752
+ "required": [
753
+ "path"
754
+ ],
755
+ "title": "EncryptedInspectRequest",
756
+ "type": "object"
757
+ },
734
758
  "EncryptedRestoreRequest": {
735
759
  "properties": {
760
+ "confirm": {
761
+ "default": false,
762
+ "title": "Confirm",
763
+ "type": "boolean"
764
+ },
765
+ "dry_run": {
766
+ "default": false,
767
+ "title": "Dry Run",
768
+ "type": "boolean"
769
+ },
736
770
  "passphrase": {
737
771
  "title": "Passphrase",
738
772
  "type": "string"
@@ -749,6 +783,24 @@
749
783
  "title": "EncryptedRestoreRequest",
750
784
  "type": "object"
751
785
  },
786
+ "EncryptedVerifyRequest": {
787
+ "properties": {
788
+ "passphrase": {
789
+ "title": "Passphrase",
790
+ "type": "string"
791
+ },
792
+ "path": {
793
+ "title": "Path",
794
+ "type": "string"
795
+ }
796
+ },
797
+ "required": [
798
+ "path",
799
+ "passphrase"
800
+ ],
801
+ "title": "EncryptedVerifyRequest",
802
+ "type": "object"
803
+ },
752
804
  "ExportRequest": {
753
805
  "properties": {
754
806
  "filters": {
@@ -1297,6 +1349,11 @@
1297
1349
  ],
1298
1350
  "title": "Adapter Path"
1299
1351
  },
1352
+ "allow_download": {
1353
+ "default": false,
1354
+ "title": "Allow Download",
1355
+ "type": "boolean"
1356
+ },
1300
1357
  "draft_model_id": {
1301
1358
  "anyOf": [
1302
1359
  {
@@ -1768,6 +1825,11 @@
1768
1825
  },
1769
1826
  "PrepareModelRequest": {
1770
1827
  "properties": {
1828
+ "allow_download": {
1829
+ "default": false,
1830
+ "title": "Allow Download",
1831
+ "type": "boolean"
1832
+ },
1771
1833
  "engine": {
1772
1834
  "anyOf": [
1773
1835
  {
@@ -1946,6 +2008,16 @@
1946
2008
  },
1947
2009
  "RestoreRequest": {
1948
2010
  "properties": {
2011
+ "confirm": {
2012
+ "default": false,
2013
+ "title": "Confirm",
2014
+ "type": "boolean"
2015
+ },
2016
+ "dry_run": {
2017
+ "default": false,
2018
+ "title": "Dry Run",
2019
+ "type": "boolean"
2020
+ },
1949
2021
  "path": {
1950
2022
  "title": "Path",
1951
2023
  "type": "string"
@@ -3481,7 +3553,7 @@
3481
3553
  },
3482
3554
  "info": {
3483
3555
  "title": "Lattice AI Server (local)",
3484
- "version": "4.2.0"
3556
+ "version": "4.3.1"
3485
3557
  },
3486
3558
  "openapi": "3.1.0",
3487
3559
  "paths": {
@@ -3763,6 +3835,22 @@
3763
3835
  "summary": "Admin Policies"
3764
3836
  }
3765
3837
  },
3838
+ "/admin/product-hardening": {
3839
+ "get": {
3840
+ "operationId": "admin_product_hardening_admin_product_hardening_get",
3841
+ "responses": {
3842
+ "200": {
3843
+ "content": {
3844
+ "application/json": {
3845
+ "schema": {}
3846
+ }
3847
+ },
3848
+ "description": "Successful Response"
3849
+ }
3850
+ },
3851
+ "summary": "Admin Product Hardening"
3852
+ }
3853
+ },
3766
3854
  "/admin/roles": {
3767
3855
  "get": {
3768
3856
  "operationId": "admin_roles_admin_roles_get",
@@ -6156,6 +6244,78 @@
6156
6244
  "summary": "Encrypted Archive"
6157
6245
  }
6158
6246
  },
6247
+ "/api/knowledge-graph/archive/import": {
6248
+ "post": {
6249
+ "operationId": "import_encrypted_archive_api_knowledge_graph_archive_import_post",
6250
+ "requestBody": {
6251
+ "content": {
6252
+ "application/json": {
6253
+ "schema": {
6254
+ "$ref": "#/components/schemas/EncryptedRestoreRequest"
6255
+ }
6256
+ }
6257
+ },
6258
+ "required": true
6259
+ },
6260
+ "responses": {
6261
+ "200": {
6262
+ "content": {
6263
+ "application/json": {
6264
+ "schema": {}
6265
+ }
6266
+ },
6267
+ "description": "Successful Response"
6268
+ },
6269
+ "422": {
6270
+ "content": {
6271
+ "application/json": {
6272
+ "schema": {
6273
+ "$ref": "#/components/schemas/HTTPValidationError"
6274
+ }
6275
+ }
6276
+ },
6277
+ "description": "Validation Error"
6278
+ }
6279
+ },
6280
+ "summary": "Import Encrypted Archive"
6281
+ }
6282
+ },
6283
+ "/api/knowledge-graph/archive/inspect": {
6284
+ "post": {
6285
+ "operationId": "inspect_encrypted_archive_api_knowledge_graph_archive_inspect_post",
6286
+ "requestBody": {
6287
+ "content": {
6288
+ "application/json": {
6289
+ "schema": {
6290
+ "$ref": "#/components/schemas/EncryptedInspectRequest"
6291
+ }
6292
+ }
6293
+ },
6294
+ "required": true
6295
+ },
6296
+ "responses": {
6297
+ "200": {
6298
+ "content": {
6299
+ "application/json": {
6300
+ "schema": {}
6301
+ }
6302
+ },
6303
+ "description": "Successful Response"
6304
+ },
6305
+ "422": {
6306
+ "content": {
6307
+ "application/json": {
6308
+ "schema": {
6309
+ "$ref": "#/components/schemas/HTTPValidationError"
6310
+ }
6311
+ }
6312
+ },
6313
+ "description": "Validation Error"
6314
+ }
6315
+ },
6316
+ "summary": "Inspect Encrypted Archive"
6317
+ }
6318
+ },
6159
6319
  "/api/knowledge-graph/archive/restore": {
6160
6320
  "post": {
6161
6321
  "operationId": "restore_encrypted_archive_api_knowledge_graph_archive_restore_post",
@@ -6192,6 +6352,42 @@
6192
6352
  "summary": "Restore Encrypted Archive"
6193
6353
  }
6194
6354
  },
6355
+ "/api/knowledge-graph/archive/verify": {
6356
+ "post": {
6357
+ "operationId": "verify_encrypted_archive_api_knowledge_graph_archive_verify_post",
6358
+ "requestBody": {
6359
+ "content": {
6360
+ "application/json": {
6361
+ "schema": {
6362
+ "$ref": "#/components/schemas/EncryptedVerifyRequest"
6363
+ }
6364
+ }
6365
+ },
6366
+ "required": true
6367
+ },
6368
+ "responses": {
6369
+ "200": {
6370
+ "content": {
6371
+ "application/json": {
6372
+ "schema": {}
6373
+ }
6374
+ },
6375
+ "description": "Successful Response"
6376
+ },
6377
+ "422": {
6378
+ "content": {
6379
+ "application/json": {
6380
+ "schema": {
6381
+ "$ref": "#/components/schemas/HTTPValidationError"
6382
+ }
6383
+ }
6384
+ },
6385
+ "description": "Validation Error"
6386
+ }
6387
+ },
6388
+ "summary": "Verify Encrypted Archive"
6389
+ }
6390
+ },
6195
6391
  "/api/knowledge-graph/backup": {
6196
6392
  "post": {
6197
6393
  "operationId": "backup_graph_api_knowledge_graph_backup_post",
@@ -6228,6 +6424,22 @@
6228
6424
  "summary": "Backup Graph"
6229
6425
  }
6230
6426
  },
6427
+ "/api/knowledge-graph/backup-health": {
6428
+ "get": {
6429
+ "operationId": "backup_health_api_knowledge_graph_backup_health_get",
6430
+ "responses": {
6431
+ "200": {
6432
+ "content": {
6433
+ "application/json": {
6434
+ "schema": {}
6435
+ }
6436
+ },
6437
+ "description": "Successful Response"
6438
+ }
6439
+ },
6440
+ "summary": "Backup Health"
6441
+ }
6442
+ },
6231
6443
  "/api/knowledge-graph/export": {
6232
6444
  "post": {
6233
6445
  "description": "Logical JSON export of the whole graph (read-only).",
@@ -82,6 +82,12 @@ export default function App() {
82
82
  const [drawer, setDrawer] = React.useState(false);
83
83
  const [palette, setPalette] = React.useState(false);
84
84
  const health = useQuery({ queryKey: ["health"], queryFn: latticeApi.health });
85
+ const desktop = useQuery({
86
+ queryKey: ["desktopBackendStatus"],
87
+ queryFn: latticeApi.desktopBackendStatus,
88
+ enabled: Boolean(window.__TAURI_INTERNALS__),
89
+ refetchInterval: 5000,
90
+ });
85
91
  const workspace = useQuery({ queryKey: ["workspaceOs"], queryFn: latticeApi.workspaceOs });
86
92
 
87
93
  React.useEffect(() => {
@@ -98,6 +104,11 @@ export default function App() {
98
104
  return () => window.removeEventListener("keydown", onKey);
99
105
  }, []);
100
106
 
107
+ const healthData = (health.data?.data || {}) as Record<string, unknown>;
108
+ const appVersion = typeof healthData.version === "string" ? healthData.version : null;
109
+ const desktopData = (desktop.data?.data || {}) as Record<string, unknown>;
110
+ const desktopError = typeof desktopData.last_error === "string" ? desktopData.last_error : desktop.data?.error;
111
+
101
112
  const rail = (
102
113
  <aside className="flex h-full w-64 shrink-0 flex-col border-r border-border bg-card">
103
114
  <div className="flex h-16 items-center gap-3 border-b border-border px-4">
@@ -134,6 +145,9 @@ export default function App() {
134
145
  </nav>
135
146
  <div className="border-t border-border p-3 text-xs text-muted-foreground">
136
147
  <div>Server: {health.data?.ok ? "online" : "unavailable"}</div>
148
+ {window.__TAURI_INTERNALS__ ? (
149
+ <div>Sidecar: {desktopData.running ? "running" : desktopError ? `unavailable (${desktopError})` : "starting"}</div>
150
+ ) : null}
137
151
  <div>Workspace: {String((workspace.data?.data as Record<string, unknown>)?.active_workspace || "local")}</div>
138
152
  </div>
139
153
  </aside>
@@ -154,7 +168,7 @@ export default function App() {
154
168
  <div className="flex min-w-0 items-center gap-2">
155
169
  <Button variant="ghost" size="icon" className="lg:hidden" onClick={() => setDrawer(true)}><Menu className="h-5 w-5" /></Button>
156
170
  <div className="min-w-0">
157
- <div className="truncate text-sm text-muted-foreground">v4.1.0 Release Candidate</div>
171
+ <div className="truncate text-sm text-muted-foreground">{appVersion ? `v${appVersion}` : "Version unavailable"}</div>
158
172
  <div className="truncate font-medium">{primaryRoutes.find((item) => item.id === route.primary)?.label}</div>
159
173
  </div>
160
174
  </div>
@@ -38,6 +38,16 @@ async function tauriBackendOrigin(): Promise<string | null> {
38
38
  return desktopBase;
39
39
  }
40
40
 
41
+ async function tauriInvoke<T>(command: string): Promise<T | null> {
42
+ if (!window.__TAURI_INTERNALS__) return null;
43
+ try {
44
+ const { invoke } = await import("@tauri-apps/api/core");
45
+ return await invoke<T>(command);
46
+ } catch {
47
+ return null;
48
+ }
49
+ }
50
+
41
51
  async function apiBase() {
42
52
  const stateBase = useAppStore.getState().apiBase;
43
53
  if (stateBase) return stateBase;
@@ -203,6 +213,11 @@ async function streamChat(body: Record<string, unknown>, handlers: ChatEventHand
203
213
 
204
214
  export const latticeApi = {
205
215
  raw: get,
216
+ desktopBackendStatus: async (): Promise<ApiResult<Record<string, unknown>>> => {
217
+ const status = await tauriInvoke<Record<string, unknown>>("backend_status");
218
+ if (status) return { ok: true, status: 200, data: status, source: "live" };
219
+ return { ok: false, status: 0, data: {}, source: "unavailable", error: "Desktop backend status is available only inside the Tauri shell." };
220
+ },
206
221
  health: () => get("/health", {}),
207
222
  workspaceOs: () => get("/workspace/os", { counts: {}, models: {}, workspace_registry: { workspaces: [] } }),
208
223
  indexStatus: () => get("/api/index/status", {}),
@@ -211,6 +226,7 @@ export const latticeApi = {
211
226
  graphStats: () => get("/knowledge-graph/stats", { nodes: {}, edges: {}, total_nodes: 0, total_edges: 0 }),
212
227
  graphPortability: () => get("/api/knowledge-graph/portability", {}),
213
228
  brainStorage: () => get("/api/brain/storage", {}),
229
+ backupHealth: () => get("/api/knowledge-graph/backup-health", {}),
214
230
  dockerPostgres: (body: { consent: boolean; dry_run?: boolean; port?: number }) => post("/api/brain/storage/postgres/docker", body, {}),
215
231
  migratePostgres: (body: { dsn: string; schema_name?: string; dry_run?: boolean }) => post("/api/brain/storage/migrate-postgres", body, {}),
216
232
  graphProvenance: (limit = 50) => get("/api/knowledge-graph/provenance", { items: [] }, { limit }),
@@ -218,6 +234,11 @@ export const latticeApi = {
218
234
  graphExport: () => post("/api/knowledge-graph/export", {}, {}),
219
235
  graphBackup: () => post("/api/knowledge-graph/backup", {}, {}),
220
236
  graphImport: (artifact: unknown, dry_run = true) => post("/api/knowledge-graph/import", { artifact, mode: "merge", dry_run }, {}),
237
+ brainArchive: (body: { path?: string | null; passphrase: string }) => post("/api/knowledge-graph/archive", body, {}),
238
+ brainArchiveInspect: (body: { path: string; passphrase?: string | null }) => post("/api/knowledge-graph/archive/inspect", body, {}),
239
+ brainArchiveVerify: (body: { path: string; passphrase: string }) => post("/api/knowledge-graph/archive/verify", body, {}),
240
+ brainArchiveRestore: (body: { path: string; passphrase: string; dry_run?: boolean; confirm?: boolean }) => post("/api/knowledge-graph/archive/restore", body, {}),
241
+ brainArchiveImport: (body: { path: string; passphrase: string; dry_run?: boolean; confirm?: boolean }) => post("/api/knowledge-graph/archive/import", body, {}),
221
242
  hybridSearch: async (query: string, weights?: unknown) => {
222
243
  const res = await post<Record<string, unknown>>("/api/search/hybrid", { query, ...(weights ? { weights } : {}) }, { matches: [] });
223
244
  const data = res.data as Record<string, unknown>;
@@ -242,7 +263,7 @@ export const latticeApi = {
242
263
  connectFolder: (path: string) => post("/knowledge-graph/local/index", { path, approved: true, watch_enabled: true, consent: { approved: true, source: "desktop-spa" } }, {}),
243
264
  localWatchStop: (source_id: string) => post("/knowledge-graph/local/watch/stop", { source_id }, {}),
244
265
  models: () => get("/models", { catalog: [], loaded: [], recommended: [] }),
245
- loadModel: (model_id: string, engine?: string) => post("/models/load", { model_id, engine: engine || null }, {}),
266
+ loadModel: (model_id: string, engine?: string, allow_download = false) => post("/models/load", { model_id, engine: engine || null, allow_download }, {}),
246
267
  unloadModel: (model_id: string) => del(`/models/unload/${encodeURIComponent(model_id)}`, {}),
247
268
  embeddingsStatus: () => get("/api/embeddings/status", {}),
248
269
  agentRuntime: () => get("/agents/api/runtime/status", { runtime: {}, agents: [], runs: [] }),
@@ -257,6 +278,9 @@ export const latticeApi = {
257
278
  workflowDefinitions: () => get("/workflows/api/definitions", { workflows: [] }),
258
279
  workflowRuns: () => get("/workflows/api/runs", { runs: [] }),
259
280
  workflowTriggers: () => get("/workflows/api/triggers", { armed: [] }),
281
+ createWorkflow: (body: { name: string; nodes: Array<Record<string, unknown>>; metadata?: Record<string, unknown> }) => post("/workflows/api/definitions", body, {}),
282
+ importWorkflow: (data: Record<string, unknown>) => post("/workflows/api/import", { data }, {}),
283
+ exportWorkflow: (id: string) => get(`/workflows/api/export/${encodeURIComponent(id)}`, {}),
260
284
  runWorkflow: (id: string) => post(`/workflows/api/definitions/${encodeURIComponent(id)}/run`, {}, {}),
261
285
  updateWorkflow: (id: string, body: unknown) => patch(`/workflows/api/definitions/${encodeURIComponent(id)}`, body, {}),
262
286
  stopWorkflowRun: (id: string) => post(`/workflows/api/runs/${encodeURIComponent(id)}/stop`, {}, {}),
@@ -314,6 +338,7 @@ export const latticeApi = {
314
338
  adminAudit: () => get("/admin/audit", { recent_events: [] }),
315
339
  adminRoles: () => get("/admin/roles", { roles: [] }),
316
340
  adminPolicies: () => get("/admin/policies", { policies: [] }),
341
+ adminProductHardening: () => get("/admin/product-hardening", {}),
317
342
  adminSecurity: () => get("/admin/security/overview", {}),
318
343
  vpcStatus: () => get("/vpc/status", {}),
319
344
  toolPermissions: () => get("/tools/permissions", { permissions: [] }),