ltcai 11.2.0 → 11.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 +46 -53
- package/docs/CHANGELOG.md +61 -0
- package/docs/COMMUNITY_AND_PLUGINS.md +1 -1
- package/docs/DEVELOPMENT.md +1 -1
- package/docs/MULTI_AGENT_RUNTIME.md +1 -1
- package/docs/ONBOARDING.md +1 -1
- package/docs/OPERATIONS.md +6 -2
- package/docs/PERMISSION_MODE.md +1 -1
- package/docs/TRUST_MODEL.md +1 -1
- package/docs/WHY_LATTICE.md +1 -1
- package/docs/kg-schema.md +2 -2
- package/docs/v11.3.0_PLAN.md +202 -0
- package/docs/v11.4.0_RUST_FOUNDATION_PLAN.md +176 -0
- package/lattice_brain/__init__.py +1 -1
- package/lattice_brain/graph/_kg_common/__init__.py +287 -0
- package/lattice_brain/graph/_kg_common/extraction.py +516 -0
- package/lattice_brain/graph/_kg_common/relations.py +161 -0
- package/lattice_brain/graph/_kg_common/text.py +479 -0
- package/lattice_brain/graph/discovery_index/__init__.py +35 -0
- package/lattice_brain/graph/discovery_index/cleanup.py +182 -0
- package/lattice_brain/graph/discovery_index/extract.py +137 -0
- package/lattice_brain/graph/discovery_index/scan.py +411 -0
- package/lattice_brain/graph/discovery_index/upsert.py +495 -0
- package/lattice_brain/graph/projection/__init__.py +42 -0
- package/lattice_brain/graph/projection/curation.py +500 -0
- package/lattice_brain/graph/{projection.py → projection/v2_schema.py} +15 -477
- package/lattice_brain/graph/retrieval/__init__.py +54 -0
- package/lattice_brain/graph/retrieval/context.py +197 -0
- package/lattice_brain/graph/retrieval/graph_view.py +319 -0
- package/lattice_brain/graph/retrieval/hybrid.py +488 -0
- package/lattice_brain/graph/retrieval/maintenance.py +121 -0
- package/lattice_brain/graph/retrieval/signals.py +95 -0
- package/lattice_brain/graph/retrieval_vector/__init__.py +42 -0
- package/lattice_brain/graph/retrieval_vector/fingerprint.py +97 -0
- package/lattice_brain/graph/retrieval_vector/indexing.py +347 -0
- package/lattice_brain/graph/retrieval_vector/search.py +560 -0
- package/lattice_brain/graph/retrieval_vector/status.py +374 -0
- package/lattice_brain/ingestion/__init__.py +130 -0
- package/lattice_brain/ingestion/_contract.py +90 -0
- package/lattice_brain/ingestion/constants.py +127 -0
- package/lattice_brain/ingestion/folder_scan.py +57 -0
- package/lattice_brain/ingestion/folders.py +258 -0
- package/lattice_brain/ingestion/hashing.py +26 -0
- package/lattice_brain/ingestion/jobs_api.py +107 -0
- package/lattice_brain/ingestion/models.py +80 -0
- package/lattice_brain/ingestion/pipeline.py +486 -0
- package/lattice_brain/ingestion/quality.py +209 -0
- package/lattice_brain/ingestion/routing.py +295 -0
- package/lattice_brain/multimodal/__init__.py +164 -0
- package/lattice_brain/multimodal/audio.py +77 -0
- package/lattice_brain/multimodal/common.py +118 -0
- package/lattice_brain/multimodal/images.py +498 -0
- package/lattice_brain/multimodal/ports.py +169 -0
- package/lattice_brain/multimodal/video.py +410 -0
- package/lattice_brain/portability/__init__.py +90 -0
- package/lattice_brain/portability/_contract.py +42 -0
- package/lattice_brain/portability/backups.py +338 -0
- package/lattice_brain/portability/bundles.py +136 -0
- package/lattice_brain/portability/constants.py +93 -0
- package/lattice_brain/portability/fsops.py +138 -0
- package/lattice_brain/portability/service.py +41 -0
- package/lattice_brain/{portability.py → portability/sharing.py} +44 -677
- package/lattice_brain/runtime/__init__.py +1 -1
- package/lattice_brain/runtime/multi_agent.py +1 -1
- package/latticeai/__init__.py +1 -1
- package/latticeai/api/chronicle.py +63 -0
- package/latticeai/core/agent/__init__.py +93 -0
- package/latticeai/core/agent/_contract.py +79 -0
- package/latticeai/core/agent/context.py +57 -0
- package/latticeai/core/agent/deps.py +125 -0
- package/latticeai/core/agent/execution.py +622 -0
- package/latticeai/core/agent/planning.py +145 -0
- package/latticeai/core/agent/recovery.py +157 -0
- package/latticeai/core/agent/runtime.py +210 -0
- package/latticeai/core/agent/verification.py +231 -0
- package/latticeai/core/embedding_providers/__init__.py +151 -0
- package/latticeai/core/embedding_providers/base.py +199 -0
- package/latticeai/core/embedding_providers/captions.py +162 -0
- package/latticeai/core/embedding_providers/profiles.py +126 -0
- package/latticeai/core/embedding_providers/text.py +350 -0
- package/latticeai/core/embedding_providers/vision.py +352 -0
- package/latticeai/core/file_generation/__init__.py +115 -0
- package/latticeai/core/file_generation/bundles.py +76 -0
- package/latticeai/core/file_generation/extraction.py +154 -0
- package/latticeai/core/file_generation/inference.py +235 -0
- package/latticeai/core/file_generation/orchestration.py +152 -0
- package/latticeai/core/file_generation/prompting.py +117 -0
- package/latticeai/core/file_generation/repair.py +114 -0
- package/latticeai/core/file_generation/sanitize.py +61 -0
- package/latticeai/core/file_generation/validation.py +201 -0
- package/latticeai/core/legacy_compatibility.py +1 -1
- package/latticeai/core/marketplace.py +1 -1
- package/latticeai/core/messages.py +9 -0
- package/latticeai/core/workspace_os_constants.py +1 -1
- package/latticeai/integrations/telegram_bot/__init__.py +123 -0
- package/latticeai/integrations/telegram_bot/__main__.py +17 -0
- package/latticeai/integrations/telegram_bot/config.py +86 -0
- package/latticeai/integrations/telegram_bot/dispatch.py +311 -0
- package/latticeai/integrations/telegram_bot/flows.py +478 -0
- package/latticeai/integrations/telegram_bot/helpers.py +322 -0
- package/latticeai/integrations/telegram_bot/screens.py +394 -0
- package/latticeai/models/router/__init__.py +88 -0
- package/latticeai/models/router/_contract.py +66 -0
- package/latticeai/models/router/branding.py +56 -0
- package/latticeai/models/router/catalog.py +69 -0
- package/latticeai/models/router/documents.py +199 -0
- package/latticeai/models/router/errors.py +37 -0
- package/latticeai/models/router/generation.py +258 -0
- package/latticeai/models/router/loading.py +291 -0
- package/latticeai/models/router/local_models.py +85 -0
- package/latticeai/models/router/registry.py +147 -0
- package/latticeai/runtime/build_phases/__init__.py +82 -0
- package/latticeai/runtime/build_phases/features.py +407 -0
- package/latticeai/runtime/build_phases/foundation.py +555 -0
- package/latticeai/runtime/build_phases/web.py +492 -0
- package/latticeai/runtime/runtime_context.py +1 -0
- package/latticeai/services/architecture_readiness.py +48 -19
- package/latticeai/services/brain_intelligence/__init__.py +58 -0
- package/latticeai/services/brain_intelligence/_contract.py +71 -0
- package/latticeai/services/brain_intelligence/consistency.py +193 -0
- package/latticeai/services/brain_intelligence/constants.py +47 -0
- package/latticeai/services/brain_intelligence/digest.py +258 -0
- package/latticeai/services/brain_intelligence/health.py +331 -0
- package/latticeai/services/brain_intelligence/proposals.py +264 -0
- package/latticeai/services/brain_intelligence/sampling.py +84 -0
- package/latticeai/services/brain_intelligence/service.py +48 -0
- package/latticeai/services/chronicle.py +557 -0
- package/latticeai/services/memory_service/__init__.py +52 -0
- package/latticeai/services/memory_service/_contract.py +100 -0
- package/latticeai/services/memory_service/brief.py +431 -0
- package/latticeai/services/memory_service/constants.py +57 -0
- package/latticeai/services/memory_service/maintenance.py +138 -0
- package/latticeai/services/memory_service/manager.py +186 -0
- package/latticeai/services/memory_service/proof.py +136 -0
- package/latticeai/services/memory_service/recall.py +225 -0
- package/latticeai/services/memory_service/service.py +48 -0
- package/latticeai/services/memory_service/stores.py +110 -0
- package/latticeai/services/model_runtime/__init__.py +322 -0
- package/latticeai/services/model_runtime/cloud.py +87 -0
- package/latticeai/services/model_runtime/download.py +282 -0
- package/latticeai/services/model_runtime/engines.py +341 -0
- package/latticeai/services/model_runtime/loading.py +178 -0
- package/latticeai/services/model_runtime/service.py +129 -0
- package/latticeai/services/model_runtime/state.py +131 -0
- package/latticeai/services/model_runtime/status.py +255 -0
- package/latticeai/services/product_readiness.py +15 -7
- package/latticeai/setup/wizard/__init__.py +126 -0
- package/latticeai/setup/wizard/catalog.py +172 -0
- package/latticeai/setup/wizard/detect.py +323 -0
- package/latticeai/setup/wizard/install.py +348 -0
- package/latticeai/setup/wizard/paths.py +168 -0
- package/latticeai/setup/wizard/plans.py +74 -0
- package/latticeai/setup/wizard/recommend.py +320 -0
- package/package.json +6 -2
- package/scripts/bump_version.py +14 -0
- package/scripts/capture_release_evidence.mjs +33 -21
- package/scripts/check_current_release_docs.mjs +1 -1
- package/scripts/check_i18n_namespace_coverage.mjs +41 -4
- package/scripts/check_max_file_lines.mjs +102 -0
- package/scripts/check_release_evidence_bound.mjs +30 -15
- package/scripts/check_screenshot_pixel_delta.py +34 -4
- package/scripts/check_server_i18n.mjs +1 -0
- package/scripts/generate_rust_parity_fixtures.py +562 -0
- package/scripts/lib/mock_server_fingerprint.mjs +94 -0
- package/scripts/release_screen_claims.json +31 -2
- package/src-tauri/Cargo.lock +361 -3
- package/src-tauri/Cargo.toml +6 -1
- package/src-tauri/src/backend.rs +349 -0
- package/src-tauri/src/folder.rs +33 -0
- package/src-tauri/src/main.rs +97 -399
- package/src-tauri/tauri.conf.json +1 -1
- package/static/app/asset-manifest.json +41 -37
- package/static/app/assets/Act-yYpYnn0v.js +1 -0
- package/static/app/assets/AdminConsole-DL3Cr5pL.js +1 -0
- package/static/app/assets/{Brain-tuhI4sOC.js → Brain-C1HBN0Wf.js} +2 -2
- package/static/app/assets/BrainHome-DoXRhUUC.js +2 -0
- package/static/app/assets/BrainSignals-6yR6ir5t.js +1 -0
- package/static/app/assets/Capture-CFIRsFNE.js +1 -0
- package/static/app/assets/Chronicle-BZbEgiwN.js +1 -0
- package/static/app/assets/CommandPalette-D2pMxC2I.js +1 -0
- package/static/app/assets/Library-DwO3yZST.js +1 -0
- package/static/app/assets/{LivingBrain-DBwhto14.js → LivingBrain-Jn1GK0-S.js} +1 -1
- package/static/app/assets/ProductFlow-B-w1R4Oo.js +1 -0
- package/static/app/assets/ReviewCard-6B27X8Vg.js +3 -0
- package/static/app/assets/System-DW8F-2xL.js +1 -0
- package/static/app/assets/arrow-left-DXvKg9U6.js +1 -0
- package/static/app/assets/{bot-Cia42c2h.js → bot-IM_E_Y12.js} +1 -1
- package/static/app/assets/brain-Ci1CkWjM.js +1 -0
- package/static/app/assets/{button-2j2Ijzgq.js → button-COwyqfHM.js} +1 -1
- package/static/app/assets/circle-check-DfInj-qD.js +1 -0
- package/static/app/assets/{circle-pause-BEFeWpVW.js → circle-pause-DEM4A1Y5.js} +1 -1
- package/static/app/assets/{circle-play-ujXMcHxl.js → circle-play-C9djDuLd.js} +1 -1
- package/static/app/assets/{cpu-k4awryFq.js → cpu-DFdo1gw-.js} +1 -1
- package/static/app/assets/{download-DFbLJ_ig.js → download-SnJL6oqk.js} +1 -1
- package/static/app/assets/{folder-open-7y_b6xkM.js → folder-open-CqZeDkjE.js} +1 -1
- package/static/app/assets/{hard-drive-Bidh02Kr.js → hard-drive-j1jJXYYf.js} +1 -1
- package/static/app/assets/{index-DwDl9-8Y.css → index-BLPb5lmE.css} +1 -1
- package/static/app/assets/index-_u5iUHDr.js +10 -0
- package/static/app/assets/input-B0lPdRQZ.js +1 -0
- package/static/app/assets/link-2-CoFbooHS.js +1 -0
- package/static/app/assets/{permissionCopy-Bpb83Hx9.js → permissionCopy-BsyLxtao.js} +1 -1
- package/static/app/assets/primitives-DEbN-d6p.js +1 -0
- package/static/app/assets/search-BybIWPNd.js +1 -0
- package/static/app/assets/{share-2-BH1M-WNi.js → share-2-CVtZ_ewX.js} +1 -1
- package/static/app/assets/{shield-alert-BlKdBXcG.js → shield-alert-CBi2GNWM.js} +1 -1
- package/static/app/assets/{textarea-CCWbUfFB.js → textarea-DNMpB5ih.js} +1 -1
- package/static/app/assets/{useFocusTrap-YdHQ7pJ1.js → useFocusTrap-C83t3GXF.js} +1 -1
- package/static/app/assets/useMutation-DtbJDoyz.js +1 -0
- package/static/app/assets/{useQuery-CXQiwbVT.js → useQuery-Dcp1OChy.js} +1 -1
- package/static/app/assets/utils-BlZr7Pd4.js +4 -0
- package/static/app/assets/workspace-jJY4RuAV.js +1 -0
- package/static/app/index.html +4 -4
- package/static/sw.js +1 -1
- package/lattice_brain/graph/_kg_common.py +0 -1331
- package/lattice_brain/graph/discovery_index.py +0 -1141
- package/lattice_brain/graph/retrieval.py +0 -1120
- package/lattice_brain/graph/retrieval_vector.py +0 -1293
- package/lattice_brain/ingestion.py +0 -1525
- package/lattice_brain/multimodal.py +0 -1258
- package/latticeai/core/agent.py +0 -1465
- package/latticeai/core/embedding_providers.py +0 -1196
- package/latticeai/core/file_generation.py +0 -1047
- package/latticeai/integrations/telegram_bot.py +0 -1390
- package/latticeai/models/router.py +0 -1007
- package/latticeai/runtime/build_phases.py +0 -1450
- package/latticeai/services/brain_intelligence.py +0 -1083
- package/latticeai/services/memory_service.py +0 -1177
- package/latticeai/services/model_runtime.py +0 -1281
- package/latticeai/setup/wizard.py +0 -1310
- package/static/app/assets/Act-AWf0SAKp.js +0 -1
- package/static/app/assets/AdminConsole-D0u8Tiyj.js +0 -1
- package/static/app/assets/BrainHome-Ts7G_Ila.js +0 -2
- package/static/app/assets/BrainSignals-jMYgQ2Ar.js +0 -1
- package/static/app/assets/Capture-CqOSzyPr.js +0 -1
- package/static/app/assets/CommandPalette-DC0Bzh-I.js +0 -1
- package/static/app/assets/Library-CX-bbhmK.js +0 -1
- package/static/app/assets/ProductFlow-BHA2cfKI.js +0 -1
- package/static/app/assets/ReviewCard-BUhCKRNM.js +0 -3
- package/static/app/assets/System-Bu2t5hn1.js +0 -1
- package/static/app/assets/arrow-left-Dzwa5zRb.js +0 -1
- package/static/app/assets/brain-DJMoqrwx.js +0 -1
- package/static/app/assets/index-BpYkzcVm.js +0 -10
- package/static/app/assets/input-DSlJJxRs.js +0 -1
- package/static/app/assets/primitives-BCx6TvfG.js +0 -1
- package/static/app/assets/search-Cgy8cCFJ.js +0 -1
- package/static/app/assets/utils-zqPZJxdx.js +0 -4
- package/static/app/assets/workspace-DXTihhfU.js +0 -1
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
"""Atomic file swaps for restore, and the rollback that never masks a failure.
|
|
2
|
+
|
|
3
|
+
A restore replaces a live SQLite database and a blob tree in place. Each helper
|
|
4
|
+
here stages the new copy, keeps a pre-restore backup of what it is about to
|
|
5
|
+
overwrite, and — on any failure — puts the old files back *best-effort* while
|
|
6
|
+
always re-raising the original error. Recovery I/O that raises would hide the
|
|
7
|
+
failure being reported, which is the bug these guards exist for.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
import logging
|
|
13
|
+
import os
|
|
14
|
+
import shutil
|
|
15
|
+
import sqlite3
|
|
16
|
+
from contextlib import closing
|
|
17
|
+
from pathlib import Path, PurePosixPath
|
|
18
|
+
from typing import Optional
|
|
19
|
+
|
|
20
|
+
from ..quiet import quiet
|
|
21
|
+
from .constants import _stamp
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def _safe_zip_names(names) -> None:
|
|
25
|
+
for name in names:
|
|
26
|
+
path = PurePosixPath(name)
|
|
27
|
+
if path.is_absolute() or ".." in path.parts:
|
|
28
|
+
raise ValueError(f"Backup archive contains unsafe path: {name}")
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def _pre_restore_backup_dir(anchor: Path) -> Path:
|
|
32
|
+
backup_dir = anchor.parent / f"{anchor.name}.pre-restore-{_stamp()}"
|
|
33
|
+
index = 1
|
|
34
|
+
while backup_dir.exists():
|
|
35
|
+
backup_dir = anchor.parent / f"{anchor.name}.pre-restore-{_stamp()}-{index}"
|
|
36
|
+
index += 1
|
|
37
|
+
backup_dir.mkdir(parents=True)
|
|
38
|
+
return backup_dir
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def _sqlite_siblings(db_path: Path) -> tuple[Path, Path, Path]:
|
|
42
|
+
return (db_path, Path(str(db_path) + "-wal"), Path(str(db_path) + "-shm"))
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def _checkpoint_sqlite(db_path: Path) -> None:
|
|
46
|
+
if not db_path.exists():
|
|
47
|
+
return
|
|
48
|
+
try:
|
|
49
|
+
with closing(sqlite3.connect(str(db_path))) as conn, conn:
|
|
50
|
+
conn.execute("PRAGMA wal_checkpoint(FULL)")
|
|
51
|
+
except sqlite3.Error:
|
|
52
|
+
# Best-effort only. Existing sibling backup/restore still preserves
|
|
53
|
+
# the WAL files if a live connection prevents a checkpoint.
|
|
54
|
+
return
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def _restore_sibling(path: Path, backup: Path) -> None:
|
|
58
|
+
try:
|
|
59
|
+
shutil.copy2(backup, path)
|
|
60
|
+
except FileNotFoundError:
|
|
61
|
+
# The backup vanished (or never existed — transient -wal/-shm): the
|
|
62
|
+
# honest reconstruction is "no such sibling", never a crash that
|
|
63
|
+
# masks the error that triggered the rollback.
|
|
64
|
+
path.unlink(missing_ok=True)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def _replace_sqlite_atomically(src: Path, dest: Path, backup_dir: Path) -> None:
|
|
68
|
+
dest.parent.mkdir(parents=True, exist_ok=True)
|
|
69
|
+
tmp = dest.parent / f".{dest.name}.restore-{_stamp()}-{os.getpid()}.tmp"
|
|
70
|
+
shutil.copyfile(src, tmp)
|
|
71
|
+
backups: dict[Path, Path] = {}
|
|
72
|
+
try:
|
|
73
|
+
_checkpoint_sqlite(dest)
|
|
74
|
+
# -wal/-shm are transient: another live connection can checkpoint and
|
|
75
|
+
# remove them between exists() and the copy/unlink. Treat a vanished
|
|
76
|
+
# sibling as "nothing to preserve" instead of crashing the restore.
|
|
77
|
+
for sibling in _sqlite_siblings(dest):
|
|
78
|
+
backup = backup_dir / sibling.name
|
|
79
|
+
try:
|
|
80
|
+
shutil.copy2(sibling, backup)
|
|
81
|
+
except FileNotFoundError:
|
|
82
|
+
quiet()
|
|
83
|
+
continue
|
|
84
|
+
backups[sibling] = backup
|
|
85
|
+
for sibling in _sqlite_siblings(dest)[1:]:
|
|
86
|
+
sibling.unlink(missing_ok=True)
|
|
87
|
+
os.replace(tmp, dest)
|
|
88
|
+
except Exception:
|
|
89
|
+
# Recovery I/O must never replace the swap error being reported —
|
|
90
|
+
# an exception raised here would mask it (the CI-observed
|
|
91
|
+
# "[Errno 2]" over the real failure). Best-effort restore, then
|
|
92
|
+
# always re-raise the original.
|
|
93
|
+
try:
|
|
94
|
+
tmp.unlink(missing_ok=True)
|
|
95
|
+
except OSError as cleanup_exc:
|
|
96
|
+
logging.warning("restore tmp cleanup failed: %s", cleanup_exc)
|
|
97
|
+
for sibling in _sqlite_siblings(dest):
|
|
98
|
+
try:
|
|
99
|
+
_restore_sibling(sibling, backups.get(sibling, backup_dir / sibling.name))
|
|
100
|
+
except OSError as rollback_exc:
|
|
101
|
+
logging.warning(
|
|
102
|
+
"restore sibling rollback incomplete for %s: %s",
|
|
103
|
+
sibling, rollback_exc,
|
|
104
|
+
)
|
|
105
|
+
raise
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def _rollback_sqlite_from_backup(dest: Path, backup_dir: Path) -> None:
|
|
109
|
+
for sibling in _sqlite_siblings(dest):
|
|
110
|
+
_restore_sibling(sibling, backup_dir / sibling.name)
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def _replace_tree_with_backup(src: Optional[Path], dest: Path, backup_dir: Path) -> None:
|
|
114
|
+
dest.parent.mkdir(parents=True, exist_ok=True)
|
|
115
|
+
staged = dest.parent / f".{dest.name}.restore-{_stamp()}-{os.getpid()}"
|
|
116
|
+
backup = backup_dir / dest.name
|
|
117
|
+
if src and src.exists():
|
|
118
|
+
shutil.copytree(src, staged)
|
|
119
|
+
else:
|
|
120
|
+
staged.mkdir(parents=True)
|
|
121
|
+
try:
|
|
122
|
+
if dest.exists():
|
|
123
|
+
shutil.copytree(dest, backup)
|
|
124
|
+
shutil.rmtree(dest)
|
|
125
|
+
os.replace(staged, dest)
|
|
126
|
+
except Exception:
|
|
127
|
+
# Same masking guard as the sqlite swap: rollback I/O is
|
|
128
|
+
# best-effort and the original failure always propagates.
|
|
129
|
+
try:
|
|
130
|
+
if staged.exists():
|
|
131
|
+
shutil.rmtree(staged)
|
|
132
|
+
if dest.exists():
|
|
133
|
+
shutil.rmtree(dest)
|
|
134
|
+
if backup.exists():
|
|
135
|
+
shutil.copytree(backup, dest)
|
|
136
|
+
except OSError as rollback_exc:
|
|
137
|
+
logging.warning("blob tree rollback incomplete: %s", rollback_exc)
|
|
138
|
+
raise
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"""The composed portability service — state, availability, and the two halves.
|
|
2
|
+
|
|
3
|
+
``KGPortabilityService`` owns the constructor state every surface reads
|
|
4
|
+
(``_kg``, ``_data_dir``, ``_exports_dir``, ``_identity``, the lazily created
|
|
5
|
+
recipient key) and inherits its behaviour from the sharing and backup mixins.
|
|
6
|
+
Same public surface, same method resolution, in two readable files instead of
|
|
7
|
+
one thousand-line one.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
from typing import Any
|
|
14
|
+
|
|
15
|
+
from .backups import KGPortabilityBackupMixin
|
|
16
|
+
from .sharing import KGPortabilitySharingMixin
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class KGPortabilityService(KGPortabilitySharingMixin, KGPortabilityBackupMixin):
|
|
20
|
+
def __init__(self, *, knowledge_graph: Any, data_dir, enable_graph: bool = True, device_identity: Any = None) -> None:
|
|
21
|
+
self._kg = knowledge_graph
|
|
22
|
+
self._data_dir = Path(data_dir)
|
|
23
|
+
self._enable = bool(enable_graph)
|
|
24
|
+
self._exports_dir = self._data_dir / "workspace_exports"
|
|
25
|
+
# v4 sovereignty: when a DeviceIdentity is wired, exports are signed
|
|
26
|
+
# and imports record origin provenance. Pre-v4 unsigned bundles stay
|
|
27
|
+
# importable locally (origin='unsigned-legacy') — signatures are
|
|
28
|
+
# mandatory only on the Brain Network peer path.
|
|
29
|
+
self._identity = device_identity
|
|
30
|
+
# The receiving X25519 key is created lazily: a Brain that never asks
|
|
31
|
+
# anyone to seal anything to it should not be minting key material on
|
|
32
|
+
# every startup (v11.2.0).
|
|
33
|
+
self._recipient: Any = None
|
|
34
|
+
self._recipient_loaded = False
|
|
35
|
+
|
|
36
|
+
def available(self) -> bool:
|
|
37
|
+
return self._enable and self._kg is not None
|
|
38
|
+
|
|
39
|
+
def _require(self) -> None:
|
|
40
|
+
if not self.available():
|
|
41
|
+
raise RuntimeError("Knowledge Graph is disabled (LATTICEAI_ENABLE_GRAPH).")
|