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,42 +1,31 @@
1
- """Tool registry, governance, and dispatch helpers.
2
-
3
- HTTP routers and the agent runtime share this service so policy checks and
4
- tool-response shaping are owned outside ``server_app``.
1
+ """Tool registry, governance, and the authorization half of tool dispatch.
2
+
3
+ ``ToolRegistry`` owns the catalog and governance facts; this service owns the
4
+ request-time authorization callbacks that must be configured during app
5
+ construction.
6
+
7
+ v11.6.0 shrank it to the three questions ``POST /agent/tool`` asks before it
8
+ runs anything — what is this call's policy, may this role make it, and does the
9
+ target already exist — plus the ``configure`` seam that binds the user table.
10
+ The rest of the module was the Python agent loop's: ``build_agent_runtime``
11
+ (the edge that kept ``latticeai.core.agent`` alive), the artifact collectors,
12
+ the snapshot/rollback file surface behind the Review Center, and
13
+ ``enforce_policy``'s approval flow. Orchestration lives in ``lattice-agent``
14
+ now, and the mode gating is the kernel's decision, made with the run's approval
15
+ state — which HTTP does not have.
5
16
  """
6
17
 
7
18
  from __future__ import annotations
8
19
 
9
- import subprocess
10
20
  from dataclasses import dataclass, field
11
21
  from pathlib import Path
12
22
  from typing import Any, Callable, Dict, Mapping, Optional
13
23
 
14
24
  from fastapi import HTTPException
15
25
 
16
- from latticeai.core.agent import AgentDeps, SingleAgentRuntime
17
- from latticeai.core.agent_permission import call_mode_source
18
- from latticeai.core.agent_prompts import (
19
- CRITIC_PROMPT,
20
- EXECUTOR_PROMPT,
21
- MEMORY_UPDATER_PROMPT,
22
- PLANNER_PROMPT,
23
- )
24
- from latticeai.core.permission_mode import (
25
- DEFAULT_MODE,
26
- effective_auto_approve,
27
- is_circuit_breaker,
28
- normalize_mode,
29
- )
30
26
  from latticeai.core.policy import role_has_capability
31
- from latticeai.core.tool_governor import classify_tool_call
32
27
  from latticeai.core.tool_registry import ToolPermission, ToolPolicy
33
- from latticeai.tools import (
34
- AGENT_ROOT,
35
- DEFAULT_TOOL_REGISTRY,
36
- ToolError,
37
- document_output_target,
38
- ensure_agent_root,
39
- )
28
+ from latticeai.tools import AGENT_ROOT, DEFAULT_TOOL_REGISTRY, document_output_target
40
29
 
41
30
 
42
31
  def _default_load_users() -> Dict[str, Any]:
@@ -47,21 +36,8 @@ def _default_get_user_role(_email, _users=None) -> str:
47
36
  return "user"
48
37
 
49
38
 
50
- def _default_permission_mode() -> Any:
51
- return DEFAULT_MODE
52
-
53
-
54
- FILE_CREATE_ACTIONS = set(DEFAULT_TOOL_REGISTRY.file_create_actions)
55
39
  TOOL_GOVERNANCE: Dict[str, ToolPolicy] = dict(DEFAULT_TOOL_REGISTRY.governance)
56
40
  TOOL_GOVERNANCE_DEFAULT: ToolPolicy = DEFAULT_TOOL_REGISTRY.default_policy
57
- ADMIN_ONLY_TOOLS: frozenset[str] = DEFAULT_TOOL_REGISTRY.admin_only_tools
58
- EXPLICIT_CONSENT_TOOLS: frozenset[str] = frozenset({
59
- "local_list",
60
- "local_read",
61
- "local_write",
62
- "read_document",
63
- })
64
- LOCAL_WRITE_BLOCKED_PREFIXES = DEFAULT_TOOL_REGISTRY.local_write_blocked_prefixes
65
41
  RISK_LEVEL_MAP = DEFAULT_TOOL_REGISTRY.risk_level_map
66
42
 
67
43
 
@@ -69,24 +45,14 @@ RISK_LEVEL_MAP = DEFAULT_TOOL_REGISTRY.risk_level_map
69
45
  class ToolDispatchService:
70
46
  """Runtime-facing tool policy and authorization boundary.
71
47
 
72
- ``ToolRegistry`` owns the catalog and governance facts; this service owns
73
- request/runtime authorization callbacks that must be configured during app
74
- construction. Keeping those callbacks on an instance gives future runtime
75
- assembly code an injectable seam while the module-level functions below
76
- preserve the historical ``server_app`` surface.
48
+ Keeping the callbacks on an instance rather than in module globals is what
49
+ lets a second application — or a test — bind its own user table without
50
+ reaching into this one's.
77
51
  """
78
52
 
79
53
  registry: Any = field(default_factory=lambda: DEFAULT_TOOL_REGISTRY)
80
54
  load_users: Callable[[], Dict[str, Any]] = field(default=_default_load_users)
81
55
  get_user_role: Callable[..., str] = field(default=_default_get_user_role)
82
- # Static mode, or a callable resolved per enforce_policy call. A callable
83
- # may accept ``user_email``/``workspace_id`` scope kwargs (preferred) or no
84
- # arguments at all — see ``call_mode_source``.
85
- permission_mode: Any = field(default_factory=lambda: _default_permission_mode)
86
-
87
- @property
88
- def file_create_actions(self) -> frozenset[str]:
89
- return self.registry.file_create_actions
90
56
 
91
57
  @property
92
58
  def tool_governance(self) -> Mapping[str, ToolPolicy]:
@@ -101,29 +67,9 @@ class ToolDispatchService:
101
67
  *,
102
68
  load_users: Callable[[], Dict[str, Any]],
103
69
  get_user_role: Callable[..., str],
104
- permission_mode: Any = None,
105
70
  ) -> None:
106
71
  self.load_users = load_users
107
72
  self.get_user_role = get_user_role
108
- if permission_mode is not None:
109
- self.permission_mode = permission_mode
110
-
111
- def resolve_permission_mode(
112
- self,
113
- *,
114
- user_email: Optional[str] = None,
115
- workspace_id: Optional[str] = None,
116
- ) -> Any:
117
- """Active autonomy dial for this caller.
118
-
119
- The scope matters: per-user and per-workspace overrides are stored by
120
- ``PermissionModeService``, so resolving without a scope would silently
121
- collapse every caller onto the process-wide default.
122
- """
123
- raw = call_mode_source(
124
- self.permission_mode, user_email=user_email, workspace_id=workspace_id,
125
- )
126
- return normalize_mode(raw if raw is not None else DEFAULT_MODE)
127
73
 
128
74
  def policy_for(self, action_name: str, args: dict) -> ToolPolicy:
129
75
  return self.registry.policy_for(action_name, args)
@@ -131,9 +77,6 @@ class ToolDispatchService:
131
77
  def risk_level(self, action_name: str, args: dict) -> str:
132
78
  return self.registry.risk_level(action_name, args)
133
79
 
134
- def risk_level_for_policy(self, policy: ToolPolicy) -> str:
135
- return self.risk_level_map.get(policy["risk"], "medium")
136
-
137
80
  def permission(self, name: str, args: Optional[dict] = None) -> ToolPermission:
138
81
  return self.registry.permission(name, args or {})
139
82
 
@@ -191,160 +134,6 @@ class ToolDispatchService:
191
134
  except Exception:
192
135
  return "user"
193
136
 
194
- def enforce_policy(
195
- self,
196
- tool_name: str,
197
- args: Optional[dict],
198
- *,
199
- current_user: str,
200
- source: str,
201
- require_auto_approval: bool = True,
202
- trusted_admin: bool = False,
203
- permission_mode: Any = None,
204
- workspace_id: Optional[str] = None,
205
- ) -> ToolPolicy:
206
- """Authorize a tool call before any hook or handler can execute.
207
-
208
- This is the shared policy gate for direct HTTP/MCP/tool surfaces. Agent
209
- runtime uses the same policy data and blocks non-auto-approved steps in
210
- its state machine so Computer Use direct endpoints can remain unchanged
211
- when explicitly excluded from a hardening pass.
212
-
213
- The active :class:`PermissionMode` widens what may run without an extra
214
- approval prompt; it never widens a circuit breaker.
215
- """
216
- policy = self.policy_for(tool_name, args or {})
217
- mode = normalize_mode(
218
- permission_mode
219
- if permission_mode is not None
220
- else self.resolve_permission_mode(
221
- user_email=current_user, workspace_id=workspace_id,
222
- )
223
- )
224
- if source in {"mcp", "plugin"} and tool_name in EXPLICIT_CONSENT_TOOLS:
225
- raise HTTPException(
226
- status_code=403,
227
- detail=(
228
- f"'{tool_name}' requires the dedicated local-file approval flow "
229
- "and cannot run through MCP or plugins."
230
- ),
231
- )
232
- if not trusted_admin:
233
- self.check_role(tool_name, current_user)
234
-
235
- breaker = is_circuit_breaker(tool_name, policy, args or {})
236
- if breaker:
237
- raise HTTPException(status_code=403, detail=f"'{tool_name}' 차단: {breaker}")
238
-
239
- if policy["destructive"] or policy["risk"] == "destructive":
240
- raise HTTPException(
241
- status_code=403,
242
- detail=f"'{tool_name}' 툴은 파괴적 작업으로 차단되었습니다.",
243
- )
244
-
245
- # Fail-closed governance: a call that would rewrite existing content but
246
- # cannot be staged as a reviewable proposal is blocked, never applied
247
- # silently. New-file (additive) calls are unaffected. This is
248
- # mode-invariant — trusted/bypass skip *proposal staging*, but a binary
249
- # overwrite has no safe apply path in any mode, so it stays blocked.
250
- verdict = classify_tool_call(
251
- tool_name, args or {},
252
- policy=dict(policy),
253
- path_exists=lambda candidate: self._governed_path_exists(tool_name, candidate),
254
- )
255
- if verdict.get("fail_closed"):
256
- raise HTTPException(
257
- status_code=409,
258
- detail=(
259
- f"'{tool_name}' 툴은 기존 콘텐츠를 변경하지만 검토 가능한 제안으로 "
260
- "적용할 수 없어 차단되었습니다. 새 파일 이름으로 생성하거나 "
261
- "지원되는 편집 도구(write_file/edit_file)를 사용하세요."
262
- ),
263
- )
264
-
265
- auto = policy["auto_approve"] or effective_auto_approve(
266
- mode, tool_name, policy, change_class=verdict.get("change_class"), args=args or {},
267
- )
268
- if (
269
- require_auto_approval
270
- and not trusted_admin
271
- and not auto
272
- and self.user_role(current_user) not in {"admin", "owner"}
273
- ):
274
- raise HTTPException(
275
- status_code=403,
276
- detail=(
277
- f"'{tool_name}' 툴은 명시 승인이 필요합니다 "
278
- f"(permission_mode={mode.value}). "
279
- "승인 UI가 없는 직접 실행 경로에서 차단되었습니다."
280
- ),
281
- )
282
- return policy
283
-
284
- def rollback_file(self, path: str) -> Dict[str, Any]:
285
- r = subprocess.run(
286
- ["git", "checkout", "--", path],
287
- cwd=str(AGENT_ROOT),
288
- capture_output=True,
289
- text=True,
290
- timeout=10,
291
- )
292
- return {"path": path, "ok": r.returncode == 0, "stderr": r.stderr[:200]}
293
-
294
- # ── snapshot rollback ports (review L7) ──────────────────────────────
295
- # Pre-write capture + restore for workspaces where git rollback does not
296
- # apply (untracked new files, no git). Strictly confined to AGENT_ROOT.
297
-
298
- _SNAPSHOT_MAX_BYTES = 512 * 1024
299
-
300
- def _workspace_path(self, path: str) -> Optional[Path]:
301
- raw = Path(path)
302
- candidate = raw if raw.is_absolute() else AGENT_ROOT / raw
303
- try:
304
- resolved = candidate.resolve()
305
- root = AGENT_ROOT.resolve()
306
- except OSError:
307
- return None
308
- if resolved != root and root not in resolved.parents:
309
- return None
310
- return resolved
311
-
312
- def snapshot_file(self, path: str) -> Dict[str, Any]:
313
- """Pre-write state of a workspace file: existence + bounded content."""
314
- target = self._workspace_path(path)
315
- if target is None:
316
- return {"existed": False, "content": None, "too_large": False,
317
- "error": "path escapes the agent workspace"}
318
- if not target.exists() or not target.is_file():
319
- return {"existed": False, "content": None, "too_large": False}
320
- try:
321
- if target.stat().st_size > self._SNAPSHOT_MAX_BYTES:
322
- return {"existed": True, "content": None, "too_large": True}
323
- return {
324
- "existed": True,
325
- "content": target.read_text(encoding="utf-8", errors="replace"),
326
- "too_large": False,
327
- }
328
- except OSError as exc:
329
- return {"existed": True, "content": None, "too_large": True,
330
- "error": str(exc)}
331
-
332
- def restore_snapshot(self, path: str, content: Optional[str]) -> Dict[str, Any]:
333
- """Restore a pre-write snapshot: rewrite prior content, or delete a
334
- file the run created (``content=None``)."""
335
- target = self._workspace_path(path)
336
- if target is None:
337
- return {"path": path, "ok": False, "error": "path escapes the agent workspace"}
338
- try:
339
- if content is None:
340
- target.unlink(missing_ok=True)
341
- return {"path": path, "ok": True, "action": "deleted"}
342
- target.parent.mkdir(parents=True, exist_ok=True)
343
- target.write_text(content, encoding="utf-8")
344
- return {"path": path, "ok": True, "action": "restored"}
345
- except OSError as exc:
346
- return {"path": path, "ok": False, "error": str(exc)}
347
-
348
137
 
349
138
  DEFAULT_TOOL_DISPATCH_SERVICE = ToolDispatchService()
350
139
 
@@ -353,172 +142,27 @@ def configure_tool_dispatch(
353
142
  *,
354
143
  load_users: Callable[[], Dict[str, Any]],
355
144
  get_user_role: Callable[..., str],
356
- permission_mode: Any = None,
357
145
  ) -> None:
358
146
  DEFAULT_TOOL_DISPATCH_SERVICE.configure(
359
- load_users=load_users,
360
- get_user_role=get_user_role,
361
- permission_mode=permission_mode,
147
+ load_users=load_users, get_user_role=get_user_role
362
148
  )
363
149
 
364
150
 
365
- def agent_risk(action_name: str, args: dict) -> str:
366
- return DEFAULT_TOOL_DISPATCH_SERVICE.risk_level(action_name, args)
367
-
368
-
369
151
  def get_tool_permission(name: str, args: Optional[dict] = None) -> ToolPermission:
370
152
  return DEFAULT_TOOL_DISPATCH_SERVICE.permission(name, args or {})
371
153
 
372
154
 
373
- def list_tool_permissions() -> list:
374
- return DEFAULT_TOOL_DISPATCH_SERVICE.permissions()
375
-
376
-
377
- def tool_registry_diagnostics() -> Dict[str, Any]:
378
- return DEFAULT_TOOL_DISPATCH_SERVICE.diagnostics()
379
-
380
-
381
- def tool_registry_manifest() -> Dict[str, Any]:
382
- return DEFAULT_TOOL_DISPATCH_SERVICE.manifest()
383
-
384
-
385
155
  def check_tool_role(tool_name: str, current_user: str) -> None:
386
156
  DEFAULT_TOOL_DISPATCH_SERVICE.check_role(tool_name, current_user)
387
157
 
388
158
 
389
- def enforce_tool_policy(
390
- tool_name: str,
391
- args: Optional[dict],
392
- *,
393
- current_user: str,
394
- source: str,
395
- require_auto_approval: bool = True,
396
- trusted_admin: bool = False,
397
- permission_mode: Any = None,
398
- workspace_id: Optional[str] = None,
399
- ) -> ToolPolicy:
400
- return DEFAULT_TOOL_DISPATCH_SERVICE.enforce_policy(
401
- tool_name,
402
- args or {},
403
- current_user=current_user,
404
- source=source,
405
- require_auto_approval=require_auto_approval,
406
- trusted_admin=trusted_admin,
407
- workspace_id=workspace_id,
408
- permission_mode=permission_mode,
409
- )
410
-
411
-
412
- def collect_created_files(transcript: list) -> list:
413
- files = []
414
- for step in transcript:
415
- if step.get("action") in FILE_CREATE_ACTIONS:
416
- result = step.get("result", {})
417
- if isinstance(result.get("created_files"), list):
418
- for rel_path in result["created_files"]:
419
- files.append({
420
- "path": rel_path,
421
- "filename": Path(rel_path).name,
422
- "bytes": 0,
423
- "action": step["action"],
424
- })
425
- continue
426
- path = result.get("path")
427
- if path:
428
- files.append({
429
- "path": path,
430
- "filename": Path(path).name,
431
- "bytes": result.get("bytes", 0),
432
- "action": step["action"],
433
- })
434
- return files
435
-
436
-
437
- def collect_artifacts(transcript: list) -> list:
438
- """Artifact-first view of a run: every produced file with honesty flags.
439
-
440
- ``repaired`` surfaces the ArtifactWritePipeline verdict recorded on the
441
- transcript step (``content_sanitize``) so the UI can badge deterministic
442
- scaffolds instead of presenting them as high-quality model output.
443
- """
444
- from latticeai.core.file_generation import PREVIEWABLE_EXTENSIONS
445
-
446
- artifacts = []
447
- for step in transcript:
448
- if step.get("action") not in FILE_CREATE_ACTIONS:
449
- continue
450
- result = step.get("result", {})
451
- if not isinstance(result, dict):
452
- continue
453
- sanitize = step.get("content_sanitize") or {}
454
- repaired = bool(sanitize.get("repaired"))
455
- rel_paths = (
456
- result["created_files"]
457
- if isinstance(result.get("created_files"), list)
458
- else ([result["path"]] if result.get("path") else [])
459
- )
460
- for rel_path in rel_paths:
461
- artifacts.append({
462
- "kind": "file",
463
- "path": rel_path,
464
- "filename": Path(rel_path).name,
465
- "bytes": result.get("bytes", 0) if len(rel_paths) == 1 else 0,
466
- "previewable": Path(rel_path).suffix.lower() in PREVIEWABLE_EXTENSIONS,
467
- "valid": True,
468
- "repaired": repaired,
469
- })
470
- return artifacts
471
-
472
-
473
- def build_agent_runtime(
474
- *,
475
- model_router: Any,
476
- execute_tool: Callable[..., Dict[str, Any]],
477
- recent_chat_context: Callable[..., str],
478
- clear_history: Callable[[int], Dict[str, Any]],
479
- knowledge_save: Callable[..., Dict[str, Any]],
480
- audit: Callable[..., None],
481
- hooks: Any = None,
482
- brain_memory: Any = None,
483
- dispatch_service: ToolDispatchService = DEFAULT_TOOL_DISPATCH_SERVICE,
484
- permission_mode: Any = None,
485
- self_model_summary: Any = None,
486
- ) -> SingleAgentRuntime:
487
- ensure_agent_root()
488
- mode = permission_mode if permission_mode is not None else dispatch_service.permission_mode
489
- deps = AgentDeps(
490
- generate_as=model_router.generate_as,
491
- generate=model_router.generate,
492
- execute_tool=execute_tool,
493
- policy_for=dispatch_service.policy_for,
494
- risk_level=dispatch_service.risk_level_for_policy,
495
- check_role=dispatch_service.check_role,
496
- tool_governance=dispatch_service.tool_governance,
497
- file_create_actions=dispatch_service.file_create_actions,
498
- recent_chat_context=recent_chat_context,
499
- clear_history=clear_history,
500
- knowledge_save=knowledge_save,
501
- audit=audit,
502
- planner_prompt=PLANNER_PROMPT,
503
- executor_prompt=EXECUTOR_PROMPT,
504
- critic_prompt=CRITIC_PROMPT,
505
- memory_updater_prompt=MEMORY_UPDATER_PROMPT,
506
- agent_root=AGENT_ROOT,
507
- rollback_file=dispatch_service.rollback_file,
508
- snapshot_file=dispatch_service.snapshot_file,
509
- restore_snapshot=dispatch_service.restore_snapshot,
510
- hooks=hooks,
511
- brain_memory=brain_memory,
512
- permission_mode=mode,
513
- # v11.2.0: what the Brain knows about its owner reaches the agent loop.
514
- # ``None`` keeps the pre-11.2.0 prompt bytes exactly.
515
- self_model_summary=self_model_summary,
516
- )
517
- return SingleAgentRuntime(deps)
518
-
519
-
520
- def tool_response(fn, *args):
521
- try:
522
- return {"status": "ok", "workspace": str(AGENT_ROOT), "result": fn(*args)}
523
- except ToolError as exc:
524
- raise HTTPException(status_code=400, detail=str(exc))
159
+ __all__ = [
160
+ "DEFAULT_TOOL_DISPATCH_SERVICE",
161
+ "RISK_LEVEL_MAP",
162
+ "TOOL_GOVERNANCE",
163
+ "TOOL_GOVERNANCE_DEFAULT",
164
+ "ToolDispatchService",
165
+ "check_tool_role",
166
+ "configure_tool_dispatch",
167
+ "get_tool_permission",
168
+ ]
@@ -25,6 +25,11 @@ scan is transcribed by whatever this service was given — see
25
25
  :meth:`VoiceCaptureService.multimodal_ports`. One transcriber, one honesty
26
26
  story: if a voice memo cannot become text here, a scanned recording cannot
27
27
  either, and both say so the same way.
28
+
29
+ v11.6.0 took the *ingest* half away: ``POST /api/capture/voice`` is a native
30
+ route that stores the memo itself and asks ``POST /worker/asr`` for the words.
31
+ What is left is the transcriber and the two questions only this process can
32
+ answer about it — can it hear, and what will it accept.
28
33
  """
29
34
 
30
35
  from __future__ import annotations
@@ -57,16 +62,14 @@ class TranscriptionUnavailable(RuntimeError):
57
62
 
58
63
 
59
64
  class VoiceCaptureService:
60
- """Ingest a voice memo through the unified ingestion pipeline."""
65
+ """The local transcriber, and the honest report of whether there is one."""
61
66
 
62
67
  def __init__(
63
68
  self,
64
69
  *,
65
- pipeline: Any,
66
70
  transcriber: Optional[Callable[[str], str]] = None,
67
71
  max_bytes: int = MAX_AUDIO_BYTES,
68
72
  ) -> None:
69
- self._pipeline = pipeline
70
73
  self._transcriber = transcriber
71
74
  self._max_bytes = max(1, int(max_bytes))
72
75
 
@@ -74,7 +77,11 @@ class VoiceCaptureService:
74
77
  def status(self) -> Dict[str, Any]:
75
78
  """What this install can actually do with a voice memo, honestly."""
76
79
  return {
77
- "capture": self._pipeline is not None,
80
+ # Storing the memo is native (``POST /api/capture/voice`` in
81
+ # lattice-platform), so capture is always available to a caller
82
+ # that reached this answer through the gateway. Only the
83
+ # transcriber is a fact about this process.
84
+ "capture": True,
78
85
  "transcription": self._transcriber is not None,
79
86
  "supported_extensions": sorted(SUPPORTED_AUDIO_EXTENSIONS),
80
87
  "max_bytes": self._max_bytes,
@@ -110,106 +117,5 @@ class VoiceCaptureService:
110
117
  raise TranscriptionUnavailable("the transcriber returned no text")
111
118
  return cleaned
112
119
 
113
- # ── capture ──────────────────────────────────────────────────────────
114
- def capture(
115
- self,
116
- audio_path: str,
117
- *,
118
- title: Optional[str] = None,
119
- user_email: Optional[str] = None,
120
- workspace_id: Optional[str] = None,
121
- conversation_id: Optional[str] = None,
122
- transcript: Optional[str] = None,
123
- ) -> Dict[str, Any]:
124
- """Ingest one voice memo.
125
-
126
- ``transcript`` lets a client that already has text (a phone's own
127
- dictation, for example) skip local transcription entirely — the memo
128
- still lands in the Brain through the same pipeline.
129
- """
130
- from lattice_brain.ingestion import IngestionItem
131
-
132
- path = Path(str(audio_path or "")).expanduser()
133
- if not path.exists() or not path.is_file():
134
- return self._failed("FILE_NOT_FOUND", f"audio file not found: {path}")
135
- suffix = path.suffix.lower()
136
- if suffix not in SUPPORTED_AUDIO_EXTENSIONS:
137
- return self._failed(
138
- "UNSUPPORTED_FORMAT",
139
- f"{suffix or 'this file'} is not a supported audio container",
140
- )
141
- try:
142
- size = path.stat().st_size
143
- except OSError as exc:
144
- return self._failed("FILE_NOT_FOUND", f"audio file unreadable: {exc}")
145
- if size > self._max_bytes:
146
- return self._failed(
147
- "SIZE_LIMIT",
148
- f"audio is {size} bytes; the memo limit is {self._max_bytes}",
149
- )
150
- if self._pipeline is None:
151
- return self._failed("UNAVAILABLE", "the ingestion pipeline is not available")
152
-
153
- supplied = str(transcript or "").strip()
154
- transcription_state = "supplied" if supplied else "ok"
155
- transcription_detail = ""
156
- text = supplied
157
- if not text:
158
- try:
159
- text = self._transcribe(path)
160
- except TranscriptionUnavailable as exc:
161
- transcription_state = "unavailable"
162
- transcription_detail = str(exc)
163
- text = ""
164
- except Exception as exc: # noqa: BLE001 — a broken transcriber is a state
165
- LOGGER.exception("voice transcription failed")
166
- transcription_state = "failed"
167
- transcription_detail = str(exc)
168
- text = ""
169
-
170
- display_title = str(title or "").strip() or path.stem
171
- # With no transcript there is no text to index — the memo is recorded
172
- # by what we honestly know (title + the audio file itself), and the
173
- # response says so. It is never presented as a searchable note.
174
- body = text or (
175
- f"[음성 메모] {display_title}\n"
176
- "이 메모는 아직 글로 바뀌지 않았습니다 — 음성 인식기가 없어 내용 검색은 되지 않습니다."
177
- )
178
- item = IngestionItem(
179
- source_type="note",
180
- title=display_title,
181
- text=body,
182
- source_uri=str(path),
183
- mime_type=f"audio/{suffix.lstrip('.')}",
184
- owner=user_email,
185
- workspace_id=workspace_id,
186
- conversation_id=conversation_id,
187
- metadata={
188
- "capture": "voice",
189
- # Same key the multi-modal ingest door writes, so a memo and a
190
- # scanned recording are one kind of thing in the graph.
191
- "modality": "audio",
192
- "audio_path": str(path),
193
- "audio_bytes": size,
194
- "transcription": transcription_state,
195
- **({"transcription_detail": transcription_detail} if transcription_detail else {}),
196
- },
197
- )
198
- result = self._pipeline.ingest(item, user_email=user_email)
199
- payload = result.as_dict() if hasattr(result, "as_dict") else dict(result)
200
- payload["transcription"] = transcription_state
201
- payload["searchable"] = bool(text)
202
- if transcription_detail:
203
- payload["transcription_detail"] = transcription_detail
204
- return payload
205
-
206
- @staticmethod
207
- def _failed(error: str, message: str) -> Dict[str, Any]:
208
- return {
209
- "status": "failed",
210
- "source_type": "note",
211
- "error": error,
212
- "message": message,
213
- "transcription": "skipped",
214
- "searchable": False,
215
- }
120
+
121
+ __all__ = ["VoiceCaptureService", "TranscriptionUnavailable", "SUPPORTED_AUDIO_EXTENSIONS"]