ltcai 11.5.2 → 11.6.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 (439) hide show
  1. package/README.md +91 -148
  2. package/bin/ltcai.js +234 -24
  3. package/docs/CHANGELOG.md +75 -0
  4. package/docs/COMMUNITY_AND_PLUGINS.md +1 -1
  5. package/docs/DEVELOPMENT.md +8 -6
  6. package/docs/ENTERPRISE.md +2 -1
  7. package/docs/MULTI_AGENT_RUNTIME.md +12 -5
  8. package/docs/ONBOARDING.md +1 -1
  9. package/docs/OPERATIONS.md +6 -3
  10. package/docs/REALTIME_COLLABORATION.md +1 -1
  11. package/docs/TRUST_MODEL.md +1 -1
  12. package/docs/WHY_LATTICE.md +1 -1
  13. package/docs/WORKFLOW_DESIGNER.md +3 -2
  14. package/docs/kg-schema.md +1 -1
  15. package/docs/v11.6.0_ONE_DOOR_PLAN.md +170 -0
  16. package/lattice_brain/__init__.py +42 -79
  17. package/lattice_brain/graph/__init__.py +9 -24
  18. package/lattice_brain/graph/_kg_common/__init__.py +13 -24
  19. package/lattice_brain/ingestion/__init__.py +19 -58
  20. package/lattice_brain/ingestion/pipeline.py +20 -398
  21. package/lattice_brain/multimodal/__init__.py +7 -18
  22. package/lattice_brain/multimodal/images.py +6 -264
  23. package/lattice_brain/multimodal/video.py +8 -247
  24. package/lattice_brain/runtime/__init__.py +8 -79
  25. package/lattice_brain/runtime/hooks.py +22 -584
  26. package/latticeai/__init__.py +1 -1
  27. package/latticeai/api/agent_worker_seam.py +7 -80
  28. package/latticeai/api/health.py +6 -20
  29. package/latticeai/api/local_files.py +16 -613
  30. package/latticeai/api/models.py +12 -87
  31. package/latticeai/api/search.py +17 -255
  32. package/latticeai/api/tools.py +69 -750
  33. package/latticeai/api/voice_capture.py +8 -70
  34. package/latticeai/api/worker_compute.py +842 -0
  35. package/latticeai/api/worker_seams.py +216 -0
  36. package/latticeai/app_factory.py +23 -232
  37. package/latticeai/cli/entrypoint.py +36 -249
  38. package/latticeai/core/agent_permission.py +16 -91
  39. package/latticeai/core/messages.py +89 -531
  40. package/latticeai/runtime/access_runtime.py +0 -14
  41. package/latticeai/runtime/bootstrap.py +10 -21
  42. package/latticeai/runtime/brain_runtime.py +35 -43
  43. package/latticeai/runtime/build_phases/__init__.py +21 -37
  44. package/latticeai/runtime/build_phases/features.py +99 -389
  45. package/latticeai/runtime/build_phases/foundation.py +82 -392
  46. package/latticeai/runtime/build_phases/web.py +91 -408
  47. package/latticeai/runtime/build_phases/worker_profile.py +244 -0
  48. package/latticeai/runtime/lifespan_runtime.py +7 -16
  49. package/latticeai/runtime/platform_services_runtime.py +1 -15
  50. package/latticeai/runtime/runtime_context.py +13 -130
  51. package/latticeai/runtime/security_runtime.py +10 -103
  52. package/latticeai/services/architecture_readiness.py +82 -43
  53. package/latticeai/services/model_runtime/__init__.py +2 -11
  54. package/latticeai/services/model_runtime/service.py +4 -28
  55. package/latticeai/services/p_reinforce.py +10 -261
  56. package/latticeai/services/product_readiness.py +31 -38
  57. package/latticeai/services/search_service.py +37 -795
  58. package/latticeai/services/tool_dispatch.py +30 -386
  59. package/latticeai/services/voice_capture.py +13 -107
  60. package/latticeai/tools/__init__.py +22 -49
  61. package/latticeai/tools/commands.py +0 -163
  62. package/latticeai/tools/computer.py +0 -39
  63. package/latticeai/tools/documents.py +1 -134
  64. package/latticeai/tools/filesystem.py +1 -247
  65. package/latticeai/tools/knowledge.py +1 -52
  66. package/latticeai/tools/local_files.py +0 -20
  67. package/latticeai/worker_app.py +75 -0
  68. package/package.json +2 -3
  69. package/requirements.txt +0 -5
  70. package/scripts/agent_eval.py +16 -28
  71. package/scripts/brain_quality_eval.py +20 -183
  72. package/scripts/bump_version.py +5 -5
  73. package/scripts/check_current_release_docs.mjs +7 -4
  74. package/scripts/check_openapi_drift.mjs +10 -0
  75. package/scripts/check_server_i18n.mjs +2 -18
  76. package/scripts/compose_openapi.py +377 -0
  77. package/scripts/export_openapi.py +32 -4
  78. package/scripts/gen_messages_catalog_fixture.py +302 -0
  79. package/scripts/gen_openapi_fragments.py +365 -0
  80. package/scripts/gen_redact_fixture.py +196 -0
  81. package/scripts/gen_worker_allowlist_fixture.py +115 -0
  82. package/scripts/generate_agent_parity_fixtures.py +33 -14
  83. package/scripts/openapi_route_families.json +2161 -0
  84. package/scripts/release_screen_claims.json +21 -0
  85. package/scripts/run_integration_tests.mjs +134 -29
  86. package/scripts/run_sidecar_e2e.mjs +104 -11
  87. package/scripts/wheel_smoke.py +32 -22
  88. package/src-tauri/Cargo.lock +375 -9
  89. package/src-tauri/Cargo.toml +1 -1
  90. package/src-tauri/src/backend.rs +151 -45
  91. package/src-tauri/src/main.rs +18 -18
  92. package/src-tauri/src/topology.rs +58 -88
  93. package/src-tauri/tauri.conf.json +1 -2
  94. package/static/app/asset-manifest.json +41 -41
  95. package/static/app/assets/{Act-DcQizkl1.js → Act-CToZnOHz.js} +1 -1
  96. package/static/app/assets/{AdminConsole-cf4npybT.js → AdminConsole-D5kbrBu8.js} +1 -1
  97. package/static/app/assets/{Brain-3VCSHFcn.js → Brain-C6zCdv4S.js} +1 -1
  98. package/static/app/assets/{BrainHome-Qm8eaztx.js → BrainHome-D4n3EDUt.js} +1 -1
  99. package/static/app/assets/{BrainSignals-DS9BtKOW.js → BrainSignals-CzeZ-vQ9.js} +1 -1
  100. package/static/app/assets/{Capture-DiQ219jW.js → Capture-DPcyTczq.js} +1 -1
  101. package/static/app/assets/{Chronicle-BGvuAchH.js → Chronicle-D3Nx-oWS.js} +1 -1
  102. package/static/app/assets/{CommandPalette-Bqhm0Urn.js → CommandPalette-DPPvpp28.js} +1 -1
  103. package/static/app/assets/{Library-BV6NnF0a.js → Library-Co2qTu3I.js} +1 -1
  104. package/static/app/assets/{LivingBrain-GzenJchP.js → LivingBrain-Bsmzwy8_.js} +1 -1
  105. package/static/app/assets/{ProductFlow-DEP6-vML.js → ProductFlow-CBLdmesT.js} +1 -1
  106. package/static/app/assets/{ReviewCard-CNZ7XjWG.js → ReviewCard-CfR6DLs7.js} +1 -1
  107. package/static/app/assets/{System-CieofHQa.js → System-Ce-P2YL6.js} +1 -1
  108. package/static/app/assets/arrow-left-DD5jFGHV.js +1 -0
  109. package/static/app/assets/{bot-B_K1Tdmw.js → bot-C5-XIww2.js} +1 -1
  110. package/static/app/assets/{brain-DWyaV1L1.js → brain-CFRJUvdX.js} +1 -1
  111. package/static/app/assets/{button-aTn4s84A.js → button-DI1KcQP-.js} +1 -1
  112. package/static/app/assets/circle-check-CY20XMCh.js +1 -0
  113. package/static/app/assets/{circle-pause-xKgeGXkT.js → circle-pause-C8HHyood.js} +1 -1
  114. package/static/app/assets/{circle-play-DkT6tYPX.js → circle-play-Cufc235E.js} +1 -1
  115. package/static/app/assets/{cpu-85xYObUC.js → cpu-DW2_JVsv.js} +1 -1
  116. package/static/app/assets/{download-B5Fm7YXo.js → download-gbdKTBb8.js} +1 -1
  117. package/static/app/assets/{folder-open-kk2Xa52u.js → folder-open-C9ydyX33.js} +1 -1
  118. package/static/app/assets/{hard-drive-DkA3zBW_.js → hard-drive-BxNMzBM3.js} +1 -1
  119. package/static/app/assets/index-BovSWRmQ.css +2 -0
  120. package/static/app/assets/{index-BMPdTmlY.js → index-q9XyDY7A.js} +3 -3
  121. package/static/app/assets/{input-B0nRf2jO.js → input-GLD94Y_K.js} +1 -1
  122. package/static/app/assets/{link-2-Dwb4gnTc.js → link-2-B4Y0gz1S.js} +1 -1
  123. package/static/app/assets/{permissionCopy-CQDUBrOZ.js → permissionCopy-CxrV0fvU.js} +1 -1
  124. package/static/app/assets/{primitives-SNp0LRJz.js → primitives-2ANdAsu2.js} +1 -1
  125. package/static/app/assets/search-DTxtjQ07.js +1 -0
  126. package/static/app/assets/{share-2-BsrxFglO.js → share-2-DxArfiuI.js} +1 -1
  127. package/static/app/assets/{shield-alert-5BStfp2_.js → shield-alert-CgftuFFs.js} +1 -1
  128. package/static/app/assets/{textarea-Cg8IUA-k.js → textarea-BHVBAoCr.js} +1 -1
  129. package/static/app/assets/{useFocusTrap-CYKvE46M.js → useFocusTrap-Dzq2j_OG.js} +1 -1
  130. package/static/app/assets/{useMutation-CSn9t1op.js → useMutation-BcqfMLNq.js} +1 -1
  131. package/static/app/assets/{useQuery-CY2OI2uy.js → useQuery-DBzLKCOv.js} +1 -1
  132. package/static/app/assets/{utils-Ddol2RWD.js → utils-B-Aah1bO.js} +1 -1
  133. package/static/app/assets/{workspace-BqDwOz_p.js → workspace-B6fFhoko.js} +1 -1
  134. package/static/app/index.html +4 -4
  135. package/static/sw.js +1 -1
  136. package/lattice_brain/archive.py +0 -522
  137. package/lattice_brain/context.py +0 -325
  138. package/lattice_brain/conversations.py +0 -382
  139. package/lattice_brain/core.py +0 -82
  140. package/lattice_brain/graph/_kg_contract.py +0 -249
  141. package/lattice_brain/graph/curator.py +0 -676
  142. package/lattice_brain/graph/discovery.py +0 -595
  143. package/lattice_brain/graph/discovery_index/__init__.py +0 -35
  144. package/lattice_brain/graph/discovery_index/cleanup.py +0 -182
  145. package/lattice_brain/graph/discovery_index/extract.py +0 -137
  146. package/lattice_brain/graph/discovery_index/scan.py +0 -411
  147. package/lattice_brain/graph/discovery_index/upsert.py +0 -495
  148. package/lattice_brain/graph/documents.py +0 -380
  149. package/lattice_brain/graph/fusion.py +0 -395
  150. package/lattice_brain/graph/identity.py +0 -175
  151. package/lattice_brain/graph/image_vectors.py +0 -230
  152. package/lattice_brain/graph/ingest.py +0 -829
  153. package/lattice_brain/graph/network.py +0 -205
  154. package/lattice_brain/graph/proactive.py +0 -724
  155. package/lattice_brain/graph/projection/__init__.py +0 -42
  156. package/lattice_brain/graph/projection/curation.py +0 -500
  157. package/lattice_brain/graph/projection/v2_schema.py +0 -518
  158. package/lattice_brain/graph/provenance.py +0 -524
  159. package/lattice_brain/graph/rerank.py +0 -163
  160. package/lattice_brain/graph/retrieval/__init__.py +0 -54
  161. package/lattice_brain/graph/retrieval/context.py +0 -197
  162. package/lattice_brain/graph/retrieval/graph_view.py +0 -319
  163. package/lattice_brain/graph/retrieval/hybrid.py +0 -488
  164. package/lattice_brain/graph/retrieval/maintenance.py +0 -121
  165. package/lattice_brain/graph/retrieval/signals.py +0 -95
  166. package/lattice_brain/graph/retrieval_docgen.py +0 -253
  167. package/lattice_brain/graph/retrieval_policy.py +0 -180
  168. package/lattice_brain/graph/retrieval_reads.py +0 -769
  169. package/lattice_brain/graph/retrieval_vector/__init__.py +0 -42
  170. package/lattice_brain/graph/retrieval_vector/fingerprint.py +0 -97
  171. package/lattice_brain/graph/retrieval_vector/indexing.py +0 -347
  172. package/lattice_brain/graph/retrieval_vector/search.py +0 -560
  173. package/lattice_brain/graph/retrieval_vector/status.py +0 -374
  174. package/lattice_brain/graph/schema.py +0 -792
  175. package/lattice_brain/graph/store.py +0 -268
  176. package/lattice_brain/graph/vector_index/__init__.py +0 -85
  177. package/lattice_brain/graph/vector_index/base.py +0 -167
  178. package/lattice_brain/graph/vector_index/brute_force.py +0 -110
  179. package/lattice_brain/graph/vector_index/hnsw.py +0 -290
  180. package/lattice_brain/graph/vector_index/jobs.py +0 -287
  181. package/lattice_brain/graph/vector_index/quantized.py +0 -148
  182. package/lattice_brain/graph/vector_index/selector.py +0 -161
  183. package/lattice_brain/graph/write_master.py +0 -308
  184. package/lattice_brain/ingestion/_contract.py +0 -90
  185. package/lattice_brain/ingestion/folder_scan.py +0 -57
  186. package/lattice_brain/ingestion/folders.py +0 -258
  187. package/lattice_brain/ingestion/jobs_api.py +0 -107
  188. package/lattice_brain/ingestion/routing.py +0 -295
  189. package/lattice_brain/ingestion_jobs.py +0 -380
  190. package/lattice_brain/memory.py +0 -75
  191. package/lattice_brain/portability/__init__.py +0 -90
  192. package/lattice_brain/portability/_contract.py +0 -42
  193. package/lattice_brain/portability/backups.py +0 -338
  194. package/lattice_brain/portability/bundles.py +0 -136
  195. package/lattice_brain/portability/constants.py +0 -93
  196. package/lattice_brain/portability/fsops.py +0 -138
  197. package/lattice_brain/portability/service.py +0 -41
  198. package/lattice_brain/portability/sharing.py +0 -710
  199. package/lattice_brain/quality.py +0 -543
  200. package/lattice_brain/retrieval_benchmark_fixtures.py +0 -92
  201. package/lattice_brain/runtime/agent_runtime.py +0 -859
  202. package/lattice_brain/runtime/contracts.py +0 -460
  203. package/lattice_brain/runtime/multi_agent.py +0 -942
  204. package/lattice_brain/runtime/statuses.py +0 -10
  205. package/lattice_brain/sealed_box.py +0 -240
  206. package/lattice_brain/self_model.py +0 -675
  207. package/lattice_brain/sensitivity.py +0 -94
  208. package/lattice_brain/storage/__init__.py +0 -22
  209. package/lattice_brain/storage/base.py +0 -100
  210. package/lattice_brain/storage/docker.py +0 -105
  211. package/lattice_brain/storage/factory.py +0 -31
  212. package/lattice_brain/storage/migration.py +0 -191
  213. package/lattice_brain/storage/postgres.py +0 -123
  214. package/lattice_brain/storage/sqlite.py +0 -143
  215. package/lattice_brain/synthesis.py +0 -824
  216. package/lattice_brain/workflow.py +0 -497
  217. package/latticeai/api/admin.py +0 -471
  218. package/latticeai/api/agent_registry.py +0 -105
  219. package/latticeai/api/agents.py +0 -228
  220. package/latticeai/api/auth.py +0 -383
  221. package/latticeai/api/automation_intelligence.py +0 -401
  222. package/latticeai/api/brain_intelligence.py +0 -199
  223. package/latticeai/api/browser.py +0 -493
  224. package/latticeai/api/change_proposals.py +0 -89
  225. package/latticeai/api/chat.py +0 -572
  226. package/latticeai/api/chat_agent_http.py +0 -892
  227. package/latticeai/api/chat_contracts.py +0 -73
  228. package/latticeai/api/chat_documents.py +0 -276
  229. package/latticeai/api/chat_helpers.py +0 -460
  230. package/latticeai/api/chat_history.py +0 -101
  231. package/latticeai/api/chat_hybrid.py +0 -113
  232. package/latticeai/api/chat_intents.py +0 -646
  233. package/latticeai/api/chat_stream.py +0 -216
  234. package/latticeai/api/chronicle.py +0 -63
  235. package/latticeai/api/command_center.py +0 -51
  236. package/latticeai/api/computer_use.py +0 -474
  237. package/latticeai/api/evidence_actions.py +0 -48
  238. package/latticeai/api/features.py +0 -70
  239. package/latticeai/api/funnel_metrics.py +0 -31
  240. package/latticeai/api/garden.py +0 -34
  241. package/latticeai/api/hooks.py +0 -165
  242. package/latticeai/api/index_jobs.py +0 -145
  243. package/latticeai/api/invitations.py +0 -100
  244. package/latticeai/api/knowledge_graph.py +0 -536
  245. package/latticeai/api/marketplace.py +0 -105
  246. package/latticeai/api/mcp.py +0 -482
  247. package/latticeai/api/memory.py +0 -270
  248. package/latticeai/api/network.py +0 -81
  249. package/latticeai/api/network_boundary.py +0 -225
  250. package/latticeai/api/permission_mode.py +0 -61
  251. package/latticeai/api/permissions.py +0 -436
  252. package/latticeai/api/plugins.py +0 -126
  253. package/latticeai/api/portability.py +0 -391
  254. package/latticeai/api/project_sessions.py +0 -114
  255. package/latticeai/api/realtime.py +0 -118
  256. package/latticeai/api/review_queue.py +0 -364
  257. package/latticeai/api/security_dashboard.py +0 -604
  258. package/latticeai/api/setup.py +0 -319
  259. package/latticeai/api/static_routes.py +0 -354
  260. package/latticeai/api/ui_redirects.py +0 -26
  261. package/latticeai/api/workflow_designer.py +0 -394
  262. package/latticeai/api/workspace.py +0 -856
  263. package/latticeai/api/workspace_scope.py +0 -125
  264. package/latticeai/core/agent/__init__.py +0 -93
  265. package/latticeai/core/agent/_contract.py +0 -79
  266. package/latticeai/core/agent/context.py +0 -57
  267. package/latticeai/core/agent/deps.py +0 -125
  268. package/latticeai/core/agent/execution.py +0 -622
  269. package/latticeai/core/agent/planning.py +0 -145
  270. package/latticeai/core/agent/recovery.py +0 -157
  271. package/latticeai/core/agent/runtime.py +0 -210
  272. package/latticeai/core/agent/verification.py +0 -231
  273. package/latticeai/core/agent_eval.py +0 -739
  274. package/latticeai/core/agent_helpers.py +0 -493
  275. package/latticeai/core/agent_profiles.py +0 -110
  276. package/latticeai/core/agent_prompts.py +0 -171
  277. package/latticeai/core/agent_registry.py +0 -232
  278. package/latticeai/core/agent_state.py +0 -41
  279. package/latticeai/core/agent_trace.py +0 -104
  280. package/latticeai/core/artifact_ledger.py +0 -109
  281. package/latticeai/core/audit.py +0 -260
  282. package/latticeai/core/builtin_hooks.py +0 -105
  283. package/latticeai/core/context_builder.py +0 -394
  284. package/latticeai/core/document_generator.py +0 -103
  285. package/latticeai/core/enterprise.py +0 -154
  286. package/latticeai/core/enterprise_admin.py +0 -158
  287. package/latticeai/core/file_generation/__init__.py +0 -115
  288. package/latticeai/core/file_generation/bundles.py +0 -76
  289. package/latticeai/core/file_generation/extraction.py +0 -154
  290. package/latticeai/core/file_generation/inference.py +0 -235
  291. package/latticeai/core/file_generation/orchestration.py +0 -152
  292. package/latticeai/core/file_generation/prompting.py +0 -117
  293. package/latticeai/core/file_generation/repair.py +0 -114
  294. package/latticeai/core/file_generation/sanitize.py +0 -61
  295. package/latticeai/core/file_generation/validation.py +0 -201
  296. package/latticeai/core/invitations.py +0 -132
  297. package/latticeai/core/legacy_compatibility.py +0 -243
  298. package/latticeai/core/logging_safety.py +0 -46
  299. package/latticeai/core/marketplace.py +0 -293
  300. package/latticeai/core/mcp_catalog.py +0 -452
  301. package/latticeai/core/mcp_registry.py +0 -506
  302. package/latticeai/core/network_boundary.py +0 -168
  303. package/latticeai/core/oidc.py +0 -208
  304. package/latticeai/core/plugins.py +0 -432
  305. package/latticeai/core/product_hardening.py +0 -218
  306. package/latticeai/core/project_sessions.py +0 -337
  307. package/latticeai/core/realtime.py +0 -238
  308. package/latticeai/core/run_explain.py +0 -426
  309. package/latticeai/core/run_store.py +0 -252
  310. package/latticeai/core/timezones.py +0 -80
  311. package/latticeai/core/workspace_computer_memory.py +0 -84
  312. package/latticeai/core/workspace_graph_trace.py +0 -155
  313. package/latticeai/core/workspace_indexing.py +0 -102
  314. package/latticeai/core/workspace_memory.py +0 -77
  315. package/latticeai/core/workspace_onboarding.py +0 -104
  316. package/latticeai/core/workspace_os.py +0 -978
  317. package/latticeai/core/workspace_os_constants.py +0 -126
  318. package/latticeai/core/workspace_os_state.py +0 -180
  319. package/latticeai/core/workspace_os_utils.py +0 -103
  320. package/latticeai/core/workspace_permissions.py +0 -101
  321. package/latticeai/core/workspace_plugins.py +0 -97
  322. package/latticeai/core/workspace_relationships.py +0 -99
  323. package/latticeai/core/workspace_reorganization.py +0 -335
  324. package/latticeai/core/workspace_review_items.py +0 -112
  325. package/latticeai/core/workspace_runs.py +0 -726
  326. package/latticeai/core/workspace_skills.py +0 -109
  327. package/latticeai/core/workspace_snapshots.py +0 -198
  328. package/latticeai/core/workspace_timeline.py +0 -110
  329. package/latticeai/integrations/__init__.py +0 -0
  330. package/latticeai/integrations/telegram_bot/__init__.py +0 -123
  331. package/latticeai/integrations/telegram_bot/__main__.py +0 -17
  332. package/latticeai/integrations/telegram_bot/config.py +0 -86
  333. package/latticeai/integrations/telegram_bot/dispatch.py +0 -311
  334. package/latticeai/integrations/telegram_bot/flows.py +0 -478
  335. package/latticeai/integrations/telegram_bot/helpers.py +0 -322
  336. package/latticeai/integrations/telegram_bot/screens.py +0 -394
  337. package/latticeai/runtime/audit_runtime.py +0 -76
  338. package/latticeai/runtime/automation_runtime.py +0 -81
  339. package/latticeai/runtime/chat_wiring.py +0 -141
  340. package/latticeai/runtime/context_runtime.py +0 -66
  341. package/latticeai/runtime/feature_toggle_wiring.py +0 -157
  342. package/latticeai/runtime/history_runtime.py +0 -163
  343. package/latticeai/runtime/history_writer.py +0 -138
  344. package/latticeai/runtime/hooks_runtime.py +0 -77
  345. package/latticeai/runtime/model_wiring.py +0 -68
  346. package/latticeai/runtime/namespace_runtime.py +0 -163
  347. package/latticeai/runtime/network_boundary_wiring.py +0 -117
  348. package/latticeai/runtime/network_config_runtime.py +0 -56
  349. package/latticeai/runtime/permission_mode_wiring.py +0 -112
  350. package/latticeai/runtime/persistence_runtime.py +0 -159
  351. package/latticeai/runtime/platform_runtime_wiring.py +0 -89
  352. package/latticeai/runtime/review_wiring.py +0 -42
  353. package/latticeai/runtime/router_registration.py +0 -693
  354. package/latticeai/runtime/service_singletons.py +0 -55
  355. package/latticeai/runtime/sso_config_runtime.py +0 -128
  356. package/latticeai/runtime/user_key_runtime.py +0 -106
  357. package/latticeai/runtime/web_runtime.py +0 -92
  358. package/latticeai/server_app.py +0 -51
  359. package/latticeai/services/app_context.py +0 -130
  360. package/latticeai/services/automation_execution.py +0 -266
  361. package/latticeai/services/automation_intelligence.py +0 -614
  362. package/latticeai/services/brain_automation.py +0 -191
  363. package/latticeai/services/brain_intelligence/__init__.py +0 -58
  364. package/latticeai/services/brain_intelligence/_contract.py +0 -71
  365. package/latticeai/services/brain_intelligence/consistency.py +0 -193
  366. package/latticeai/services/brain_intelligence/constants.py +0 -47
  367. package/latticeai/services/brain_intelligence/digest.py +0 -258
  368. package/latticeai/services/brain_intelligence/health.py +0 -331
  369. package/latticeai/services/brain_intelligence/proposals.py +0 -259
  370. package/latticeai/services/brain_intelligence/sampling.py +0 -84
  371. package/latticeai/services/brain_intelligence/service.py +0 -48
  372. package/latticeai/services/change_proposals.py +0 -471
  373. package/latticeai/services/chat_service.py +0 -243
  374. package/latticeai/services/chronicle.py +0 -555
  375. package/latticeai/services/cloud_egress_audit.py +0 -85
  376. package/latticeai/services/cloud_extraction.py +0 -129
  377. package/latticeai/services/cloud_streaming.py +0 -268
  378. package/latticeai/services/cloud_token_guard.py +0 -84
  379. package/latticeai/services/command_center.py +0 -548
  380. package/latticeai/services/evidence_actions.py +0 -258
  381. package/latticeai/services/feature_toggles.py +0 -502
  382. package/latticeai/services/folder_watch.py +0 -520
  383. package/latticeai/services/funnel_metrics.py +0 -307
  384. package/latticeai/services/hybrid_chat.py +0 -316
  385. package/latticeai/services/hybrid_context.py +0 -228
  386. package/latticeai/services/hybrid_policy.py +0 -129
  387. package/latticeai/services/interop_bridges.py +0 -978
  388. package/latticeai/services/local_knowledge.py +0 -465
  389. package/latticeai/services/memory_service/__init__.py +0 -52
  390. package/latticeai/services/memory_service/_contract.py +0 -100
  391. package/latticeai/services/memory_service/brief.py +0 -431
  392. package/latticeai/services/memory_service/constants.py +0 -57
  393. package/latticeai/services/memory_service/maintenance.py +0 -138
  394. package/latticeai/services/memory_service/manager.py +0 -186
  395. package/latticeai/services/memory_service/proof.py +0 -136
  396. package/latticeai/services/memory_service/recall.py +0 -225
  397. package/latticeai/services/memory_service/service.py +0 -48
  398. package/latticeai/services/memory_service/stores.py +0 -110
  399. package/latticeai/services/mode_store.py +0 -132
  400. package/latticeai/services/model_recommendation.py +0 -224
  401. package/latticeai/services/model_runtime/cloud.py +0 -87
  402. package/latticeai/services/network_boundary_service.py +0 -117
  403. package/latticeai/services/obsidian_bridge.py +0 -609
  404. package/latticeai/services/openai_compatible_adapter.py +0 -101
  405. package/latticeai/services/permission_mode_service.py +0 -122
  406. package/latticeai/services/platform_runtime.py +0 -366
  407. package/latticeai/services/review_queue.py +0 -380
  408. package/latticeai/services/router_context.py +0 -59
  409. package/latticeai/services/run_executor.py +0 -387
  410. package/latticeai/services/self_model_service.py +0 -171
  411. package/latticeai/services/setup_detection.py +0 -147
  412. package/latticeai/services/triggers.py +0 -378
  413. package/latticeai/services/upload_service.py +0 -172
  414. package/latticeai/services/workspace_service.py +0 -165
  415. package/latticeai/setup/__init__.py +0 -25
  416. package/latticeai/setup/auto_setup.py +0 -846
  417. package/latticeai/setup/demo_corpus.py +0 -98
  418. package/latticeai/setup/wizard/__init__.py +0 -126
  419. package/latticeai/setup/wizard/catalog.py +0 -175
  420. package/latticeai/setup/wizard/detect.py +0 -302
  421. package/latticeai/setup/wizard/install.py +0 -348
  422. package/latticeai/setup/wizard/paths.py +0 -165
  423. package/latticeai/setup/wizard/plans.py +0 -74
  424. package/latticeai/setup/wizard/recommend.py +0 -320
  425. package/scripts/bench_agent_smoke.py +0 -409
  426. package/scripts/bench_models.py +0 -540
  427. package/scripts/bench_vector_index.py +0 -295
  428. package/scripts/funnel_soft_gate.py +0 -192
  429. package/scripts/generate_agent_loop_fixtures.py +0 -994
  430. package/scripts/generate_rust_parity_fixtures.py +0 -908
  431. package/scripts/migrate_brain_storage.py +0 -57
  432. package/scripts/parity_fixture_corpus_context.py +0 -162
  433. package/scripts/parity_fixture_corpus_docgen.py +0 -341
  434. package/scripts/profile_kg.py +0 -355
  435. package/server.py +0 -30
  436. package/static/app/assets/arrow-left-kfsrk0mv.js +0 -1
  437. package/static/app/assets/circle-check-qqLug9nU.js +0 -1
  438. package/static/app/assets/index-DxmOfNRi.css +0 -2
  439. package/static/app/assets/search-BcHqkjoy.js +0 -1
@@ -1,10 +1,10 @@
1
- """A picture as a first-class memory: what was observed, and what was written.
1
+ """A picture as a first-class memory: what can be observed about one file.
2
2
 
3
- Two halves, in order: :func:`extract_image_facts` observes one file (size, OCR,
4
- caption, vector — each with its own status), and :func:`write_image_memory`
5
- turns those facts into an ``Image`` node with its chunks, concepts and source
6
- link. Video reuses both verbatim for its keyframes, which is why nothing here
7
- knows what a video is.
3
+ :func:`extract_image_facts` observes one file — size, OCR, caption, vector, each
4
+ with its own status — and answers with :class:`ImageFacts`. Turning those facts
5
+ into an ``Image`` node with its chunks, concepts and source link was the other
6
+ half of this module; ``lattice-core``'s graph write engine owns it since
7
+ v11.6.0, and this side never learned what a node was anyway.
8
8
  """
9
9
 
10
10
  from __future__ import annotations
@@ -12,20 +12,14 @@ from __future__ import annotations
12
12
  import base64
13
13
  import io
14
14
  from dataclasses import dataclass
15
- from pathlib import Path
16
15
  from typing import Any, Callable, Dict, List, Optional
17
16
 
18
17
  from ..quiet import quiet
19
- from ..utils import utc_now_iso
20
18
  from .common import (
21
19
  MAX_INDEX_TEXT_CHARS,
22
20
  MAX_THUMBNAIL_CHARS,
23
21
  MODALITY_IMAGE,
24
- SUMMARY_CHARS,
25
22
  THUMBNAIL_EDGE,
26
- _sha256_file,
27
- _sha256_text,
28
- _split_index_text,
29
23
  )
30
24
  from .ports import MultimodalPorts
31
25
 
@@ -244,255 +238,3 @@ def image_quality_score(facts: ImageFacts) -> Dict[str, Any]:
244
238
  return {"score": round(max(0.0, min(1.0, score)), 4), "reasons": reasons}
245
239
 
246
240
 
247
- def image_node_id(content_hash: str, workspace_id: Optional[str] = None) -> str:
248
- """Workspace-scoped, content-addressed id — re-ingesting is idempotent."""
249
- scoped = f"{workspace_id or 'legacy-global'}|{content_hash}"
250
- return f"image:{_sha256_text(scoped)[:24]}"
251
-
252
-
253
- def write_image_memory(
254
- store: Any,
255
- *,
256
- path: Path,
257
- facts: ImageFacts,
258
- title: str,
259
- source_type: str = MODALITY_IMAGE,
260
- source_uri: Optional[str] = None,
261
- owner: Optional[str] = None,
262
- workspace_id: Optional[str] = None,
263
- conversation_id: Optional[str] = None,
264
- captured_at: Optional[str] = None,
265
- modified_at: Optional[str] = None,
266
- permissions: Optional[Dict[str, Any]] = None,
267
- extra_metadata: Optional[Dict[str, Any]] = None,
268
- ) -> Dict[str, Any]:
269
- """Write one ``Image`` node (plus ``ImageText``/chunks) into the graph.
270
-
271
- The node is the image itself rather than a ``Document`` that happens to be
272
- a picture, because that is what the rest of the product reasons about: the
273
- graph already declares ``Image``/``ImageText``/``CONTAINS_IMAGE``, and
274
- ``hybrid_search`` already ranks ``Image`` as a first-class result type.
275
-
276
- Uses the same cross-mixin write door (``_upsert_node``/``_upsert_edge``/
277
- ``_upsert_chunk``) that every other ingest path uses — the store exposes no
278
- public node writer, and inventing a second one for images would be a
279
- parallel write path to keep in sync forever.
280
- """
281
- captured_at = captured_at or utc_now_iso()
282
- content_hash = _sha256_file(path)
283
- node_id = image_node_id(content_hash, workspace_id)
284
- index_text = facts.index_text()
285
- metadata: Dict[str, Any] = {
286
- "filename": path.name,
287
- "file_path": str(path),
288
- "ext": path.suffix.lower(),
289
- "bytes": path.stat().st_size,
290
- "sha256": content_hash,
291
- "content_hash": content_hash,
292
- "source_type": source_type,
293
- "source_uri": source_uri or str(path),
294
- "captured_at": captured_at,
295
- "modified_at": modified_at,
296
- "owner": owner,
297
- "workspace_id": workspace_id,
298
- "permissions": permissions or {},
299
- "conversation_id": conversation_id,
300
- **facts.as_metadata(),
301
- **(extra_metadata or {}),
302
- }
303
- # Honest summary: when nothing could be read out of the picture, say so
304
- # rather than leaving a blank card that looks like a failed render.
305
- summary = index_text[:SUMMARY_CHARS] or f"[{MODALITY_IMAGE}] {path.name}"
306
- chunk_ids: List[str] = []
307
-
308
- with store._connect() as conn:
309
- duplicate = (
310
- conn.execute("SELECT 1 FROM nodes WHERE id=? LIMIT 1", (node_id,)).fetchone()
311
- is not None
312
- )
313
- store._upsert_node(
314
- conn,
315
- node_id,
316
- "Image",
317
- title or path.name,
318
- summary=summary,
319
- metadata=metadata,
320
- raw=metadata,
321
- owner=owner,
322
- workspace_id=workspace_id,
323
- )
324
- if facts.ocr_text:
325
- image_text_id = f"imagetext:{_sha256_text(f'{node_id}:ocr')[:24]}"
326
- store._upsert_node(
327
- conn,
328
- image_text_id,
329
- "ImageText",
330
- f"{path.name} OCR",
331
- summary=facts.ocr_text[:700],
332
- metadata={
333
- "source_node": node_id,
334
- "chars": len(facts.ocr_text),
335
- "workspace_id": workspace_id,
336
- },
337
- owner=owner,
338
- workspace_id=workspace_id,
339
- )
340
- store._upsert_edge(
341
- conn,
342
- node_id,
343
- image_text_id,
344
- "포함함",
345
- weight=0.8,
346
- metadata={"source": "ocr", "workspace_id": workspace_id},
347
- )
348
- for index, piece in enumerate(_split_index_text(index_text)):
349
- chunk_id = f"chunk:{_sha256_text(f'{node_id}:{index}:{piece}')[:24]}"
350
- chunk_ids.append(chunk_id)
351
- chunk_meta = {
352
- "index": index,
353
- "source_node": node_id,
354
- "workspace_id": workspace_id,
355
- "modality": MODALITY_IMAGE,
356
- }
357
- store._upsert_node(
358
- conn,
359
- chunk_id,
360
- "Chunk",
361
- f"{path.name} chunk {index + 1}",
362
- summary=piece[:SUMMARY_CHARS],
363
- metadata=chunk_meta,
364
- owner=owner,
365
- workspace_id=workspace_id,
366
- )
367
- store._upsert_chunk(
368
- conn,
369
- chunk_id=chunk_id,
370
- source_node=node_id,
371
- text=piece,
372
- metadata=chunk_meta,
373
- )
374
- store._upsert_edge(conn, node_id, chunk_id, "포함함")
375
- # Concepts come from what is *in* the picture, never from its name:
376
- # "IMG_2381" is not a topic, and turning filenames into concept nodes
377
- # would fill the graph with hubs that mean nothing.
378
- concept_ids = _attach_concepts(
379
- store,
380
- conn,
381
- node_id=node_id,
382
- text=index_text,
383
- owner=owner,
384
- workspace_id=workspace_id,
385
- )
386
- source_node_id = _attach_source(
387
- store,
388
- conn,
389
- node_id=node_id,
390
- source_type=source_type,
391
- source_uri=source_uri or str(path),
392
- title=title or path.name,
393
- content_hash=content_hash,
394
- captured_at=captured_at,
395
- owner=owner,
396
- workspace_id=workspace_id,
397
- )
398
- metadata["concepts"] = concept_ids
399
- return {
400
- "node_id": node_id,
401
- "type": "Image",
402
- "title": title or path.name,
403
- "sha256": content_hash,
404
- "content_hash": content_hash,
405
- "source_node_id": source_node_id,
406
- "chunk_ids": chunk_ids,
407
- "chunk_count": len(chunk_ids),
408
- "duplicate": duplicate,
409
- "captured_at": captured_at,
410
- "metadata": metadata,
411
- }
412
-
413
-
414
- def _attach_concepts(
415
- store: Any,
416
- conn: Any,
417
- *,
418
- node_id: str,
419
- text: str,
420
- owner: Optional[str],
421
- workspace_id: Optional[str],
422
- ) -> List[str]:
423
- """Pull concepts out of the caption/OCR so the picture joins the graph.
424
-
425
- This is what "the caption contributes to the graph" means concretely: the
426
- same extractor every text door uses, over the same text, producing the same
427
- ``Concept``/``Feature``/… nodes and ``포함함`` edges — so a photo of a
428
- whiteboard about Q3 planning is one hop from every note about Q3 planning,
429
- instead of being an island that only exact search can reach.
430
-
431
- An image with nothing readable in it yields no concepts, which is correct:
432
- there is nothing to say about it.
433
- """
434
- body = str(text or "").strip()
435
- if not body:
436
- return []
437
- from ..graph._kg_common import ( # local: keeps this module's import light
438
- _classify_node_type,
439
- _extract_concepts,
440
- )
441
-
442
- # The id derivation is imported rather than re-derived: two copies of a
443
- # node-id rule diverge, and a diverged id is a duplicate concept nobody
444
- # can see.
445
- from ..graph.ingest import _scoped_slug_id
446
-
447
- concept_ids: List[str] = []
448
- for concept in _extract_concepts(body, limit=10):
449
- node_type = _classify_node_type(concept, body)
450
- concept_id = _scoped_slug_id(node_type.lower(), concept, workspace_id)
451
- store._upsert_node(
452
- conn,
453
- concept_id,
454
- node_type,
455
- concept,
456
- metadata={
457
- "auto_extracted": True,
458
- "source_node": node_id,
459
- "modality": MODALITY_IMAGE,
460
- "workspace_id": workspace_id,
461
- },
462
- owner=owner,
463
- workspace_id=workspace_id,
464
- )
465
- store._upsert_edge(conn, node_id, concept_id, "포함함", weight=0.8)
466
- concept_ids.append(concept_id)
467
- return concept_ids
468
-
469
-
470
- def _attach_source(
471
- store: Any,
472
- conn: Any,
473
- *,
474
- node_id: str,
475
- source_type: str,
476
- source_uri: str,
477
- title: str,
478
- content_hash: str,
479
- captured_at: str,
480
- owner: Optional[str],
481
- workspace_id: Optional[str],
482
- ) -> Optional[str]:
483
- """Link the image to a ``Source`` node when the store can make one."""
484
- attach = getattr(store, "_attach_source_node", None)
485
- if not callable(attach):
486
- return None
487
- return str(
488
- attach(
489
- conn,
490
- node_id,
491
- source_type=source_type,
492
- source_uri=source_uri,
493
- title=title,
494
- content_hash=content_hash,
495
- captured_at=captured_at,
496
- extra={"owner": owner, "workspace_id": workspace_id, "modality": MODALITY_IMAGE},
497
- )
498
- )
@@ -1,10 +1,11 @@
1
- """A video as keyframes through the image door, plus its subtitles as text.
2
-
3
- Nothing here is a new retrieval path. The stills go through
4
- :func:`~lattice_brain.multimodal.images.write_image_memory` exactly as a
5
- photograph does, the subtitles become ordinary chunks, and what the ``Video``
6
- node adds is the thing they belong to. Keyframe *extraction* lives in
7
- ``ports.py`` beside the decoder it falls back to; this module only consumes it.
1
+ """What can be read off a video file: its container facts and its subtitles.
2
+
3
+ Writing the ``Video`` node, the keyframe stills and the edges between them is
4
+ ``lattice-core``'s since v11.6.0. What is left is the reading:
5
+ :func:`read_video_facts` opens the container, :func:`find_subtitle` looks for
6
+ the ``.srt``/``.vtt`` beside it and :func:`parse_subtitles` turns cues into
7
+ plain text. Keyframe *extraction* lives in ``ports.py`` beside the decoder it
8
+ falls back to.
8
9
  """
9
10
 
10
11
  from __future__ import annotations
@@ -15,22 +16,10 @@ from pathlib import Path
15
16
  from typing import Any, Dict, List, Optional
16
17
 
17
18
  from ..quiet import quiet
18
- from ..utils import utc_now_iso
19
19
  from .common import (
20
20
  MAX_INDEX_TEXT_CHARS,
21
- MODALITY_IMAGE,
22
21
  MODALITY_VIDEO,
23
22
  SUBTITLE_EXTENSIONS,
24
- SUMMARY_CHARS,
25
- _sha256_file,
26
- _sha256_text,
27
- _split_index_text,
28
- )
29
- from .images import (
30
- _attach_concepts,
31
- _attach_source,
32
- extract_image_facts,
33
- write_image_memory,
34
23
  )
35
24
  from .ports import DEFAULT_KEYFRAMES, MultimodalPorts, extract_keyframes
36
25
 
@@ -180,231 +169,3 @@ def video_quality_score(facts: VideoFacts) -> Dict[str, Any]:
180
169
  return {"score": round(max(0.0, min(1.0, score)), 4), "reasons": reasons}
181
170
 
182
171
 
183
- def video_node_id(content_hash: str, workspace_id: Optional[str] = None) -> str:
184
- """Workspace-scoped, content-addressed id — re-ingesting is idempotent."""
185
- scoped = f"{workspace_id or 'legacy-global'}|{content_hash}"
186
- return f"video:{_sha256_text(scoped)[:24]}"
187
-
188
-
189
- def video_frame_dir(blob_dir: Any, content_hash: str) -> Path:
190
- """Where this video's stills live: content-addressed, stable across runs.
191
-
192
- Deliberately under the Brain's own blob directory rather than a temp dir.
193
- A frame referenced by an ``Image`` node has to still be there the next time
194
- someone opens that memory, and a backup that copies the blobs copies these.
195
- """
196
- return Path(str(blob_dir)) / "video_frames" / str(content_hash)[:32]
197
-
198
-
199
- def write_video_memory(
200
- store: Any,
201
- *,
202
- path: Path,
203
- facts: VideoFacts,
204
- title: str,
205
- source_type: str = MODALITY_VIDEO,
206
- source_uri: Optional[str] = None,
207
- owner: Optional[str] = None,
208
- workspace_id: Optional[str] = None,
209
- conversation_id: Optional[str] = None,
210
- captured_at: Optional[str] = None,
211
- modified_at: Optional[str] = None,
212
- permissions: Optional[Dict[str, Any]] = None,
213
- extra_metadata: Optional[Dict[str, Any]] = None,
214
- ports: Optional[MultimodalPorts] = None,
215
- ) -> Dict[str, Any]:
216
- """Write one ``Video`` node, its keyframes, and its subtitles.
217
-
218
- Each keyframe goes through the **existing image path** — the same
219
- :func:`extract_image_facts` and :func:`write_image_memory` a photograph
220
- uses — so a still from a video is OCR'd, captioned, vectorized and made
221
- searchable by exactly the machinery that already does that, and joined to
222
- its video by ``CONTAINS_IMAGE``. Subtitles ride the ordinary text index as
223
- chunks. Nothing about video gets its own retrieval path.
224
-
225
- The frames are written before the video node so their (short-lived) write
226
- transactions never nest inside the video's.
227
- """
228
- ports = ports or MultimodalPorts()
229
- captured_at = captured_at or utc_now_iso()
230
- content_hash = _sha256_file(path)
231
- node_id = video_node_id(content_hash, workspace_id)
232
- frames = _write_keyframes(
233
- store,
234
- facts=facts,
235
- title=title,
236
- owner=owner,
237
- workspace_id=workspace_id,
238
- conversation_id=conversation_id,
239
- captured_at=captured_at,
240
- permissions=permissions,
241
- ports=ports,
242
- )
243
- body = facts.subtitle_text or (
244
- f"[{MODALITY_VIDEO}] {title}\n"
245
- "이 영상에는 자막이 없어 말의 내용은 검색되지 않습니다 — "
246
- "대신 대표 장면 이미지로 찾을 수 있습니다."
247
- )
248
- metadata: Dict[str, Any] = {
249
- "filename": path.name,
250
- "file_path": str(path),
251
- "ext": path.suffix.lower(),
252
- "bytes": path.stat().st_size,
253
- "sha256": content_hash,
254
- "content_hash": content_hash,
255
- "source_type": source_type,
256
- "source_uri": source_uri or str(path),
257
- "captured_at": captured_at,
258
- "modified_at": modified_at,
259
- "owner": owner,
260
- "workspace_id": workspace_id,
261
- "permissions": permissions or {},
262
- "conversation_id": conversation_id,
263
- "keyframe_nodes": [frame["node_id"] for frame in frames],
264
- **facts.as_metadata(),
265
- **(extra_metadata or {}),
266
- }
267
- # Honest card: a video nobody captioned says so, rather than rendering as a
268
- # blank summary that looks like a failed read.
269
- summary = body[:SUMMARY_CHARS]
270
- chunk_ids: List[str] = []
271
- with store._connect() as conn:
272
- duplicate = (
273
- conn.execute("SELECT 1 FROM nodes WHERE id=? LIMIT 1", (node_id,)).fetchone()
274
- is not None
275
- )
276
- store._upsert_node(
277
- conn,
278
- node_id,
279
- VIDEO_NODE_TYPE,
280
- title or path.name,
281
- summary=summary,
282
- metadata=metadata,
283
- raw=metadata,
284
- owner=owner,
285
- workspace_id=workspace_id,
286
- )
287
- for frame in frames:
288
- store._upsert_edge(
289
- conn,
290
- node_id,
291
- frame["node_id"],
292
- VIDEO_FRAME_RELATION,
293
- weight=0.8,
294
- metadata={
295
- "source": VIDEO_FRAME_SOURCE_TYPE,
296
- "index": frame["index"],
297
- "workspace_id": workspace_id,
298
- },
299
- )
300
- for index, piece in enumerate(_split_index_text(facts.subtitle_text)):
301
- chunk_id = f"chunk:{_sha256_text(f'{node_id}:{index}:{piece}')[:24]}"
302
- chunk_ids.append(chunk_id)
303
- chunk_meta = {
304
- "index": index,
305
- "source_node": node_id,
306
- "workspace_id": workspace_id,
307
- "modality": MODALITY_VIDEO,
308
- }
309
- store._upsert_node(
310
- conn,
311
- chunk_id,
312
- "Chunk",
313
- f"{path.name} chunk {index + 1}",
314
- summary=piece[:SUMMARY_CHARS],
315
- metadata=chunk_meta,
316
- owner=owner,
317
- workspace_id=workspace_id,
318
- )
319
- store._upsert_chunk(
320
- conn,
321
- chunk_id=chunk_id,
322
- source_node=node_id,
323
- text=piece,
324
- metadata=chunk_meta,
325
- )
326
- store._upsert_edge(conn, node_id, chunk_id, "포함함")
327
- concept_ids = _attach_concepts(
328
- store,
329
- conn,
330
- node_id=node_id,
331
- text=facts.subtitle_text,
332
- owner=owner,
333
- workspace_id=workspace_id,
334
- )
335
- source_node_id = _attach_source(
336
- store,
337
- conn,
338
- node_id=node_id,
339
- source_type=source_type,
340
- source_uri=source_uri or str(path),
341
- title=title or path.name,
342
- content_hash=content_hash,
343
- captured_at=captured_at,
344
- owner=owner,
345
- workspace_id=workspace_id,
346
- )
347
- metadata["concepts"] = concept_ids
348
- return {
349
- "node_id": node_id,
350
- "type": VIDEO_NODE_TYPE,
351
- "title": title or path.name,
352
- "sha256": content_hash,
353
- "content_hash": content_hash,
354
- "source_node_id": source_node_id,
355
- "chunk_ids": chunk_ids,
356
- "chunk_count": len(chunk_ids),
357
- "duplicate": duplicate,
358
- "captured_at": captured_at,
359
- "keyframes": frames,
360
- "metadata": metadata,
361
- }
362
-
363
-
364
- def _write_keyframes(
365
- store: Any,
366
- *,
367
- facts: VideoFacts,
368
- title: str,
369
- owner: Optional[str],
370
- workspace_id: Optional[str],
371
- conversation_id: Optional[str],
372
- captured_at: str,
373
- permissions: Optional[Dict[str, Any]],
374
- ports: MultimodalPorts,
375
- ) -> List[Dict[str, Any]]:
376
- """Every extracted still, through the ordinary image door."""
377
- written: List[Dict[str, Any]] = []
378
- for index, frame_path in enumerate(facts.keyframes):
379
- frame = Path(frame_path)
380
- image_facts = extract_image_facts(str(frame), ports=ports)
381
- if not image_facts.readable:
382
- # A frame ffmpeg wrote that Pillow cannot open is a state worth
383
- # skipping, not worth failing the whole video over.
384
- continue
385
- result = write_image_memory(
386
- store,
387
- path=frame,
388
- facts=image_facts,
389
- title=f"{title} · 장면 {index + 1}",
390
- source_type=VIDEO_FRAME_SOURCE_TYPE,
391
- source_uri=str(frame),
392
- owner=owner,
393
- workspace_id=workspace_id,
394
- conversation_id=conversation_id,
395
- captured_at=captured_at,
396
- permissions=permissions,
397
- extra_metadata={
398
- "modality": MODALITY_IMAGE,
399
- "video_path": facts.path,
400
- "keyframe_index": index,
401
- },
402
- )
403
- written.append({
404
- "node_id": result["node_id"],
405
- "index": index,
406
- "path": str(frame),
407
- "ocr_status": image_facts.ocr_status,
408
- "vision_embedding": image_facts.embedding_status,
409
- })
410
- return written
@@ -1,90 +1,19 @@
1
- """Agent and hook runtime subsystem of the Brain Core.
1
+ """The tool dispatch lifecycle.
2
2
 
3
- Physically hosts the hooks registry/dispatch lifecycle, the multi-agent
4
- orchestrator, and the agent runtime service. Lazy-loaded so importing
5
- ``lattice_brain.runtime`` stays cheap.
6
-
7
- === lattice_brain/runtime 책임 + 의존성 그래프 (A방향 + Act/automation 기준) ===
8
- - multi_agent.py (핵심 실행체)
9
- 책임: OrchestrationContext, AgentContextPacket, AgentHandoff, AgentRunResult,
10
- MultiAgentOrchestrator, default_role_runner, llm_role_runner,
11
- CORE_PIPELINE/AGENT_ROLES, handoff/review/retry 타임라인 생성.
12
- 의존: 없음 (순수, deterministic/LLM runner 주입).
13
- 진입: orchestrator_factory 로 AgentRuntime에 주입됨.
14
-
15
- - hooks.py (라이프사이클 확장)
16
- 책임: HookContext/HookResult, HooksRegistry (builtin+user, order/enabled persist),
17
- dispatch_tool (pre_tool/post_tool 통합), fire_hook/run_hooks,
18
- BUILTIN_HOOKS (redact, memory-snapshot, tool-permission-gate 등).
19
- 의존: subprocess (user command hooks).
20
- 진입: AgentRuntime, tool_dispatch, api/tools, core/agent 등에 주입/등록.
21
-
22
- - agent_runtime.py (공개 퍼사드 / 바운더리)
23
- 책임: AgentRuntime (store+orchestrator_factory+workspace_graph+audit+hooks 주입),
24
- start/reserve_run/complete_reserved_run, stop, status/health/config,
25
- list_runs/get_run/events/replay, _fire_pre_run / _post_run_hooks.
26
- 의존: .multi_agent, .hooks (간접), store (WORKSPACE_OS).
27
- 진입점: **여기가 제품 경계**. app_factory에서 AGENT_RUNTIME으로 생성,
28
- api/agents.py (런타임 라우터), RunExecutor (async agent/workflow),
29
- workflow_designer에 주입. frontend BrainAutomationPanel / Act 가
30
- /agents/* 를 통해 이 바운더리만 의존.
31
-
32
- - __init__.py : lazy re-export (import 비용 최소화).
33
-
34
- 실제 진입점 매핑 (app_factory + 호출 스택):
35
- app_factory.py:1508
36
- AGENT_RUNTIME = AgentRuntime(store=WORKSPACE_OS, orchestrator_factory=PLATFORM.build_orchestrator, hooks=HOOKS_REGISTRY, ...)
37
- RUN_EXECUTOR = RunExecutor(..., agent_runtime=AGENT_RUNTIME)
38
- AGENT_RUNTIME.attach_executor(RUN_EXECUTOR)
39
- create_agents_router(..., agent_runtime=AGENT_RUNTIME)
40
- create_workflow_designer_router(..., run_executor=RUN_EXECUTOR, trigger_service=TRIGGER_SERVICE)
41
- api/agents.py:49
42
- from lattice_brain.runtime.agent_runtime import AgentRuntime
43
- runtime = agent_runtime or AgentRuntime(...) # fallback
44
- /agents/api/runtime/* , POST /agents (start via runtime.start or executor)
45
- api/chat.py
46
- from latticeai.services.tool_dispatch import build_agent_runtime
47
- -> latticeai/core/agent/:SingleAgentRuntime (별도 state/plan/transcript 머신, single-agent /agent 경로. dispatch_tool 만 공유)
48
- latticeai/services/tool_dispatch.py:14
49
- from latticeai.core.agent import SingleAgentRuntime
50
- (tool governance + core single-agent용)
51
- latticeai/services/platform_runtime.py
52
- from lattice_brain.runtime.{hooks, multi_agent}
53
- tests: test_hooks_dispatch.py, test_t7_triggers.py 등에서 직접 import lattice_brain.runtime.*
54
-
55
- core/tool_registry.py (신규) + services/tool_dispatch.py 가 tool build 주도.
56
- lattice_brain/runtime 는 multi-agent + hooks + facade 에 집중. core/agent 는 chat/agent 단일 루프.
57
- 기존 latticeai.core.agent.AgentRuntime import 는 SingleAgentRuntime 호환 alias 로 유지.
58
-
59
- 이 매핑으로 중복 제거 및 wiring 명확화 완료 (feat(Act, automation) 방향).
3
+ This subsystem held the multi-agent orchestrator, the agent runtime facade and
4
+ the persisted hooks registry. Orchestration is ``lattice-agent``'s and the hooks
5
+ platform is ``lattice-platform``'s since v11.6.0; what remains is
6
+ :func:`~lattice_brain.runtime.hooks.dispatch_tool` and the two context objects a
7
+ bound hook runner is handed.
60
8
  """
61
9
 
62
10
  from __future__ import annotations
63
11
 
64
- __all__ = [
65
- "AgentRuntime",
66
- "AgentRuntimeUnavailable",
67
- "MultiAgentOrchestrator",
68
- "RuntimeBoundaryProtocol",
69
- "HooksRegistry",
70
- "dispatch_tool",
71
- ]
12
+ __all__ = ["HookContext", "HookResult", "dispatch_tool"]
72
13
 
73
14
 
74
15
  def __getattr__(name: str):
75
- if name in {"AgentRuntime", "AgentRuntimeUnavailable"}:
76
- from . import agent_runtime
77
-
78
- return getattr(agent_runtime, name)
79
- if name == "MultiAgentOrchestrator":
80
- from .multi_agent import MultiAgentOrchestrator
81
-
82
- return MultiAgentOrchestrator
83
- if name == "RuntimeBoundaryProtocol":
84
- from .contracts import RuntimeBoundaryProtocol
85
-
86
- return RuntimeBoundaryProtocol
87
- if name in {"HooksRegistry", "dispatch_tool"}:
16
+ if name in set(__all__):
88
17
  from . import hooks
89
18
 
90
19
  return getattr(hooks, name)