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.
Files changed (69) hide show
  1. package/README.md +39 -44
  2. package/docs/CHANGELOG.md +48 -0
  3. package/docs/COMMUNITY_AND_PLUGINS.md +1 -1
  4. package/docs/DEVELOPMENT.md +1 -1
  5. package/docs/MYPY_BACKLOG.md +98 -0
  6. package/docs/ONBOARDING.md +1 -1
  7. package/docs/OPERATIONS.md +1 -1
  8. package/docs/TRUST_MODEL.md +1 -1
  9. package/docs/WHY_LATTICE.md +1 -1
  10. package/docs/kg-schema.md +1 -1
  11. package/lattice_brain/__init__.py +1 -1
  12. package/lattice_brain/graph/_kg_fsutil.py +1 -1
  13. package/lattice_brain/runtime/agent_runtime.py +7 -2
  14. package/lattice_brain/runtime/hooks.py +1 -1
  15. package/lattice_brain/runtime/multi_agent.py +1 -1
  16. package/latticeai/__init__.py +1 -1
  17. package/latticeai/app_factory.py +43 -67
  18. package/latticeai/core/legacy_compatibility.py +1 -1
  19. package/latticeai/core/marketplace.py +1 -1
  20. package/latticeai/core/workspace_os_constants.py +1 -1
  21. package/latticeai/runtime/history_writer.py +138 -0
  22. package/latticeai/services/architecture_readiness.py +1 -1
  23. package/latticeai/services/product_readiness.py +1 -1
  24. package/package.json +4 -2
  25. package/scripts/check_current_release_docs.mjs +1 -1
  26. package/src-tauri/Cargo.lock +1 -1
  27. package/src-tauri/Cargo.toml +1 -1
  28. package/src-tauri/tauri.conf.json +1 -1
  29. package/static/app/asset-manifest.json +37 -37
  30. package/static/app/assets/{Act-CbdGD-2i.js → Act-DrKIGZeJ.js} +1 -1
  31. package/static/app/assets/{AdminConsole-LgCkXpnf.js → AdminConsole-CEV1VefA.js} +1 -1
  32. package/static/app/assets/{Brain-CoPGJI1L.js → Brain-BZSXnwWU.js} +1 -1
  33. package/static/app/assets/{BrainHome-gVnaxSwp.js → BrainHome-CEBd86k6.js} +1 -1
  34. package/static/app/assets/{BrainSignals-ChxAYHtj.js → BrainSignals-O4s6R7av.js} +1 -1
  35. package/static/app/assets/{Capture-DBtgkHZg.js → Capture-AGbOxsg-.js} +1 -1
  36. package/static/app/assets/{CommandPalette-Ovtv5I0Y.js → CommandPalette-CeqBjCn2.js} +1 -1
  37. package/static/app/assets/Library-CXnbNiQW.js +1 -0
  38. package/static/app/assets/{LivingBrain-CNz-6NSd.js → LivingBrain-D9Iei2ZJ.js} +1 -1
  39. package/static/app/assets/{ProductFlow-ePX-Y73J.js → ProductFlow-JlXiDfMM.js} +1 -1
  40. package/static/app/assets/{ReviewCard-C4mpvkwH.js → ReviewCard-QQly2bTV.js} +1 -1
  41. package/static/app/assets/{System-BvWNK1zc.js → System-Bp9ZY4ER.js} +1 -1
  42. package/static/app/assets/{activity-CiauPV_3.js → activity-CxRPOQ2F.js} +1 -1
  43. package/static/app/assets/{bot-Dwct-Q1x.js → bot-1bcsWB2s.js} +1 -1
  44. package/static/app/assets/{brain-BKDW1F0T.js → brain-CxaeKCRM.js} +1 -1
  45. package/static/app/assets/{button-CCB9Kuw7.js → button-ZYFqGan7.js} +1 -1
  46. package/static/app/assets/{circle-pause-DNa8WHC5.js → circle-pause-B5NOIqvk.js} +1 -1
  47. package/static/app/assets/{circle-play-8jmxn5mf.js → circle-play-BiJVFXf7.js} +1 -1
  48. package/static/app/assets/{cpu-mxvF3V1I.js → cpu-Vvq0OKTr.js} +1 -1
  49. package/static/app/assets/{download--SmCcx0p.js → download-SEcXzkx1.js} +1 -1
  50. package/static/app/assets/{folder-open-D4Wy5roo.js → folder-open-Dw1ff_Qa.js} +1 -1
  51. package/static/app/assets/{hard-drive-BwQcSPlS.js → hard-drive-B00fR7_r.js} +1 -1
  52. package/static/app/assets/index-CEkL4_M-.css +2 -0
  53. package/static/app/assets/{index-DDV2YZwM.js → index-Dm9VXl1o.js} +3 -3
  54. package/static/app/assets/{input-DFhSjmLS.js → input-DtdWYQEa.js} +1 -1
  55. package/static/app/assets/{network-Bts7VO94.js → network-CpjBu0kI.js} +1 -1
  56. package/static/app/assets/{primitives-BuSMEJY8.js → primitives-CwsudBwc.js} +1 -1
  57. package/static/app/assets/search-CwEJWBEA.js +1 -0
  58. package/static/app/assets/{shield-alert-BfTO6X_G.js → shield-alert-CpjBc-gQ.js} +1 -1
  59. package/static/app/assets/{textarea-BjctW1oh.js → textarea-DUPyjHQs.js} +1 -1
  60. package/static/app/assets/{useFocusTrap-CE43-LrA.js → useFocusTrap-BWcMtCWH.js} +1 -1
  61. package/static/app/assets/{useQuery-Bv3ejLL5.js → useQuery-Dg5fPqTS.js} +1 -1
  62. package/static/app/assets/{users-CsNqLZAj.js → users-BgcdTQGM.js} +1 -1
  63. package/static/app/assets/{utils-KFFdVG_t.js → utils-a3YXFtEO.js} +1 -1
  64. package/static/app/assets/{workspace-wdCvdyPF.js → workspace-BMUEiXUm.js} +1 -1
  65. package/static/app/index.html +4 -4
  66. package/static/sw.js +1 -1
  67. package/static/app/assets/Library-CBia2Fvm.js +0 -1
  68. package/static/app/assets/index-CQWdDU3z.css +0 -2
  69. 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"]
@@ -16,7 +16,7 @@ from typing import Any, Dict, List
16
16
 
17
17
  from latticeai.core.legacy_compatibility import legacy_shim_report
18
18
 
19
- ARCHITECTURE_VERSION_TARGET = "10.2.0"
19
+ ARCHITECTURE_VERSION_TARGET = "10.3.0"
20
20
 
21
21
  PREFERRED_REFACTORING_ORDER = [
22
22
  "agent-runtime",
@@ -18,7 +18,7 @@ from typing import Any, Dict, List
18
18
 
19
19
  from latticeai.services.architecture_readiness import architecture_readiness
20
20
 
21
- PRODUCT_VERSION_TARGET = "10.2.0"
21
+ PRODUCT_VERSION_TARGET = "10.3.0"
22
22
 
23
23
 
24
24
  @dataclass(frozen=True)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ltcai",
3
- "version": "10.2.0",
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 = "Load-Bearing Fixes";
9
+ const releaseTheme = "Measured Ground";
10
10
  const title = `${version} — ${releaseTheme}`;
11
11
  const escapedVersion = version.replaceAll(".", "\\.");
12
12
 
@@ -1584,7 +1584,7 @@ dependencies = [
1584
1584
 
1585
1585
  [[package]]
1586
1586
  name = "lattice-ai-desktop"
1587
- version = "10.2.0"
1587
+ version = "10.3.0"
1588
1588
  dependencies = [
1589
1589
  "plist",
1590
1590
  "serde",
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "lattice-ai-desktop"
3
- version = "10.2.0"
3
+ version = "10.3.0"
4
4
  description = "Lattice AI Digital Brain desktop shell"
5
5
  authors = ["TaeSoo Park"]
6
6
  edition = "2021"
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://schema.tauri.app/config/2",
3
3
  "productName": "Lattice AI",
4
- "version": "10.2.0",
4
+ "version": "10.3.0",
5
5
  "identifier": "ai.lattice.desktop",
6
6
  "build": {
7
7
  "beforeDevCommand": "npm run frontend:dev",
@@ -1,45 +1,45 @@
1
1
  {
2
- "version": "10.2.0",
2
+ "version": "10.3.0",
3
3
  "generated_at": "vite",
4
4
  "entrypoints": {
5
- "app": "/static/app/assets/index-DDV2YZwM.js"
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-ChxAYHtj.js": "/static/app/assets/BrainSignals-ChxAYHtj.js",
10
- "_LivingBrain-CNz-6NSd.js": "/static/app/assets/LivingBrain-CNz-6NSd.js",
11
- "_ReviewCard-C4mpvkwH.js": "/static/app/assets/ReviewCard-C4mpvkwH.js",
12
- "_activity-CiauPV_3.js": "/static/app/assets/activity-CiauPV_3.js",
13
- "_bot-Dwct-Q1x.js": "/static/app/assets/bot-Dwct-Q1x.js",
14
- "_brain-BKDW1F0T.js": "/static/app/assets/brain-BKDW1F0T.js",
15
- "_button-CCB9Kuw7.js": "/static/app/assets/button-CCB9Kuw7.js",
16
- "_circle-pause-DNa8WHC5.js": "/static/app/assets/circle-pause-DNa8WHC5.js",
17
- "_circle-play-8jmxn5mf.js": "/static/app/assets/circle-play-8jmxn5mf.js",
18
- "_cpu-mxvF3V1I.js": "/static/app/assets/cpu-mxvF3V1I.js",
19
- "_download--SmCcx0p.js": "/static/app/assets/download--SmCcx0p.js",
20
- "_folder-open-D4Wy5roo.js": "/static/app/assets/folder-open-D4Wy5roo.js",
21
- "_hard-drive-BwQcSPlS.js": "/static/app/assets/hard-drive-BwQcSPlS.js",
22
- "_input-DFhSjmLS.js": "/static/app/assets/input-DFhSjmLS.js",
23
- "_network-Bts7VO94.js": "/static/app/assets/network-Bts7VO94.js",
24
- "_primitives-BuSMEJY8.js": "/static/app/assets/primitives-BuSMEJY8.js",
25
- "_search-DZzxhWaQ.js": "/static/app/assets/search-DZzxhWaQ.js",
26
- "_shield-alert-BfTO6X_G.js": "/static/app/assets/shield-alert-BfTO6X_G.js",
27
- "_textarea-BjctW1oh.js": "/static/app/assets/textarea-BjctW1oh.js",
28
- "_useFocusTrap-CE43-LrA.js": "/static/app/assets/useFocusTrap-CE43-LrA.js",
29
- "_useQuery-Bv3ejLL5.js": "/static/app/assets/useQuery-Bv3ejLL5.js",
30
- "_users-CsNqLZAj.js": "/static/app/assets/users-CsNqLZAj.js",
31
- "_utils-KFFdVG_t.js": "/static/app/assets/utils-KFFdVG_t.js",
32
- "_workspace-wdCvdyPF.js": "/static/app/assets/workspace-wdCvdyPF.js",
33
- "index.html": "/static/app/assets/index-DDV2YZwM.js",
34
- "assets/index-CQWdDU3z.css": "/static/app/assets/index-CQWdDU3z.css",
35
- "src/components/ProductFlow.tsx": "/static/app/assets/ProductFlow-ePX-Y73J.js",
36
- "src/features/admin/AdminConsole.tsx": "/static/app/assets/AdminConsole-LgCkXpnf.js",
37
- "src/features/brain/BrainHome.tsx": "/static/app/assets/BrainHome-gVnaxSwp.js",
38
- "src/features/command/CommandPalette.tsx": "/static/app/assets/CommandPalette-Ovtv5I0Y.js",
39
- "src/pages/Act.tsx": "/static/app/assets/Act-CbdGD-2i.js",
40
- "src/pages/Brain.tsx": "/static/app/assets/Brain-CoPGJI1L.js",
41
- "src/pages/Capture.tsx": "/static/app/assets/Capture-DBtgkHZg.js",
42
- "src/pages/Library.tsx": "/static/app/assets/Library-CBia2Fvm.js",
43
- "src/pages/System.tsx": "/static/app/assets/System-BvWNK1zc.js"
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
  }