ltcai 9.0.0 → 9.2.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 (189) hide show
  1. package/README.md +88 -46
  2. package/auto_setup.py +7 -853
  3. package/desktop/electron/README.md +9 -0
  4. package/desktop/electron/main.cjs +5 -3
  5. package/docs/CHANGELOG.md +178 -2
  6. package/docs/COMMUNITY_AND_PLUGINS.md +4 -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/TRUST_MODEL.md +5 -2
  13. package/docs/WHY_LATTICE.md +15 -10
  14. package/docs/WORKFLOW_DESIGNER.md +22 -0
  15. package/docs/kg-schema.md +13 -2
  16. package/docs/mcp-tools.md +17 -6
  17. package/docs/privacy.md +19 -3
  18. package/docs/public-deploy.md +32 -3
  19. package/docs/security-model.md +46 -11
  20. package/lattice_brain/__init__.py +1 -1
  21. package/lattice_brain/archive.py +1 -6
  22. package/lattice_brain/context.py +66 -9
  23. package/lattice_brain/graph/_kg_fsutil.py +1 -5
  24. package/lattice_brain/graph/discovery_index.py +174 -20
  25. package/lattice_brain/graph/documents.py +44 -9
  26. package/lattice_brain/graph/ingest.py +47 -20
  27. package/lattice_brain/graph/provenance.py +13 -6
  28. package/lattice_brain/graph/retrieval.py +140 -39
  29. package/lattice_brain/graph/retrieval_docgen.py +37 -4
  30. package/lattice_brain/ingestion.py +4 -7
  31. package/lattice_brain/portability.py +28 -14
  32. package/lattice_brain/runtime/agent_runtime.py +5 -9
  33. package/lattice_brain/runtime/hooks.py +30 -13
  34. package/lattice_brain/runtime/multi_agent.py +27 -8
  35. package/lattice_brain/runtime/statuses.py +10 -0
  36. package/lattice_brain/utils.py +20 -2
  37. package/lattice_brain/workflow.py +1 -5
  38. package/latticeai/__init__.py +1 -1
  39. package/latticeai/api/admin.py +1 -1
  40. package/latticeai/api/agent_registry.py +10 -8
  41. package/latticeai/api/agents.py +22 -3
  42. package/latticeai/api/auth.py +78 -16
  43. package/latticeai/api/browser.py +303 -34
  44. package/latticeai/api/chat.py +320 -850
  45. package/latticeai/api/chat_agent_http.py +356 -0
  46. package/latticeai/api/chat_contracts.py +54 -0
  47. package/latticeai/api/chat_documents.py +256 -0
  48. package/latticeai/api/chat_helpers.py +28 -1
  49. package/latticeai/api/chat_history.py +99 -0
  50. package/latticeai/api/chat_intents.py +316 -0
  51. package/latticeai/api/chat_stream.py +115 -0
  52. package/latticeai/api/computer_use.py +62 -9
  53. package/latticeai/api/health.py +9 -3
  54. package/latticeai/api/hooks.py +17 -6
  55. package/latticeai/api/knowledge_graph.py +78 -14
  56. package/latticeai/api/local_files.py +7 -2
  57. package/latticeai/api/mcp.py +102 -23
  58. package/latticeai/api/models.py +72 -33
  59. package/latticeai/api/network.py +5 -5
  60. package/latticeai/api/permissions.py +67 -26
  61. package/latticeai/api/plugins.py +21 -7
  62. package/latticeai/api/portability.py +5 -5
  63. package/latticeai/api/realtime.py +33 -5
  64. package/latticeai/api/setup.py +2 -2
  65. package/latticeai/api/static_routes.py +123 -24
  66. package/latticeai/api/tools.py +96 -14
  67. package/latticeai/api/workflow_designer.py +37 -0
  68. package/latticeai/api/workspace.py +2 -1
  69. package/latticeai/app_factory.py +110 -52
  70. package/latticeai/core/agent.py +50 -13
  71. package/latticeai/core/agent_prompts.py +5 -0
  72. package/latticeai/core/agent_registry.py +1 -5
  73. package/latticeai/core/config.py +23 -3
  74. package/latticeai/core/context_builder.py +21 -3
  75. package/latticeai/core/file_generation.py +451 -0
  76. package/latticeai/core/invitations.py +1 -4
  77. package/latticeai/core/io_utils.py +9 -1
  78. package/latticeai/core/legacy_compatibility.py +24 -3
  79. package/latticeai/core/marketplace.py +1 -1
  80. package/latticeai/core/plugins.py +15 -0
  81. package/latticeai/core/policy.py +1 -1
  82. package/latticeai/core/realtime.py +38 -15
  83. package/latticeai/core/sessions.py +7 -2
  84. package/latticeai/core/timeutil.py +10 -0
  85. package/latticeai/core/tool_registry.py +90 -18
  86. package/latticeai/core/users.py +1 -5
  87. package/latticeai/core/workspace_graph_trace.py +31 -5
  88. package/latticeai/core/workspace_memory.py +3 -1
  89. package/latticeai/core/workspace_os.py +18 -7
  90. package/latticeai/core/workspace_os_utils.py +0 -5
  91. package/latticeai/core/workspace_permissions.py +2 -1
  92. package/latticeai/core/workspace_plugins.py +1 -1
  93. package/latticeai/core/workspace_runs.py +14 -5
  94. package/latticeai/core/workspace_skills.py +1 -1
  95. package/latticeai/core/workspace_snapshots.py +2 -1
  96. package/latticeai/core/workspace_timeline.py +2 -1
  97. package/latticeai/integrations/telegram_bot.py +94 -43
  98. package/latticeai/models/router.py +130 -36
  99. package/latticeai/runtime/access_runtime.py +62 -4
  100. package/latticeai/runtime/automation_runtime.py +13 -7
  101. package/latticeai/runtime/brain_runtime.py +19 -7
  102. package/latticeai/runtime/chat_wiring.py +2 -0
  103. package/latticeai/runtime/config_runtime.py +58 -26
  104. package/latticeai/runtime/context_runtime.py +16 -4
  105. package/latticeai/runtime/hooks_runtime.py +1 -1
  106. package/latticeai/runtime/model_wiring.py +33 -5
  107. package/latticeai/runtime/namespace_runtime.py +62 -72
  108. package/latticeai/runtime/platform_runtime_wiring.py +4 -3
  109. package/latticeai/runtime/review_wiring.py +1 -1
  110. package/latticeai/runtime/router_registration.py +34 -1
  111. package/latticeai/runtime/security_runtime.py +110 -13
  112. package/latticeai/runtime/stages.py +27 -0
  113. package/latticeai/server_app.py +5 -1
  114. package/latticeai/services/architecture_readiness.py +74 -5
  115. package/latticeai/services/brain_automation.py +3 -26
  116. package/latticeai/services/chat_service.py +205 -15
  117. package/latticeai/services/local_knowledge.py +423 -0
  118. package/latticeai/services/memory_service.py +55 -21
  119. package/latticeai/services/model_engines.py +48 -33
  120. package/latticeai/services/model_errors.py +17 -0
  121. package/latticeai/services/model_loading.py +28 -25
  122. package/latticeai/services/model_runtime.py +228 -162
  123. package/latticeai/services/p_reinforce.py +22 -3
  124. package/latticeai/services/platform_runtime.py +83 -23
  125. package/latticeai/services/product_readiness.py +19 -17
  126. package/latticeai/services/review_queue.py +12 -0
  127. package/latticeai/services/router_context.py +1 -0
  128. package/latticeai/services/run_executor.py +4 -9
  129. package/latticeai/services/search_service.py +203 -28
  130. package/latticeai/services/tool_dispatch.py +28 -5
  131. package/latticeai/services/triggers.py +53 -4
  132. package/latticeai/services/upload_service.py +13 -2
  133. package/latticeai/setup/__init__.py +25 -0
  134. package/latticeai/setup/auto_setup.py +857 -0
  135. package/latticeai/setup/wizard.py +1264 -0
  136. package/latticeai/tools/__init__.py +278 -0
  137. package/{tools → latticeai/tools}/commands.py +67 -7
  138. package/{tools → latticeai/tools}/computer.py +1 -1
  139. package/{tools → latticeai/tools}/documents.py +1 -1
  140. package/{tools → latticeai/tools}/filesystem.py +3 -3
  141. package/latticeai/tools/knowledge.py +178 -0
  142. package/{tools → latticeai/tools}/local_files.py +1 -1
  143. package/{tools → latticeai/tools}/network.py +0 -1
  144. package/local_knowledge_api.py +4 -342
  145. package/package.json +22 -2
  146. package/scripts/brain_quality_eval.py +3 -1
  147. package/scripts/bump_version.py +3 -0
  148. package/scripts/capture_release_evidence.mjs +180 -0
  149. package/scripts/check_current_release_docs.mjs +142 -0
  150. package/scripts/check_i18n_literals.mjs +107 -31
  151. package/scripts/check_openapi_drift.mjs +56 -0
  152. package/scripts/export_openapi.py +67 -7
  153. package/scripts/i18n_literal_allowlist.json +22 -24
  154. package/scripts/run_integration_tests.mjs +72 -10
  155. package/scripts/validate_release_artifacts.py +49 -7
  156. package/scripts/wheel_smoke.py +5 -0
  157. package/setup_wizard.py +3 -1260
  158. package/src-tauri/Cargo.lock +1 -1
  159. package/src-tauri/Cargo.toml +1 -1
  160. package/src-tauri/tauri.conf.json +1 -1
  161. package/static/app/asset-manifest.json +11 -11
  162. package/static/app/assets/Act-C3dBrWE-.js +1 -0
  163. package/static/app/assets/Brain-DBYgdcjt.js +321 -0
  164. package/static/app/assets/Capture-Cf3hqRtN.js +1 -0
  165. package/static/app/assets/Library-CFfkNn3s.js +1 -0
  166. package/static/app/assets/System-BOurbT-v.js +1 -0
  167. package/static/app/assets/index-A3M9sElj.js +17 -0
  168. package/static/app/assets/index-Bmx9rzTc.css +2 -0
  169. package/static/app/assets/primitives-DcUUmhdC.js +1 -0
  170. package/static/app/assets/textarea-BklR6zN4.js +1 -0
  171. package/static/app/index.html +2 -2
  172. package/static/sw.js +1 -1
  173. package/tools/__init__.py +21 -269
  174. package/docs/CODE_REVIEW_2026-07-06.md +0 -764
  175. package/latticeai/runtime/app_context_runtime.py +0 -13
  176. package/latticeai/runtime/tail_wiring.py +0 -21
  177. package/scripts/com.pts.claudecode.discord.plist +0 -31
  178. package/scripts/pts-claudecode-discord-bridge.mjs +0 -207
  179. package/scripts/start-pts-claudecode-discord.sh +0 -51
  180. package/static/app/assets/Act-21lIXx2E.js +0 -1
  181. package/static/app/assets/Brain-BqUd5UJJ.js +0 -321
  182. package/static/app/assets/Capture-BA7Z2Q1u.js +0 -1
  183. package/static/app/assets/Library-bFMtyni3.js +0 -1
  184. package/static/app/assets/System-K6krGCqn.js +0 -1
  185. package/static/app/assets/index-C4R3ws30.js +0 -17
  186. package/static/app/assets/index-ChSeOB02.css +0 -2
  187. package/static/app/assets/primitives-sQU3it5I.js +0 -1
  188. package/static/app/assets/textarea-DK3Fd_lR.js +0 -1
  189. package/tools/knowledge.py +0 -95
@@ -2,39 +2,71 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
- from typing import TYPE_CHECKING, Any, Dict, Optional
5
+ from dataclasses import dataclass
6
+ from typing import TYPE_CHECKING, Any, Optional
7
+
8
+ from latticeai.runtime.stages import RuntimeStage
6
9
 
7
10
  if TYPE_CHECKING:
8
11
  from latticeai.core.config import Config
9
12
 
10
13
 
11
- def build_config_runtime(config: "Optional[Config]" = None) -> Dict[str, Any]:
14
+ @dataclass(frozen=True)
15
+ class ConfigRuntime(RuntimeStage):
16
+ CONFIG: Any
17
+ APP_MODE: str
18
+ IS_PUBLIC_MODE: bool
19
+ DEFAULT_HOST: str
20
+ DEFAULT_PORT: int
21
+ NETWORK_EXPOSED: bool
22
+ ENABLE_TELEGRAM: bool
23
+ ENABLE_GRAPH: bool
24
+ AUTOLOAD_MODELS: bool
25
+ MODEL_IDLE_UNLOAD_SECONDS: int
26
+ ALLOW_LOCAL_MODELS: bool
27
+ REQUIRE_AUTH: bool
28
+ ALLOW_PLAINTEXT_API_KEYS: bool
29
+ CORS_ALLOW_NETWORK: bool
30
+ CORS_EXTRA_ORIGINS: Any
31
+ PUBLIC_MODEL: str
32
+ LOCAL_MODEL: str
33
+ LOCAL_DRAFT_MODEL: str
34
+ TIMEZONE: str
35
+ MAX_LOCAL_MODELS: int
36
+ ALLOW_MODEL_DOWNLOADS: bool
37
+ MODEL_DOWNLOAD_TIMEOUT: int
38
+
39
+
40
+ def build_config_runtime(config: "Optional[Config]" = None) -> ConfigRuntime:
12
41
  """Build app configuration values without importing model/runtime code."""
13
42
 
14
43
  from latticeai.core.config import Config
15
44
 
16
45
  cfg = config if config is not None else Config.from_env()
17
- return {
18
- "CONFIG": cfg,
19
- "APP_MODE": cfg.app_mode,
20
- "IS_PUBLIC_MODE": cfg.is_public,
21
- "DEFAULT_HOST": cfg.host,
22
- "DEFAULT_PORT": cfg.port,
23
- "NETWORK_EXPOSED": cfg.network_exposed,
24
- "ENABLE_TELEGRAM": cfg.enable_telegram,
25
- "ENABLE_GRAPH": cfg.enable_graph,
26
- "AUTOLOAD_MODELS": cfg.autoload_models,
27
- "MODEL_IDLE_UNLOAD_SECONDS": cfg.model_idle_unload_seconds,
28
- "ALLOW_LOCAL_MODELS": cfg.allow_local_models,
29
- "REQUIRE_AUTH": cfg.require_auth,
30
- "ALLOW_PLAINTEXT_API_KEYS": cfg.allow_plaintext_api_keys,
31
- "CORS_ALLOW_NETWORK": cfg.cors_allow_network,
32
- "CORS_EXTRA_ORIGINS": cfg.cors_extra_origins,
33
- "PUBLIC_MODEL": cfg.public_model,
34
- "LOCAL_MODEL": cfg.local_model,
35
- "LOCAL_DRAFT_MODEL": cfg.local_draft_model,
36
- "TIMEZONE": cfg.timezone,
37
- "MAX_LOCAL_MODELS": cfg.max_local_models,
38
- "ALLOW_MODEL_DOWNLOADS": cfg.allow_model_downloads,
39
- "MODEL_DOWNLOAD_TIMEOUT": cfg.model_download_timeout,
40
- }
46
+ return ConfigRuntime(
47
+ CONFIG=cfg,
48
+ APP_MODE=cfg.app_mode,
49
+ IS_PUBLIC_MODE=cfg.is_public,
50
+ DEFAULT_HOST=cfg.host,
51
+ DEFAULT_PORT=cfg.port,
52
+ NETWORK_EXPOSED=cfg.network_exposed,
53
+ ENABLE_TELEGRAM=cfg.enable_telegram,
54
+ ENABLE_GRAPH=cfg.enable_graph,
55
+ AUTOLOAD_MODELS=cfg.autoload_models,
56
+ MODEL_IDLE_UNLOAD_SECONDS=cfg.model_idle_unload_seconds,
57
+ ALLOW_LOCAL_MODELS=cfg.allow_local_models,
58
+ REQUIRE_AUTH=cfg.require_auth,
59
+ ALLOW_PLAINTEXT_API_KEYS=cfg.allow_plaintext_api_keys,
60
+ CORS_ALLOW_NETWORK=cfg.cors_allow_network,
61
+ CORS_EXTRA_ORIGINS=cfg.cors_extra_origins,
62
+ PUBLIC_MODEL=cfg.public_model,
63
+ LOCAL_MODEL=cfg.local_model,
64
+ LOCAL_DRAFT_MODEL=cfg.local_draft_model,
65
+ TIMEZONE=cfg.timezone,
66
+ MAX_LOCAL_MODELS=cfg.max_local_models,
67
+ ALLOW_MODEL_DOWNLOADS=cfg.allow_model_downloads,
68
+ MODEL_DOWNLOAD_TIMEOUT=cfg.model_download_timeout,
69
+ )
70
+
71
+
72
+ __all__ = ["ConfigRuntime", "build_config_runtime"]
@@ -23,16 +23,28 @@ def build_context_runtime(
23
23
  search_service = SearchService(graph_store=graph_store)
24
24
  brain_memory = BrainMemory(ingestion_pipeline)
25
25
 
26
- def scoped_hybrid_search(q, user_email=None, **kw):
26
+ def scoped_hybrid_search(q, user_email=None, workspace_id=None, **kw):
27
27
  allowed = None
28
- if require_auth and user_email:
29
- allowed = allowed_scopes_for_user(user_email)
28
+ if require_auth:
29
+ if workspace_id is not None:
30
+ allowed = {workspace_id}
31
+ elif user_email:
32
+ allowed = allowed_scopes_for_user(user_email)
30
33
  return search_service.hybrid_search(q, allowed_workspaces=allowed, **kw)
31
34
 
35
+ def scoped_notes_context(q, user_email=None, workspace_id=None, **kw):
36
+ allowed = None
37
+ if require_auth:
38
+ if workspace_id is not None:
39
+ allowed = {workspace_id}
40
+ elif user_email:
41
+ allowed = allowed_scopes_for_user(user_email)
42
+ return gardener.get_relevant_context(q, allowed_workspaces=allowed, **kw)
43
+
32
44
  context_assembler = ContextAssembler(
33
45
  memory_recall=memory_service.recall,
34
46
  hybrid_search=scoped_hybrid_search,
35
- notes_context=gardener.get_relevant_context,
47
+ notes_context=scoped_notes_context,
36
48
  )
37
49
 
38
50
  return {
@@ -21,7 +21,7 @@ def build_hooks_runtime(
21
21
  """Construct the hooks registry and local-knowledge watcher behind one seam."""
22
22
 
23
23
  from lattice_brain.runtime.hooks import HooksRegistry
24
- from local_knowledge_api import LocalKnowledgeWatcher
24
+ from latticeai.services.local_knowledge import LocalKnowledgeWatcher
25
25
 
26
26
  hooks_registry = HooksRegistry(data_dir / "hooks.json")
27
27
  local_kg_watcher = (
@@ -2,14 +2,28 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
+ from dataclasses import dataclass
5
6
  from typing import Any
6
7
 
7
8
  from latticeai.runtime.platform_services_runtime import build_model_service
8
9
 
9
10
 
10
- def configure_model_runtime_from_context(**kwargs: Any) -> None:
11
- configure_model_runtime = kwargs.pop("configure_model_runtime")
12
- configure_model_runtime(**kwargs)
11
+ @dataclass(frozen=True)
12
+ class ModelRuntime:
13
+ """Typed model-provider stage registered with the HTTP application."""
14
+
15
+ router: Any
16
+ runtime_service: Any
17
+ service: Any
18
+ runtime_features: Any
19
+ is_public: bool
20
+
21
+
22
+ def configure_model_runtime_from_context(**kwargs: Any) -> Any:
23
+ """Build and return an isolated model runtime from app-owned values."""
24
+
25
+ build_model_runtime = kwargs.pop("build_model_runtime")
26
+ return build_model_runtime(**kwargs)
13
27
 
14
28
 
15
29
  def register_model_runtime_routers(
@@ -19,10 +33,11 @@ def register_model_runtime_routers(
19
33
  create_models_router: Any,
20
34
  register_health_and_model_routers: Any,
21
35
  model_router: Any,
36
+ runtime_service: Any,
22
37
  runtime_features: Any,
23
38
  is_public_mode: bool,
24
39
  **kwargs: Any,
25
- ) -> Any:
40
+ ) -> ModelRuntime:
26
41
  model_service = build_model_service(
27
42
  model_router=model_router,
28
43
  runtime_features=runtime_features,
@@ -37,4 +52,17 @@ def register_model_runtime_routers(
37
52
  is_public_mode=is_public_mode,
38
53
  **kwargs,
39
54
  )
40
- return model_service
55
+ return ModelRuntime(
56
+ router=model_router,
57
+ runtime_service=runtime_service,
58
+ service=model_service,
59
+ runtime_features=runtime_features,
60
+ is_public=is_public_mode,
61
+ )
62
+
63
+
64
+ __all__ = [
65
+ "ModelRuntime",
66
+ "configure_model_runtime_from_context",
67
+ "register_model_runtime_routers",
68
+ ]
@@ -1,21 +1,19 @@
1
- """Runtime namespace filtering for legacy server_app compatibility.
1
+ """Explicit runtime exports for the legacy :mod:`server_app` facade.
2
2
 
3
- The app factory still constructs a broad local namespace for historical
4
- ``server_app`` attribute access. This module keeps that surface deliberate:
5
- public runtime objects and known legacy helpers stay visible, while internal
6
- assembly scratch values do not leak into ``server_app.__getattr__``.
3
+ The composition root must never export ``locals()``. Every compatibility name
4
+ is selected here and every typed assembly stage remains available through the
5
+ ``RuntimeBundle`` without leaking construction scratch state.
7
6
  """
8
7
 
9
8
  from __future__ import annotations
10
9
 
11
- from dataclasses import dataclass, fields
12
- from types import ModuleType
10
+ from dataclasses import dataclass
13
11
  from typing import Any, Dict, Mapping
14
12
 
15
13
 
16
14
  @dataclass(frozen=True)
17
15
  class RuntimeBundle:
18
- """Typed migration target for app-factory runtime dependencies."""
16
+ """Typed application assembly result and its five explicit stages."""
19
17
 
20
18
  app: Any
21
19
  CONFIG: Any
@@ -29,26 +27,45 @@ class RuntimeBundle:
29
27
  build_runtime: Any
30
28
  get_shared_runtime: Any
31
29
  create_app: Any
30
+ config_runtime: Any
31
+ security_runtime: Any
32
+ brain_runtime: Any
33
+ model_runtime: Any
34
+ router_bundle: Any
32
35
 
33
36
  def as_legacy_dict(self) -> Dict[str, Any]:
34
- return {field.name: getattr(self, field.name) for field in fields(self)}
35
-
36
-
37
- INTERNAL_RUNTIME_NAMES = {
38
- "config",
39
- "logging",
40
- "os",
41
- "threading",
42
- "Path",
43
- "mx",
44
- "uvicorn",
45
- "HTTPException",
46
- "Request",
47
- "BaseModel",
48
- "keyring",
49
- "datetime",
50
- "runtime_bundle",
51
- }
37
+ return {
38
+ name: getattr(self, name)
39
+ for name in RUNTIME_BUNDLE_EXPORTS
40
+ }
41
+
42
+ @property
43
+ def stages(self) -> Dict[str, Any]:
44
+ return {
45
+ "config": self.config_runtime,
46
+ "security": self.security_runtime,
47
+ "brain": self.brain_runtime,
48
+ "models": self.model_runtime,
49
+ "routers": self.router_bundle,
50
+ }
51
+
52
+
53
+ RUNTIME_BUNDLE_EXPORTS = frozenset(
54
+ {
55
+ "app",
56
+ "CONFIG",
57
+ "KNOWLEDGE_GRAPH",
58
+ "INGESTION_PIPELINE",
59
+ "AGENT_RUNTIME",
60
+ "HOOKS_REGISTRY",
61
+ "REVIEW_QUEUE",
62
+ "AGENT_REGISTRY",
63
+ "model_router",
64
+ "build_runtime",
65
+ "get_shared_runtime",
66
+ "create_app",
67
+ }
68
+ )
52
69
 
53
70
  LEGACY_UNDERSCORE_EXPORTS = {
54
71
  "_LOCAL_WRITE_BLOCKED_PREFIXES",
@@ -102,72 +119,45 @@ LEGACY_PUBLIC_EXPORTS = {
102
119
  }
103
120
 
104
121
 
105
- def _is_internal_runtime_dict(name: str, value: Any) -> bool:
106
- if not isinstance(value, dict):
107
- return False
108
- if name in {"_RUNTIME_BUNDLE"}:
109
- return False
110
- return (
111
- name.endswith("_runtime")
112
- or name.endswith("_router_bundle")
113
- or name.endswith("_rt")
114
- or name
115
- in {
116
- "_mcp_state",
117
- "_garden_import",
118
- "_foundation_router_bundle",
119
- "_static_routes_bundle",
120
- "_vpc_runtime",
121
- "_sso_runtime",
122
- "_security_runtime",
123
- "_session_runtime",
124
- "_config_runtime",
125
- "_context_runtime",
126
- "_brain_runtime",
127
- "_hooks_runtime",
128
- "_history_query_runtime",
129
- "_persistence_runtime",
130
- "_platform_automation_runtime",
131
- "_user_key_runtime",
132
- "_web_runtime",
133
- }
134
- )
135
-
136
-
137
122
  def build_runtime_namespace(
138
- local_namespace: Mapping[str, Any],
139
- *,
140
- runtime_bundle: RuntimeBundle | Mapping[str, Any],
123
+ *, runtime_bundle: RuntimeBundle | Mapping[str, Any], legacy_exports: Mapping[str, Any]
141
124
  ) -> Dict[str, Any]:
142
- """Return the compatibility namespace without assembly scratch values."""
125
+ """Return only explicit compatibility exports and typed bundle handles."""
143
126
  legacy_bundle = (
144
127
  runtime_bundle.as_legacy_dict()
145
128
  if isinstance(runtime_bundle, RuntimeBundle)
146
129
  else dict(runtime_bundle)
147
130
  )
148
131
  exported: Dict[str, Any] = dict(legacy_bundle)
149
- allowed = set(legacy_bundle) | LEGACY_PUBLIC_EXPORTS | LEGACY_UNDERSCORE_EXPORTS
150
- for name in allowed:
132
+ allowed = LEGACY_PUBLIC_EXPORTS | LEGACY_UNDERSCORE_EXPORTS
133
+ unexpected = set(legacy_exports) - allowed
134
+ if unexpected:
135
+ raise ValueError(f"unapproved runtime exports: {sorted(unexpected)}")
136
+ for name in sorted(allowed):
151
137
  if name in exported:
152
138
  continue
153
- if name in INTERNAL_RUNTIME_NAMES:
154
- continue
155
- value = local_namespace.get(name)
139
+ value = legacy_exports.get(name)
156
140
  if value is None:
157
141
  continue
158
- if isinstance(value, ModuleType):
159
- continue
160
- if _is_internal_runtime_dict(name, value):
161
- continue
162
142
  exported[name] = value
163
143
  exported["RUNTIME_BUNDLE"] = runtime_bundle
164
144
  exported["_RUNTIME_BUNDLE"] = legacy_bundle
165
145
  return exported
166
146
 
167
147
 
148
+ SERVER_APP_EXPORTS = frozenset(
149
+ RUNTIME_BUNDLE_EXPORTS
150
+ | LEGACY_PUBLIC_EXPORTS
151
+ | LEGACY_UNDERSCORE_EXPORTS
152
+ | {"RUNTIME_BUNDLE", "_RUNTIME_BUNDLE"}
153
+ )
154
+
155
+
168
156
  __all__ = [
169
- "INTERNAL_RUNTIME_NAMES",
170
157
  "LEGACY_UNDERSCORE_EXPORTS",
158
+ "LEGACY_PUBLIC_EXPORTS",
159
+ "RUNTIME_BUNDLE_EXPORTS",
171
160
  "RuntimeBundle",
161
+ "SERVER_APP_EXPORTS",
172
162
  "build_runtime_namespace",
173
163
  ]
@@ -24,13 +24,13 @@ def build_platform_automation_runtime(
24
24
  agent_registry: Any,
25
25
  data_dir: Any,
26
26
  append_audit_event: Callable[..., Any],
27
+ memory_service: Any = None,
27
28
  tz_name: Optional[str] = None,
28
29
  ) -> Dict[str, Any]:
29
30
  """Build platform services, automation services, and hook bindings.
30
31
 
31
- The returned names intentionally match the historical local variables in
32
- ``app_factory._build`` so ``dict(locals())`` continues to expose the legacy
33
- ``server_app`` compatibility surface.
32
+ The returned names intentionally match the explicit composition-root
33
+ bindings consumed by the typed application stages.
34
34
  """
35
35
  from latticeai.runtime.automation_runtime import build_automation_runtime
36
36
  from latticeai.services.platform_runtime import PlatformRuntime
@@ -65,6 +65,7 @@ def build_platform_automation_runtime(
65
65
  llm_generate=_llm_generate_sync,
66
66
  llm_available=lambda: bool(getattr(model_router, "current_model_id", None)),
67
67
  agent_registry=agent_registry,
68
+ memory_recall=memory_service.recall if memory_service is not None else None,
68
69
  )
69
70
 
70
71
  automation_runtime = build_automation_runtime(
@@ -27,7 +27,7 @@ def build_review_run_now_runner(platform: Any, http_exception: type[Exception])
27
27
  workflow_id,
28
28
  user_email,
29
29
  scope,
30
- with_agent=False,
30
+ with_agent=True,
31
31
  inputs={"__review_item__": item.get("id")},
32
32
  )
33
33
 
@@ -8,17 +8,33 @@ exact include order while creating a narrow seam for the later
8
8
 
9
9
  from __future__ import annotations
10
10
 
11
+ from dataclasses import dataclass
11
12
  from typing import Any
12
13
 
13
14
  from latticeai.services.router_context import InteractionRouterContext
14
15
 
15
16
 
17
+ @dataclass(frozen=True)
18
+ class RouterBundle:
19
+ """Final typed router stage after all domain routers are registered."""
20
+
21
+ app: Any
22
+ context: Any
23
+ route_count: int
24
+
25
+
26
+ def build_router_bundle(app: Any, context: Any) -> RouterBundle:
27
+ return RouterBundle(app=app, context=context, route_count=len(getattr(app, "routes", ())))
28
+
29
+
16
30
  def build_static_routes_bundle(
17
31
  *,
18
32
  create_static_routes_router: Any,
19
33
  static_dir: Any,
20
34
  invite_gate_enabled: bool,
21
35
  invite_code: str,
36
+ invite_cookie_secret: str,
37
+ secure_cookies: bool,
22
38
  app_mode: str,
23
39
  model_router: Any,
24
40
  require_user: Any,
@@ -29,6 +45,8 @@ def build_static_routes_bundle(
29
45
  static_dir=static_dir,
30
46
  invite_gate_enabled=invite_gate_enabled,
31
47
  invite_code=invite_code,
48
+ invite_cookie_secret=invite_cookie_secret,
49
+ secure_cookies=secure_cookies,
32
50
  app_mode=app_mode,
33
51
  model_router=model_router,
34
52
  require_user=require_user,
@@ -37,6 +55,7 @@ def build_static_routes_bundle(
37
55
  "STATIC_ROUTES": static_routes,
38
56
  "ui_file_response": static_routes.ui_file_response,
39
57
  "local_sysinfo": static_routes.local_sysinfo,
58
+ "invite_authorized": static_routes.invite_authorized,
40
59
  }
41
60
 
42
61
 
@@ -61,6 +80,8 @@ def build_auth_admin_security_router_bundle(
61
80
  open_registration: bool,
62
81
  session_ttl: int,
63
82
  require_auth: bool,
83
+ secure_cookies: bool,
84
+ invite_authorized: Any,
64
85
  ensure_identity: Any,
65
86
  create_admin_router: Any,
66
87
  require_admin: Any,
@@ -113,6 +134,9 @@ def build_auth_admin_security_router_bundle(
113
134
  open_registration=open_registration,
114
135
  session_ttl=session_ttl,
115
136
  require_auth=require_auth,
137
+ secure_cookies=secure_cookies,
138
+ invite_gate_enabled=invite_gate_enabled,
139
+ invite_authorized=invite_authorized,
116
140
  ensure_identity=ensure_identity,
117
141
  )
118
142
 
@@ -290,8 +314,9 @@ def register_platform_feature_routers(
290
314
  require_user=require_user,
291
315
  require_admin=require_admin,
292
316
  append_audit_event=append_audit_event,
317
+ gate_write=platform.gate_write,
293
318
  register_skill=platform.register_plugin_skill,
294
- plugin_runners_factory=lambda: platform.plugin_capability_runners(None, None),
319
+ plugin_runners_factory=platform.plugin_capability_runners,
295
320
  ui_file_response=ui_file_response,
296
321
  static_dir=static_dir,
297
322
  ),
@@ -356,6 +381,7 @@ def register_health_and_model_routers(
356
381
  create_models_router: Any,
357
382
  model_router: Any,
358
383
  require_user: Any,
384
+ require_admin: Any,
359
385
  load_users: Any,
360
386
  get_user_role: Any,
361
387
  install_engine: Any,
@@ -391,6 +417,7 @@ def register_health_and_model_routers(
391
417
  create_models_router(
392
418
  model_router=model_router,
393
419
  require_user=require_user,
420
+ require_admin=require_admin,
394
421
  get_current_user=get_current_user,
395
422
  load_users=load_users,
396
423
  get_user_role=get_user_role,
@@ -469,6 +496,7 @@ def register_interaction_routers(
469
496
  require_user = interaction_context.require_user
470
497
  embedding_info = interaction_context.embedding_info
471
498
  tool_context = interaction_context.tool_context
499
+ require_admin = tool_context.require_admin
472
500
  get_current_user = tool_context.get_current_user
473
501
  append_audit_event = tool_context.append_audit_event
474
502
  hooks = interaction_context.hooks
@@ -515,11 +543,13 @@ def register_interaction_routers(
515
543
  create_hooks_router(
516
544
  registry=hooks,
517
545
  require_user=require_user,
546
+ require_admin=require_admin,
518
547
  append_audit_event=append_audit_event,
519
548
  ),
520
549
  create_agent_registry_router(
521
550
  registry=agent_registry,
522
551
  require_user=require_user,
552
+ require_admin=require_admin,
523
553
  append_audit_event=append_audit_event,
524
554
  ),
525
555
  create_memory_router(
@@ -546,6 +576,7 @@ def register_review_and_brain_tail_routers(
546
576
  append_audit_event: Any,
547
577
  create_browser_router: Any,
548
578
  ingestion_pipeline: Any,
579
+ workspace_service: Any,
549
580
  create_portability_router: Any,
550
581
  kg_portability: Any,
551
582
  require_admin: Any,
@@ -573,6 +604,7 @@ def register_review_and_brain_tail_routers(
573
604
  create_browser_router(
574
605
  pipeline=ingestion_pipeline,
575
606
  require_user=require_user,
607
+ workspace_service=workspace_service,
576
608
  ),
577
609
  create_portability_router(
578
610
  service=kg_portability,
@@ -591,6 +623,7 @@ def register_review_and_brain_tail_routers(
591
623
  network=brain_network,
592
624
  identity=device_identity,
593
625
  require_user=require_user,
626
+ require_admin=require_admin,
594
627
  ),
595
628
  create_garden_router(gardener=gardener, require_user=require_user),
596
629
  create_setup_router(model_router=model_router, require_user=require_user),