ltcai 10.2.0 → 10.3.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 +39 -44
- package/docs/CHANGELOG.md +48 -0
- package/docs/COMMUNITY_AND_PLUGINS.md +1 -1
- package/docs/DEVELOPMENT.md +1 -1
- package/docs/MYPY_BACKLOG.md +98 -0
- package/docs/ONBOARDING.md +1 -1
- package/docs/OPERATIONS.md +1 -1
- package/docs/TRUST_MODEL.md +1 -1
- package/docs/WHY_LATTICE.md +1 -1
- package/docs/kg-schema.md +1 -1
- package/lattice_brain/__init__.py +1 -1
- package/lattice_brain/graph/_kg_fsutil.py +1 -1
- package/lattice_brain/runtime/agent_runtime.py +7 -2
- package/lattice_brain/runtime/hooks.py +1 -1
- package/lattice_brain/runtime/multi_agent.py +1 -1
- package/latticeai/__init__.py +1 -1
- package/latticeai/app_factory.py +43 -67
- package/latticeai/core/legacy_compatibility.py +1 -1
- package/latticeai/core/marketplace.py +1 -1
- package/latticeai/core/workspace_os_constants.py +1 -1
- package/latticeai/runtime/history_writer.py +138 -0
- package/latticeai/services/architecture_readiness.py +1 -1
- package/latticeai/services/product_readiness.py +1 -1
- package/package.json +4 -2
- package/scripts/check_current_release_docs.mjs +1 -1
- package/src-tauri/Cargo.lock +1 -1
- package/src-tauri/Cargo.toml +1 -1
- package/src-tauri/tauri.conf.json +1 -1
- package/static/app/asset-manifest.json +37 -37
- package/static/app/assets/{Act-CbdGD-2i.js → Act-DrKIGZeJ.js} +1 -1
- package/static/app/assets/{AdminConsole-LgCkXpnf.js → AdminConsole-CEV1VefA.js} +1 -1
- package/static/app/assets/{Brain-CoPGJI1L.js → Brain-BZSXnwWU.js} +1 -1
- package/static/app/assets/{BrainHome-gVnaxSwp.js → BrainHome-CEBd86k6.js} +1 -1
- package/static/app/assets/{BrainSignals-ChxAYHtj.js → BrainSignals-O4s6R7av.js} +1 -1
- package/static/app/assets/{Capture-DBtgkHZg.js → Capture-AGbOxsg-.js} +1 -1
- package/static/app/assets/{CommandPalette-Ovtv5I0Y.js → CommandPalette-CeqBjCn2.js} +1 -1
- package/static/app/assets/Library-CXnbNiQW.js +1 -0
- package/static/app/assets/{LivingBrain-CNz-6NSd.js → LivingBrain-D9Iei2ZJ.js} +1 -1
- package/static/app/assets/{ProductFlow-ePX-Y73J.js → ProductFlow-JlXiDfMM.js} +1 -1
- package/static/app/assets/{ReviewCard-C4mpvkwH.js → ReviewCard-QQly2bTV.js} +1 -1
- package/static/app/assets/{System-BvWNK1zc.js → System-Bp9ZY4ER.js} +1 -1
- package/static/app/assets/{activity-CiauPV_3.js → activity-CxRPOQ2F.js} +1 -1
- package/static/app/assets/{bot-Dwct-Q1x.js → bot-1bcsWB2s.js} +1 -1
- package/static/app/assets/{brain-BKDW1F0T.js → brain-CxaeKCRM.js} +1 -1
- package/static/app/assets/{button-CCB9Kuw7.js → button-ZYFqGan7.js} +1 -1
- package/static/app/assets/{circle-pause-DNa8WHC5.js → circle-pause-B5NOIqvk.js} +1 -1
- package/static/app/assets/{circle-play-8jmxn5mf.js → circle-play-BiJVFXf7.js} +1 -1
- package/static/app/assets/{cpu-mxvF3V1I.js → cpu-Vvq0OKTr.js} +1 -1
- package/static/app/assets/{download--SmCcx0p.js → download-SEcXzkx1.js} +1 -1
- package/static/app/assets/{folder-open-D4Wy5roo.js → folder-open-Dw1ff_Qa.js} +1 -1
- package/static/app/assets/{hard-drive-BwQcSPlS.js → hard-drive-B00fR7_r.js} +1 -1
- package/static/app/assets/index-CEkL4_M-.css +2 -0
- package/static/app/assets/{index-DDV2YZwM.js → index-Dm9VXl1o.js} +3 -3
- package/static/app/assets/{input-DFhSjmLS.js → input-DtdWYQEa.js} +1 -1
- package/static/app/assets/{network-Bts7VO94.js → network-CpjBu0kI.js} +1 -1
- package/static/app/assets/{primitives-BuSMEJY8.js → primitives-CwsudBwc.js} +1 -1
- package/static/app/assets/search-CwEJWBEA.js +1 -0
- package/static/app/assets/{shield-alert-BfTO6X_G.js → shield-alert-CpjBc-gQ.js} +1 -1
- package/static/app/assets/{textarea-BjctW1oh.js → textarea-DUPyjHQs.js} +1 -1
- package/static/app/assets/{useFocusTrap-CE43-LrA.js → useFocusTrap-BWcMtCWH.js} +1 -1
- package/static/app/assets/{useQuery-Bv3ejLL5.js → useQuery-Dg5fPqTS.js} +1 -1
- package/static/app/assets/{users-CsNqLZAj.js → users-BgcdTQGM.js} +1 -1
- package/static/app/assets/{utils-KFFdVG_t.js → utils-a3YXFtEO.js} +1 -1
- package/static/app/assets/{workspace-wdCvdyPF.js → workspace-BMUEiXUm.js} +1 -1
- package/static/app/index.html +4 -4
- package/static/sw.js +1 -1
- package/static/app/assets/Library-CBia2Fvm.js +0 -1
- package/static/app/assets/index-CQWdDU3z.css +0 -2
- package/static/app/assets/search-DZzxhWaQ.js +0 -1
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
"""Persisting one chat turn: redact, audit, store, then grow the Brain.
|
|
2
|
+
|
|
3
|
+
Extracted from the `app_factory._build` closure in 10.3.0. It was 66 lines
|
|
4
|
+
deep inside a 1,343-line composition root, which meant the one function that
|
|
5
|
+
decides what a chat message looks like *after* redaction — and what the audit
|
|
6
|
+
log records about it — could not be exercised without standing up the whole
|
|
7
|
+
application.
|
|
8
|
+
|
|
9
|
+
The order here is the contract, and it is deliberate:
|
|
10
|
+
|
|
11
|
+
1. **Redact first.** Everything downstream (the audit preview, the durable
|
|
12
|
+
store, the knowledge graph) sees the redacted text, never the original.
|
|
13
|
+
2. **Audit before storing.** The audit row carries a masked preview and the
|
|
14
|
+
sensitivity verdict, so a message that should not have been sent is
|
|
15
|
+
visible in the log even if the store write later fails.
|
|
16
|
+
3. **Ingest through the pipeline, not the store.** Chat messages enter the
|
|
17
|
+
Brain the same way files and web pages do, so they get provenance and the
|
|
18
|
+
hook lifecycle rather than a direct write that bypasses both.
|
|
19
|
+
4. **A failed ingest does not lose the message.** Graph growth is best-effort;
|
|
20
|
+
the conversation store is not.
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
from __future__ import annotations
|
|
24
|
+
|
|
25
|
+
import logging
|
|
26
|
+
from dataclasses import dataclass
|
|
27
|
+
from datetime import datetime
|
|
28
|
+
from typing import Any, Callable, Dict, Optional
|
|
29
|
+
|
|
30
|
+
logger = logging.getLogger(__name__)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
@dataclass(frozen=True)
|
|
34
|
+
class HistoryWriterDeps:
|
|
35
|
+
"""Everything one chat turn needs, injected so this is testable in isolation."""
|
|
36
|
+
|
|
37
|
+
conversations: Any
|
|
38
|
+
append_audit_event: Callable[..., None]
|
|
39
|
+
classify_sensitive_message: Callable[[Dict[str, Any], int], Dict[str, Any]]
|
|
40
|
+
redact_secret_text: Callable[[str], str]
|
|
41
|
+
normalize_branding: Callable[[str], str]
|
|
42
|
+
ingestion_pipeline: Any = None
|
|
43
|
+
ingestion_item_factory: Any = None
|
|
44
|
+
enable_graph: bool = False
|
|
45
|
+
knowledge_graph: Any = None
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def write_chat_turn(
|
|
49
|
+
role: str,
|
|
50
|
+
message: str,
|
|
51
|
+
*,
|
|
52
|
+
user_email: Optional[str] = None,
|
|
53
|
+
user_nickname: Optional[str] = None,
|
|
54
|
+
source: Optional[str] = None,
|
|
55
|
+
conversation_id: Optional[str] = None,
|
|
56
|
+
workspace_id: Optional[str] = None,
|
|
57
|
+
deps: HistoryWriterDeps,
|
|
58
|
+
) -> None:
|
|
59
|
+
"""Persist one chat turn. Never raises: a failure here must not lose a reply."""
|
|
60
|
+
redact_secret_text = deps.redact_secret_text
|
|
61
|
+
normalize_branding = deps.normalize_branding
|
|
62
|
+
classify_sensitive_message = deps.classify_sensitive_message
|
|
63
|
+
append_audit_event = deps.append_audit_event
|
|
64
|
+
conversations = deps.conversations
|
|
65
|
+
ingestion_pipeline = deps.ingestion_pipeline
|
|
66
|
+
ingestion_item_factory = deps.ingestion_item_factory
|
|
67
|
+
enable_graph = deps.enable_graph
|
|
68
|
+
knowledge_graph = deps.knowledge_graph
|
|
69
|
+
|
|
70
|
+
try:
|
|
71
|
+
# 1. Redact before anything else sees the text.
|
|
72
|
+
message = redact_secret_text(message)
|
|
73
|
+
if role == "assistant":
|
|
74
|
+
message = normalize_branding(message)
|
|
75
|
+
|
|
76
|
+
item: Dict[str, Any] = {
|
|
77
|
+
"role": role,
|
|
78
|
+
"content": message,
|
|
79
|
+
"timestamp": datetime.now().isoformat(),
|
|
80
|
+
}
|
|
81
|
+
for key, value in (
|
|
82
|
+
("user_email", user_email),
|
|
83
|
+
("user_nickname", user_nickname),
|
|
84
|
+
("source", source),
|
|
85
|
+
("conversation_id", conversation_id),
|
|
86
|
+
("workspace_id", workspace_id),
|
|
87
|
+
):
|
|
88
|
+
if value:
|
|
89
|
+
item[key] = value
|
|
90
|
+
|
|
91
|
+
# 2. Audit carries a masked preview and the verdict, never the body.
|
|
92
|
+
sensitive = classify_sensitive_message(item, -1)
|
|
93
|
+
append_audit_event(
|
|
94
|
+
"chat_message",
|
|
95
|
+
role=role,
|
|
96
|
+
user_email=user_email,
|
|
97
|
+
user_nickname=user_nickname,
|
|
98
|
+
source=source,
|
|
99
|
+
conversation_id=conversation_id,
|
|
100
|
+
workspace_id=workspace_id,
|
|
101
|
+
content_preview=sensitive.get("preview"),
|
|
102
|
+
content_chars=len(message or ""),
|
|
103
|
+
sensitivity=sensitive.get("sensitivity"),
|
|
104
|
+
sensitive_labels=sensitive.get("labels") or [],
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
# 3. Durable episodic memory. Unbounded SQLite; the old 50-message
|
|
108
|
+
# chat_history.json cap is gone.
|
|
109
|
+
conversations.append(item)
|
|
110
|
+
|
|
111
|
+
# 4. Best-effort Brain growth through the unified pipeline.
|
|
112
|
+
if enable_graph and knowledge_graph and ingestion_pipeline and ingestion_item_factory:
|
|
113
|
+
try:
|
|
114
|
+
ingestion_pipeline.ingest(
|
|
115
|
+
ingestion_item_factory(
|
|
116
|
+
source_type="chat_message",
|
|
117
|
+
text=message,
|
|
118
|
+
owner=user_email,
|
|
119
|
+
workspace_id=workspace_id,
|
|
120
|
+
conversation_id=conversation_id,
|
|
121
|
+
metadata={
|
|
122
|
+
"role": role,
|
|
123
|
+
"user_nickname": user_nickname,
|
|
124
|
+
"source": source,
|
|
125
|
+
"raw": item,
|
|
126
|
+
},
|
|
127
|
+
),
|
|
128
|
+
user_email=user_email,
|
|
129
|
+
)
|
|
130
|
+
except Exception as graph_error: # noqa: BLE001
|
|
131
|
+
# The message is already stored; a graph failure must not
|
|
132
|
+
# look like a lost turn.
|
|
133
|
+
logger.warning("knowledge graph message ingest failed: %s", graph_error)
|
|
134
|
+
except Exception as exc: # noqa: BLE001
|
|
135
|
+
logger.warning("save_to_history failed: %s", exc)
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
__all__ = ["HistoryWriterDeps", "write_chat_turn"]
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ltcai",
|
|
3
|
-
"version": "10.
|
|
3
|
+
"version": "10.3.0",
|
|
4
4
|
"description": "Lattice AI — local-first Digital Brain that keeps your knowledge durable across any AI model.",
|
|
5
5
|
"homepage": "https://github.com/TaeSooPark-PTS/LatticeAI#readme",
|
|
6
6
|
"repository": {
|
|
@@ -61,7 +61,8 @@
|
|
|
61
61
|
"test:vscode-extension": "cd vscode-extension && npm run build && cd .. && node --test tests/vscode-extension.test.cjs",
|
|
62
62
|
"check:i18n-namespaces": "node scripts/check_i18n_namespace_coverage.mjs",
|
|
63
63
|
"typecheck:python": "node scripts/run_python.mjs -m mypy",
|
|
64
|
-
"test:coverage": "node scripts/run_python.mjs -m pytest tests/ -q --cov --cov-report=term:skip-covered"
|
|
64
|
+
"test:coverage": "node scripts/run_python.mjs -m pytest tests/ -q --cov --cov-report=term:skip-covered",
|
|
65
|
+
"test:frontend:coverage": "vitest run --coverage"
|
|
65
66
|
},
|
|
66
67
|
"keywords": [
|
|
67
68
|
"ltcai",
|
|
@@ -137,6 +138,7 @@
|
|
|
137
138
|
"@types/react": "^19.2.17",
|
|
138
139
|
"@types/react-dom": "^19.2.3",
|
|
139
140
|
"@vitejs/plugin-react": "^6.0.2",
|
|
141
|
+
"@vitest/coverage-v8": "^4.1.10",
|
|
140
142
|
"autoprefixer": "^10.5.0",
|
|
141
143
|
"class-variance-authority": "^0.7.1",
|
|
142
144
|
"clsx": "^2.1.1",
|
|
@@ -6,7 +6,7 @@ const root = process.cwd();
|
|
|
6
6
|
const pkg = JSON.parse(readFileSync(path.join(root, "package.json"), "utf8"));
|
|
7
7
|
const version = pkg.version;
|
|
8
8
|
const releaseDir = `output/release/v${version}`;
|
|
9
|
-
const releaseTheme = "
|
|
9
|
+
const releaseTheme = "Measured Ground";
|
|
10
10
|
const title = `${version} — ${releaseTheme}`;
|
|
11
11
|
const escapedVersion = version.replaceAll(".", "\\.");
|
|
12
12
|
|
package/src-tauri/Cargo.lock
CHANGED
package/src-tauri/Cargo.toml
CHANGED
|
@@ -1,45 +1,45 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "10.
|
|
2
|
+
"version": "10.3.0",
|
|
3
3
|
"generated_at": "vite",
|
|
4
4
|
"entrypoints": {
|
|
5
|
-
"app": "/static/app/assets/index-
|
|
5
|
+
"app": "/static/app/assets/index-Dm9VXl1o.js"
|
|
6
6
|
},
|
|
7
7
|
"assets": {
|
|
8
8
|
"../node_modules/@tauri-apps/api/core.js": "/static/app/assets/core-CwxXejkd.js",
|
|
9
|
-
"_BrainSignals-
|
|
10
|
-
"_LivingBrain-
|
|
11
|
-
"_ReviewCard-
|
|
12
|
-
"_activity-
|
|
13
|
-
"_bot-
|
|
14
|
-
"_brain-
|
|
15
|
-
"_button-
|
|
16
|
-
"_circle-pause-
|
|
17
|
-
"_circle-play-
|
|
18
|
-
"_cpu-
|
|
19
|
-
"_download
|
|
20
|
-
"_folder-open-
|
|
21
|
-
"_hard-drive-
|
|
22
|
-
"_input-
|
|
23
|
-
"_network-
|
|
24
|
-
"_primitives-
|
|
25
|
-
"_search-
|
|
26
|
-
"_shield-alert-
|
|
27
|
-
"_textarea-
|
|
28
|
-
"_useFocusTrap-
|
|
29
|
-
"_useQuery-
|
|
30
|
-
"_users-
|
|
31
|
-
"_utils-
|
|
32
|
-
"_workspace-
|
|
33
|
-
"index.html": "/static/app/assets/index-
|
|
34
|
-
"assets/index-
|
|
35
|
-
"src/components/ProductFlow.tsx": "/static/app/assets/ProductFlow-
|
|
36
|
-
"src/features/admin/AdminConsole.tsx": "/static/app/assets/AdminConsole-
|
|
37
|
-
"src/features/brain/BrainHome.tsx": "/static/app/assets/BrainHome-
|
|
38
|
-
"src/features/command/CommandPalette.tsx": "/static/app/assets/CommandPalette-
|
|
39
|
-
"src/pages/Act.tsx": "/static/app/assets/Act-
|
|
40
|
-
"src/pages/Brain.tsx": "/static/app/assets/Brain-
|
|
41
|
-
"src/pages/Capture.tsx": "/static/app/assets/Capture-
|
|
42
|
-
"src/pages/Library.tsx": "/static/app/assets/Library-
|
|
43
|
-
"src/pages/System.tsx": "/static/app/assets/System-
|
|
9
|
+
"_BrainSignals-O4s6R7av.js": "/static/app/assets/BrainSignals-O4s6R7av.js",
|
|
10
|
+
"_LivingBrain-D9Iei2ZJ.js": "/static/app/assets/LivingBrain-D9Iei2ZJ.js",
|
|
11
|
+
"_ReviewCard-QQly2bTV.js": "/static/app/assets/ReviewCard-QQly2bTV.js",
|
|
12
|
+
"_activity-CxRPOQ2F.js": "/static/app/assets/activity-CxRPOQ2F.js",
|
|
13
|
+
"_bot-1bcsWB2s.js": "/static/app/assets/bot-1bcsWB2s.js",
|
|
14
|
+
"_brain-CxaeKCRM.js": "/static/app/assets/brain-CxaeKCRM.js",
|
|
15
|
+
"_button-ZYFqGan7.js": "/static/app/assets/button-ZYFqGan7.js",
|
|
16
|
+
"_circle-pause-B5NOIqvk.js": "/static/app/assets/circle-pause-B5NOIqvk.js",
|
|
17
|
+
"_circle-play-BiJVFXf7.js": "/static/app/assets/circle-play-BiJVFXf7.js",
|
|
18
|
+
"_cpu-Vvq0OKTr.js": "/static/app/assets/cpu-Vvq0OKTr.js",
|
|
19
|
+
"_download-SEcXzkx1.js": "/static/app/assets/download-SEcXzkx1.js",
|
|
20
|
+
"_folder-open-Dw1ff_Qa.js": "/static/app/assets/folder-open-Dw1ff_Qa.js",
|
|
21
|
+
"_hard-drive-B00fR7_r.js": "/static/app/assets/hard-drive-B00fR7_r.js",
|
|
22
|
+
"_input-DtdWYQEa.js": "/static/app/assets/input-DtdWYQEa.js",
|
|
23
|
+
"_network-CpjBu0kI.js": "/static/app/assets/network-CpjBu0kI.js",
|
|
24
|
+
"_primitives-CwsudBwc.js": "/static/app/assets/primitives-CwsudBwc.js",
|
|
25
|
+
"_search-CwEJWBEA.js": "/static/app/assets/search-CwEJWBEA.js",
|
|
26
|
+
"_shield-alert-CpjBc-gQ.js": "/static/app/assets/shield-alert-CpjBc-gQ.js",
|
|
27
|
+
"_textarea-DUPyjHQs.js": "/static/app/assets/textarea-DUPyjHQs.js",
|
|
28
|
+
"_useFocusTrap-BWcMtCWH.js": "/static/app/assets/useFocusTrap-BWcMtCWH.js",
|
|
29
|
+
"_useQuery-Dg5fPqTS.js": "/static/app/assets/useQuery-Dg5fPqTS.js",
|
|
30
|
+
"_users-BgcdTQGM.js": "/static/app/assets/users-BgcdTQGM.js",
|
|
31
|
+
"_utils-a3YXFtEO.js": "/static/app/assets/utils-a3YXFtEO.js",
|
|
32
|
+
"_workspace-BMUEiXUm.js": "/static/app/assets/workspace-BMUEiXUm.js",
|
|
33
|
+
"index.html": "/static/app/assets/index-Dm9VXl1o.js",
|
|
34
|
+
"assets/index-CEkL4_M-.css": "/static/app/assets/index-CEkL4_M-.css",
|
|
35
|
+
"src/components/ProductFlow.tsx": "/static/app/assets/ProductFlow-JlXiDfMM.js",
|
|
36
|
+
"src/features/admin/AdminConsole.tsx": "/static/app/assets/AdminConsole-CEV1VefA.js",
|
|
37
|
+
"src/features/brain/BrainHome.tsx": "/static/app/assets/BrainHome-CEBd86k6.js",
|
|
38
|
+
"src/features/command/CommandPalette.tsx": "/static/app/assets/CommandPalette-CeqBjCn2.js",
|
|
39
|
+
"src/pages/Act.tsx": "/static/app/assets/Act-DrKIGZeJ.js",
|
|
40
|
+
"src/pages/Brain.tsx": "/static/app/assets/Brain-BZSXnwWU.js",
|
|
41
|
+
"src/pages/Capture.tsx": "/static/app/assets/Capture-AGbOxsg-.js",
|
|
42
|
+
"src/pages/Library.tsx": "/static/app/assets/Library-CXnbNiQW.js",
|
|
43
|
+
"src/pages/System.tsx": "/static/app/assets/System-Bp9ZY4ER.js"
|
|
44
44
|
}
|
|
45
45
|
}
|