ltcai 8.9.0 → 9.1.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 (208) hide show
  1. package/README.md +81 -58
  2. package/auto_setup.py +7 -904
  3. package/desktop/electron/README.md +9 -0
  4. package/desktop/electron/main.cjs +5 -3
  5. package/docs/CHANGELOG.md +221 -238
  6. package/docs/COMMUNITY_AND_PLUGINS.md +3 -2
  7. package/docs/DEVELOPMENT.md +53 -14
  8. package/docs/LEGACY_COMPATIBILITY.md +4 -4
  9. package/docs/ONBOARDING.md +10 -2
  10. package/docs/OPERATIONS.md +8 -4
  11. package/docs/PRODUCT_DIRECTION_REVIEW.md +4 -0
  12. package/docs/ROADMAP_RECOMMENDATIONS.md +61 -36
  13. package/docs/TRUST_MODEL.md +5 -2
  14. package/docs/WHY_LATTICE.md +15 -10
  15. package/docs/WORKFLOW_DESIGNER.md +22 -0
  16. package/docs/kg-schema.md +13 -2
  17. package/docs/mcp-tools.md +17 -6
  18. package/docs/privacy.md +19 -3
  19. package/docs/public-deploy.md +32 -3
  20. package/docs/security-model.md +46 -11
  21. package/lattice_brain/__init__.py +1 -1
  22. package/lattice_brain/archive.py +4 -14
  23. package/lattice_brain/context.py +66 -9
  24. package/lattice_brain/embeddings.py +38 -2
  25. package/lattice_brain/graph/_kg_common.py +27 -462
  26. package/lattice_brain/graph/_kg_constants.py +243 -0
  27. package/lattice_brain/graph/_kg_fsutil.py +293 -0
  28. package/lattice_brain/graph/discovery.py +0 -948
  29. package/lattice_brain/graph/discovery_index.py +1126 -0
  30. package/lattice_brain/graph/documents.py +44 -9
  31. package/lattice_brain/graph/ingest.py +47 -20
  32. package/lattice_brain/graph/provenance.py +13 -6
  33. package/lattice_brain/graph/retrieval.py +141 -610
  34. package/lattice_brain/graph/retrieval_docgen.py +243 -0
  35. package/lattice_brain/graph/retrieval_vector.py +460 -0
  36. package/lattice_brain/graph/store.py +6 -0
  37. package/lattice_brain/ingestion.py +69 -4
  38. package/lattice_brain/portability.py +29 -23
  39. package/lattice_brain/quality.py +98 -4
  40. package/lattice_brain/runtime/agent_runtime.py +169 -7
  41. package/lattice_brain/runtime/hooks.py +30 -13
  42. package/lattice_brain/runtime/multi_agent.py +27 -8
  43. package/lattice_brain/runtime/statuses.py +10 -0
  44. package/lattice_brain/utils.py +46 -0
  45. package/lattice_brain/workflow.py +1 -5
  46. package/latticeai/__init__.py +1 -1
  47. package/latticeai/api/admin.py +1 -1
  48. package/latticeai/api/agent_registry.py +10 -8
  49. package/latticeai/api/agents.py +22 -3
  50. package/latticeai/api/auth.py +78 -16
  51. package/latticeai/api/browser.py +303 -34
  52. package/latticeai/api/chat.py +355 -952
  53. package/latticeai/api/chat_agent_http.py +356 -0
  54. package/latticeai/api/chat_contracts.py +54 -0
  55. package/latticeai/api/chat_documents.py +256 -0
  56. package/latticeai/api/chat_helpers.py +250 -0
  57. package/latticeai/api/chat_history.py +99 -0
  58. package/latticeai/api/chat_intents.py +302 -0
  59. package/latticeai/api/chat_stream.py +115 -0
  60. package/latticeai/api/computer_use.py +211 -40
  61. package/latticeai/api/health.py +9 -3
  62. package/latticeai/api/hooks.py +17 -6
  63. package/latticeai/api/knowledge_graph.py +78 -14
  64. package/latticeai/api/local_files.py +7 -2
  65. package/latticeai/api/marketplace.py +11 -0
  66. package/latticeai/api/mcp.py +102 -23
  67. package/latticeai/api/models.py +72 -33
  68. package/latticeai/api/network.py +5 -5
  69. package/latticeai/api/permissions.py +70 -29
  70. package/latticeai/api/plugins.py +21 -7
  71. package/latticeai/api/portability.py +5 -5
  72. package/latticeai/api/realtime.py +33 -5
  73. package/latticeai/api/setup.py +2 -2
  74. package/latticeai/api/static_routes.py +123 -24
  75. package/latticeai/api/tools.py +97 -14
  76. package/latticeai/api/workflow_designer.py +37 -0
  77. package/latticeai/api/workspace.py +2 -1
  78. package/latticeai/app_factory.py +185 -405
  79. package/latticeai/core/agent.py +19 -9
  80. package/latticeai/core/agent_registry.py +1 -5
  81. package/latticeai/core/config.py +23 -3
  82. package/latticeai/core/context_builder.py +21 -3
  83. package/latticeai/core/invitations.py +1 -4
  84. package/latticeai/core/io_utils.py +45 -0
  85. package/latticeai/core/legacy_compatibility.py +24 -3
  86. package/latticeai/core/local_embeddings.py +2 -4
  87. package/latticeai/core/marketplace.py +33 -2
  88. package/latticeai/core/mcp_catalog.py +450 -0
  89. package/latticeai/core/mcp_registry.py +2 -441
  90. package/latticeai/core/plugins.py +15 -0
  91. package/latticeai/core/policy.py +1 -1
  92. package/latticeai/core/realtime.py +38 -15
  93. package/latticeai/core/sessions.py +7 -2
  94. package/latticeai/core/timeutil.py +10 -0
  95. package/latticeai/core/tool_registry.py +90 -18
  96. package/latticeai/core/users.py +5 -14
  97. package/latticeai/core/workspace_graph_trace.py +31 -5
  98. package/latticeai/core/workspace_memory.py +3 -1
  99. package/latticeai/core/workspace_os.py +18 -7
  100. package/latticeai/core/workspace_os_utils.py +2 -21
  101. package/latticeai/core/workspace_permissions.py +2 -1
  102. package/latticeai/core/workspace_plugins.py +1 -1
  103. package/latticeai/core/workspace_runs.py +14 -5
  104. package/latticeai/core/workspace_skills.py +1 -1
  105. package/latticeai/core/workspace_snapshots.py +2 -1
  106. package/latticeai/core/workspace_timeline.py +2 -1
  107. package/latticeai/integrations/telegram_bot.py +96 -40
  108. package/latticeai/models/model_providers.py +111 -0
  109. package/latticeai/models/router.py +189 -173
  110. package/latticeai/runtime/access_runtime.py +62 -4
  111. package/latticeai/runtime/audit_runtime.py +27 -16
  112. package/latticeai/runtime/automation_runtime.py +22 -7
  113. package/latticeai/runtime/brain_runtime.py +19 -7
  114. package/latticeai/runtime/chat_wiring.py +2 -0
  115. package/latticeai/runtime/config_runtime.py +58 -26
  116. package/latticeai/runtime/context_runtime.py +16 -4
  117. package/latticeai/runtime/history_runtime.py +163 -0
  118. package/latticeai/runtime/hooks_runtime.py +1 -1
  119. package/latticeai/runtime/model_wiring.py +33 -5
  120. package/latticeai/runtime/namespace_runtime.py +163 -0
  121. package/latticeai/runtime/network_config_runtime.py +56 -0
  122. package/latticeai/runtime/platform_runtime_wiring.py +4 -3
  123. package/latticeai/runtime/review_wiring.py +1 -1
  124. package/latticeai/runtime/router_registration.py +34 -1
  125. package/latticeai/runtime/security_runtime.py +110 -13
  126. package/latticeai/runtime/sso_config_runtime.py +128 -0
  127. package/latticeai/runtime/stages.py +27 -0
  128. package/latticeai/runtime/user_key_runtime.py +106 -0
  129. package/latticeai/server_app.py +5 -1
  130. package/latticeai/services/architecture_readiness.py +74 -5
  131. package/latticeai/services/brain_automation.py +3 -26
  132. package/latticeai/services/chat_service.py +205 -15
  133. package/latticeai/services/local_knowledge.py +423 -0
  134. package/latticeai/services/memory_service.py +268 -21
  135. package/latticeai/services/model_engines.py +48 -33
  136. package/latticeai/services/model_errors.py +17 -0
  137. package/latticeai/services/model_loading.py +28 -25
  138. package/latticeai/services/model_runtime.py +228 -162
  139. package/latticeai/services/p_reinforce.py +22 -3
  140. package/latticeai/services/platform_runtime.py +92 -24
  141. package/latticeai/services/product_readiness.py +19 -17
  142. package/latticeai/services/review_queue.py +76 -11
  143. package/latticeai/services/router_context.py +1 -0
  144. package/latticeai/services/run_executor.py +25 -9
  145. package/latticeai/services/search_service.py +203 -28
  146. package/latticeai/services/setup_detection.py +80 -0
  147. package/latticeai/services/tool_dispatch.py +28 -5
  148. package/latticeai/services/triggers.py +53 -4
  149. package/latticeai/services/upload_service.py +13 -2
  150. package/latticeai/setup/__init__.py +25 -0
  151. package/latticeai/setup/auto_setup.py +857 -0
  152. package/latticeai/setup/wizard.py +1264 -0
  153. package/latticeai/tools/__init__.py +278 -0
  154. package/{tools → latticeai/tools}/commands.py +67 -7
  155. package/{tools → latticeai/tools}/computer.py +1 -1
  156. package/{tools → latticeai/tools}/documents.py +1 -1
  157. package/{tools → latticeai/tools}/filesystem.py +3 -3
  158. package/latticeai/tools/knowledge.py +178 -0
  159. package/{tools → latticeai/tools}/local_files.py +7 -1
  160. package/{tools → latticeai/tools}/network.py +0 -1
  161. package/local_knowledge_api.py +4 -342
  162. package/package.json +22 -2
  163. package/scripts/brain_quality_eval.py +3 -1
  164. package/scripts/bump_version.py +3 -0
  165. package/scripts/capture_release_evidence.mjs +180 -0
  166. package/scripts/check_current_release_docs.mjs +142 -0
  167. package/scripts/check_i18n_literals.mjs +107 -31
  168. package/scripts/check_openapi_drift.mjs +56 -0
  169. package/scripts/export_openapi.py +67 -7
  170. package/scripts/i18n_literal_allowlist.json +22 -24
  171. package/scripts/run_integration_tests.mjs +72 -10
  172. package/scripts/validate_release_artifacts.py +49 -7
  173. package/scripts/wheel_smoke.py +5 -0
  174. package/setup_wizard.py +3 -1304
  175. package/src-tauri/Cargo.lock +1 -1
  176. package/src-tauri/Cargo.toml +1 -1
  177. package/src-tauri/tauri.conf.json +1 -1
  178. package/static/app/asset-manifest.json +11 -11
  179. package/static/app/assets/Act-Bzz0bUyW.js +1 -0
  180. package/static/app/assets/Brain-Dj2J20YA.js +321 -0
  181. package/static/app/assets/Capture-CqlEl1Ga.js +1 -0
  182. package/static/app/assets/Library-B03FP1Yx.js +1 -0
  183. package/static/app/assets/System-80lHW0Ux.js +1 -0
  184. package/static/app/assets/index-BiMofBTM.js +17 -0
  185. package/static/app/assets/index-Bmx9rzTc.css +2 -0
  186. package/static/app/assets/primitives-Q1A96_7v.js +1 -0
  187. package/static/app/assets/textarea-D13RtnTo.js +1 -0
  188. package/static/app/index.html +2 -2
  189. package/static/css/tokens.css +4 -2
  190. package/static/sw.js +1 -1
  191. package/tools/__init__.py +21 -269
  192. package/docs/CODE_REVIEW_2026-07-06.md +0 -764
  193. package/latticeai/runtime/app_context_runtime.py +0 -13
  194. package/latticeai/runtime/sso_runtime.py +0 -52
  195. package/latticeai/runtime/tail_wiring.py +0 -21
  196. package/scripts/com.pts.claudecode.discord.plist +0 -31
  197. package/scripts/pts-claudecode-discord-bridge.mjs +0 -207
  198. package/scripts/start-pts-claudecode-discord.sh +0 -51
  199. package/static/app/assets/Act-fZokUnC0.js +0 -1
  200. package/static/app/assets/Brain-DtyuWubr.js +0 -321
  201. package/static/app/assets/Capture-D5KV3Cu7.js +0 -1
  202. package/static/app/assets/Library-C9kyFkSt.js +0 -1
  203. package/static/app/assets/System-VbChmX7r.js +0 -1
  204. package/static/app/assets/index-DCh5AoXt.css +0 -2
  205. package/static/app/assets/index-DPdcPoF0.js +0 -17
  206. package/static/app/assets/primitives-DFeanEV6.js +0 -1
  207. package/static/app/assets/textarea-CD8UNKIy.js +0 -1
  208. package/tools/knowledge.py +0 -95
@@ -30,7 +30,8 @@ from typing import Any, Awaitable, Callable, Dict, FrozenSet, List, Optional
30
30
 
31
31
  from lattice_brain.runtime.hooks import dispatch_tool
32
32
  from lattice_brain.runtime.contracts import runtime_boundary_contract, single_agent_contract
33
- from tools import ToolError
33
+ from latticeai.core.tool_registry import SCOPED_KNOWLEDGE_TOOLS
34
+ from latticeai.tools import ToolError
34
35
 
35
36
 
36
37
  class AgentState(str, Enum):
@@ -155,7 +156,7 @@ class SingleAgentRuntime:
155
156
  entrypoint="latticeai.core.agent.SingleAgentRuntime",
156
157
  surface="/agent",
157
158
  owns="single-agent PLAN / EXECUTE / VERIFY state machine over injected ports",
158
- compatibility_aliases=["latticeai.core.agent.AgentRuntime"],
159
+ compatibility_aliases=[],
159
160
  )
160
161
 
161
162
  def config(self) -> Dict[str, Any]:
@@ -254,12 +255,20 @@ class SingleAgentRuntime:
254
255
  + "\n".join(f"- {c}" for c in ctx.corrections)
255
256
  ) if ctx.corrections else ""
256
257
 
258
+ request_workspace = getattr(req, "workspace_id", None)
259
+ recent_kwargs = {
260
+ "conversation_id": req.conversation_id,
261
+ "user_email": current_user or None,
262
+ }
263
+ if request_workspace is not None:
264
+ recent_kwargs["workspace_id"] = request_workspace
265
+ recent_conversation = d.recent_chat_context(**recent_kwargs) or "(none)"
257
266
  context = (
258
267
  f"{d.executor_prompt}\n\n"
259
268
  f"[LANGUAGE HINT: {lang_hint}]\n"
260
269
  f"Workspace root: {d.agent_root}\n\n"
261
270
  f"PLAN:\n{json.dumps(ctx.plan, ensure_ascii=False)}\n\n"
262
- f"Recent conversation:\n{d.recent_chat_context(conversation_id=req.conversation_id) or '(none)'}\n\n"
271
+ f"Recent conversation:\n{recent_conversation}\n\n"
263
272
  f"User request: {req.message}{corrections_hint}\n\n"
264
273
  f"Execution transcript:\n{json.dumps(ctx.transcript, ensure_ascii=False, indent=2)}"
265
274
  )
@@ -281,6 +290,13 @@ class SingleAgentRuntime:
281
290
  thoughts = str(action.get("thoughts") or "")[:600]
282
291
  args = action.get("args") or {}
283
292
 
293
+ if name in SCOPED_KNOWLEDGE_TOOLS:
294
+ # Scope is server-owned, never model-owned. Overwrite any
295
+ # claimed values before policy evaluation, audit, and dispatch.
296
+ args = dict(args)
297
+ args["workspace_id"] = request_workspace or "personal"
298
+ args["user_email"] = current_user or "local"
299
+
284
300
  if name == "final":
285
301
  ctx.final_message = action.get("message", "작업을 완료했습니다.")
286
302
  ctx.transcript.append({
@@ -530,9 +546,3 @@ class SingleAgentRuntime:
530
546
  ctx.state = AgentState.FAILED
531
547
 
532
548
  ctx.state_history.append(ctx.state.value)
533
-
534
-
535
- # Backward compatibility: external callers historically imported
536
- # ``latticeai.core.agent.AgentRuntime`` for the single-agent state machine.
537
- # The product/runtime facade lives at ``lattice_brain.runtime.agent_runtime``.
538
- AgentRuntime = SingleAgentRuntime
@@ -18,7 +18,6 @@ from __future__ import annotations
18
18
  import json
19
19
  import os
20
20
  import tempfile
21
- from datetime import datetime
22
21
  from pathlib import Path
23
22
  from typing import Any, Dict, List, Optional
24
23
 
@@ -28,14 +27,11 @@ from lattice_brain.runtime.multi_agent import (
28
27
  MULTI_AGENT_VERSION,
29
28
  ROLE_AGENT_IDS,
30
29
  )
30
+ from .timeutil import now_iso as _now
31
31
 
32
32
  AGENT_TYPES = ("planner", "researcher", "executor", "reviewer", "release", "custom")
33
33
 
34
34
 
35
- def _now() -> str:
36
- return datetime.now().isoformat(timespec="seconds")
37
-
38
-
39
35
  # Capabilities + descriptions for the built-in role agents. Kept here as the
40
36
  # registry's metadata projection of the roles defined in multi_agent.py.
41
37
  ROLE_META: Dict[str, Dict[str, Any]] = {
@@ -99,6 +99,7 @@ class Config:
99
99
  rate_limit_enabled: bool
100
100
  open_registration: bool
101
101
  invite_code: str
102
+ invite_cookie_secret: str
102
103
  invite_gate_enabled: bool
103
104
  admin_emails: List[str]
104
105
  trusted_proxies: List[str]
@@ -160,6 +161,7 @@ class Config:
160
161
  host = _value(env, "LATTICEAI_HOST", "127.0.0.1")
161
162
  port = _port(env, "LATTICEAI_PORT", 4825)
162
163
  network_exposed = not host_is_loopback(host)
164
+ externally_reachable = is_public or network_exposed
163
165
 
164
166
  cors_extra = [item.strip() for item in _value(env, "LATTICEAI_CORS_ALLOWED_ORIGINS", "").split(",") if item.strip()]
165
167
  admin_emails = [item.strip().lower() for item in _value(env, "LATTICEAI_ADMIN_EMAILS", "").split(",") if item.strip()]
@@ -189,13 +191,31 @@ class Config:
189
191
  autoload_models=_bool(env, "LATTICEAI_AUTOLOAD_MODELS", default=is_public),
190
192
  model_idle_unload_seconds=_int(env, "LATTICEAI_MODEL_IDLE_UNLOAD_SECONDS", 0),
191
193
  allow_local_models=_bool(env, "LATTICEAI_ALLOW_LOCAL_MODELS", default=not is_public),
192
- require_auth=_bool(env, "LATTICEAI_REQUIRE_AUTH", default=is_public or network_exposed),
194
+ # Authentication is optional only for the local-first loopback
195
+ # profile. An explicit ``false`` must never turn a public/LAN
196
+ # binding into an unauthenticated service.
197
+ require_auth=(
198
+ True
199
+ if externally_reachable
200
+ else _bool(env, "LATTICEAI_REQUIRE_AUTH", default=False)
201
+ ),
193
202
  allow_plaintext_api_keys=_bool(env, "LATTICEAI_ALLOW_PLAINTEXT_API_KEYS", default=False),
194
203
  cors_allow_network=_bool(env, "LATTICEAI_CORS_ALLOW_NETWORK", default=False),
195
204
  cors_extra_origins=cors_extra,
196
205
  rate_limit_enabled=_str(env, "LATTICEAI_RATE_LIMIT", "1") != "0",
197
- open_registration=_bool(env, "LATTICEAI_OPEN_REGISTRATION", default=not network_exposed and not is_public),
198
- invite_code=_value(env, "LATTICEAI_INVITE_CODE", "gemma-lattice-ai"),
206
+ # Public/LAN startup is closed-registration even if a stale or
207
+ # unsafe environment file attempts to opt back in.
208
+ open_registration=(
209
+ False
210
+ if externally_reachable
211
+ else _bool(env, "LATTICEAI_OPEN_REGISTRATION", default=True)
212
+ ),
213
+ # There is deliberately no repository-wide/default invitation
214
+ # code. When the gate is enabled, the security runtime persists a
215
+ # cryptographically random code if the operator did not provide
216
+ # one explicitly.
217
+ invite_code=_value(env, "LATTICEAI_INVITE_CODE", ""),
218
+ invite_cookie_secret=_value(env, "LATTICEAI_INVITE_COOKIE_SECRET", ""),
199
219
  invite_gate_enabled=_bool(env, "LATTICEAI_INVITE_GATE_ENABLED", default=False),
200
220
  admin_emails=admin_emails,
201
221
  trusted_proxies=trusted_proxies,
@@ -23,6 +23,8 @@ def retrieve_context_for_generation(
23
23
  *,
24
24
  max_results: int = 10,
25
25
  max_hops: int = 2,
26
+ allowed_workspaces=None,
27
+ include_legacy_global: bool = False,
26
28
  ) -> Dict[str, Any]:
27
29
  """Knowledge Graph에서 문서 생성에 필요한 컨텍스트를 검색·조합한다.
28
30
 
@@ -38,9 +40,21 @@ def retrieve_context_for_generation(
38
40
  if not query or not kg_store:
39
41
  return {"query": query, "context_markdown": "", "sources": [], "stats": {}}
40
42
 
41
- results = kg_store.search_for_document_generation(query, limit=max_results)
43
+ scope_kwargs = (
44
+ {
45
+ "allowed_workspaces": allowed_workspaces,
46
+ "include_legacy_global": include_legacy_global,
47
+ }
48
+ if allowed_workspaces is not None
49
+ else {}
50
+ )
51
+ results = kg_store.search_for_document_generation(query, limit=max_results, **scope_kwargs)
42
52
  if not results:
43
- fallback_ctx = kg_store.context_for_query(query, limit=max_results)
53
+ fallback_ctx = kg_store.context_for_query(
54
+ query,
55
+ limit=max_results,
56
+ **scope_kwargs,
57
+ )
44
58
  return {
45
59
  "query": query,
46
60
  "context_markdown": fallback_ctx,
@@ -49,7 +63,11 @@ def retrieve_context_for_generation(
49
63
  }
50
64
 
51
65
  seed_ids = [r["id"] for r in results]
52
- hop_data = kg_store.multi_hop_context(seed_ids, max_hops=max_hops)
66
+ hop_data = kg_store.multi_hop_context(
67
+ seed_ids,
68
+ max_hops=max_hops,
69
+ **scope_kwargs,
70
+ )
53
71
 
54
72
  extra_nodes_by_id = {}
55
73
  for node in hop_data.get("nodes", []):
@@ -9,10 +9,7 @@ from datetime import datetime, timedelta
9
9
  from pathlib import Path
10
10
  from typing import Any, Dict, List, Optional
11
11
 
12
-
13
- def _now() -> datetime:
14
- return datetime.now()
15
-
12
+ from .timeutil import local_now as _now
16
13
 
17
14
  def _iso(dt: datetime) -> str:
18
15
  return dt.isoformat(timespec="seconds")
@@ -0,0 +1,45 @@
1
+ """Shared small IO helpers for JSON, timestamps, and file hashes."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import hashlib
6
+ import json
7
+ import os
8
+ from datetime import datetime
9
+ from pathlib import Path
10
+ from typing import Any, Dict, Optional
11
+
12
+
13
+ def atomic_write_json(path: Path, payload: Dict[str, Any]) -> None:
14
+ path.parent.mkdir(parents=True, exist_ok=True)
15
+ tmp_path = path.with_suffix(path.suffix + ".tmp")
16
+ fd = os.open(tmp_path, os.O_WRONLY | os.O_CREAT | os.O_TRUNC, 0o600)
17
+ with os.fdopen(fd, "w", encoding="utf-8") as handle:
18
+ handle.write(json.dumps(payload, ensure_ascii=False, indent=2))
19
+ os.replace(tmp_path, path)
20
+ try:
21
+ path.chmod(0o600)
22
+ except OSError:
23
+ # Windows and unusual filesystems may not expose POSIX mode bits; the
24
+ # atomic write is still the safest supported fallback there.
25
+ pass
26
+
27
+
28
+ def parse_iso(value: Optional[str]) -> Optional[datetime]:
29
+ if not value:
30
+ return None
31
+ try:
32
+ return datetime.fromisoformat(str(value))
33
+ except (TypeError, ValueError):
34
+ return None
35
+
36
+
37
+ def sha256_file(path: Path) -> str:
38
+ digest = hashlib.sha256()
39
+ with open(path, "rb") as fh:
40
+ for block in iter(lambda: fh.read(65536), b""):
41
+ digest.update(block)
42
+ return digest.hexdigest()
43
+
44
+
45
+ __all__ = ["atomic_write_json", "parse_iso", "sha256_file"]
@@ -14,7 +14,7 @@ from pathlib import Path
14
14
  from typing import Any, Dict, List
15
15
 
16
16
 
17
- LEGACY_COMPATIBILITY_VERSION = "8.9.0"
17
+ LEGACY_COMPATIBILITY_VERSION = "9.1.0"
18
18
 
19
19
 
20
20
  @dataclass(frozen=True)
@@ -89,13 +89,34 @@ LEGACY_SHIMS: List[LegacyShim] = [
89
89
  reason="Deployment docs and local launch scripts historically targeted server.py.",
90
90
  removal_phase="major-release-after-8.x",
91
91
  ),
92
+ LegacyShim(
93
+ path="tools/",
94
+ owner="latticeai.tools",
95
+ replacement="from latticeai.tools import execute_tool",
96
+ reason="Existing integrations and tests import the historical root tools package.",
97
+ removal_phase="major-release-after-9.x",
98
+ ),
92
99
  LegacyShim(
93
100
  path="local_knowledge_api.py",
94
- owner="latticeai.api.local_files",
95
- replacement="from latticeai.api.local_files import create_local_files_router",
101
+ owner="latticeai.services.local_knowledge",
102
+ replacement="from latticeai.services.local_knowledge import create_local_knowledge_router",
96
103
  reason="Local folder ingestion integrations used the root local knowledge API.",
97
104
  removal_phase="requires-api-route-migration",
98
105
  ),
106
+ LegacyShim(
107
+ path="auto_setup.py",
108
+ owner="latticeai.setup.auto_setup",
109
+ replacement="from latticeai.setup.auto_setup import probe, recommend",
110
+ reason="Historical scripts invoke the zero-config setup module from the repo root.",
111
+ removal_phase="major-release-after-9.x",
112
+ ),
113
+ LegacyShim(
114
+ path="setup_wizard.py",
115
+ owner="latticeai.setup.wizard",
116
+ replacement="from latticeai.setup.wizard import scan_environment",
117
+ reason="Older integrations imported the setup wizard from the repo root.",
118
+ removal_phase="major-release-after-9.x",
119
+ ),
99
120
  ]
100
121
 
101
122
  # Shim layers that have completed their compatibility window and were
@@ -11,15 +11,13 @@ from __future__ import annotations
11
11
 
12
12
  import hashlib
13
13
  import math
14
+ import os
14
15
  import re
15
16
  import struct
16
17
  from dataclasses import dataclass
17
18
  from typing import Iterable, List
18
19
 
19
- # Default controlled via latticeai.core.config.Config.embedding_dim (LATTICEAI_VECTOR_DIM).
20
- # Removed direct os.getenv per 7.6.0 config centralization (review.md item 3).
21
- # Callers that need override should pass dim= from Config.
22
- DEFAULT_EMBEDDING_DIM = 384
20
+ DEFAULT_EMBEDDING_DIM = int(os.getenv("LATTICEAI_VECTOR_DIM", "384"))
23
21
  EMBEDDING_MODEL_ID = f"lattice-local-hash-v1:{DEFAULT_EMBEDDING_DIM}"
24
22
 
25
23
 
@@ -11,8 +11,8 @@ from copy import deepcopy
11
11
  from typing import Any, Dict, List, Optional
12
12
 
13
13
 
14
- MARKETPLACE_VERSION = "8.9.0"
15
- TEMPLATE_KINDS = ("plugin", "workflow", "agent")
14
+ MARKETPLACE_VERSION = "9.1.0"
15
+ TEMPLATE_KINDS = ("plugin", "workflow", "agent", "ingestion_bridge")
16
16
 
17
17
 
18
18
  def _agent_template(
@@ -144,6 +144,37 @@ BUILTIN_TEMPLATES: Dict[str, List[Dict[str, Any]]] = {
144
144
  category="automation",
145
145
  ),
146
146
  ],
147
+ "ingestion_bridge": [
148
+ {
149
+ "id": "bridge-obsidian-markdown",
150
+ "kind": "ingestion_bridge",
151
+ "name": "Obsidian Markdown Bridge",
152
+ "version": "1.0.0",
153
+ "description": "Import a local Markdown vault through the unified ingestion pipeline.",
154
+ "metadata": {"category": "interop", "installable": True},
155
+ "definition": {
156
+ "source_types": ["local_file", "markdown"],
157
+ "file_patterns": ["*.md"],
158
+ "pipeline": "unified-ingestion",
159
+ "provenance": True,
160
+ "graph_edges": ["indexed_from", "mentions"],
161
+ },
162
+ },
163
+ {
164
+ "id": "bridge-calendar-notes",
165
+ "kind": "ingestion_bridge",
166
+ "name": "Calendar Notes Bridge",
167
+ "version": "1.0.0",
168
+ "description": "Normalize calendar meeting notes into Brain events and source-linked notes.",
169
+ "metadata": {"category": "interop", "installable": True},
170
+ "definition": {
171
+ "source_types": ["workspace_event", "note"],
172
+ "pipeline": "unified-ingestion",
173
+ "provenance": True,
174
+ "graph_edges": ["indexed_from", "mentions"],
175
+ },
176
+ },
177
+ ],
147
178
  }
148
179
 
149
180