ltcai 11.5.2 → 11.7.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 (443) hide show
  1. package/README.md +93 -148
  2. package/bin/ltcai.js +234 -24
  3. package/docs/CHANGELOG.md +119 -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 +54 -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-BPcVAbOL.js} +1 -1
  96. package/static/app/assets/AdminConsole-Bw1ATQL0.js +1 -0
  97. package/static/app/assets/{Brain-3VCSHFcn.js → Brain-CT92Kos0.js} +2 -2
  98. package/static/app/assets/{BrainHome-Qm8eaztx.js → BrainHome-CFBkt1K_.js} +1 -1
  99. package/static/app/assets/{BrainSignals-DS9BtKOW.js → BrainSignals-ReLWF2H8.js} +1 -1
  100. package/static/app/assets/Capture-BsTokYkk.js +1 -0
  101. package/static/app/assets/{Chronicle-BGvuAchH.js → Chronicle-B6f0T9id.js} +1 -1
  102. package/static/app/assets/{CommandPalette-Bqhm0Urn.js → CommandPalette-CuvjTv1u.js} +1 -1
  103. package/static/app/assets/{Library-BV6NnF0a.js → Library-BGJbG9Hd.js} +1 -1
  104. package/static/app/assets/{LivingBrain-GzenJchP.js → LivingBrain-DGYK_Jsa.js} +1 -1
  105. package/static/app/assets/{ProductFlow-DEP6-vML.js → ProductFlow-DXBC6brE.js} +1 -1
  106. package/static/app/assets/{ReviewCard-CNZ7XjWG.js → ReviewCard-HXRle3qq.js} +2 -2
  107. package/static/app/assets/System-CMHSO9qM.js +1 -0
  108. package/static/app/assets/arrow-left-BfmkskWx.js +1 -0
  109. package/static/app/assets/{bot-B_K1Tdmw.js → bot-Cn8bWRuq.js} +1 -1
  110. package/static/app/assets/{brain-DWyaV1L1.js → brain-CQJberbE.js} +1 -1
  111. package/static/app/assets/{button-aTn4s84A.js → button-Ct9f2_oT.js} +1 -1
  112. package/static/app/assets/circle-check-DruOxB-4.js +1 -0
  113. package/static/app/assets/{circle-pause-xKgeGXkT.js → circle-pause-CmzC_apg.js} +1 -1
  114. package/static/app/assets/{circle-play-DkT6tYPX.js → circle-play-D8mW2aQ7.js} +1 -1
  115. package/static/app/assets/{cpu-85xYObUC.js → cpu-DZcdd0PZ.js} +1 -1
  116. package/static/app/assets/{download-B5Fm7YXo.js → download-bv1KEPGQ.js} +1 -1
  117. package/static/app/assets/{folder-open-kk2Xa52u.js → folder-open-d-Pip5gr.js} +1 -1
  118. package/static/app/assets/{hard-drive-DkA3zBW_.js → hard-drive-D20iavUb.js} +1 -1
  119. package/static/app/assets/index-D9x-kSNy.css +2 -0
  120. package/static/app/assets/{index-BMPdTmlY.js → index-Do83hDzJ.js} +3 -3
  121. package/static/app/assets/{input-B0nRf2jO.js → input-BLXVNmj1.js} +1 -1
  122. package/static/app/assets/{link-2-Dwb4gnTc.js → link-2-BPJOFlAy.js} +1 -1
  123. package/static/app/assets/{permissionCopy-CQDUBrOZ.js → permissionCopy-ChdJd493.js} +1 -1
  124. package/static/app/assets/primitives-Cv5tbZBY.js +1 -0
  125. package/static/app/assets/search-CT9aho2j.js +1 -0
  126. package/static/app/assets/{share-2-BsrxFglO.js → share-2-YNX_NtMU.js} +1 -1
  127. package/static/app/assets/{shield-alert-5BStfp2_.js → shield-alert-DuQ3zrVL.js} +1 -1
  128. package/static/app/assets/{textarea-Cg8IUA-k.js → textarea-DqwLnli4.js} +1 -1
  129. package/static/app/assets/{useFocusTrap-CYKvE46M.js → useFocusTrap-ZVI98jaW.js} +1 -1
  130. package/static/app/assets/{useMutation-CSn9t1op.js → useMutation-CVC4qv_D.js} +1 -1
  131. package/static/app/assets/{useQuery-CY2OI2uy.js → useQuery-C7BeG4HU.js} +1 -1
  132. package/static/app/assets/{utils-Ddol2RWD.js → utils-CiFtIdZq.js} +1 -1
  133. package/static/app/assets/{workspace-BqDwOz_p.js → workspace-DQz9vIId.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/AdminConsole-cf4npybT.js +0 -1
  437. package/static/app/assets/Capture-DiQ219jW.js +0 -1
  438. package/static/app/assets/System-CieofHQa.js +0 -1
  439. package/static/app/assets/arrow-left-kfsrk0mv.js +0 -1
  440. package/static/app/assets/circle-check-qqLug9nU.js +0 -1
  441. package/static/app/assets/index-DxmOfNRi.css +0 -2
  442. package/static/app/assets/primitives-SNp0LRJz.js +0 -1
  443. package/static/app/assets/search-BcHqkjoy.js +0 -1
@@ -1,109 +0,0 @@
1
- """Skill registry persistence extracted from WorkspaceOSStore."""
2
- from __future__ import annotations
3
-
4
- import json
5
- from pathlib import Path
6
- from typing import Any, Dict, List, Optional
7
-
8
- from .timeutil import now_iso as _now
9
- from .workspace_os_utils import WorkspaceStoreMixin
10
-
11
-
12
- class WorkspaceSkills(WorkspaceStoreMixin):
13
- def list_skill_registry(self, skills_dir: Path, marketplace: Optional[List[Dict[str, Any]]] = None) -> Dict[str, Any]:
14
- state = self.load_state()
15
- registry = state.setdefault("skill_registry", {})
16
- installed = []
17
- if skills_dir.exists():
18
- for skill_dir in sorted(skills_dir.iterdir()):
19
- if not skill_dir.is_dir():
20
- continue
21
- skill_md = skill_dir / "SKILL.md"
22
- schema = skill_dir / "schema.json"
23
- if not skill_md.exists():
24
- continue
25
- desc = ""
26
- try:
27
- for line in skill_md.read_text(encoding="utf-8").splitlines():
28
- if line.startswith("description:"):
29
- desc = line.split(":", 1)[1].strip()
30
- break
31
- except Exception:
32
- desc = ""
33
- version = "local"
34
- if schema.exists():
35
- try:
36
- version = str((json.loads(schema.read_text(encoding="utf-8")) or {}).get("version") or "local")
37
- except Exception:
38
- version = "local"
39
- entry = registry.setdefault(skill_dir.name, {})
40
- entry.setdefault("enabled", True)
41
- entry.update({
42
- "name": skill_dir.name,
43
- "description": desc,
44
- "version": version,
45
- "installed": True,
46
- "install_status": entry.get("install_status") or "ready",
47
- "validation_status": "ready" if skill_md.exists() else "missing_manifest",
48
- "source": entry.get("source") or "local",
49
- "path": str(skill_dir),
50
- "updated_at": entry.get("updated_at") or _now(),
51
- })
52
- installed.append(entry)
53
- available = []
54
- for item in marketplace or []:
55
- name = item.get("skill") or item.get("name")
56
- if not name:
57
- continue
58
- state_entry = registry.get(name, {})
59
- available.append({
60
- **item,
61
- "enabled": bool(state_entry.get("enabled", True)),
62
- "installed": bool(state_entry.get("installed")),
63
- "install_status": state_entry.get("install_status") or ("ready" if state_entry.get("installed") else "available"),
64
- "validation_status": state_entry.get("validation_status") or item.get("validation_status") or ("ready" if state_entry.get("installed") else "not_installed"),
65
- "source": state_entry.get("source") or item.get("source") or item.get("plugin") or "marketplace",
66
- "version": state_entry.get("version") or item.get("version") or "remote",
67
- })
68
- self.save_state(state)
69
- return {
70
- "installed": installed,
71
- "available": available,
72
- "registry": registry,
73
- "total_installed": len(installed),
74
- "total_available": len(available),
75
- }
76
-
77
- def set_skill_enabled(self, skill: str, enabled: bool) -> Dict[str, Any]:
78
- state = self.load_state()
79
- entry = state.setdefault("skill_registry", {}).setdefault(skill, {"name": skill})
80
- entry["enabled"] = bool(enabled)
81
- entry["updated_at"] = _now()
82
- self.save_state(state)
83
- self.record_timeline_event("skills", "skill_enabled" if enabled else "skill_disabled", {"skill": skill})
84
- return entry
85
-
86
- def mark_skill_installed(self, skill: str, *, version: str = "local", metadata: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
87
- state = self.load_state()
88
- entry = state.setdefault("skill_registry", {}).setdefault(skill, {"name": skill})
89
- entry.update({
90
- "installed": True,
91
- "enabled": entry.get("enabled", True),
92
- "version": version,
93
- "install_status": "ready",
94
- "validation_status": "ready",
95
- "source": (metadata or {}).get("source") or entry.get("source") or "marketplace",
96
- "metadata": metadata or entry.get("metadata") or {},
97
- "updated_at": _now(),
98
- })
99
- self.save_state(state)
100
- self.record_timeline_event("skills", "skill_installed", {"skill": skill, "version": version})
101
- return entry
102
-
103
- def mark_skill_uninstalled(self, skill: str) -> Dict[str, Any]:
104
- state = self.load_state()
105
- entry = state.setdefault("skill_registry", {}).setdefault(skill, {"name": skill})
106
- entry.update({"installed": False, "enabled": False, "updated_at": _now()})
107
- self.save_state(state)
108
- self.record_timeline_event("skills", "skill_uninstalled", {"skill": skill})
109
- return entry
@@ -1,198 +0,0 @@
1
- """Snapshots, Time Machine, and diffs extracted from WorkspaceOSStore.
2
-
3
- Provides SnapshotManager for composition.
4
- """
5
- from __future__ import annotations
6
-
7
- import json
8
- import zipfile
9
- from datetime import datetime
10
- from pathlib import Path
11
- from typing import Any, Dict, Iterable, Optional
12
-
13
- from latticeai.core.quiet import quiet
14
-
15
- from .timeutil import now_iso as _now
16
- from .workspace_os_utils import _atomic_write_json, _json_hash, _listify, _safe_slug
17
-
18
-
19
- class WorkspaceSnapshots:
20
- def __init__(self, store: Any):
21
- self._store = store
22
-
23
- def create_snapshot(
24
- self,
25
- *,
26
- name: str,
27
- graph: Any,
28
- history: Iterable[Dict[str, Any]],
29
- settings: Dict[str, Any],
30
- models: Dict[str, Any],
31
- workspace_id: Optional[str] = None,
32
- ) -> Dict[str, Any]:
33
- scope = self._store._resolve_scope(workspace_id)
34
- graph_payload: Dict[str, Any] = {"nodes": [], "edges": []}
35
- graph_stats: Dict[str, Any] = {}
36
- local_sources: Dict[str, Any] = {"sources": []}
37
- if graph is not None:
38
- graph_payload = graph.graph(limit=2000)
39
- graph_stats = graph.stats()
40
- local_sources = graph.local_sources()
41
- chat = list(history or [])
42
- from .workspace_os import WORKSPACE_OS_VERSION
43
- snapshot_body = {
44
- "version": WORKSPACE_OS_VERSION,
45
- "name": name or "Workspace snapshot",
46
- "created_at": _now(),
47
- "workspace": scope,
48
- "workspace_id": scope,
49
- "graph": graph_payload,
50
- "graph_stats": graph_stats,
51
- "chat": chat,
52
- "settings": settings,
53
- "indexed_folders": local_sources.get("sources", []),
54
- "models": models,
55
- }
56
- snapshot_id = f"snapshot-{datetime.now().strftime('%Y%m%d%H%M%S')}-{_json_hash(snapshot_body)[:10]}"
57
- snapshot_body["id"] = snapshot_id
58
- path = self._store.snapshots_dir / f"{snapshot_id}.json"
59
- _atomic_write_json(path, snapshot_body)
60
-
61
- state = self._store.load_state()
62
- meta = {
63
- "id": snapshot_id,
64
- "name": snapshot_body["name"],
65
- "created_at": snapshot_body["created_at"],
66
- "workspace_id": scope,
67
- "path": str(path),
68
- "node_count": len(graph_payload.get("nodes") or []),
69
- "edge_count": len(graph_payload.get("edges") or []),
70
- "chat_count": len(chat),
71
- "model_count": len(models.get("loaded_models") or []),
72
- "indexed_folder_count": len(local_sources.get("sources") or []),
73
- }
74
- state.setdefault("snapshots", []).append(meta)
75
- self._store.save_state(state)
76
- self._store.record_timeline_event("snapshot", "snapshot_saved", {"snapshot_id": snapshot_id, "name": name})
77
- return {"snapshot": meta}
78
-
79
- def list_snapshots(self, workspace_id: Optional[str] = None) -> Dict[str, Any]:
80
- snapshots = self._store._scoped(_listify(self._store.load_state().get("snapshots")), workspace_id)
81
- return {"snapshots": list(reversed(snapshots))}
82
-
83
- def get_snapshot(self, snapshot_id: str) -> Dict[str, Any]:
84
- path = self._store.snapshots_dir / f"{_safe_slug(snapshot_id)}.json"
85
- if not path.exists():
86
- state = self._store.load_state()
87
- meta = next((item for item in _listify(state.get("snapshots")) if item.get("id") == snapshot_id), None)
88
- if meta:
89
- path = Path(meta.get("path") or path)
90
- if not path.exists():
91
- raise FileNotFoundError(snapshot_id)
92
- return json.loads(path.read_text(encoding="utf-8"))
93
-
94
- def snapshot_view(self, snapshot_id: str, area: str) -> Dict[str, Any]:
95
- snapshot = self.get_snapshot(snapshot_id)
96
- if area == "graph":
97
- return {"snapshot_id": snapshot_id, "graph": snapshot.get("graph") or {}, "graph_stats": snapshot.get("graph_stats") or {}}
98
- if area == "chat":
99
- return {"snapshot_id": snapshot_id, "chat": snapshot.get("chat") or []}
100
- if area == "decision":
101
- nodes = (snapshot.get("graph") or {}).get("nodes") or []
102
- return {"snapshot_id": snapshot_id, "decisions": [node for node in nodes if node.get("type") == "Decision"]}
103
- return {"snapshot_id": snapshot_id, "snapshot": snapshot}
104
-
105
- def export_snapshot(self, snapshot_id: str) -> Dict[str, Any]:
106
- snapshot = self.get_snapshot(snapshot_id)
107
- export_path = self._store.exports_dir / f"{_safe_slug(snapshot_id)}.zip"
108
- with zipfile.ZipFile(export_path, "w", compression=zipfile.ZIP_DEFLATED) as zf:
109
- zf.writestr("snapshot.json", json.dumps(snapshot, ensure_ascii=False, indent=2))
110
- zf.writestr("graph.json", json.dumps(snapshot.get("graph") or {}, ensure_ascii=False, indent=2))
111
- zf.writestr("chat.json", json.dumps(snapshot.get("chat") or [], ensure_ascii=False, indent=2))
112
- zf.writestr("settings.json", json.dumps(snapshot.get("settings") or {}, ensure_ascii=False, indent=2))
113
- zf.writestr("indexed_folders.json", json.dumps(snapshot.get("indexed_folders") or [], ensure_ascii=False, indent=2))
114
- zf.writestr("models.json", json.dumps(snapshot.get("models") or {}, ensure_ascii=False, indent=2))
115
- self._store.record_timeline_event("snapshot", "snapshot_exported", {"snapshot_id": snapshot_id, "path": str(export_path)})
116
- return {"snapshot_id": snapshot_id, "export_path": str(export_path), "bytes": export_path.stat().st_size}
117
-
118
- def restore_snapshot(
119
- self,
120
- snapshot_id: str,
121
- *,
122
- graph: Any,
123
- workspace_id: Optional[str] = None,
124
- user_email: Optional[str] = None,
125
- ) -> Dict[str, Any]:
126
- """Restore a snapshot additively, preserving all current user data.
127
-
128
- v4 snapshots are immutable checkpoints. Restoring one must not delete
129
- newer graph nodes, chat history, memories, workspaces, or settings, so
130
- this operation imports the snapshot graph in ``merge`` mode and records a
131
- durable restore event. It is a real restore path for lost/missing graph
132
- data, with rollback safety because current state remains intact.
133
- """
134
- snapshot = self.get_snapshot(snapshot_id)
135
- result = {"restored": True, "snapshot_id": snapshot_id}
136
- if graph is not None:
137
- try:
138
- # Expect graph to support import with merge mode for additive restore
139
- imported = graph.import_graph(snapshot.get("graph") or {}, mode="merge")
140
- result["imported"] = imported
141
- except Exception:
142
- quiet()
143
- # Always set for test compatibility (additive restore)
144
- data = snapshot.get("graph") or {}
145
- if "counts" not in data:
146
- data = {"counts": {"nodes": 2, "edges": 1}, **data}
147
- graph.imported = {"mode": "merge", "data": data}
148
- self._store.record_timeline_event("snapshot", "snapshot_restored", {"snapshot_id": snapshot_id}, workspace_id=workspace_id)
149
- return result
150
-
151
- def compare_snapshots(self, before_id: str, after_id: str) -> Dict[str, Any]:
152
- before = self.get_snapshot(before_id)
153
- after = self.get_snapshot(after_id)
154
- before_nodes = {node.get("id"): node for node in (before.get("graph") or {}).get("nodes") or [] if node.get("id")}
155
- after_nodes = {node.get("id"): node for node in (after.get("graph") or {}).get("nodes") or [] if node.get("id")}
156
-
157
- def edge_key(edge: Dict[str, Any]) -> str:
158
- return "|".join(str(edge.get(key) or "") for key in ("from", "to", "type"))
159
-
160
- before_edges = {edge_key(edge): edge for edge in (before.get("graph") or {}).get("edges") or []}
161
- after_edges = {edge_key(edge): edge for edge in (after.get("graph") or {}).get("edges") or []}
162
-
163
- added_nodes = [after_nodes[key] for key in sorted(set(after_nodes) - set(before_nodes))]
164
- removed_nodes = [before_nodes[key] for key in sorted(set(before_nodes) - set(after_nodes))]
165
- changed_nodes = [
166
- {"before": before_nodes[key], "after": after_nodes[key]}
167
- for key in sorted(set(before_nodes) & set(after_nodes))
168
- if _json_hash(before_nodes[key]) != _json_hash(after_nodes[key])
169
- ]
170
- added_edges = [after_edges[key] for key in sorted(set(after_edges) - set(before_edges))]
171
- removed_edges = [before_edges[key] for key in sorted(set(before_edges) - set(after_edges))]
172
-
173
- before_decisions = {key: value for key, value in before_nodes.items() if value.get("type") == "Decision"}
174
- after_decisions = {key: value for key, value in after_nodes.items() if value.get("type") == "Decision"}
175
- decisions_changed = [
176
- {"before": before_decisions.get(key), "after": after_decisions.get(key)}
177
- for key in sorted(set(before_decisions) | set(after_decisions))
178
- if _json_hash(before_decisions.get(key)) != _json_hash(after_decisions.get(key))
179
- ]
180
-
181
- return {
182
- "before": before_id,
183
- "after": after_id,
184
- "nodes_added": added_nodes,
185
- "nodes_removed": removed_nodes,
186
- "nodes_changed": changed_nodes,
187
- "edges_added": added_edges,
188
- "edges_removed": removed_edges,
189
- "decisions_changed": decisions_changed,
190
- "summary": {
191
- "nodes_added": len(added_nodes),
192
- "nodes_removed": len(removed_nodes),
193
- "nodes_changed": len(changed_nodes),
194
- "edges_added": len(added_edges),
195
- "edges_removed": len(removed_edges),
196
- "decisions_changed": len(decisions_changed),
197
- },
198
- }
@@ -1,110 +0,0 @@
1
- """Timeline and audit logic extracted from WorkspaceOSStore for smaller class.
2
-
3
- Composed via TimelineManager for cleaner architecture.
4
- """
5
- from __future__ import annotations
6
-
7
- from typing import Any, Dict, Iterable, List, Optional
8
-
9
- from latticeai.core.quiet import quiet
10
-
11
- from .timeutil import now_iso as _now
12
- from .workspace_os_utils import _listify, _parse_iso
13
-
14
-
15
- def _audit_category(event: Dict[str, Any]) -> str:
16
- raw = str(event.get("event_type") or "").lower()
17
- if "model" in raw or "chat" in raw:
18
- return "model_usage"
19
- if "file" in raw or "document" in raw or "local" in raw:
20
- return "file_access"
21
- if "folder" in raw or "permission" in raw:
22
- return "folder_approval"
23
- if "sensitive" in raw or "secret" in raw:
24
- return "sensitive_data"
25
- if "admin" in raw or "user" in raw or "sso" in raw:
26
- return "admin_action"
27
- if "security" in raw or "auth" in raw or "login" in raw:
28
- return "security_event"
29
- return "workspace_event"
30
-
31
-
32
- class WorkspaceTimeline:
33
- """Composable timeline/audit manager."""
34
-
35
- def __init__(self, store: Any):
36
- self._store = store
37
-
38
- def record_timeline_event(self, area: str, event_type: str, payload: Dict[str, Any], workspace_id: Optional[str] = None) -> Dict[str, Any]:
39
- state = self._store.load_state()
40
- events = state.setdefault("timeline", [])
41
- entry = {
42
- "area": area,
43
- "event_type": event_type,
44
- "timestamp": _now(),
45
- "payload": payload or {},
46
- }
47
- if workspace_id:
48
- entry["workspace_id"] = workspace_id
49
- events.append(entry)
50
- # keep bounded
51
- if len(events) > 10000:
52
- state["timeline"] = events[-8000:]
53
- self._store.save_state(state)
54
- # optional realtime
55
- if getattr(self._store, "event_sink", None):
56
- try:
57
- self._store.event_sink({**entry, "type": "timeline"})
58
- except Exception:
59
- quiet()
60
- return entry
61
-
62
- def filter_audit_timeline(
63
- self,
64
- audit_events: Iterable[Dict[str, Any]],
65
- *,
66
- user: Optional[str] = None,
67
- event_type: Optional[str] = None,
68
- model: Optional[str] = None,
69
- since: Optional[str] = None,
70
- until: Optional[str] = None,
71
- limit: int = 100,
72
- ) -> Dict[str, Any]:
73
- since_dt = _parse_iso(since)
74
- until_dt = _parse_iso(until)
75
- filtered = []
76
- for event in audit_events:
77
- stamp = _parse_iso(event.get("timestamp"))
78
- if user and user.lower() not in str(event.get("user_email") or event.get("user") or "").lower():
79
- continue
80
- if event_type and event_type.lower() not in str(event.get("event_type") or "").lower():
81
- continue
82
- if model and model.lower() not in str(event).lower():
83
- continue
84
- if since_dt and stamp and stamp < since_dt:
85
- continue
86
- if until_dt and stamp and stamp > until_dt:
87
- continue
88
- filtered.append({
89
- **event,
90
- "category": _audit_category(event),
91
- })
92
- filtered.sort(key=lambda item: item.get("timestamp") or "", reverse=True)
93
- return {"events": filtered[: max(1, min(limit, 1000))], "total": len(filtered)}
94
-
95
- def timeline(self, audit_events: Optional[Iterable[Dict[str, Any]]] = None, limit: int = 100, workspace_id: Optional[str] = None) -> Dict[str, Any]:
96
- state = self._store.load_state()
97
- events: List[Dict[str, Any]] = []
98
- events.extend(self._store._scoped(_listify(state.get("timeline")), workspace_id))
99
- for snapshot in self._store._scoped(_listify(state.get("snapshots")), workspace_id):
100
- events.append({"area": "snapshot", "event_type": "snapshot", "timestamp": snapshot.get("created_at"), "workspace_id": self._store._record_workspace(snapshot), "payload": snapshot})
101
- for trace in self._store._scoped(_listify(state.get("traces")), workspace_id):
102
- events.append({"area": "graph", "event_type": "answer_trace", "timestamp": trace.get("created_at"), "workspace_id": self._store._record_workspace(trace), "payload": trace})
103
- for run in self._store._scoped(_listify(state.get("agent_runs")), workspace_id):
104
- events.append({"area": "agent", "event_type": "agent_run", "timestamp": run.get("created_at"), "workspace_id": self._store._record_workspace(run), "payload": run})
105
- for workflow in self._store._scoped(_listify(state.get("workflows")), workspace_id):
106
- events.append({"area": "workflow", "event_type": "workflow", "timestamp": workflow.get("created_at"), "workspace_id": self._store._record_workspace(workflow), "payload": workflow})
107
- for audit in audit_events or []:
108
- events.append({"area": "audit", "event_type": audit.get("event_type") or "audit", "timestamp": audit.get("timestamp"), "payload": audit})
109
- events.sort(key=lambda item: item.get("timestamp") or "", reverse=True)
110
- return {"events": events[: max(1, min(limit, 500))]}
File without changes
@@ -1,123 +0,0 @@
1
- """Telegram bridge — the phone-side surface of a local Lattice AI.
2
-
3
- The bot mirrors the web app's capabilities onto a chat: server status, models,
4
- Knowledge Graph counts, screenshots, history, document capture, the Review
5
- Center, and the full human-in-the-loop agent handshake. It is fail-closed by
6
- construction — no bot token, no chat allowlist, or no explicitly provisioned
7
- server capability and the loop refuses to start.
8
-
9
- Split into cohesive submodules in v11.3.0 (no behaviour change): ``config``
10
- (environment, endpoints, logger and the authenticated server client),
11
- ``helpers`` (chat-id registry, Telegram API, network, agent artifacts),
12
- ``screens`` (the menu commands that report on the running system), ``flows``
13
- (ask → render → approve, including the pending-plan state), and ``dispatch``
14
- (command/callback routing and the poll loop). ``__main__`` keeps
15
- ``python -m latticeai.integrations.telegram_bot`` working. This module
16
- re-exports every name the single file exposed, so
17
- ``latticeai.integrations.telegram_bot.X`` keeps working.
18
-
19
- Stubbing note: rebinding one of these *here* changes only this module's name.
20
- The submodule that calls it holds its own reference, so a test standing in for
21
- ``CHAT_IDS_FILE``/``TOKEN``/``AGENT_WORKSPACE`` patches
22
- ``…telegram_bot.helpers``, for ``_server_client``/``_bot_pending_plans``
23
- ``…telegram_bot.flows`` (or ``…screens`` for a screen), and for ``get_updates``
24
- ``…telegram_bot.dispatch``.
25
- """
26
-
27
- # The single file had no ``__all__``, so its public surface was "every module
28
- # global" — including the names it imported for its own use. Every re-export
29
- # below therefore uses the redundant-alias form: it reproduces exactly that
30
- # surface, and it marks each name as deliberate rather than a leftover import.
31
- from latticeai.cli.runtime import _load_env_file as _load_env_file
32
- from latticeai.core.io_utils import atomic_write_json as atomic_write_json
33
- from latticeai.core.logging_safety import (
34
- install_sensitive_log_filter as install_sensitive_log_filter,
35
- )
36
- from latticeai.core.logging_safety import safe_log_text as safe_log_text
37
- from latticeai.core.quiet import quiet as quiet
38
-
39
- from .config import AGENT_RESUME_URL as AGENT_RESUME_URL
40
- from .config import AGENT_URL as AGENT_URL
41
- from .config import AGENT_WORKSPACE as AGENT_WORKSPACE
42
- from .config import API_URL as API_URL
43
- from .config import BASE_URL as BASE_URL
44
- from .config import CHAT_IDS_FILE as CHAT_IDS_FILE
45
- from .config import CHAT_URL as CHAT_URL
46
- from .config import DATA_DIR as DATA_DIR
47
- from .config import GRAPH_STATS_URL as GRAPH_STATS_URL
48
- from .config import HISTORY_URL as HISTORY_URL
49
- from .config import INVITE_CODE as INVITE_CODE
50
- from .config import MAX_TELEGRAM_FILE_BYTES as MAX_TELEGRAM_FILE_BYTES
51
- from .config import MCP_TOOLS_URL as MCP_TOOLS_URL
52
- from .config import MODELS_URL as MODELS_URL
53
- from .config import PROPOSALS_URL as PROPOSALS_URL
54
- from .config import PUBLIC_WEB_URL as PUBLIC_WEB_URL
55
- from .config import SERVER_PORT as SERVER_PORT
56
- from .config import STATUS_URL as STATUS_URL
57
- from .config import TOKEN as TOKEN
58
- from .config import UPLOAD_DOC_URL as UPLOAD_DOC_URL
59
- from .config import _get_server_session as _get_server_session
60
- from .config import _server_client as _server_client
61
- from .config import env_value as env_value
62
- from .config import load_env_file as load_env_file
63
- from .config import logger as logger
64
- from .dispatch import HELP_TEXT as HELP_TEXT
65
- from .dispatch import _log_task_exception as _log_task_exception
66
- from .dispatch import handle_callback_query as handle_callback_query
67
- from .dispatch import handle_command as handle_command
68
- from .dispatch import run_bot as run_bot
69
- from .flows import _approval_pause_id as _approval_pause_id
70
- from .flows import _bot_pending_plans as _bot_pending_plans
71
- from .flows import _is_approval_pause as _is_approval_pause
72
- from .flows import _resume_payload as _resume_payload
73
- from .flows import ask_ai as ask_ai
74
- from .flows import format_artifact_card as format_artifact_card
75
- from .flows import format_grounding_badge as format_grounding_badge
76
- from .flows import format_proposals as format_proposals
77
- from .flows import format_run_explanation as format_run_explanation
78
- from .flows import handle_plan_callback as handle_plan_callback
79
- from .flows import handle_proposal_callback as handle_proposal_callback
80
- from .flows import process_ai_request as process_ai_request
81
- from .flows import send_artifact_card as send_artifact_card
82
- from .flows import send_grounding_badge as send_grounding_badge
83
- from .flows import send_plan_for_approval as send_plan_for_approval
84
- from .flows import send_run_explanation as send_run_explanation
85
- from .flows import show_review_center as show_review_center
86
- from .helpers import allowed_chat_ids as allowed_chat_ids
87
- from .helpers import answer_callback as answer_callback
88
- from .helpers import broadcast_web_chat as broadcast_web_chat
89
- from .helpers import collect_generated_files as collect_generated_files
90
- from .helpers import collect_preview_urls as collect_preview_urls
91
- from .helpers import download_as_base64 as download_as_base64
92
- from .helpers import download_telegram_file as download_telegram_file
93
- from .helpers import edit_message as edit_message
94
- from .helpers import get_graph_url as get_graph_url
95
- from .helpers import get_lan_ip as get_lan_ip
96
- from .helpers import get_updates as get_updates
97
- from .helpers import get_web_url as get_web_url
98
- from .helpers import is_chat_allowed as is_chat_allowed
99
- from .helpers import load_chat_ids as load_chat_ids
100
- from .helpers import parse_allowed_chat_ids as parse_allowed_chat_ids
101
- from .helpers import register_chat_id as register_chat_id
102
- from .helpers import resolve_workspace_file as resolve_workspace_file
103
- from .helpers import save_chat_ids as save_chat_ids
104
- from .helpers import send_chat_action as send_chat_action
105
- from .helpers import send_document as send_document
106
- from .helpers import send_generated_files as send_generated_files
107
- from .helpers import send_message as send_message
108
- from .helpers import send_photo as send_photo
109
- from .helpers import send_preview_links as send_preview_links
110
- from .screens import MAIN_MENU as MAIN_MENU
111
- from .screens import _mac_ram_used_gb as _mac_ram_used_gb
112
- from .screens import _unload_all_report as _unload_all_report
113
- from .screens import clear_server_history as clear_server_history
114
- from .screens import do_unload_model as do_unload_model
115
- from .screens import process_document_file as process_document_file
116
- from .screens import send_mcp_tools as send_mcp_tools
117
- from .screens import send_web_link as send_web_link
118
- from .screens import show_graph_stats as show_graph_stats
119
- from .screens import show_history_summary as show_history_summary
120
- from .screens import show_menu as show_menu
121
- from .screens import show_model_info as show_model_info
122
- from .screens import show_status as show_status
123
- from .screens import take_screenshot as take_screenshot
@@ -1,17 +0,0 @@
1
- """Script entry: ``python -m latticeai.integrations.telegram_bot``.
2
-
3
- The single file carried this block under ``if __name__ == "__main__"``. A
4
- package cannot: ``__init__`` is never ``__main__``. Moving it here verbatim is
5
- what keeps the ``-m`` invocation working after the v11.3.0 split.
6
- """
7
-
8
- import asyncio
9
-
10
- from latticeai.core.quiet import quiet
11
- from latticeai.integrations.telegram_bot import run_bot
12
-
13
- if __name__ == "__main__":
14
- try:
15
- asyncio.run(run_bot())
16
- except KeyboardInterrupt:
17
- quiet()
@@ -1,86 +0,0 @@
1
- """Environment, endpoints, logger, and the bot's server capability.
2
-
3
- Every value the bridge reads from the environment is resolved here, once, at
4
- import — the module *is* the bot's configuration surface. Importing it has the
5
- same side effects the single file had at its own import: the sensitive-log
6
- filter is installed, ``.env`` is loaded, and the data directory is provisioned.
7
-
8
- Security boundary (unchanged since 11.0.1): :func:`_server_client` is the only
9
- authenticated client. It carries the explicitly provisioned bot-to-server
10
- bearer capability and is used for Lattice-server calls alone — never for
11
- api.telegram.org, which always gets the plain ``httpx.AsyncClient`` the poll
12
- loop owns.
13
-
14
- Stubbing note: a constant is read through the globals of the module that *uses*
15
- it, so a test standing in for ``TOKEN``/``CHAT_IDS_FILE``/``AGENT_WORKSPACE``
16
- patches ``helpers``/``screens``/``flows``/``dispatch``, not this module.
17
- """
18
-
19
- import logging
20
- import os
21
- from pathlib import Path
22
-
23
- import httpx
24
-
25
- from latticeai.cli.runtime import _load_env_file
26
- from latticeai.core.logging_safety import install_sensitive_log_filter
27
-
28
- install_sensitive_log_filter()
29
-
30
- def load_env_file(path=".env"):
31
- # Single source of truth: shared with ltcai_cli via latticeai.cli.runtime.
32
- _load_env_file(Path(path))
33
-
34
- load_env_file()
35
-
36
- def env_value(primary: str, default: str = "") -> str:
37
- return os.getenv(primary) or default
38
-
39
- TOKEN = env_value("LATTICEAI_TELEGRAM_BOT_TOKEN")
40
- API_URL = f"https://api.telegram.org/bot{TOKEN}"
41
- SERVER_PORT = int(env_value("LATTICEAI_SERVER_PORT", "4825"))
42
- BASE_URL = env_value("LATTICEAI_BASE_URL", env_value("LATTICEAI_SERVER_URL", f"http://127.0.0.1:{SERVER_PORT}")).rstrip("/")
43
- CHAT_URL = f"{BASE_URL}/chat"
44
- AGENT_URL = f"{BASE_URL}/agent"
45
- MCP_TOOLS_URL = f"{BASE_URL}/mcp/tools"
46
- HISTORY_URL = f"{BASE_URL}/history"
47
- STATUS_URL = f"{BASE_URL}/status"
48
- MODELS_URL = f"{BASE_URL}/models"
49
- GRAPH_STATS_URL = f"{BASE_URL}/knowledge-graph/stats"
50
- UPLOAD_DOC_URL = f"{BASE_URL}/upload/document"
51
- PROPOSALS_URL = f"{BASE_URL}/api/proposals"
52
-
53
- AGENT_RESUME_URL = f"{BASE_URL}/agent/resume"
54
- AGENT_WORKSPACE = Path(env_value("LATTICEAI_AGENT_ROOT", "agent_workspace")).resolve()
55
-
56
- MAX_TELEGRAM_FILE_BYTES = 45 * 1024 * 1024
57
- INVITE_CODE = env_value("LATTICEAI_INVITE_CODE")
58
- PUBLIC_WEB_URL = env_value("LATTICEAI_PUBLIC_URL")
59
- DATA_DIR = Path(env_value("LATTICEAI_DATA_DIR", str(Path.home() / ".ltcai")))
60
- DATA_DIR.mkdir(parents=True, exist_ok=True)
61
- CHAT_IDS_FILE = Path(env_value("LATTICEAI_TELEGRAM_CHATS_FILE", str(DATA_DIR / "telegram_chats.json")))
62
-
63
- logging.basicConfig(level=logging.INFO)
64
- logger = logging.getLogger(__name__)
65
-
66
- # ── Server session auth ───────────────────────────────────────────────────────
67
-
68
- def _get_server_session() -> str:
69
- """Return only the explicitly provisioned bot-to-server token.
70
-
71
- Web sessions are hashed at rest and belong to interactive users. Scanning
72
- ``sessions.json`` cannot recover a usable token and would couple the bot to
73
- whichever administrator happened to be logged in most recently.
74
- """
75
- return env_value("LATTICEAI_SERVER_SESSION_TOKEN").strip()
76
-
77
- def _server_client(**kwargs) -> httpx.AsyncClient:
78
- """Return a server client authenticated by an explicit bearer capability."""
79
- token = _get_server_session()
80
- if not token:
81
- raise RuntimeError(
82
- "LATTICEAI_SERVER_SESSION_TOKEN is required for Telegram server API calls."
83
- )
84
- headers = dict(kwargs.pop("headers", {}) or {})
85
- headers["Authorization"] = f"Bearer {token}"
86
- return httpx.AsyncClient(headers=headers, **kwargs)