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
@@ -0,0 +1,377 @@
1
+ #!/usr/bin/env python3
2
+ """Rebuild the OpenAPI contract from committed fragments and prove it exact.
3
+
4
+ README — what this is, and the one step that flips it on
5
+ ========================================================
6
+
7
+ ``frontend/openapi.json`` is the single committed contract and stays that way.
8
+ v11.6.0 moves most of the surface it describes out of Python, so the spec of
9
+ each migrated family lives beside the Rust crates as a committed fragment
10
+ (``rust/fixtures/openapi/<family>.json``, written by
11
+ ``scripts/gen_openapi_fragments.py``). This script runs that split backwards:
12
+
13
+ envelope + every fragment == frontend/openapi.json, byte for byte
14
+
15
+ Byte-for-byte is the whole point. It is compared as *bytes*, not as parsed
16
+ JSON, so a re-ordered key, a changed indent or an ``int`` that became a
17
+ ``float`` all fail here instead of surfacing as client drift later.
18
+
19
+ **Cutover (Wave 3 / WP-P1) — one step, deliberately not taken yet.**
20
+ Today ``scripts/export_openapi.py`` builds the whole FastAPI app and dumps its
21
+ schema; this script only *verifies*. Once the Python app is reduced to the
22
+ worker surface, its schema no longer describes the product, and the pipeline
23
+ flips:
24
+
25
+ 1. Keep ``scripts/export_openapi.py`` exporting the (now worker-only) app, but
26
+ to a scratch path — it becomes an input, not the contract.
27
+ 2. Regenerate ``rust/fixtures/openapi/worker_keep.json`` from that worker spec
28
+ (``gen_openapi_fragments.py`` still owns the fragment shape); the other
29
+ fragments are already committed and stop depending on Python entirely.
30
+ ``info.version`` lives in ``_envelope.json``, so the fragments are
31
+ regenerated **after** the version bump, never before — the composer refuses
32
+ a worker spec whose version disagrees with theirs.
33
+ 3. Point ``npm run frontend:openapi`` at
34
+ ``scripts/compose_openapi.py --worker-spec <scratch> --output frontend/openapi.json``
35
+ followed by the existing ``openapi-typescript`` step, so the committed
36
+ contract is the composition rather than a Python dump.
37
+ 4. Leave ``scripts/check_openapi_drift.mjs`` exactly as it is: it re-runs the
38
+ generator into a temp dir and byte-compares. Composition is deterministic,
39
+ so the gate keeps biting — it now bites on fragment drift too.
40
+
41
+ Until step 3 lands, ``--worker-spec`` is verification only: it asserts the
42
+ worker app's operations are a **subset** of the ``worker_keep`` fragment, i.e.
43
+ that Python has not grown a route the gateway would have to proxy without a
44
+ contract. That is the cutover's precondition, checkable today. The exception is
45
+ ``EXEMPT_WORKER_PATHS``: WP-I6's internal ``/worker/*`` seams are loopback-only
46
+ and stay out of the public contract by design, and FastAPI's own docs
47
+ endpoints describe the schema rather than being surface. Both are named
48
+ explicitly below so nothing else slips through with them.
49
+
50
+ Usage::
51
+
52
+ .venv/bin/python scripts/compose_openapi.py
53
+ .venv/bin/python scripts/compose_openapi.py --worker-spec /tmp/worker.json
54
+ .venv/bin/python scripts/compose_openapi.py --output /tmp/composed.json
55
+ """
56
+
57
+ from __future__ import annotations
58
+
59
+ import argparse
60
+ import copy
61
+ import difflib
62
+ import importlib.util
63
+ import json
64
+ import sys
65
+ from pathlib import Path
66
+ from typing import Any, Dict, List, Mapping, Tuple
67
+
68
+ REPO_ROOT = Path(__file__).resolve().parents[1]
69
+
70
+ # ``scripts`` is not a package, so the generator is loaded by path — the same
71
+ # trick the parity generators and their tests use. Importing it rather than
72
+ # retyping its constants is what keeps *one* serialiser in the pipeline: if the
73
+ # two halves could disagree about ``sort_keys`` or ``indent``, byte-exactness
74
+ # would be a coincidence instead of a property.
75
+ def _load_module(name: str, path: Path) -> Any:
76
+ spec = importlib.util.spec_from_file_location(name, path)
77
+ if spec is None or spec.loader is None: # pragma: no cover - packaging accident
78
+ raise ImportError(f"cannot load {path}")
79
+ module = importlib.util.module_from_spec(spec)
80
+ spec.loader.exec_module(module)
81
+ return module
82
+
83
+
84
+ fragments_module = _load_module(
85
+ "gen_openapi_fragments",
86
+ Path(__file__).resolve().parent / "gen_openapi_fragments.py",
87
+ )
88
+
89
+ SPEC_PATH = fragments_module.SPEC_PATH
90
+ FRAGMENT_DIR = fragments_module.FRAGMENT_DIR
91
+ ENVELOPE_NAME = fragments_module.ENVELOPE_NAME
92
+ INDEX_NAME = fragments_module.INDEX_NAME
93
+ WORKER_FAMILY = fragments_module.WORKER_FAMILY
94
+ canonical_json = fragments_module.canonical_json
95
+ sha256_text = fragments_module.sha256_text
96
+ load_json = fragments_module.load_json
97
+ operation_key = fragments_module.operation_key
98
+ spec_operations = fragments_module.spec_operations
99
+
100
+
101
+ #: WP-I6's internal worker seams. The gateway calls them over loopback behind
102
+ #: the seam gate; no client ever does, so they are deliberately absent from the
103
+ #: committed public contract and must not be filed into the ``worker_keep``
104
+ #: fragment. They are exempt from the subset check — and only they are.
105
+ INTERNAL_WORKER_PATHS = frozenset(
106
+ {
107
+ # ``/worker/chat/record-turn`` was the third state seam. WP-W3a moved
108
+ # the history chain into lattice-chat and WP-P1 deleted the handler.
109
+ "/worker/sysinfo",
110
+ "/worker/llm/stream",
111
+ # Wave 2.5 §W2 — the pure-compute seams. Same reasoning: the gateway
112
+ # calls them over loopback behind the seam gate, and the shapes they
113
+ # exchange (vectors, document bytes, parsed text) are an internal
114
+ # protocol between lattice-host and its own worker, not client surface.
115
+ "/worker/embed",
116
+ "/worker/parse",
117
+ "/worker/render/docx",
118
+ "/worker/render/xlsx",
119
+ "/worker/render/pptx",
120
+ "/worker/render/pdf",
121
+ "/worker/asr",
122
+ "/worker/multimodal/describe",
123
+ "/worker/extract",
124
+ }
125
+ )
126
+
127
+ #: FastAPI's own documentation endpoints. They are normally ``include_in_schema
128
+ #: = False`` and never reach ``paths`` (the committed contract has none of
129
+ #: them), but a worker app that turns docs on should not fail this check either
130
+ #: — they describe the schema, they are not product surface.
131
+ DOC_PATHS = frozenset({"/openapi.json", "/docs", "/docs/oauth2-redirect", "/redoc"})
132
+
133
+ #: Everything a worker spec may serve without a committed contract entry.
134
+ EXEMPT_WORKER_PATHS = INTERNAL_WORKER_PATHS | DOC_PATHS
135
+
136
+
137
+ class CompositionError(Exception):
138
+ """A fragment set that cannot be reassembled into one coherent spec."""
139
+
140
+
141
+ def read_fragment_set(
142
+ directory: Path,
143
+ ) -> Tuple[Dict[str, Any], Dict[str, Dict[str, Any]], List[str]]:
144
+ """Load the envelope and every fragment ``_index.json`` lists.
145
+
146
+ Returns ``(envelope, fragments_by_family, problems)``. Recorded sha256 values
147
+ are checked here: a hand-edited fragment is reported as stale rather than
148
+ silently composed, because the index is what the Rust tests and CI read.
149
+ """
150
+ index_path = directory / INDEX_NAME
151
+ if not index_path.is_file():
152
+ raise CompositionError(
153
+ f"{index_path} is missing — run scripts/gen_openapi_fragments.py"
154
+ )
155
+ index = load_json(index_path)
156
+ problems: List[str] = []
157
+
158
+ envelope_path = directory / index["envelope"]["file"]
159
+ if not envelope_path.is_file():
160
+ raise CompositionError(f"{envelope_path} is missing — regenerate the fragments")
161
+ envelope_text = envelope_path.read_text(encoding="utf-8")
162
+ if sha256_text(envelope_text) != index["envelope"]["sha256"]:
163
+ problems.append(f"{envelope_path.name}: sha256 does not match {INDEX_NAME}")
164
+ envelope = load_json(envelope_path)
165
+
166
+ fragments: Dict[str, Dict[str, Any]] = {}
167
+ for entry in index["fragments"]:
168
+ path = directory / entry["file"]
169
+ if not path.is_file():
170
+ raise CompositionError(f"{path} is missing — regenerate the fragments")
171
+ text = path.read_text(encoding="utf-8")
172
+ if sha256_text(text) != entry["sha256"]:
173
+ problems.append(f"{entry['file']}: sha256 does not match {INDEX_NAME}")
174
+ fragments[entry["family"]] = load_json(path)
175
+ return envelope, fragments, problems
176
+
177
+
178
+ def merge_fragments(
179
+ envelope: Mapping[str, Any],
180
+ fragments: Mapping[str, Dict[str, Any]],
181
+ ) -> Dict[str, Any]:
182
+ """Reassemble one spec. Raises on a collision rather than picking a winner."""
183
+ spec: Dict[str, Any] = copy.deepcopy(dict(envelope["spec"]))
184
+ paths: Dict[str, Any] = {}
185
+ owner: Dict[str, str] = {}
186
+ schemas: Dict[str, Any] = dict(spec["components"]["schemas"])
187
+
188
+ for family in sorted(fragments):
189
+ fragment = fragments[family]
190
+ for path, item in fragment["paths"].items():
191
+ for method, operation in item.items():
192
+ key = operation_key(method, path)
193
+ if key in owner:
194
+ raise CompositionError(
195
+ f"{key} is claimed by both {owner[key]} and {family} — "
196
+ "one operation, one family"
197
+ )
198
+ owner[key] = family
199
+ paths.setdefault(path, {})[method] = operation
200
+ for name, schema in fragment["components"]["schemas"].items():
201
+ existing = schemas.get(name)
202
+ if existing is not None and canonical_json(existing) != canonical_json(schema):
203
+ raise CompositionError(
204
+ f"schema {name} differs between fragments (seen again in {family}) — "
205
+ "shared schemas must be byte-identical copies"
206
+ )
207
+ schemas[name] = schema
208
+
209
+ spec["paths"] = paths
210
+ spec["components"]["schemas"] = schemas
211
+ return spec
212
+
213
+
214
+ def diff_report(composed_text: str, committed_text: str, limit: int = 40) -> List[str]:
215
+ """A readable account of how the composition misses the committed bytes."""
216
+ composed = load_json_text(composed_text)
217
+ committed = load_json_text(committed_text)
218
+ lines: List[str] = []
219
+
220
+ composed_ops = {key for key, _, _ in spec_operations(composed)}
221
+ committed_ops = {key for key, _, _ in spec_operations(committed)}
222
+ for label, missing in (
223
+ ("missing from the composition", sorted(committed_ops - composed_ops)),
224
+ ("present only in the composition", sorted(composed_ops - committed_ops)),
225
+ ):
226
+ if missing:
227
+ lines.append(f"{len(missing)} operation(s) {label}:")
228
+ lines.extend(f" {key}" for key in missing[:limit])
229
+ if len(missing) > limit:
230
+ lines.append(f" … {len(missing) - limit} more")
231
+
232
+ composed_schemas = set(composed["components"]["schemas"])
233
+ committed_schemas = set(committed["components"]["schemas"])
234
+ for label, names in (
235
+ ("missing from the composition", sorted(committed_schemas - composed_schemas)),
236
+ ("present only in the composition", sorted(composed_schemas - committed_schemas)),
237
+ ):
238
+ if names:
239
+ lines.append(f"{len(names)} schema(s) {label}: {', '.join(names[:limit])}")
240
+
241
+ delta = list(
242
+ difflib.unified_diff(
243
+ committed_text.splitlines(),
244
+ composed_text.splitlines(),
245
+ fromfile="frontend/openapi.json (committed)",
246
+ tofile="composed from rust/fixtures/openapi/",
247
+ lineterm="",
248
+ n=1,
249
+ )
250
+ )
251
+ if delta:
252
+ lines.append(f"first {min(limit, len(delta))} diff line(s) of {len(delta)}:")
253
+ lines.extend(f" {line}" for line in delta[:limit])
254
+ return lines
255
+
256
+
257
+ def load_json_text(text: str) -> Dict[str, Any]:
258
+ parsed: Dict[str, Any] = json.loads(text)
259
+ return parsed
260
+
261
+
262
+ def worker_spec_problems(
263
+ worker_spec: Mapping[str, Any],
264
+ worker_fragment: Mapping[str, Any],
265
+ envelope: Mapping[str, Any],
266
+ ) -> List[str]:
267
+ """Check the worker app against the ``worker_keep`` fragment.
268
+
269
+ Two things are asserted. First the subset: every operation the worker app
270
+ still serves must have a committed contract in the fragment, or the gateway
271
+ would be proxying a route nothing describes. Second the version: the
272
+ fragments embed ``info.version``, so a bump that regenerated the worker spec
273
+ without regenerating the fragments is drift — the same trap that reddens the
274
+ drift gate when ``npm run frontend:openapi`` runs before the bump.
275
+
276
+ ``EXEMPT_WORKER_PATHS`` is the one hole, and it is a named list rather than
277
+ a pattern: WP-I6's internal seams are loopback-only and seam-gated, so they
278
+ are product surface for the gateway alone and never enter the committed
279
+ public contract. Anything else the worker grows still fails here.
280
+ """
281
+ problems: List[str] = []
282
+ kept = {
283
+ operation_key(method, path)
284
+ for path, item in worker_fragment["paths"].items()
285
+ for method in item
286
+ }
287
+ served = {
288
+ key for key, path, _ in spec_operations(worker_spec) if path not in EXEMPT_WORKER_PATHS
289
+ }
290
+ extra = sorted(served - kept)
291
+ if extra:
292
+ problems.append(
293
+ f"{len(extra)} worker operation(s) are not in the {WORKER_FAMILY} fragment — "
294
+ "the gateway would have to proxy a route with no committed contract:"
295
+ )
296
+ problems.extend(f" {key}" for key in extra)
297
+
298
+ worker_version = worker_spec.get("info", {}).get("version")
299
+ fragment_version = envelope["spec"]["info"]["version"]
300
+ if worker_version != fragment_version:
301
+ problems.append(
302
+ f"worker spec is version {worker_version!r} but the fragments carry "
303
+ f"{fragment_version!r} — regenerate the fragments after the bump"
304
+ )
305
+ return problems
306
+
307
+
308
+ def compose(
309
+ directory: Path = FRAGMENT_DIR,
310
+ spec_path: Path = SPEC_PATH,
311
+ worker_spec_path: Path | None = None,
312
+ ) -> Tuple[str, List[str]]:
313
+ """Compose, verify, and return ``(composed_text, problems)``."""
314
+ envelope, fragments, problems = read_fragment_set(directory)
315
+ composed_text = canonical_json(merge_fragments(envelope, fragments))
316
+ committed_text = spec_path.read_text(encoding="utf-8")
317
+
318
+ if composed_text != committed_text:
319
+ problems.append(
320
+ f"composition does not reproduce {spec_path.name} byte for byte "
321
+ f"({len(composed_text)} vs {len(committed_text)} bytes)"
322
+ )
323
+ problems.extend(diff_report(composed_text, committed_text))
324
+
325
+ if worker_spec_path is not None:
326
+ worker_spec = load_json(worker_spec_path)
327
+ problems.extend(worker_spec_problems(worker_spec, fragments[WORKER_FAMILY], envelope))
328
+ return composed_text, problems
329
+
330
+
331
+ def main(argv: List[str] | None = None) -> int:
332
+ parser = argparse.ArgumentParser(description="Compose the OpenAPI contract from fragments.")
333
+ parser.add_argument("--fragments", type=Path, default=FRAGMENT_DIR, help="fragment directory")
334
+ parser.add_argument("--spec", type=Path, default=SPEC_PATH, help="committed contract")
335
+ parser.add_argument(
336
+ "--worker-spec",
337
+ type=Path,
338
+ default=None,
339
+ help="OpenAPI schema of the Python worker app; asserted ⊆ the worker_keep fragment",
340
+ )
341
+ parser.add_argument(
342
+ "--output",
343
+ type=Path,
344
+ default=None,
345
+ help="write the composed spec here (the P1 cutover writes frontend/openapi.json)",
346
+ )
347
+ args = parser.parse_args(argv)
348
+
349
+ try:
350
+ composed_text, problems = compose(args.fragments, args.spec, args.worker_spec)
351
+ except CompositionError as error:
352
+ print(f"openapi composition failed: {error}")
353
+ return 1
354
+
355
+ if problems:
356
+ print("openapi composition failed:")
357
+ for problem in problems:
358
+ print(f" {problem}")
359
+ print(" fix: rerun scripts/gen_openapi_fragments.py and commit the fragments")
360
+ return 1
361
+
362
+ if args.output is not None:
363
+ args.output.parent.mkdir(parents=True, exist_ok=True)
364
+ args.output.write_text(composed_text, encoding="utf-8")
365
+ print(f"wrote {args.output}")
366
+
367
+ spec = load_json_text(composed_text)
368
+ checked = f" (+worker spec ⊆ {WORKER_FAMILY})" if args.worker_spec else ""
369
+ print(
370
+ f"composed {len(spec['paths'])} paths / {len(spec_operations(spec))} operations "
371
+ f"from {args.fragments} — byte-identical to {args.spec}{checked}"
372
+ )
373
+ return 0
374
+
375
+
376
+ if __name__ == "__main__":
377
+ sys.exit(main())
@@ -1,5 +1,22 @@
1
1
  #!/usr/bin/env python3
2
- """Export the FastAPI OpenAPI schema used by the desktop frontend client."""
2
+ """Export the AI-Worker's OpenAPI schema — one input to the composed contract.
3
+
4
+ Until v11.6.0 this *was* the contract: it built the 464-route product app and
5
+ wrote ``frontend/openapi.json``. WP-P1 deleted that app. The committed contract
6
+ is unchanged (421 paths / 463 operations — clients still call all of them) but
7
+ its source is now ``scripts/compose_openapi.py``, which reassembles the
8
+ per-crate fragments in ``rust/fixtures/openapi/``.
9
+
10
+ What this script exports is the worker half, to a **scratch** path, so the
11
+ composer can check it: every operation the worker serves must already be in
12
+ ``worker_keep.json`` (the gateway will not proxy a route with no contract), and
13
+ its ``info.version`` must agree with the fragments. It is an input, not the
14
+ output — see WP-I5 §"P1 cutover".
15
+
16
+ node scripts/run_python.mjs scripts/export_openapi.py <scratch>/worker.json
17
+ node scripts/run_python.mjs scripts/compose_openapi.py \
18
+ --worker-spec <scratch>/worker.json --output frontend/openapi.json
19
+ """
3
20
 
4
21
  from __future__ import annotations
5
22
 
@@ -72,15 +89,26 @@ def isolated_runtime_environment(root: Path) -> Iterator[Mapping[str, str]]:
72
89
 
73
90
 
74
91
  def main() -> int:
75
- target = Path(sys.argv[1] if len(sys.argv) > 1 else "frontend/openapi.json")
92
+ # No default of ``frontend/openapi.json``: that file is the composer's
93
+ # output now, and silently overwriting it with the 28-route worker spec is
94
+ # exactly the mistake this argument exists to prevent.
95
+ if len(sys.argv) < 2:
96
+ print(
97
+ "usage: export_openapi.py <output.json>\n"
98
+ " the committed contract is written by scripts/compose_openapi.py;\n"
99
+ " this exports the worker half to a scratch path it consumes.",
100
+ file=sys.stderr,
101
+ )
102
+ return 2
103
+ target = Path(sys.argv[1])
76
104
  target.parent.mkdir(parents=True, exist_ok=True)
77
105
  with tempfile.TemporaryDirectory(prefix="ltcai-openapi-") as temp_dir:
78
106
  with isolated_runtime_environment(Path(temp_dir)):
79
107
  # Import only after isolation is active in case a future dependency
80
108
  # starts reading environment or user paths during module import.
81
- from latticeai.app_factory import create_app
109
+ from latticeai.worker_app import create_worker_app
82
110
 
83
- app = create_app()
111
+ app = create_worker_app()
84
112
  schema = app.openapi()
85
113
  target.write_text(json.dumps(schema, sort_keys=True, indent=2) + "\n", encoding="utf-8")
86
114
  print(f"wrote {target} with {len(schema.get('paths', {}))} paths")