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.
Files changed (247) hide show
  1. package/README.md +46 -53
  2. package/docs/CHANGELOG.md +61 -0
  3. package/docs/COMMUNITY_AND_PLUGINS.md +1 -1
  4. package/docs/DEVELOPMENT.md +1 -1
  5. package/docs/MULTI_AGENT_RUNTIME.md +1 -1
  6. package/docs/ONBOARDING.md +1 -1
  7. package/docs/OPERATIONS.md +6 -2
  8. package/docs/PERMISSION_MODE.md +1 -1
  9. package/docs/TRUST_MODEL.md +1 -1
  10. package/docs/WHY_LATTICE.md +1 -1
  11. package/docs/kg-schema.md +2 -2
  12. package/docs/v11.3.0_PLAN.md +202 -0
  13. package/docs/v11.4.0_RUST_FOUNDATION_PLAN.md +176 -0
  14. package/lattice_brain/__init__.py +1 -1
  15. package/lattice_brain/graph/_kg_common/__init__.py +287 -0
  16. package/lattice_brain/graph/_kg_common/extraction.py +516 -0
  17. package/lattice_brain/graph/_kg_common/relations.py +161 -0
  18. package/lattice_brain/graph/_kg_common/text.py +479 -0
  19. package/lattice_brain/graph/discovery_index/__init__.py +35 -0
  20. package/lattice_brain/graph/discovery_index/cleanup.py +182 -0
  21. package/lattice_brain/graph/discovery_index/extract.py +137 -0
  22. package/lattice_brain/graph/discovery_index/scan.py +411 -0
  23. package/lattice_brain/graph/discovery_index/upsert.py +495 -0
  24. package/lattice_brain/graph/projection/__init__.py +42 -0
  25. package/lattice_brain/graph/projection/curation.py +500 -0
  26. package/lattice_brain/graph/{projection.py → projection/v2_schema.py} +15 -477
  27. package/lattice_brain/graph/retrieval/__init__.py +54 -0
  28. package/lattice_brain/graph/retrieval/context.py +197 -0
  29. package/lattice_brain/graph/retrieval/graph_view.py +319 -0
  30. package/lattice_brain/graph/retrieval/hybrid.py +488 -0
  31. package/lattice_brain/graph/retrieval/maintenance.py +121 -0
  32. package/lattice_brain/graph/retrieval/signals.py +95 -0
  33. package/lattice_brain/graph/retrieval_vector/__init__.py +42 -0
  34. package/lattice_brain/graph/retrieval_vector/fingerprint.py +97 -0
  35. package/lattice_brain/graph/retrieval_vector/indexing.py +347 -0
  36. package/lattice_brain/graph/retrieval_vector/search.py +560 -0
  37. package/lattice_brain/graph/retrieval_vector/status.py +374 -0
  38. package/lattice_brain/ingestion/__init__.py +130 -0
  39. package/lattice_brain/ingestion/_contract.py +90 -0
  40. package/lattice_brain/ingestion/constants.py +127 -0
  41. package/lattice_brain/ingestion/folder_scan.py +57 -0
  42. package/lattice_brain/ingestion/folders.py +258 -0
  43. package/lattice_brain/ingestion/hashing.py +26 -0
  44. package/lattice_brain/ingestion/jobs_api.py +107 -0
  45. package/lattice_brain/ingestion/models.py +80 -0
  46. package/lattice_brain/ingestion/pipeline.py +486 -0
  47. package/lattice_brain/ingestion/quality.py +209 -0
  48. package/lattice_brain/ingestion/routing.py +295 -0
  49. package/lattice_brain/multimodal/__init__.py +164 -0
  50. package/lattice_brain/multimodal/audio.py +77 -0
  51. package/lattice_brain/multimodal/common.py +118 -0
  52. package/lattice_brain/multimodal/images.py +498 -0
  53. package/lattice_brain/multimodal/ports.py +169 -0
  54. package/lattice_brain/multimodal/video.py +410 -0
  55. package/lattice_brain/portability/__init__.py +90 -0
  56. package/lattice_brain/portability/_contract.py +42 -0
  57. package/lattice_brain/portability/backups.py +338 -0
  58. package/lattice_brain/portability/bundles.py +136 -0
  59. package/lattice_brain/portability/constants.py +93 -0
  60. package/lattice_brain/portability/fsops.py +138 -0
  61. package/lattice_brain/portability/service.py +41 -0
  62. package/lattice_brain/{portability.py → portability/sharing.py} +44 -677
  63. package/lattice_brain/runtime/__init__.py +1 -1
  64. package/lattice_brain/runtime/multi_agent.py +1 -1
  65. package/latticeai/__init__.py +1 -1
  66. package/latticeai/api/chronicle.py +63 -0
  67. package/latticeai/core/agent/__init__.py +93 -0
  68. package/latticeai/core/agent/_contract.py +79 -0
  69. package/latticeai/core/agent/context.py +57 -0
  70. package/latticeai/core/agent/deps.py +125 -0
  71. package/latticeai/core/agent/execution.py +622 -0
  72. package/latticeai/core/agent/planning.py +145 -0
  73. package/latticeai/core/agent/recovery.py +157 -0
  74. package/latticeai/core/agent/runtime.py +210 -0
  75. package/latticeai/core/agent/verification.py +231 -0
  76. package/latticeai/core/embedding_providers/__init__.py +151 -0
  77. package/latticeai/core/embedding_providers/base.py +199 -0
  78. package/latticeai/core/embedding_providers/captions.py +162 -0
  79. package/latticeai/core/embedding_providers/profiles.py +126 -0
  80. package/latticeai/core/embedding_providers/text.py +350 -0
  81. package/latticeai/core/embedding_providers/vision.py +352 -0
  82. package/latticeai/core/file_generation/__init__.py +115 -0
  83. package/latticeai/core/file_generation/bundles.py +76 -0
  84. package/latticeai/core/file_generation/extraction.py +154 -0
  85. package/latticeai/core/file_generation/inference.py +235 -0
  86. package/latticeai/core/file_generation/orchestration.py +152 -0
  87. package/latticeai/core/file_generation/prompting.py +117 -0
  88. package/latticeai/core/file_generation/repair.py +114 -0
  89. package/latticeai/core/file_generation/sanitize.py +61 -0
  90. package/latticeai/core/file_generation/validation.py +201 -0
  91. package/latticeai/core/legacy_compatibility.py +1 -1
  92. package/latticeai/core/marketplace.py +1 -1
  93. package/latticeai/core/messages.py +9 -0
  94. package/latticeai/core/workspace_os_constants.py +1 -1
  95. package/latticeai/integrations/telegram_bot/__init__.py +123 -0
  96. package/latticeai/integrations/telegram_bot/__main__.py +17 -0
  97. package/latticeai/integrations/telegram_bot/config.py +86 -0
  98. package/latticeai/integrations/telegram_bot/dispatch.py +311 -0
  99. package/latticeai/integrations/telegram_bot/flows.py +478 -0
  100. package/latticeai/integrations/telegram_bot/helpers.py +322 -0
  101. package/latticeai/integrations/telegram_bot/screens.py +394 -0
  102. package/latticeai/models/router/__init__.py +88 -0
  103. package/latticeai/models/router/_contract.py +66 -0
  104. package/latticeai/models/router/branding.py +56 -0
  105. package/latticeai/models/router/catalog.py +69 -0
  106. package/latticeai/models/router/documents.py +199 -0
  107. package/latticeai/models/router/errors.py +37 -0
  108. package/latticeai/models/router/generation.py +258 -0
  109. package/latticeai/models/router/loading.py +291 -0
  110. package/latticeai/models/router/local_models.py +85 -0
  111. package/latticeai/models/router/registry.py +147 -0
  112. package/latticeai/runtime/build_phases/__init__.py +82 -0
  113. package/latticeai/runtime/build_phases/features.py +407 -0
  114. package/latticeai/runtime/build_phases/foundation.py +555 -0
  115. package/latticeai/runtime/build_phases/web.py +492 -0
  116. package/latticeai/runtime/runtime_context.py +1 -0
  117. package/latticeai/services/architecture_readiness.py +48 -19
  118. package/latticeai/services/brain_intelligence/__init__.py +58 -0
  119. package/latticeai/services/brain_intelligence/_contract.py +71 -0
  120. package/latticeai/services/brain_intelligence/consistency.py +193 -0
  121. package/latticeai/services/brain_intelligence/constants.py +47 -0
  122. package/latticeai/services/brain_intelligence/digest.py +258 -0
  123. package/latticeai/services/brain_intelligence/health.py +331 -0
  124. package/latticeai/services/brain_intelligence/proposals.py +264 -0
  125. package/latticeai/services/brain_intelligence/sampling.py +84 -0
  126. package/latticeai/services/brain_intelligence/service.py +48 -0
  127. package/latticeai/services/chronicle.py +557 -0
  128. package/latticeai/services/memory_service/__init__.py +52 -0
  129. package/latticeai/services/memory_service/_contract.py +100 -0
  130. package/latticeai/services/memory_service/brief.py +431 -0
  131. package/latticeai/services/memory_service/constants.py +57 -0
  132. package/latticeai/services/memory_service/maintenance.py +138 -0
  133. package/latticeai/services/memory_service/manager.py +186 -0
  134. package/latticeai/services/memory_service/proof.py +136 -0
  135. package/latticeai/services/memory_service/recall.py +225 -0
  136. package/latticeai/services/memory_service/service.py +48 -0
  137. package/latticeai/services/memory_service/stores.py +110 -0
  138. package/latticeai/services/model_runtime/__init__.py +322 -0
  139. package/latticeai/services/model_runtime/cloud.py +87 -0
  140. package/latticeai/services/model_runtime/download.py +282 -0
  141. package/latticeai/services/model_runtime/engines.py +341 -0
  142. package/latticeai/services/model_runtime/loading.py +178 -0
  143. package/latticeai/services/model_runtime/service.py +129 -0
  144. package/latticeai/services/model_runtime/state.py +131 -0
  145. package/latticeai/services/model_runtime/status.py +255 -0
  146. package/latticeai/services/product_readiness.py +15 -7
  147. package/latticeai/setup/wizard/__init__.py +126 -0
  148. package/latticeai/setup/wizard/catalog.py +172 -0
  149. package/latticeai/setup/wizard/detect.py +323 -0
  150. package/latticeai/setup/wizard/install.py +348 -0
  151. package/latticeai/setup/wizard/paths.py +168 -0
  152. package/latticeai/setup/wizard/plans.py +74 -0
  153. package/latticeai/setup/wizard/recommend.py +320 -0
  154. package/package.json +6 -2
  155. package/scripts/bump_version.py +14 -0
  156. package/scripts/capture_release_evidence.mjs +33 -21
  157. package/scripts/check_current_release_docs.mjs +1 -1
  158. package/scripts/check_i18n_namespace_coverage.mjs +41 -4
  159. package/scripts/check_max_file_lines.mjs +102 -0
  160. package/scripts/check_release_evidence_bound.mjs +30 -15
  161. package/scripts/check_screenshot_pixel_delta.py +34 -4
  162. package/scripts/check_server_i18n.mjs +1 -0
  163. package/scripts/generate_rust_parity_fixtures.py +562 -0
  164. package/scripts/lib/mock_server_fingerprint.mjs +94 -0
  165. package/scripts/release_screen_claims.json +31 -2
  166. package/src-tauri/Cargo.lock +361 -3
  167. package/src-tauri/Cargo.toml +6 -1
  168. package/src-tauri/src/backend.rs +349 -0
  169. package/src-tauri/src/folder.rs +33 -0
  170. package/src-tauri/src/main.rs +97 -399
  171. package/src-tauri/tauri.conf.json +1 -1
  172. package/static/app/asset-manifest.json +41 -37
  173. package/static/app/assets/Act-yYpYnn0v.js +1 -0
  174. package/static/app/assets/AdminConsole-DL3Cr5pL.js +1 -0
  175. package/static/app/assets/{Brain-tuhI4sOC.js → Brain-C1HBN0Wf.js} +2 -2
  176. package/static/app/assets/BrainHome-DoXRhUUC.js +2 -0
  177. package/static/app/assets/BrainSignals-6yR6ir5t.js +1 -0
  178. package/static/app/assets/Capture-CFIRsFNE.js +1 -0
  179. package/static/app/assets/Chronicle-BZbEgiwN.js +1 -0
  180. package/static/app/assets/CommandPalette-D2pMxC2I.js +1 -0
  181. package/static/app/assets/Library-DwO3yZST.js +1 -0
  182. package/static/app/assets/{LivingBrain-DBwhto14.js → LivingBrain-Jn1GK0-S.js} +1 -1
  183. package/static/app/assets/ProductFlow-B-w1R4Oo.js +1 -0
  184. package/static/app/assets/ReviewCard-6B27X8Vg.js +3 -0
  185. package/static/app/assets/System-DW8F-2xL.js +1 -0
  186. package/static/app/assets/arrow-left-DXvKg9U6.js +1 -0
  187. package/static/app/assets/{bot-Cia42c2h.js → bot-IM_E_Y12.js} +1 -1
  188. package/static/app/assets/brain-Ci1CkWjM.js +1 -0
  189. package/static/app/assets/{button-2j2Ijzgq.js → button-COwyqfHM.js} +1 -1
  190. package/static/app/assets/circle-check-DfInj-qD.js +1 -0
  191. package/static/app/assets/{circle-pause-BEFeWpVW.js → circle-pause-DEM4A1Y5.js} +1 -1
  192. package/static/app/assets/{circle-play-ujXMcHxl.js → circle-play-C9djDuLd.js} +1 -1
  193. package/static/app/assets/{cpu-k4awryFq.js → cpu-DFdo1gw-.js} +1 -1
  194. package/static/app/assets/{download-DFbLJ_ig.js → download-SnJL6oqk.js} +1 -1
  195. package/static/app/assets/{folder-open-7y_b6xkM.js → folder-open-CqZeDkjE.js} +1 -1
  196. package/static/app/assets/{hard-drive-Bidh02Kr.js → hard-drive-j1jJXYYf.js} +1 -1
  197. package/static/app/assets/{index-DwDl9-8Y.css → index-BLPb5lmE.css} +1 -1
  198. package/static/app/assets/index-_u5iUHDr.js +10 -0
  199. package/static/app/assets/input-B0lPdRQZ.js +1 -0
  200. package/static/app/assets/link-2-CoFbooHS.js +1 -0
  201. package/static/app/assets/{permissionCopy-Bpb83Hx9.js → permissionCopy-BsyLxtao.js} +1 -1
  202. package/static/app/assets/primitives-DEbN-d6p.js +1 -0
  203. package/static/app/assets/search-BybIWPNd.js +1 -0
  204. package/static/app/assets/{share-2-BH1M-WNi.js → share-2-CVtZ_ewX.js} +1 -1
  205. package/static/app/assets/{shield-alert-BlKdBXcG.js → shield-alert-CBi2GNWM.js} +1 -1
  206. package/static/app/assets/{textarea-CCWbUfFB.js → textarea-DNMpB5ih.js} +1 -1
  207. package/static/app/assets/{useFocusTrap-YdHQ7pJ1.js → useFocusTrap-C83t3GXF.js} +1 -1
  208. package/static/app/assets/useMutation-DtbJDoyz.js +1 -0
  209. package/static/app/assets/{useQuery-CXQiwbVT.js → useQuery-Dcp1OChy.js} +1 -1
  210. package/static/app/assets/utils-BlZr7Pd4.js +4 -0
  211. package/static/app/assets/workspace-jJY4RuAV.js +1 -0
  212. package/static/app/index.html +4 -4
  213. package/static/sw.js +1 -1
  214. package/lattice_brain/graph/_kg_common.py +0 -1331
  215. package/lattice_brain/graph/discovery_index.py +0 -1141
  216. package/lattice_brain/graph/retrieval.py +0 -1120
  217. package/lattice_brain/graph/retrieval_vector.py +0 -1293
  218. package/lattice_brain/ingestion.py +0 -1525
  219. package/lattice_brain/multimodal.py +0 -1258
  220. package/latticeai/core/agent.py +0 -1465
  221. package/latticeai/core/embedding_providers.py +0 -1196
  222. package/latticeai/core/file_generation.py +0 -1047
  223. package/latticeai/integrations/telegram_bot.py +0 -1390
  224. package/latticeai/models/router.py +0 -1007
  225. package/latticeai/runtime/build_phases.py +0 -1450
  226. package/latticeai/services/brain_intelligence.py +0 -1083
  227. package/latticeai/services/memory_service.py +0 -1177
  228. package/latticeai/services/model_runtime.py +0 -1281
  229. package/latticeai/setup/wizard.py +0 -1310
  230. package/static/app/assets/Act-AWf0SAKp.js +0 -1
  231. package/static/app/assets/AdminConsole-D0u8Tiyj.js +0 -1
  232. package/static/app/assets/BrainHome-Ts7G_Ila.js +0 -2
  233. package/static/app/assets/BrainSignals-jMYgQ2Ar.js +0 -1
  234. package/static/app/assets/Capture-CqOSzyPr.js +0 -1
  235. package/static/app/assets/CommandPalette-DC0Bzh-I.js +0 -1
  236. package/static/app/assets/Library-CX-bbhmK.js +0 -1
  237. package/static/app/assets/ProductFlow-BHA2cfKI.js +0 -1
  238. package/static/app/assets/ReviewCard-BUhCKRNM.js +0 -3
  239. package/static/app/assets/System-Bu2t5hn1.js +0 -1
  240. package/static/app/assets/arrow-left-Dzwa5zRb.js +0 -1
  241. package/static/app/assets/brain-DJMoqrwx.js +0 -1
  242. package/static/app/assets/index-BpYkzcVm.js +0 -10
  243. package/static/app/assets/input-DSlJJxRs.js +0 -1
  244. package/static/app/assets/primitives-BCx6TvfG.js +0 -1
  245. package/static/app/assets/search-Cgy8cCFJ.js +0 -1
  246. package/static/app/assets/utils-zqPZJxdx.js +0 -4
  247. package/static/app/assets/workspace-DXTihhfU.js +0 -1
@@ -0,0 +1,492 @@
1
+ """Phases 5-8: domain singletons, the web app, services, foundation routes.
2
+
3
+ The middle of the build order. ``phase_domain`` runs before ``phase_web``
4
+ because the lifespan, the static status routes and the model runtime service
5
+ are all wired against the router it builds; ``phase_services`` runs after
6
+ ``phase_web`` because the AppContext carries handles the web phase produces.
7
+ Split out of the single ``build_phases`` module in v11.3.0.
8
+
9
+ Every heavy import lives *inside* a phase, never at module scope.
10
+ """
11
+
12
+ from __future__ import annotations
13
+
14
+ import logging
15
+ from typing import Any, Dict, Optional
16
+
17
+ from latticeai.runtime.runtime_context import RuntimeContext
18
+
19
+
20
+ # ── phase 5: domain singletons (must precede the web app) ────────────────────
21
+ def phase_domain(ctx: RuntimeContext) -> None:
22
+ """The model router, the garden, and the chat service.
23
+
24
+ Separated from :func:`phase_services` because ``phase_web`` needs the model
25
+ router: the lifespan hooks, the static status routes, and the model runtime
26
+ service are all wired against it.
27
+ """
28
+ ctx.enter("domain")
29
+
30
+ from lattice_brain.graph.runtime import set_llm_router
31
+ from latticeai.models.router import LLMRouter
32
+ from latticeai.services.chat_service import ChatService
33
+ from latticeai.services.p_reinforce import PReinforceGardener
34
+ from latticeai.services.tool_dispatch import configure_tool_dispatch
35
+
36
+ model_router = LLMRouter()
37
+ set_llm_router(model_router)
38
+ configure_tool_dispatch(load_users=ctx.load_users, get_user_role=ctx.get_user_role)
39
+ ctx.set(model_router=model_router)
40
+
41
+ # v4 garden absorption: the vault is the user-owned markdown mirror and the
42
+ # brain is authoritative. Existing notes import idempotently (content-hash
43
+ # dedup), and garden context queries the brain rather than rescanning.
44
+ gardener = PReinforceGardener(
45
+ ingestion_pipeline=ctx.INGESTION_PIPELINE if ctx.ENABLE_GRAPH else None,
46
+ knowledge_graph=ctx.KNOWLEDGE_GRAPH,
47
+ )
48
+ if ctx.ENABLE_GRAPH:
49
+ try:
50
+ garden_import = gardener.import_vault()
51
+ if garden_import.get("failed"):
52
+ logging.warning(
53
+ "garden vault import: %s notes failed to ingest",
54
+ garden_import["failed"],
55
+ )
56
+ except Exception as exc:
57
+ logging.warning("garden vault import skipped: %s", exc)
58
+ ctx.set(gardener=gardener)
59
+
60
+ # The chat service owns persistence and trace behaviour once its user-key
61
+ # dependencies exist.
62
+ ctx.set(
63
+ CHAT_SERVICE=ChatService(
64
+ store=ctx.WORKSPACE_OS,
65
+ get_history=ctx.get_history,
66
+ save_to_history=ctx.save_to_history,
67
+ get_history_user=ctx.get_history_user,
68
+ )
69
+ )
70
+
71
+
72
+ def self_model_port(workspace_graph: Any) -> Any:
73
+ """The agent loop's Self-Model port (v11.2.0).
74
+
75
+ 11.1.0 built ``executor_prompt_for(self_model_summary=…)`` and had nothing
76
+ to pass it; this is what the composition root passes. ``summary_for_prompt``
77
+ never raises and answers ``""`` for a Brain that knows nothing about its
78
+ owner — which produces exactly the pre-11.2.0 prompt bytes.
79
+
80
+ A module-level factory rather than a closure inside ``phase_services``
81
+ because a port that cannot be called on its own cannot be tested on its own.
82
+ """
83
+
84
+ def _summary(*, user_email: Any = None, workspace_id: Any = None) -> str:
85
+ from lattice_brain.self_model import summary_for_prompt
86
+
87
+ graph = workspace_graph()
88
+ if graph is None:
89
+ return ""
90
+ return summary_for_prompt(graph, workspace_id=workspace_id)
91
+
92
+ return _summary
93
+
94
+
95
+ # ── phase 6b: retrieval, agent runtime, and the typed AppContext ─────────────
96
+ def phase_services(ctx: RuntimeContext) -> None:
97
+ """Retrieval/context assembly, the chat agent runtime, and the AppContext.
98
+
99
+ Runs after ``phase_web`` because the AppContext carries handles the web
100
+ phase produces (``ui_file_response``, ``local_sysinfo``, ``graph_stats``)
101
+ and the telegram mirror needs the lifespan's ``_spawn``.
102
+ """
103
+ ctx.enter("services")
104
+
105
+ from latticeai.api.chat import build_recent_chat_context
106
+ from latticeai.core.enterprise import capability_registry
107
+ from latticeai.core.mcp_registry import (
108
+ SKILLS_DIR,
109
+ _fetch_skills_marketplace,
110
+ install_skill,
111
+ )
112
+ from latticeai.core.workspace_os import remove_skill_directory
113
+ from latticeai.runtime.chat_wiring import (
114
+ build_chat_agent_runtime_from_context,
115
+ maybe_build_telegram_chat_mirror,
116
+ )
117
+ from latticeai.runtime.context_runtime import build_context_runtime
118
+ from latticeai.services.app_context import AppContext
119
+ from latticeai.services.tool_dispatch import build_agent_runtime
120
+ from latticeai.setup.wizard import get_recommendations, scan_environment
121
+ from latticeai.tools import execute_tool, knowledge_save
122
+
123
+ gardener = ctx.gardener
124
+ model_router = ctx.model_router
125
+
126
+ def _workspace_settings_payload() -> Dict:
127
+ return {
128
+ "mode": ctx.APP_MODE,
129
+ "host": ctx.DEFAULT_HOST,
130
+ "port": ctx.DEFAULT_PORT,
131
+ "require_auth": ctx.REQUIRE_AUTH,
132
+ "enable_graph": ctx.ENABLE_GRAPH,
133
+ "allow_local_models": ctx.ALLOW_LOCAL_MODELS,
134
+ "static_dir": str(ctx.STATIC_DIR),
135
+ "data_dir": str(ctx.DATA_DIR),
136
+ }
137
+
138
+ def _workspace_models_payload() -> Dict:
139
+ return {
140
+ "current_model": ctx.model_router.current_model_id,
141
+ "loaded_models": ctx.model_router.loaded_model_ids,
142
+ "public_model": ctx.PUBLIC_MODEL,
143
+ "local_model": ctx.LOCAL_MODEL,
144
+ "local_draft_model": ctx.LOCAL_DRAFT_MODEL,
145
+ }
146
+
147
+ def _embedding_info() -> dict:
148
+ from latticeai.core.embedding_providers import (
149
+ PROVIDER_TYPES,
150
+ embedding_provider_profiles,
151
+ )
152
+
153
+ info = ctx.EMBEDDER.as_dict()
154
+ info["available_providers"] = list(PROVIDER_TYPES)
155
+ info["profile"] = ctx.CONFIG.embedding_profile or ""
156
+ info["profiles"] = embedding_provider_profiles()
157
+ return info
158
+
159
+ def _allowed_workspaces_for(user: Any) -> Any:
160
+ # No-auth local mode is single-user: no scoping. With auth, reads are
161
+ # scoped to the caller's memberships (legacy-global rows need an
162
+ # explicit maintenance-only opt-in).
163
+ if not ctx.REQUIRE_AUTH or not user:
164
+ return None
165
+ return ctx.PLATFORM.allowed_scopes(user)
166
+
167
+ def _recent_chat_context(
168
+ limit: int = 10,
169
+ include_image_missing_replies: bool = True,
170
+ user_email: Optional[str] = None,
171
+ conversation_id: Optional[str] = None,
172
+ workspace_id: Optional[str] = None,
173
+ ) -> str:
174
+ return build_recent_chat_context(
175
+ get_history=ctx.get_history,
176
+ limit=limit,
177
+ include_image_missing_replies=include_image_missing_replies,
178
+ user_email=user_email,
179
+ conversation_id=conversation_id,
180
+ workspace_id=workspace_id,
181
+ )
182
+
183
+ ctx.set(
184
+ _workspace_settings_payload=_workspace_settings_payload,
185
+ _workspace_models_payload=_workspace_models_payload,
186
+ _embedding_info=_embedding_info,
187
+ _allowed_workspaces_for=_allowed_workspaces_for,
188
+ _recent_chat_context=_recent_chat_context,
189
+ )
190
+
191
+ context_runtime = build_context_runtime(
192
+ graph_store=ctx._workspace_graph(),
193
+ ingestion_pipeline=ctx.INGESTION_PIPELINE,
194
+ memory_service=ctx.MEMORY_SERVICE,
195
+ gardener=gardener,
196
+ require_auth=ctx.REQUIRE_AUTH,
197
+ allowed_scopes_for_user=lambda user_email: ctx.PLATFORM.allowed_scopes(
198
+ user_email
199
+ ),
200
+ )
201
+ ctx.adopt(
202
+ context_runtime,
203
+ "SEARCH_SERVICE",
204
+ "BRAIN_MEMORY",
205
+ "CONTEXT_ASSEMBLER",
206
+ "ARTIFACT_LEDGER",
207
+ "_scoped_hybrid_search",
208
+ )
209
+
210
+ # Telegram chat mirror: registered only when ENABLE_TELEGRAM is truthy, so
211
+ # latticeai.api.chat never imports the 45KB telegram module.
212
+ ctx.set(
213
+ on_chat_message=maybe_build_telegram_chat_mirror(
214
+ enable_telegram=ctx.ENABLE_TELEGRAM,
215
+ spawn=ctx._spawn,
216
+ )
217
+ )
218
+
219
+ ctx.set(
220
+ CHAT_AGENT_RUNTIME=build_chat_agent_runtime_from_context(
221
+ build_agent_runtime=build_agent_runtime,
222
+ model_router=model_router,
223
+ execute_tool=execute_tool,
224
+ recent_chat_context=_recent_chat_context,
225
+ clear_history=ctx.clear_history,
226
+ knowledge_save=knowledge_save,
227
+ audit=ctx.append_audit_event,
228
+ hooks=ctx.HOOKS_REGISTRY,
229
+ brain_memory=ctx.BRAIN_MEMORY,
230
+ self_model_summary=self_model_port(
231
+ lambda: ctx.KNOWLEDGE_GRAPH if ctx.ENABLE_GRAPH else None
232
+ ),
233
+ )
234
+ )
235
+
236
+ # One typed context object replaces the historical 25-30-kwarg wiring.
237
+ ctx.set(
238
+ app_context=AppContext(
239
+ config=ctx.CONFIG,
240
+ data_dir=ctx.DATA_DIR,
241
+ static_dir=ctx.STATIC_DIR,
242
+ base_dir=ctx.BASE_DIR,
243
+ skills_dir=SKILLS_DIR,
244
+ model_router=model_router,
245
+ workspace_store=ctx.WORKSPACE_OS,
246
+ workspace_service=ctx.WORKSPACE_SERVICE,
247
+ knowledge_graph=ctx.KNOWLEDGE_GRAPH,
248
+ local_kg_watcher=ctx.LOCAL_KG_WATCHER,
249
+ chat_service=ctx.CHAT_SERVICE,
250
+ context_assembler=ctx.CONTEXT_ASSEMBLER,
251
+ artifact_ledger=ctx.ARTIFACT_LEDGER,
252
+ brain_memory=ctx.BRAIN_MEMORY,
253
+ ingestion_pipeline=ctx.INGESTION_PIPELINE if ctx.ENABLE_GRAPH else None,
254
+ chat_agent_runtime=ctx.CHAT_AGENT_RUNTIME,
255
+ gardener=gardener,
256
+ hooks=ctx.HOOKS_REGISTRY,
257
+ realtime_bus=ctx.REALTIME_BUS,
258
+ capability_registry=capability_registry,
259
+ require_user=ctx.require_user,
260
+ require_admin=ctx.require_admin,
261
+ get_current_user=ctx.get_current_user,
262
+ load_users=ctx.load_users,
263
+ get_user_role=ctx.get_user_role,
264
+ enforce_rate_limit=ctx.enforce_rate_limit,
265
+ allowed_workspaces_for=ctx._history_allowed_workspaces_for,
266
+ append_audit_event=ctx.append_audit_event,
267
+ get_audit_log=ctx.get_audit_log,
268
+ get_history=ctx.get_history,
269
+ get_history_user=ctx.get_history_user,
270
+ save_to_history=ctx.save_to_history,
271
+ clear_history=ctx.clear_history,
272
+ clear_conversation=ctx.clear_conversation,
273
+ group_history_conversations=ctx.group_history_conversations,
274
+ get_conversation_messages=ctx.get_conversation_messages,
275
+ conversation_title=ctx.conversation_title,
276
+ enable_graph=ctx.ENABLE_GRAPH,
277
+ require_graph=ctx._require_graph,
278
+ workspace_graph=ctx._workspace_graph,
279
+ graph_stats=ctx._graph_stats_safe,
280
+ # Provider, not a value: REVIEW_QUEUE lands two phases later.
281
+ review_queue=lambda: ctx.REVIEW_QUEUE,
282
+ workspace_models=_workspace_models_payload,
283
+ workspace_settings=_workspace_settings_payload,
284
+ scan_environment=scan_environment,
285
+ local_sysinfo=ctx.local_sysinfo,
286
+ get_recommendations=get_recommendations,
287
+ fetch_skills_marketplace=_fetch_skills_marketplace,
288
+ install_skill=install_skill,
289
+ remove_skill_directory=remove_skill_directory,
290
+ redact_secret_text=ctx.redact_secret_text,
291
+ ui_file_response=ctx.ui_file_response,
292
+ public_model=ctx.PUBLIC_MODEL,
293
+ local_model=ctx.LOCAL_MODEL or "",
294
+ on_chat_message=ctx.on_chat_message,
295
+ funnel_metrics=ctx.FUNNEL_METRICS,
296
+ )
297
+ )
298
+ ctx.app.state.context = ctx.app_context
299
+
300
+
301
+ # ── phase 6: web app and foundation routers ──────────────────────────────────
302
+ def phase_web(ctx: RuntimeContext) -> None:
303
+ """Lifespan, the FastAPI app, model runtime service, foundation routers."""
304
+ ctx.enter("web")
305
+
306
+ from latticeai.api.admin import create_admin_router
307
+ from latticeai.api.auth import create_auth_router
308
+ from latticeai.api.invitations import create_invitations_router
309
+ from latticeai.api.security_dashboard import (
310
+ create_security_router as _create_security_router,
311
+ )
312
+ from latticeai.api.static_routes import create_static_routes_router
313
+ from latticeai.core.policy import policy_matrix
314
+ from latticeai.core.product_hardening import build_product_hardening_status
315
+ from latticeai.core.security import hash_password
316
+ from latticeai.core.users import ensure_user_identity
317
+ from latticeai.runtime.lifespan_runtime import build_lifespan_runtime
318
+ from latticeai.runtime.model_wiring import configure_model_runtime_from_context
319
+ from latticeai.runtime.router_registration import (
320
+ build_auth_admin_security_router_bundle,
321
+ build_static_routes_bundle,
322
+ )
323
+ from latticeai.runtime.web_runtime import build_web_runtime
324
+ from latticeai.services.model_runtime import (
325
+ LOCAL_SERVER_PROCESSES,
326
+ build_model_runtime,
327
+ )
328
+ from latticeai.tools import ensure_agent_root
329
+
330
+ lifespan_runtime = build_lifespan_runtime(
331
+ app_mode=ctx.APP_MODE,
332
+ enable_telegram=ctx.ENABLE_TELEGRAM,
333
+ autoload_models=ctx.AUTOLOAD_MODELS,
334
+ is_public_mode=ctx.IS_PUBLIC_MODE,
335
+ public_model=ctx.PUBLIC_MODEL,
336
+ allow_local_models=ctx.ALLOW_LOCAL_MODELS,
337
+ local_model=ctx.LOCAL_MODEL,
338
+ local_draft_model=ctx.LOCAL_DRAFT_MODEL,
339
+ model_idle_unload_seconds=ctx.MODEL_IDLE_UNLOAD_SECONDS,
340
+ model_router=ctx.model_router,
341
+ local_kg_watcher=ctx.LOCAL_KG_WATCHER,
342
+ local_server_processes=LOCAL_SERVER_PROCESSES,
343
+ logger=logging,
344
+ )
345
+ ctx.adopt(lifespan_runtime, "_spawn", "lifespan")
346
+
347
+ web_runtime = build_web_runtime(
348
+ app_mode=ctx.APP_MODE,
349
+ app_version=ctx.APP_VERSION,
350
+ lifespan=ctx.lifespan,
351
+ default_host=ctx.DEFAULT_HOST,
352
+ default_port=ctx.DEFAULT_PORT,
353
+ cors_extra_origins=ctx.CORS_EXTRA_ORIGINS,
354
+ cors_allow_network=ctx.CORS_ALLOW_NETWORK,
355
+ static_dir=ctx.STATIC_DIR,
356
+ csrf_trusted_origins=ctx.CSRF_TRUSTED_ORIGINS,
357
+ )
358
+ ctx.adopt(web_runtime, "app")
359
+ ensure_agent_root()
360
+
361
+ ctx.set(
362
+ model_runtime_service=configure_model_runtime_from_context(
363
+ build_model_runtime=build_model_runtime,
364
+ router=ctx.model_router,
365
+ APP_MODE=ctx.APP_MODE,
366
+ DEFAULT_HOST=ctx.DEFAULT_HOST,
367
+ DEFAULT_PORT=ctx.DEFAULT_PORT,
368
+ DATA_DIR=ctx.DATA_DIR,
369
+ BASE_DIR=ctx.BASE_DIR,
370
+ ENABLE_TELEGRAM=ctx.ENABLE_TELEGRAM,
371
+ ENABLE_GRAPH=ctx.ENABLE_GRAPH,
372
+ AUTOLOAD_MODELS=ctx.AUTOLOAD_MODELS,
373
+ MODEL_IDLE_UNLOAD_SECONDS=ctx.MODEL_IDLE_UNLOAD_SECONDS,
374
+ ALLOW_MODEL_DOWNLOADS=ctx.ALLOW_MODEL_DOWNLOADS,
375
+ MODEL_DOWNLOAD_TIMEOUT=ctx.MODEL_DOWNLOAD_TIMEOUT,
376
+ ALLOW_LOCAL_MODELS=ctx.ALLOW_LOCAL_MODELS,
377
+ REQUIRE_AUTH=ctx.REQUIRE_AUTH,
378
+ INVITE_GATE_ENABLED=ctx.INVITE_GATE_ENABLED,
379
+ ALLOW_PLAINTEXT_API_KEYS=ctx.ALLOW_PLAINTEXT_API_KEYS,
380
+ CORS_ALLOW_NETWORK=ctx.CORS_ALLOW_NETWORK,
381
+ PUBLIC_MODEL=ctx.PUBLIC_MODEL,
382
+ LOCAL_MODEL=ctx.LOCAL_MODEL,
383
+ IS_PUBLIC_MODE=ctx.IS_PUBLIC_MODE,
384
+ keyring=ctx.keyring,
385
+ get_current_user=ctx.get_current_user,
386
+ get_user_api_key=ctx.get_user_api_key,
387
+ )
388
+ )
389
+
390
+ static_routes_bundle = build_static_routes_bundle(
391
+ create_static_routes_router=create_static_routes_router,
392
+ static_dir=ctx.STATIC_DIR,
393
+ invite_gate_enabled=ctx.INVITE_GATE_ENABLED,
394
+ invite_code=ctx.INVITE_CODE,
395
+ invite_cookie_secret=ctx.INVITE_COOKIE_SECRET,
396
+ secure_cookies=ctx.SECURE_COOKIES,
397
+ app_mode=ctx.APP_MODE,
398
+ model_router=ctx.model_router,
399
+ require_user=ctx.require_user,
400
+ )
401
+ ctx.adopt(
402
+ static_routes_bundle,
403
+ "STATIC_ROUTES",
404
+ "ui_file_response",
405
+ "local_sysinfo",
406
+ "invite_authorized",
407
+ )
408
+
409
+ foundation_router_bundle = build_auth_admin_security_router_bundle(
410
+ create_auth_router=create_auth_router,
411
+ load_users=ctx.load_users,
412
+ save_users=ctx.save_users,
413
+ hash_password=hash_password,
414
+ verify_and_migrate_password=ctx.verify_and_migrate_password,
415
+ create_session=ctx.create_session,
416
+ get_session_email=ctx.get_session_email,
417
+ invalidate_session=ctx.invalidate_session,
418
+ extract_bearer_token=ctx._extract_bearer_token,
419
+ get_user_role=ctx.get_user_role,
420
+ require_user=ctx.require_user,
421
+ check_ip_rate_limit=ctx._check_rate_limit,
422
+ client_ip=ctx._client_ip,
423
+ get_sso_settings=ctx.get_sso_settings,
424
+ get_sso_discovery=ctx._get_sso_discovery,
425
+ public_sso_config=ctx.public_sso_config,
426
+ open_registration=ctx.OPEN_REGISTRATION,
427
+ session_ttl=ctx._SESSION_TTL,
428
+ require_auth=ctx.REQUIRE_AUTH,
429
+ secure_cookies=ctx.SECURE_COOKIES,
430
+ invite_authorized=ctx.invite_authorized,
431
+ ensure_identity=ensure_user_identity,
432
+ create_admin_router=create_admin_router,
433
+ require_admin=ctx.require_admin,
434
+ get_history=ctx.get_history,
435
+ get_audit_log=ctx.get_audit_log,
436
+ audit_file=ctx.AUDIT_FILE,
437
+ public_user=ctx.public_user,
438
+ load_vpc_config=ctx.load_vpc_config,
439
+ save_vpc_config=ctx.save_vpc_config,
440
+ build_admin_audit_report=ctx.build_admin_audit_report,
441
+ build_sensitivity_report=ctx.build_sensitivity_report,
442
+ append_audit_event=ctx.append_audit_event,
443
+ save_sso_config=ctx.save_sso_config,
444
+ knowledge_graph=ctx.KNOWLEDGE_GRAPH,
445
+ enable_graph=ctx.ENABLE_GRAPH,
446
+ logger=logging,
447
+ invite_code=ctx.INVITE_CODE,
448
+ invite_gate_enabled=ctx.INVITE_GATE_ENABLED,
449
+ default_port=ctx.DEFAULT_PORT,
450
+ policy_matrix=policy_matrix,
451
+ build_product_hardening_status=build_product_hardening_status,
452
+ config=ctx.CONFIG,
453
+ kg_portability=ctx.KG_PORTABILITY,
454
+ device_identity=ctx.DEVICE_IDENTITY,
455
+ create_invitations_router=create_invitations_router,
456
+ invitation_store=ctx.INVITATION_STORE,
457
+ workspace_service=ctx.WORKSPACE_SERVICE,
458
+ user_id_for_email=ctx.user_id_for_email,
459
+ create_security_router=_create_security_router,
460
+ classify_sensitive_message=ctx.classify_sensitive_message,
461
+ )
462
+ ctx.adopt(
463
+ foundation_router_bundle,
464
+ "auth_router",
465
+ "admin_router",
466
+ "invitations_router",
467
+ "security_router",
468
+ "_graph_stats_safe",
469
+ "_product_hardening_status",
470
+ "_security_audit_events_safe",
471
+ "_security_list_uploaded_files",
472
+ )
473
+
474
+
475
+ def phase_foundation_routes(ctx: RuntimeContext) -> None:
476
+ """Mount the foundation routers once the AppContext exists."""
477
+ ctx.enter("foundation_routes")
478
+
479
+ from latticeai.api.workspace import create_workspace_router
480
+ from latticeai.runtime.router_registration import register_foundation_routers
481
+
482
+ register_foundation_routers(
483
+ ctx.app,
484
+ static_router=ctx.STATIC_ROUTES.router,
485
+ auth_router=ctx.auth_router,
486
+ admin_router=ctx.admin_router,
487
+ invitations_router=ctx.invitations_router,
488
+ security_router=ctx.security_router,
489
+ create_workspace_router=create_workspace_router,
490
+ context=ctx.app_context,
491
+ )
492
+
@@ -204,6 +204,7 @@ class RuntimeContext:
204
204
  AGENT_RUNTIME: Any
205
205
  RUN_EXECUTOR: Any
206
206
  COMMAND_CENTER: Any
207
+ CHRONICLE: Any
207
208
  CHANGE_PROPOSALS: Any
208
209
  EVIDENCE_ACTIONS_SERVICE: Any
209
210
  VOICE_CAPTURE: Any
@@ -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 = "11.2.0"
19
+ ARCHITECTURE_VERSION_TARGET = "11.4.0"
20
20
 
21
21
  PREFERRED_REFACTORING_ORDER = [
22
22
  "agent-runtime",
@@ -53,6 +53,19 @@ def _symbol_exists(dotted: str) -> bool:
53
53
  return False
54
54
 
55
55
 
56
+ def _package_sources(root: Path, relative_package: str) -> List[str]:
57
+ """Every ``*.py`` in a package, as repo-relative paths.
58
+
59
+ Returns the missing marker path when the package is absent, so a gate that
60
+ scans a package fails the same way it would for a deleted single file.
61
+ """
62
+ directory = root / relative_package
63
+ sources = sorted(directory.glob("*.py")) if directory.is_dir() else []
64
+ if not sources:
65
+ return [f"{relative_package}/__init__.py"]
66
+ return [str(path.relative_to(root)) for path in sources]
67
+
68
+
56
69
  def _forbidden_patterns(root: Path, relative_path: str, patterns: List[str]) -> List[str]:
57
70
  path = root / relative_path
58
71
  try:
@@ -71,18 +84,26 @@ def architecture_readiness(root: Path | None = None) -> Dict[str, Any]:
71
84
  "latticeai/app_factory.py",
72
85
  ["build_runtime_namespace(locals", "dict(locals())"],
73
86
  )
74
- model_runtime_forbidden = _forbidden_patterns(
75
- root,
76
- "latticeai/services/model_runtime.py",
77
- [
78
- "def _sync_globals",
79
- "global router",
80
- "STATE = ModelRuntimeState",
81
- "_STATE_EXPORTS",
82
- "def __getattr__(name:",
83
- "from fastapi import HTTPException",
84
- ],
85
- )
87
+ # v11.3.0: model_runtime.py became a package. The ambient-state patterns
88
+ # below could reappear in any of its submodules, so every file in the
89
+ # package is scanned — checking only __init__.py would leave the gate
90
+ # blind one directory down.
91
+ model_runtime_forbidden = [
92
+ finding
93
+ for relative_path in _package_sources(root, "latticeai/services/model_runtime")
94
+ for finding in _forbidden_patterns(
95
+ root,
96
+ relative_path,
97
+ [
98
+ "def _sync_globals",
99
+ "global router",
100
+ "STATE = ModelRuntimeState",
101
+ "_STATE_EXPORTS",
102
+ "def __getattr__(name:",
103
+ "from fastapi import HTTPException",
104
+ ],
105
+ )
106
+ ]
86
107
  model_runtime_forbidden.extend(
87
108
  _forbidden_patterns(
88
109
  root,
@@ -97,11 +118,19 @@ def architecture_readiness(root: Path | None = None) -> Dict[str, Any]:
97
118
  ["from fastapi import HTTPException", "raise HTTPException"],
98
119
  )
99
120
  )
100
- agent_alias_forbidden = _forbidden_patterns(
101
- root,
102
- "latticeai/core/agent.py",
103
- ["AgentRuntime = SingleAgentRuntime"],
104
- )
121
+ # v11.3.0: the single-agent loop is a package. The compatibility alias
122
+ # could reappear in any of its submodules, so every file in the package is
123
+ # scanned — checking only __init__.py would leave the gate blind one
124
+ # directory down.
125
+ agent_alias_forbidden = [
126
+ finding
127
+ for relative_path in _package_sources(root, "latticeai/core/agent")
128
+ for finding in _forbidden_patterns(
129
+ root,
130
+ relative_path,
131
+ ["AgentRuntime = SingleAgentRuntime"],
132
+ )
133
+ ]
105
134
 
106
135
  gates = [
107
136
  ArchitectureGate(
@@ -180,7 +209,7 @@ def architecture_readiness(root: Path | None = None) -> Dict[str, Any]:
180
209
  evidence=[
181
210
  "frontend/src/components/ProductFlow.tsx Wake Brain entry",
182
211
  "frontend/src/features/brain/BrainHome.tsx memory rings",
183
- "tests/visual/v3.spec.js first-run and Brain depth coverage",
212
+ "tests/visual/v3.home.spec.js first-run and Brain depth coverage",
184
213
  ],
185
214
  ),
186
215
  ArchitectureGate(
@@ -0,0 +1,58 @@
1
+ """Proactive Brain Intelligence service (v9.3.0).
2
+
3
+ The Brain graduates from a passive store to an active steward of its own
4
+ knowledge. This service wires the previously dormant quality layer
5
+ (:mod:`lattice_brain.quality` — dedupe, merge, conflict and temporal
6
+ contradiction detection, retention) into router-facing capabilities:
7
+
8
+ * **health_report** — scored diagnosis of the Brain across freshness,
9
+ connectivity, embedding coverage, and contradiction pressure, with
10
+ recommended next actions. Every number is read from the live stores;
11
+ a missing store degrades the dimension to ``unavailable``, never a guess.
12
+ * **insights** — a proactive digest: recent knowledge growth, most active
13
+ types, stale knowledge, orphan (disconnected) nodes, and suggested
14
+ questions grounded in real node titles.
15
+ * **contradictions** — surfaced conflicts across workspace memories
16
+ (negation/preference conflicts, temporal contradictions) plus explicit
17
+ CONTRADICTS edges already recorded in the graph.
18
+ * **consolidate** — duplicate-memory and duplicate-edge detection. Dry-run
19
+ by default (consent-first, like every Brain automation); ``apply=True``
20
+ prunes only exact duplicate workspace memories through the audited
21
+ MemoryService path and never touches graph content.
22
+
23
+ Pure service: no FastAPI, no globals. Collaborators are injected.
24
+
25
+ Split into cohesive submodules in v11.3.0 (no behaviour change): ``constants``
26
+ (windows + the two shared readings), ``proposals`` (the review-queue path and
27
+ the proactive brief), ``sampling`` (the shared graph slice and memory read),
28
+ ``health`` (the scored diagnosis and vector freshness), ``digest`` (insights,
29
+ garden, graph-layer quality reads), ``consistency`` (contradiction and
30
+ consolidation scans), and ``service`` (the composed class). This module
31
+ re-exports every name the single file exposed, so
32
+ ``latticeai.services.brain_intelligence.X`` keeps working.
33
+
34
+ Stubbing note: rebinding one of these *here* changes only this module's name.
35
+ The submodule that calls it holds its own reference, so a test standing in for
36
+ ``LOGGER`` or ``_parse_ts`` patches the submodule that uses it.
37
+ """
38
+
39
+ from __future__ import annotations
40
+
41
+ from lattice_brain.quality import GraphEdgeQualityManager as GraphEdgeQualityManager
42
+ from lattice_brain.quality import MemoryQualityManager as MemoryQualityManager
43
+
44
+ # noqa on the next line, not a redundant alias: the single file exposed this
45
+ # name *renamed* (``brain_intelligence._now``), and reproducing that surface
46
+ # exactly is the point — importing it as ``now_iso`` would add a name the
47
+ # module never had.
48
+ from latticeai.core.timeutil import now_iso as _now # noqa: F401
49
+
50
+ from .constants import _GRAPH_SAMPLE_LIMIT as _GRAPH_SAMPLE_LIMIT
51
+ from .constants import _RECENT_DAYS as _RECENT_DAYS
52
+ from .constants import _STALE_DAYS as _STALE_DAYS
53
+ from .constants import LOGGER as LOGGER
54
+ from .constants import _no_graph_reason as _no_graph_reason
55
+ from .constants import _parse_ts as _parse_ts
56
+ from .service import BrainIntelligenceService as BrainIntelligenceService
57
+
58
+ __all__ = ["BrainIntelligenceService"]