ltcai 11.5.2 → 11.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (439) hide show
  1. package/README.md +91 -148
  2. package/bin/ltcai.js +234 -24
  3. package/docs/CHANGELOG.md +75 -0
  4. package/docs/COMMUNITY_AND_PLUGINS.md +1 -1
  5. package/docs/DEVELOPMENT.md +8 -6
  6. package/docs/ENTERPRISE.md +2 -1
  7. package/docs/MULTI_AGENT_RUNTIME.md +12 -5
  8. package/docs/ONBOARDING.md +1 -1
  9. package/docs/OPERATIONS.md +6 -3
  10. package/docs/REALTIME_COLLABORATION.md +1 -1
  11. package/docs/TRUST_MODEL.md +1 -1
  12. package/docs/WHY_LATTICE.md +1 -1
  13. package/docs/WORKFLOW_DESIGNER.md +3 -2
  14. package/docs/kg-schema.md +1 -1
  15. package/docs/v11.6.0_ONE_DOOR_PLAN.md +170 -0
  16. package/lattice_brain/__init__.py +42 -79
  17. package/lattice_brain/graph/__init__.py +9 -24
  18. package/lattice_brain/graph/_kg_common/__init__.py +13 -24
  19. package/lattice_brain/ingestion/__init__.py +19 -58
  20. package/lattice_brain/ingestion/pipeline.py +20 -398
  21. package/lattice_brain/multimodal/__init__.py +7 -18
  22. package/lattice_brain/multimodal/images.py +6 -264
  23. package/lattice_brain/multimodal/video.py +8 -247
  24. package/lattice_brain/runtime/__init__.py +8 -79
  25. package/lattice_brain/runtime/hooks.py +22 -584
  26. package/latticeai/__init__.py +1 -1
  27. package/latticeai/api/agent_worker_seam.py +7 -80
  28. package/latticeai/api/health.py +6 -20
  29. package/latticeai/api/local_files.py +16 -613
  30. package/latticeai/api/models.py +12 -87
  31. package/latticeai/api/search.py +17 -255
  32. package/latticeai/api/tools.py +69 -750
  33. package/latticeai/api/voice_capture.py +8 -70
  34. package/latticeai/api/worker_compute.py +842 -0
  35. package/latticeai/api/worker_seams.py +216 -0
  36. package/latticeai/app_factory.py +23 -232
  37. package/latticeai/cli/entrypoint.py +36 -249
  38. package/latticeai/core/agent_permission.py +16 -91
  39. package/latticeai/core/messages.py +89 -531
  40. package/latticeai/runtime/access_runtime.py +0 -14
  41. package/latticeai/runtime/bootstrap.py +10 -21
  42. package/latticeai/runtime/brain_runtime.py +35 -43
  43. package/latticeai/runtime/build_phases/__init__.py +21 -37
  44. package/latticeai/runtime/build_phases/features.py +99 -389
  45. package/latticeai/runtime/build_phases/foundation.py +82 -392
  46. package/latticeai/runtime/build_phases/web.py +91 -408
  47. package/latticeai/runtime/build_phases/worker_profile.py +244 -0
  48. package/latticeai/runtime/lifespan_runtime.py +7 -16
  49. package/latticeai/runtime/platform_services_runtime.py +1 -15
  50. package/latticeai/runtime/runtime_context.py +13 -130
  51. package/latticeai/runtime/security_runtime.py +10 -103
  52. package/latticeai/services/architecture_readiness.py +82 -43
  53. package/latticeai/services/model_runtime/__init__.py +2 -11
  54. package/latticeai/services/model_runtime/service.py +4 -28
  55. package/latticeai/services/p_reinforce.py +10 -261
  56. package/latticeai/services/product_readiness.py +31 -38
  57. package/latticeai/services/search_service.py +37 -795
  58. package/latticeai/services/tool_dispatch.py +30 -386
  59. package/latticeai/services/voice_capture.py +13 -107
  60. package/latticeai/tools/__init__.py +22 -49
  61. package/latticeai/tools/commands.py +0 -163
  62. package/latticeai/tools/computer.py +0 -39
  63. package/latticeai/tools/documents.py +1 -134
  64. package/latticeai/tools/filesystem.py +1 -247
  65. package/latticeai/tools/knowledge.py +1 -52
  66. package/latticeai/tools/local_files.py +0 -20
  67. package/latticeai/worker_app.py +75 -0
  68. package/package.json +2 -3
  69. package/requirements.txt +0 -5
  70. package/scripts/agent_eval.py +16 -28
  71. package/scripts/brain_quality_eval.py +20 -183
  72. package/scripts/bump_version.py +5 -5
  73. package/scripts/check_current_release_docs.mjs +7 -4
  74. package/scripts/check_openapi_drift.mjs +10 -0
  75. package/scripts/check_server_i18n.mjs +2 -18
  76. package/scripts/compose_openapi.py +377 -0
  77. package/scripts/export_openapi.py +32 -4
  78. package/scripts/gen_messages_catalog_fixture.py +302 -0
  79. package/scripts/gen_openapi_fragments.py +365 -0
  80. package/scripts/gen_redact_fixture.py +196 -0
  81. package/scripts/gen_worker_allowlist_fixture.py +115 -0
  82. package/scripts/generate_agent_parity_fixtures.py +33 -14
  83. package/scripts/openapi_route_families.json +2161 -0
  84. package/scripts/release_screen_claims.json +21 -0
  85. package/scripts/run_integration_tests.mjs +134 -29
  86. package/scripts/run_sidecar_e2e.mjs +104 -11
  87. package/scripts/wheel_smoke.py +32 -22
  88. package/src-tauri/Cargo.lock +375 -9
  89. package/src-tauri/Cargo.toml +1 -1
  90. package/src-tauri/src/backend.rs +151 -45
  91. package/src-tauri/src/main.rs +18 -18
  92. package/src-tauri/src/topology.rs +58 -88
  93. package/src-tauri/tauri.conf.json +1 -2
  94. package/static/app/asset-manifest.json +41 -41
  95. package/static/app/assets/{Act-DcQizkl1.js → Act-CToZnOHz.js} +1 -1
  96. package/static/app/assets/{AdminConsole-cf4npybT.js → AdminConsole-D5kbrBu8.js} +1 -1
  97. package/static/app/assets/{Brain-3VCSHFcn.js → Brain-C6zCdv4S.js} +1 -1
  98. package/static/app/assets/{BrainHome-Qm8eaztx.js → BrainHome-D4n3EDUt.js} +1 -1
  99. package/static/app/assets/{BrainSignals-DS9BtKOW.js → BrainSignals-CzeZ-vQ9.js} +1 -1
  100. package/static/app/assets/{Capture-DiQ219jW.js → Capture-DPcyTczq.js} +1 -1
  101. package/static/app/assets/{Chronicle-BGvuAchH.js → Chronicle-D3Nx-oWS.js} +1 -1
  102. package/static/app/assets/{CommandPalette-Bqhm0Urn.js → CommandPalette-DPPvpp28.js} +1 -1
  103. package/static/app/assets/{Library-BV6NnF0a.js → Library-Co2qTu3I.js} +1 -1
  104. package/static/app/assets/{LivingBrain-GzenJchP.js → LivingBrain-Bsmzwy8_.js} +1 -1
  105. package/static/app/assets/{ProductFlow-DEP6-vML.js → ProductFlow-CBLdmesT.js} +1 -1
  106. package/static/app/assets/{ReviewCard-CNZ7XjWG.js → ReviewCard-CfR6DLs7.js} +1 -1
  107. package/static/app/assets/{System-CieofHQa.js → System-Ce-P2YL6.js} +1 -1
  108. package/static/app/assets/arrow-left-DD5jFGHV.js +1 -0
  109. package/static/app/assets/{bot-B_K1Tdmw.js → bot-C5-XIww2.js} +1 -1
  110. package/static/app/assets/{brain-DWyaV1L1.js → brain-CFRJUvdX.js} +1 -1
  111. package/static/app/assets/{button-aTn4s84A.js → button-DI1KcQP-.js} +1 -1
  112. package/static/app/assets/circle-check-CY20XMCh.js +1 -0
  113. package/static/app/assets/{circle-pause-xKgeGXkT.js → circle-pause-C8HHyood.js} +1 -1
  114. package/static/app/assets/{circle-play-DkT6tYPX.js → circle-play-Cufc235E.js} +1 -1
  115. package/static/app/assets/{cpu-85xYObUC.js → cpu-DW2_JVsv.js} +1 -1
  116. package/static/app/assets/{download-B5Fm7YXo.js → download-gbdKTBb8.js} +1 -1
  117. package/static/app/assets/{folder-open-kk2Xa52u.js → folder-open-C9ydyX33.js} +1 -1
  118. package/static/app/assets/{hard-drive-DkA3zBW_.js → hard-drive-BxNMzBM3.js} +1 -1
  119. package/static/app/assets/index-BovSWRmQ.css +2 -0
  120. package/static/app/assets/{index-BMPdTmlY.js → index-q9XyDY7A.js} +3 -3
  121. package/static/app/assets/{input-B0nRf2jO.js → input-GLD94Y_K.js} +1 -1
  122. package/static/app/assets/{link-2-Dwb4gnTc.js → link-2-B4Y0gz1S.js} +1 -1
  123. package/static/app/assets/{permissionCopy-CQDUBrOZ.js → permissionCopy-CxrV0fvU.js} +1 -1
  124. package/static/app/assets/{primitives-SNp0LRJz.js → primitives-2ANdAsu2.js} +1 -1
  125. package/static/app/assets/search-DTxtjQ07.js +1 -0
  126. package/static/app/assets/{share-2-BsrxFglO.js → share-2-DxArfiuI.js} +1 -1
  127. package/static/app/assets/{shield-alert-5BStfp2_.js → shield-alert-CgftuFFs.js} +1 -1
  128. package/static/app/assets/{textarea-Cg8IUA-k.js → textarea-BHVBAoCr.js} +1 -1
  129. package/static/app/assets/{useFocusTrap-CYKvE46M.js → useFocusTrap-Dzq2j_OG.js} +1 -1
  130. package/static/app/assets/{useMutation-CSn9t1op.js → useMutation-BcqfMLNq.js} +1 -1
  131. package/static/app/assets/{useQuery-CY2OI2uy.js → useQuery-DBzLKCOv.js} +1 -1
  132. package/static/app/assets/{utils-Ddol2RWD.js → utils-B-Aah1bO.js} +1 -1
  133. package/static/app/assets/{workspace-BqDwOz_p.js → workspace-B6fFhoko.js} +1 -1
  134. package/static/app/index.html +4 -4
  135. package/static/sw.js +1 -1
  136. package/lattice_brain/archive.py +0 -522
  137. package/lattice_brain/context.py +0 -325
  138. package/lattice_brain/conversations.py +0 -382
  139. package/lattice_brain/core.py +0 -82
  140. package/lattice_brain/graph/_kg_contract.py +0 -249
  141. package/lattice_brain/graph/curator.py +0 -676
  142. package/lattice_brain/graph/discovery.py +0 -595
  143. package/lattice_brain/graph/discovery_index/__init__.py +0 -35
  144. package/lattice_brain/graph/discovery_index/cleanup.py +0 -182
  145. package/lattice_brain/graph/discovery_index/extract.py +0 -137
  146. package/lattice_brain/graph/discovery_index/scan.py +0 -411
  147. package/lattice_brain/graph/discovery_index/upsert.py +0 -495
  148. package/lattice_brain/graph/documents.py +0 -380
  149. package/lattice_brain/graph/fusion.py +0 -395
  150. package/lattice_brain/graph/identity.py +0 -175
  151. package/lattice_brain/graph/image_vectors.py +0 -230
  152. package/lattice_brain/graph/ingest.py +0 -829
  153. package/lattice_brain/graph/network.py +0 -205
  154. package/lattice_brain/graph/proactive.py +0 -724
  155. package/lattice_brain/graph/projection/__init__.py +0 -42
  156. package/lattice_brain/graph/projection/curation.py +0 -500
  157. package/lattice_brain/graph/projection/v2_schema.py +0 -518
  158. package/lattice_brain/graph/provenance.py +0 -524
  159. package/lattice_brain/graph/rerank.py +0 -163
  160. package/lattice_brain/graph/retrieval/__init__.py +0 -54
  161. package/lattice_brain/graph/retrieval/context.py +0 -197
  162. package/lattice_brain/graph/retrieval/graph_view.py +0 -319
  163. package/lattice_brain/graph/retrieval/hybrid.py +0 -488
  164. package/lattice_brain/graph/retrieval/maintenance.py +0 -121
  165. package/lattice_brain/graph/retrieval/signals.py +0 -95
  166. package/lattice_brain/graph/retrieval_docgen.py +0 -253
  167. package/lattice_brain/graph/retrieval_policy.py +0 -180
  168. package/lattice_brain/graph/retrieval_reads.py +0 -769
  169. package/lattice_brain/graph/retrieval_vector/__init__.py +0 -42
  170. package/lattice_brain/graph/retrieval_vector/fingerprint.py +0 -97
  171. package/lattice_brain/graph/retrieval_vector/indexing.py +0 -347
  172. package/lattice_brain/graph/retrieval_vector/search.py +0 -560
  173. package/lattice_brain/graph/retrieval_vector/status.py +0 -374
  174. package/lattice_brain/graph/schema.py +0 -792
  175. package/lattice_brain/graph/store.py +0 -268
  176. package/lattice_brain/graph/vector_index/__init__.py +0 -85
  177. package/lattice_brain/graph/vector_index/base.py +0 -167
  178. package/lattice_brain/graph/vector_index/brute_force.py +0 -110
  179. package/lattice_brain/graph/vector_index/hnsw.py +0 -290
  180. package/lattice_brain/graph/vector_index/jobs.py +0 -287
  181. package/lattice_brain/graph/vector_index/quantized.py +0 -148
  182. package/lattice_brain/graph/vector_index/selector.py +0 -161
  183. package/lattice_brain/graph/write_master.py +0 -308
  184. package/lattice_brain/ingestion/_contract.py +0 -90
  185. package/lattice_brain/ingestion/folder_scan.py +0 -57
  186. package/lattice_brain/ingestion/folders.py +0 -258
  187. package/lattice_brain/ingestion/jobs_api.py +0 -107
  188. package/lattice_brain/ingestion/routing.py +0 -295
  189. package/lattice_brain/ingestion_jobs.py +0 -380
  190. package/lattice_brain/memory.py +0 -75
  191. package/lattice_brain/portability/__init__.py +0 -90
  192. package/lattice_brain/portability/_contract.py +0 -42
  193. package/lattice_brain/portability/backups.py +0 -338
  194. package/lattice_brain/portability/bundles.py +0 -136
  195. package/lattice_brain/portability/constants.py +0 -93
  196. package/lattice_brain/portability/fsops.py +0 -138
  197. package/lattice_brain/portability/service.py +0 -41
  198. package/lattice_brain/portability/sharing.py +0 -710
  199. package/lattice_brain/quality.py +0 -543
  200. package/lattice_brain/retrieval_benchmark_fixtures.py +0 -92
  201. package/lattice_brain/runtime/agent_runtime.py +0 -859
  202. package/lattice_brain/runtime/contracts.py +0 -460
  203. package/lattice_brain/runtime/multi_agent.py +0 -942
  204. package/lattice_brain/runtime/statuses.py +0 -10
  205. package/lattice_brain/sealed_box.py +0 -240
  206. package/lattice_brain/self_model.py +0 -675
  207. package/lattice_brain/sensitivity.py +0 -94
  208. package/lattice_brain/storage/__init__.py +0 -22
  209. package/lattice_brain/storage/base.py +0 -100
  210. package/lattice_brain/storage/docker.py +0 -105
  211. package/lattice_brain/storage/factory.py +0 -31
  212. package/lattice_brain/storage/migration.py +0 -191
  213. package/lattice_brain/storage/postgres.py +0 -123
  214. package/lattice_brain/storage/sqlite.py +0 -143
  215. package/lattice_brain/synthesis.py +0 -824
  216. package/lattice_brain/workflow.py +0 -497
  217. package/latticeai/api/admin.py +0 -471
  218. package/latticeai/api/agent_registry.py +0 -105
  219. package/latticeai/api/agents.py +0 -228
  220. package/latticeai/api/auth.py +0 -383
  221. package/latticeai/api/automation_intelligence.py +0 -401
  222. package/latticeai/api/brain_intelligence.py +0 -199
  223. package/latticeai/api/browser.py +0 -493
  224. package/latticeai/api/change_proposals.py +0 -89
  225. package/latticeai/api/chat.py +0 -572
  226. package/latticeai/api/chat_agent_http.py +0 -892
  227. package/latticeai/api/chat_contracts.py +0 -73
  228. package/latticeai/api/chat_documents.py +0 -276
  229. package/latticeai/api/chat_helpers.py +0 -460
  230. package/latticeai/api/chat_history.py +0 -101
  231. package/latticeai/api/chat_hybrid.py +0 -113
  232. package/latticeai/api/chat_intents.py +0 -646
  233. package/latticeai/api/chat_stream.py +0 -216
  234. package/latticeai/api/chronicle.py +0 -63
  235. package/latticeai/api/command_center.py +0 -51
  236. package/latticeai/api/computer_use.py +0 -474
  237. package/latticeai/api/evidence_actions.py +0 -48
  238. package/latticeai/api/features.py +0 -70
  239. package/latticeai/api/funnel_metrics.py +0 -31
  240. package/latticeai/api/garden.py +0 -34
  241. package/latticeai/api/hooks.py +0 -165
  242. package/latticeai/api/index_jobs.py +0 -145
  243. package/latticeai/api/invitations.py +0 -100
  244. package/latticeai/api/knowledge_graph.py +0 -536
  245. package/latticeai/api/marketplace.py +0 -105
  246. package/latticeai/api/mcp.py +0 -482
  247. package/latticeai/api/memory.py +0 -270
  248. package/latticeai/api/network.py +0 -81
  249. package/latticeai/api/network_boundary.py +0 -225
  250. package/latticeai/api/permission_mode.py +0 -61
  251. package/latticeai/api/permissions.py +0 -436
  252. package/latticeai/api/plugins.py +0 -126
  253. package/latticeai/api/portability.py +0 -391
  254. package/latticeai/api/project_sessions.py +0 -114
  255. package/latticeai/api/realtime.py +0 -118
  256. package/latticeai/api/review_queue.py +0 -364
  257. package/latticeai/api/security_dashboard.py +0 -604
  258. package/latticeai/api/setup.py +0 -319
  259. package/latticeai/api/static_routes.py +0 -354
  260. package/latticeai/api/ui_redirects.py +0 -26
  261. package/latticeai/api/workflow_designer.py +0 -394
  262. package/latticeai/api/workspace.py +0 -856
  263. package/latticeai/api/workspace_scope.py +0 -125
  264. package/latticeai/core/agent/__init__.py +0 -93
  265. package/latticeai/core/agent/_contract.py +0 -79
  266. package/latticeai/core/agent/context.py +0 -57
  267. package/latticeai/core/agent/deps.py +0 -125
  268. package/latticeai/core/agent/execution.py +0 -622
  269. package/latticeai/core/agent/planning.py +0 -145
  270. package/latticeai/core/agent/recovery.py +0 -157
  271. package/latticeai/core/agent/runtime.py +0 -210
  272. package/latticeai/core/agent/verification.py +0 -231
  273. package/latticeai/core/agent_eval.py +0 -739
  274. package/latticeai/core/agent_helpers.py +0 -493
  275. package/latticeai/core/agent_profiles.py +0 -110
  276. package/latticeai/core/agent_prompts.py +0 -171
  277. package/latticeai/core/agent_registry.py +0 -232
  278. package/latticeai/core/agent_state.py +0 -41
  279. package/latticeai/core/agent_trace.py +0 -104
  280. package/latticeai/core/artifact_ledger.py +0 -109
  281. package/latticeai/core/audit.py +0 -260
  282. package/latticeai/core/builtin_hooks.py +0 -105
  283. package/latticeai/core/context_builder.py +0 -394
  284. package/latticeai/core/document_generator.py +0 -103
  285. package/latticeai/core/enterprise.py +0 -154
  286. package/latticeai/core/enterprise_admin.py +0 -158
  287. package/latticeai/core/file_generation/__init__.py +0 -115
  288. package/latticeai/core/file_generation/bundles.py +0 -76
  289. package/latticeai/core/file_generation/extraction.py +0 -154
  290. package/latticeai/core/file_generation/inference.py +0 -235
  291. package/latticeai/core/file_generation/orchestration.py +0 -152
  292. package/latticeai/core/file_generation/prompting.py +0 -117
  293. package/latticeai/core/file_generation/repair.py +0 -114
  294. package/latticeai/core/file_generation/sanitize.py +0 -61
  295. package/latticeai/core/file_generation/validation.py +0 -201
  296. package/latticeai/core/invitations.py +0 -132
  297. package/latticeai/core/legacy_compatibility.py +0 -243
  298. package/latticeai/core/logging_safety.py +0 -46
  299. package/latticeai/core/marketplace.py +0 -293
  300. package/latticeai/core/mcp_catalog.py +0 -452
  301. package/latticeai/core/mcp_registry.py +0 -506
  302. package/latticeai/core/network_boundary.py +0 -168
  303. package/latticeai/core/oidc.py +0 -208
  304. package/latticeai/core/plugins.py +0 -432
  305. package/latticeai/core/product_hardening.py +0 -218
  306. package/latticeai/core/project_sessions.py +0 -337
  307. package/latticeai/core/realtime.py +0 -238
  308. package/latticeai/core/run_explain.py +0 -426
  309. package/latticeai/core/run_store.py +0 -252
  310. package/latticeai/core/timezones.py +0 -80
  311. package/latticeai/core/workspace_computer_memory.py +0 -84
  312. package/latticeai/core/workspace_graph_trace.py +0 -155
  313. package/latticeai/core/workspace_indexing.py +0 -102
  314. package/latticeai/core/workspace_memory.py +0 -77
  315. package/latticeai/core/workspace_onboarding.py +0 -104
  316. package/latticeai/core/workspace_os.py +0 -978
  317. package/latticeai/core/workspace_os_constants.py +0 -126
  318. package/latticeai/core/workspace_os_state.py +0 -180
  319. package/latticeai/core/workspace_os_utils.py +0 -103
  320. package/latticeai/core/workspace_permissions.py +0 -101
  321. package/latticeai/core/workspace_plugins.py +0 -97
  322. package/latticeai/core/workspace_relationships.py +0 -99
  323. package/latticeai/core/workspace_reorganization.py +0 -335
  324. package/latticeai/core/workspace_review_items.py +0 -112
  325. package/latticeai/core/workspace_runs.py +0 -726
  326. package/latticeai/core/workspace_skills.py +0 -109
  327. package/latticeai/core/workspace_snapshots.py +0 -198
  328. package/latticeai/core/workspace_timeline.py +0 -110
  329. package/latticeai/integrations/__init__.py +0 -0
  330. package/latticeai/integrations/telegram_bot/__init__.py +0 -123
  331. package/latticeai/integrations/telegram_bot/__main__.py +0 -17
  332. package/latticeai/integrations/telegram_bot/config.py +0 -86
  333. package/latticeai/integrations/telegram_bot/dispatch.py +0 -311
  334. package/latticeai/integrations/telegram_bot/flows.py +0 -478
  335. package/latticeai/integrations/telegram_bot/helpers.py +0 -322
  336. package/latticeai/integrations/telegram_bot/screens.py +0 -394
  337. package/latticeai/runtime/audit_runtime.py +0 -76
  338. package/latticeai/runtime/automation_runtime.py +0 -81
  339. package/latticeai/runtime/chat_wiring.py +0 -141
  340. package/latticeai/runtime/context_runtime.py +0 -66
  341. package/latticeai/runtime/feature_toggle_wiring.py +0 -157
  342. package/latticeai/runtime/history_runtime.py +0 -163
  343. package/latticeai/runtime/history_writer.py +0 -138
  344. package/latticeai/runtime/hooks_runtime.py +0 -77
  345. package/latticeai/runtime/model_wiring.py +0 -68
  346. package/latticeai/runtime/namespace_runtime.py +0 -163
  347. package/latticeai/runtime/network_boundary_wiring.py +0 -117
  348. package/latticeai/runtime/network_config_runtime.py +0 -56
  349. package/latticeai/runtime/permission_mode_wiring.py +0 -112
  350. package/latticeai/runtime/persistence_runtime.py +0 -159
  351. package/latticeai/runtime/platform_runtime_wiring.py +0 -89
  352. package/latticeai/runtime/review_wiring.py +0 -42
  353. package/latticeai/runtime/router_registration.py +0 -693
  354. package/latticeai/runtime/service_singletons.py +0 -55
  355. package/latticeai/runtime/sso_config_runtime.py +0 -128
  356. package/latticeai/runtime/user_key_runtime.py +0 -106
  357. package/latticeai/runtime/web_runtime.py +0 -92
  358. package/latticeai/server_app.py +0 -51
  359. package/latticeai/services/app_context.py +0 -130
  360. package/latticeai/services/automation_execution.py +0 -266
  361. package/latticeai/services/automation_intelligence.py +0 -614
  362. package/latticeai/services/brain_automation.py +0 -191
  363. package/latticeai/services/brain_intelligence/__init__.py +0 -58
  364. package/latticeai/services/brain_intelligence/_contract.py +0 -71
  365. package/latticeai/services/brain_intelligence/consistency.py +0 -193
  366. package/latticeai/services/brain_intelligence/constants.py +0 -47
  367. package/latticeai/services/brain_intelligence/digest.py +0 -258
  368. package/latticeai/services/brain_intelligence/health.py +0 -331
  369. package/latticeai/services/brain_intelligence/proposals.py +0 -259
  370. package/latticeai/services/brain_intelligence/sampling.py +0 -84
  371. package/latticeai/services/brain_intelligence/service.py +0 -48
  372. package/latticeai/services/change_proposals.py +0 -471
  373. package/latticeai/services/chat_service.py +0 -243
  374. package/latticeai/services/chronicle.py +0 -555
  375. package/latticeai/services/cloud_egress_audit.py +0 -85
  376. package/latticeai/services/cloud_extraction.py +0 -129
  377. package/latticeai/services/cloud_streaming.py +0 -268
  378. package/latticeai/services/cloud_token_guard.py +0 -84
  379. package/latticeai/services/command_center.py +0 -548
  380. package/latticeai/services/evidence_actions.py +0 -258
  381. package/latticeai/services/feature_toggles.py +0 -502
  382. package/latticeai/services/folder_watch.py +0 -520
  383. package/latticeai/services/funnel_metrics.py +0 -307
  384. package/latticeai/services/hybrid_chat.py +0 -316
  385. package/latticeai/services/hybrid_context.py +0 -228
  386. package/latticeai/services/hybrid_policy.py +0 -129
  387. package/latticeai/services/interop_bridges.py +0 -978
  388. package/latticeai/services/local_knowledge.py +0 -465
  389. package/latticeai/services/memory_service/__init__.py +0 -52
  390. package/latticeai/services/memory_service/_contract.py +0 -100
  391. package/latticeai/services/memory_service/brief.py +0 -431
  392. package/latticeai/services/memory_service/constants.py +0 -57
  393. package/latticeai/services/memory_service/maintenance.py +0 -138
  394. package/latticeai/services/memory_service/manager.py +0 -186
  395. package/latticeai/services/memory_service/proof.py +0 -136
  396. package/latticeai/services/memory_service/recall.py +0 -225
  397. package/latticeai/services/memory_service/service.py +0 -48
  398. package/latticeai/services/memory_service/stores.py +0 -110
  399. package/latticeai/services/mode_store.py +0 -132
  400. package/latticeai/services/model_recommendation.py +0 -224
  401. package/latticeai/services/model_runtime/cloud.py +0 -87
  402. package/latticeai/services/network_boundary_service.py +0 -117
  403. package/latticeai/services/obsidian_bridge.py +0 -609
  404. package/latticeai/services/openai_compatible_adapter.py +0 -101
  405. package/latticeai/services/permission_mode_service.py +0 -122
  406. package/latticeai/services/platform_runtime.py +0 -366
  407. package/latticeai/services/review_queue.py +0 -380
  408. package/latticeai/services/router_context.py +0 -59
  409. package/latticeai/services/run_executor.py +0 -387
  410. package/latticeai/services/self_model_service.py +0 -171
  411. package/latticeai/services/setup_detection.py +0 -147
  412. package/latticeai/services/triggers.py +0 -378
  413. package/latticeai/services/upload_service.py +0 -172
  414. package/latticeai/services/workspace_service.py +0 -165
  415. package/latticeai/setup/__init__.py +0 -25
  416. package/latticeai/setup/auto_setup.py +0 -846
  417. package/latticeai/setup/demo_corpus.py +0 -98
  418. package/latticeai/setup/wizard/__init__.py +0 -126
  419. package/latticeai/setup/wizard/catalog.py +0 -175
  420. package/latticeai/setup/wizard/detect.py +0 -302
  421. package/latticeai/setup/wizard/install.py +0 -348
  422. package/latticeai/setup/wizard/paths.py +0 -165
  423. package/latticeai/setup/wizard/plans.py +0 -74
  424. package/latticeai/setup/wizard/recommend.py +0 -320
  425. package/scripts/bench_agent_smoke.py +0 -409
  426. package/scripts/bench_models.py +0 -540
  427. package/scripts/bench_vector_index.py +0 -295
  428. package/scripts/funnel_soft_gate.py +0 -192
  429. package/scripts/generate_agent_loop_fixtures.py +0 -994
  430. package/scripts/generate_rust_parity_fixtures.py +0 -908
  431. package/scripts/migrate_brain_storage.py +0 -57
  432. package/scripts/parity_fixture_corpus_context.py +0 -162
  433. package/scripts/parity_fixture_corpus_docgen.py +0 -341
  434. package/scripts/profile_kg.py +0 -355
  435. package/server.py +0 -30
  436. package/static/app/assets/arrow-left-kfsrk0mv.js +0 -1
  437. package/static/app/assets/circle-check-qqLug9nU.js +0 -1
  438. package/static/app/assets/index-DxmOfNRi.css +0 -2
  439. package/static/app/assets/search-BcHqkjoy.js +0 -1
@@ -1,145 +0,0 @@
1
- """PLAN and APPROVAL — deciding what to do, and whether it may be done.
2
-
3
- The planner turns the request into a structured plan; the approval gate then
4
- answers one question about that plan under the run's autonomy dial: does a step
5
- in it need a human to say yes? :meth:`approval_requirements` is the read-only
6
- half of exactly the predicate :meth:`approve` enforces, so the HTTP layer can
7
- pause a run instead of failing it closed without ever weakening the gate.
8
- """
9
-
10
- from __future__ import annotations
11
-
12
- from typing import Any, Dict, Optional
13
-
14
- from latticeai.core.agent_helpers import extract_action_details, normalize_plan
15
- from latticeai.core.agent_permission import non_auto_plan_steps
16
- from latticeai.core.agent_state import AgentState
17
- from latticeai.core.permission_mode import plan_requires_approval
18
-
19
- from ._contract import AgentCore as _Core
20
- from .context import AgentRunContext
21
-
22
-
23
- class _PlanningMixin(_Core):
24
- """The PLAN and APPROVAL phases of :class:`SingleAgentRuntime`."""
25
-
26
- # ── PLAN ─────────────────────────────────────────────────────────
27
- async def plan(
28
- self, ctx: AgentRunContext, req: Any, lang_hint: str, current_user: str,
29
- model_id: Optional[str] = None,
30
- ) -> None:
31
- """PLAN: Planner role produces a structured plan JSON."""
32
- d = self.deps
33
- project_block = self._project_block(ctx)
34
- context = (
35
- f"{d.planner_prompt}\n\n"
36
- f"[LANGUAGE HINT: {lang_hint}]\n"
37
- f"Workspace root: {d.agent_root}{project_block}\n\n"
38
- f"User request: {req.message}"
39
- )
40
- raw = await d.generate_as(
41
- model_id,
42
- message="Produce a JSON execution plan for this request.",
43
- context=context, max_tokens=self.phase_budgets.plan_tokens, temperature=0.1,
44
- )
45
- ctx.trace.llm_call("plan", model=model_id)
46
- try:
47
- plan, plan_repairs = extract_action_details(str(raw))
48
- ctx.trace.repair("plan", repairs=plan_repairs)
49
- except ValueError as exc:
50
- ctx.trace.parse_error("plan", error=str(exc), recovered=True)
51
- plan = {
52
- "action": "plan", "state": "PLAN",
53
- "goal": req.message, "steps": [],
54
- "requires_approval": False, "rollback_strategy": "none", "estimated_steps": 1,
55
- }
56
- plan, plan_fixes = normalize_plan(plan, req.message)
57
- if plan_fixes:
58
- ctx.trace.repair("plan", repairs=plan_fixes)
59
- ctx.plan = plan
60
- ctx.transcript.append({
61
- "state": AgentState.PLANNING.value,
62
- "goal": plan.get("goal", req.message),
63
- "steps": plan.get("steps", []),
64
- "requires_approval": plan.get("requires_approval", False),
65
- "rollback_strategy": plan.get("rollback_strategy", "none"),
66
- "estimated_steps": plan.get("estimated_steps", 1),
67
- **({"plan_fixes": plan_fixes} if plan_fixes else {}),
68
- })
69
- self._emit_step(
70
- ctx, "plan", "planned",
71
- goal=str(plan.get("goal") or "")[:200],
72
- steps=len(plan.get("steps") or []),
73
- requires_approval=bool(plan.get("requires_approval", False)),
74
- )
75
- ctx.state = AgentState.WAITING_APPROVAL
76
-
77
- # ── APPROVAL ─────────────────────────────────────────────────────
78
- def approval_requirements(self, ctx: AgentRunContext) -> Dict[str, Any]:
79
- """Read-only preview of the approval gate for a planned run.
80
-
81
- Shares the exact predicate :meth:`approve` enforces, so the HTTP
82
- layer can pause a run as ``awaiting_approval`` (with a plan summary
83
- for the user) instead of letting it fail closed — without ever
84
- weakening the gate itself.
85
- """
86
- d = self.deps
87
- mode = self.resolve_permission_mode(ctx)
88
- # Governor-managed tools never hard-block the plan: each call is
89
- # classified at execution time — additive creates run, mutations and
90
- # deletions of existing content become review proposals.
91
- governed_tools = self._governed_tools()
92
- steps = ctx.plan.get("steps", [])
93
- non_auto = non_auto_plan_steps(
94
- mode, steps, d.tool_governance or {}, governed_tools=governed_tools,
95
- )
96
- requires = plan_requires_approval(
97
- mode,
98
- non_auto_steps=non_auto,
99
- plan_flag=bool(ctx.plan.get("requires_approval", False)),
100
- )
101
- lines = [
102
- f"{index}. {step.get('description') or step.get('action') or '?'}"
103
- for index, step in enumerate(steps, start=1)
104
- ]
105
- summary = str(ctx.plan.get("goal") or "").strip()
106
- if lines:
107
- summary = (summary + "\n" if summary else "") + "\n".join(lines)
108
- return {
109
- "requires_approval": requires,
110
- "non_auto_steps": non_auto,
111
- "permission_mode": mode.value,
112
- "plan_summary": summary,
113
- }
114
-
115
- def approve(self, ctx: AgentRunContext, current_user: str, *, approved_by_human: bool = False) -> None:
116
- """APPROVAL: Check governance, log decision, auto-approve (future: UI prompt)."""
117
- d = self.deps
118
- requirements = self.approval_requirements(ctx)
119
- non_auto = requirements["non_auto_steps"]
120
- requires = requirements["requires_approval"]
121
-
122
- ctx.transcript.append({
123
- "state": AgentState.WAITING_APPROVAL.value,
124
- "requires_approval": requires,
125
- "non_auto_approve_steps": non_auto,
126
- "decision": "human_approved" if requires and approved_by_human else ("blocked_pending_approval" if requires else "auto_approved"),
127
- })
128
- decision = "human_approved" if requires and approved_by_human else ("blocked_pending_approval" if requires else "auto_approved")
129
- ctx.trace.decision("approve", decision=decision, non_auto_steps=len(non_auto))
130
- self._emit_step(ctx, "approval", "decision", decision=decision)
131
- d.audit(
132
- "agent_approval", user_email=current_user,
133
- requires_approval=requires,
134
- non_auto_steps=non_auto,
135
- decision=decision,
136
- )
137
- if requires and not approved_by_human:
138
- ctx.final_message = (
139
- "이 작업에는 명시 승인이 필요한 도구가 포함되어 있어 자동 실행을 중단했습니다. "
140
- "human_in_loop 승인 흐름으로 다시 실행해 주세요."
141
- )
142
- ctx.state = AgentState.FAILED
143
- return
144
- ctx.approved_by_human = bool(approved_by_human)
145
- ctx.state = AgentState.EXECUTING
@@ -1,157 +0,0 @@
1
- """ROLLBACK and MEMORY — putting the workspace back, and keeping the lesson.
2
-
3
- Recovery tries git first where the policy says the tool is git-rollbackable,
4
- then the pre-write snapshot the executor captured, and otherwise reports
5
- ``mode="none"`` rather than claiming a recovery it did not perform. The memory
6
- phase records what a *terminal* run taught: DONE runs record what worked,
7
- FAILED and NEEDS_REVIEW runs record what went wrong, stored under the run's
8
- actual terminal state and never a blanket "ok".
9
- """
10
-
11
- from __future__ import annotations
12
-
13
- import json
14
- import logging
15
- from typing import Any, Dict, List, Optional
16
-
17
- from latticeai.core.agent_helpers import extract_action, filter_learnings
18
- from latticeai.core.agent_state import AGENT_TERMINAL_STATES, AgentState
19
-
20
- from ._contract import AgentCore as _Core
21
- from .context import AgentRunContext
22
-
23
-
24
- class _RecoveryMixin(_Core):
25
- """The ROLLBACK and MEMORY phases of :class:`SingleAgentRuntime`."""
26
-
27
- # ── ROLLBACK ─────────────────────────────────────────────────────
28
- def _snapshot_for(self, ctx: AgentRunContext, path: str) -> Optional[Dict[str, Any]]:
29
- for entry in ctx.rollback_log:
30
- if entry.get("path") == path:
31
- return entry
32
- return None
33
-
34
- def _rollback_one(self, ctx: AgentRunContext, path: str, gov: Dict[str, Any]) -> Dict[str, Any]:
35
- """Recover one path: git when governed and available, else the
36
- pre-write snapshot, else an honest ``mode="none"`` (review L7)."""
37
- d = self.deps
38
- if gov.get("rollback") == "git" and d.rollback_file is not None:
39
- try:
40
- result = dict(d.rollback_file(str(path)))
41
- except Exception as exc: # noqa: BLE001
42
- result = {"path": path, "ok": False, "error": str(exc)}
43
- if result.get("ok"):
44
- result["mode"] = "git"
45
- return result
46
- snapshot = self._snapshot_for(ctx, str(path))
47
- if snapshot is not None and d.restore_snapshot is not None and not snapshot.get("too_large"):
48
- content = snapshot.get("content") if snapshot.get("existed") else None
49
- try:
50
- restored = dict(d.restore_snapshot(str(path), content))
51
- except Exception as exc: # noqa: BLE001
52
- restored = {"path": path, "ok": False, "error": str(exc)}
53
- restored.setdefault("path", path)
54
- restored["mode"] = "snapshot"
55
- return restored
56
- return {
57
- "path": path, "ok": False, "mode": "none",
58
- "error": "no rollback available (git not applicable, no usable snapshot)",
59
- }
60
-
61
- def rollback(self, ctx: AgentRunContext, current_user: str) -> None:
62
- """ROLLBACK: recover written files (git → snapshot → none), then FAILED."""
63
- d = self.deps
64
- rolled: List[dict] = []
65
- seen_paths: set = set()
66
- for step in ctx.transcript:
67
- if step.get("state") != AgentState.EXECUTING.value:
68
- continue
69
- if not isinstance(step.get("result"), dict):
70
- continue
71
- gov = step.get("governance", {}) or {}
72
- path = step["result"].get("path") or (step.get("args") or {}).get("path", "")
73
- if not path or str(path) in seen_paths:
74
- continue
75
- if gov.get("rollback") != "git" and step.get("action") not in d.file_create_actions:
76
- continue
77
- seen_paths.add(str(path))
78
- rolled.append(self._rollback_one(ctx, str(path), gov))
79
-
80
- ctx.transcript.append({"state": AgentState.ROLLBACK.value, "rolled_back": rolled})
81
- ctx.trace.decision(
82
- "rollback", decision="rolled_back",
83
- attempted=len(rolled), recovered=sum(1 for r in rolled if r.get("ok")),
84
- )
85
- recovered = [f"{r['path']} ({r.get('mode')})" for r in rolled if r.get("ok")]
86
- ctx.final_message = (
87
- f"실행 실패로 롤백했습니다. 복구 파일: {recovered}"
88
- if recovered
89
- else "롤백을 시도했으나 복구할 파일이 없거나 git/스냅샷 복구 수단이 없습니다."
90
- )
91
- d.audit("agent_rollback", user_email=current_user, rolled_back=rolled)
92
- self._emit_step(ctx, "rollback", "rolled_back", recovered=len(recovered))
93
- # Rollback is a recovery from a failed verification — terminal state is FAILED
94
- ctx.state = AgentState.FAILED
95
-
96
- # ── MEMORY ───────────────────────────────────────────────────────
97
- async def memory_update(self, ctx: AgentRunContext, req: Any, current_user: str) -> None:
98
- """Background: Memory Updater role extracts learnings from a terminal run.
99
-
100
- Terminal-state learning policy (review §4.2 L6): DONE runs record what
101
- worked; FAILED / NEEDS_REVIEW runs record what went wrong — failure is
102
- exactly the experience worth remembering. The run status stored with
103
- the experience is the *actual* terminal state, never a blanket "ok".
104
- """
105
- d = self.deps
106
- terminal = ctx.state.value if ctx.state in AGENT_TERMINAL_STATES else "UNKNOWN"
107
- outcome_hint = (
108
- "The task completed successfully."
109
- if ctx.state == AgentState.DONE
110
- else (
111
- f"The task ended as {terminal} — extract what went wrong and "
112
- "what to do differently next time, not a success story."
113
- )
114
- )
115
- context = (
116
- f"{d.memory_updater_prompt}\n\n"
117
- f"Task: {req.message}\n"
118
- f"Terminal status: {terminal}. {outcome_hint}\n\n"
119
- f"Last 5 transcript steps:\n{json.dumps(ctx.transcript[-5:], ensure_ascii=False)}"
120
- )
121
- try:
122
- raw = await d.generate(
123
- message="Extract learnings from this completed task.",
124
- context=context, max_tokens=self.phase_budgets.memory_tokens, temperature=0.1,
125
- )
126
- mem = extract_action(str(raw))
127
- kept_learnings = filter_learnings(mem.get("learnings") or [])
128
- if mem.get("save_to_knowledge") and kept_learnings:
129
- learnings = "\n".join(kept_learnings)
130
- status_label = {
131
- AgentState.DONE: "ok",
132
- AgentState.NEEDS_REVIEW: "needs_review",
133
- AgentState.FAILED: "failed",
134
- }.get(ctx.state, "unknown")
135
- if d.brain_memory is not None:
136
- # This runtime is LLM-driven — its learnings are real
137
- # experiences and enter the brain with provenance.
138
- d.brain_memory.record_experience(
139
- f"Agent: {req.message[:60]}",
140
- learnings,
141
- run={
142
- "mode": "llm",
143
- "status": status_label,
144
- "agent_id": "agent:executor",
145
- "steps": len(ctx.transcript),
146
- },
147
- user_email=current_user or None,
148
- )
149
- else:
150
- d.knowledge_save(
151
- learnings,
152
- folder="30_Projects",
153
- title=f"Agent: {req.message[:60]}",
154
- )
155
- except Exception as exc:
156
- # Never crash a completed run, but never swallow silently either.
157
- logging.warning("agent memory update failed: %s", exc)
@@ -1,210 +0,0 @@
1
- """The runtime itself: shared state, the budgets, and the drive loop.
2
-
3
- :class:`SingleAgentRuntime` is assembled from the four phase mixins. What lives
4
- *here* is what all four need — the injected ports, the per-phase token and
5
- transcript budgets, the autonomy dial, the step observer, the boundary/config
6
- contracts — plus :meth:`run_to_completion`, which drives EXECUTING → VERIFYING
7
- → ROLLBACK until a terminal state.
8
- """
9
-
10
- from __future__ import annotations
11
-
12
- import logging
13
- from pathlib import Path
14
- from typing import Any, Dict, FrozenSet, Optional
15
-
16
- from lattice_brain.runtime.contracts import (
17
- runtime_boundary_contract,
18
- single_agent_contract,
19
- )
20
- from latticeai.core.agent_helpers import PhaseBudgets, TranscriptBudget
21
- from latticeai.core.agent_permission import resolve_deps_mode
22
- from latticeai.core.agent_profiles import AgentProfile, profile_for_model
23
- from latticeai.core.agent_state import AGENT_TERMINAL_STATES, AgentState
24
- from latticeai.core.permission_mode import PermissionMode
25
- from latticeai.tools import document_output_target
26
-
27
- from .context import AgentRunContext
28
- from .deps import AgentDeps
29
- from .execution import _ExecutionMixin
30
- from .planning import _PlanningMixin
31
- from .recovery import _RecoveryMixin
32
- from .verification import _VerificationMixin
33
-
34
-
35
- class SingleAgentRuntime(
36
- _PlanningMixin, _ExecutionMixin, _VerificationMixin, _RecoveryMixin
37
- ):
38
- """Drives the agent state machine over injected :class:`AgentDeps`.
39
-
40
- The four mixins define disjoint method sets, so resolution order changes
41
- nothing at runtime: this class exposes exactly the methods it exposed when
42
- all four phases lived in one 1,465-line module.
43
- """
44
-
45
- def __init__(self, deps: AgentDeps) -> None:
46
- self.deps = deps
47
- self._env_phase_budgets: Optional[PhaseBudgets] = None
48
- self._env_transcript_budget: Optional[TranscriptBudget] = None
49
-
50
- @property
51
- def phase_budgets(self) -> PhaseBudgets:
52
- # getattr twice: partially-constructed runtimes/deps (tests build them
53
- # via __new__ or minimal fakes) still get working default budgets.
54
- injected = getattr(self.deps, "phase_budgets", None)
55
- if injected is not None:
56
- return injected
57
- cached = getattr(self, "_env_phase_budgets", None)
58
- if cached is None:
59
- cached = PhaseBudgets.from_env()
60
- self._env_phase_budgets = cached
61
- return cached
62
-
63
- @property
64
- def transcript_budget(self) -> TranscriptBudget:
65
- injected = getattr(self.deps, "transcript_budget", None)
66
- if injected is not None:
67
- return injected
68
- cached = getattr(self, "_env_transcript_budget", None)
69
- if cached is None:
70
- cached = TranscriptBudget.from_env()
71
- self._env_transcript_budget = cached
72
- return cached
73
-
74
- # ── permission mode (v9.9.8) ─────────────────────────────────────
75
- def resolve_permission_mode(
76
- self,
77
- ctx: Optional[AgentRunContext] = None,
78
- *,
79
- user_email: Optional[str] = None,
80
- workspace_id: Optional[str] = None,
81
- ) -> PermissionMode:
82
- """Autonomy dial for this run.
83
-
84
- A mode stamped on ``ctx`` wins, so the plan a user approved and every
85
- tool step in the same run are judged by one dial even if the stored
86
- preference changes mid-run. Otherwise the resolver on ``deps`` is
87
- consulted with the caller's scope — resolving unscoped would collapse
88
- every caller onto the process-wide default.
89
- """
90
- return resolve_deps_mode(
91
- self.deps, ctx, user_email=user_email, workspace_id=workspace_id,
92
- )
93
-
94
- def _governed_path_exists(self, name: str, path: str) -> bool:
95
- """Does this tool call's *real* target already exist?
96
-
97
- The document creators sanitize ``filename`` into their own output
98
- directory, so the raw argument is resolved through
99
- :func:`document_output_target` first — checking it verbatim would
100
- inspect a path nothing ever writes and the fail-closed overwrite guard
101
- would never fire. Workspace-relative paths resolve under
102
- ``deps.agent_root``; absolute paths (home-sandbox writes) are honored
103
- as-is. Never raises: governance must not be able to crash the loop, and
104
- an unresolvable path degrades to "new file", which the remaining gates
105
- still cover.
106
- """
107
- try:
108
- candidate = Path(document_output_target(name, path) or path)
109
- if not candidate.is_absolute():
110
- candidate = Path(self.deps.agent_root) / candidate
111
- return candidate.exists()
112
- except Exception: # noqa: BLE001 — classification is best-effort
113
- return False
114
-
115
- def _governed_tools(self) -> FrozenSet[str]:
116
- governor = getattr(self.deps, "change_governor", None)
117
- if governor is None:
118
- return frozenset()
119
- return frozenset(getattr(governor, "governed_tools", frozenset()))
120
-
121
- def profile_for(self, model_id: Optional[str]) -> AgentProfile:
122
- """Loop profile for the model actually executing this run (v9.9.7).
123
-
124
- An injected ``AgentDeps.agent_profile`` wins (tests, explicit config);
125
- otherwise the profile is derived from the model id, so a small local
126
- model gets the compact loop without any extra configuration.
127
- """
128
- injected = getattr(self.deps, "agent_profile", None)
129
- if injected is not None:
130
- return injected
131
- return profile_for_model(model_id)
132
-
133
- def _emit_step(self, ctx: AgentRunContext, phase: str, event: str, **details: Any) -> None:
134
- """Fire the per-run / deps step observers (review Wave 1.1).
135
-
136
- Observers power the live step timeline in the UI. They are pure
137
- telemetry: any observer failure is logged and swallowed — the loop
138
- itself must never notice.
139
- """
140
- payload: Dict[str, Any] = {"phase": phase, "event": event}
141
- for key, value in details.items():
142
- if value is not None:
143
- payload[key] = value
144
- for observer in (getattr(ctx, "on_step", None), getattr(self.deps, "on_step", None)):
145
- if observer is None:
146
- continue
147
- try:
148
- observer(dict(payload))
149
- except Exception as exc: # noqa: BLE001 — observers are advisory
150
- logging.warning("agent step observer failed: %s", exc)
151
-
152
- @staticmethod
153
- def _project_block(ctx: AgentRunContext) -> str:
154
- """Project-session context for prompts, or "" for a standalone run.
155
-
156
- Multi-turn project loop (v9.9.6): a later run must see the files the
157
- project already produced and what is still open, instead of planning
158
- from a blank workspace every time.
159
- """
160
- summary = str(getattr(ctx, "project_context", "") or "").strip()
161
- return f"\n\n[PROJECT SESSION]\n{summary}" if summary else ""
162
-
163
- def boundary(self) -> Dict[str, Any]:
164
- return runtime_boundary_contract(
165
- name="SingleAgentRuntime",
166
- runtime="single_agent",
167
- entrypoint="latticeai.core.agent.SingleAgentRuntime",
168
- surface="/agent",
169
- owns="single-agent PLAN / EXECUTE / VERIFY state machine over injected ports",
170
- compatibility_aliases=[],
171
- )
172
-
173
- def config(self) -> Dict[str, Any]:
174
- return {
175
- "boundary": self.boundary(),
176
- "states": [state.value for state in AgentState],
177
- "terminal_states": sorted(state.value for state in AGENT_TERMINAL_STATES),
178
- "execution_mode": "injected_ports",
179
- }
180
-
181
- def contract(self, ctx: AgentRunContext, req: Any, *, run_id: Optional[str] = None) -> Dict[str, Any]:
182
- """Expose the shared agent-run contract for the single-agent loop."""
183
- return single_agent_contract(ctx=ctx, goal=getattr(req, "message", ""), run_id=run_id)
184
-
185
- # ── DRIVE LOOP ───────────────────────────────────────────────────
186
- async def run_to_completion(
187
- self, ctx: AgentRunContext, req: Any, lang_hint: str,
188
- current_user: str, max_steps: int, max_retry: int,
189
- ) -> None:
190
- """Run EXECUTING → VERIFYING → ROLLBACK loop until a terminal state."""
191
- while ctx.state not in AGENT_TERMINAL_STATES:
192
- ctx.state_history.append(ctx.state.value)
193
- if len(ctx.state_history) > 200:
194
- ctx.final_message = "에이전트 상태 머신이 최대 반복(200)에 도달해 중단했습니다."
195
- ctx.state = AgentState.FAILED
196
- break
197
-
198
- if ctx.state == AgentState.EXECUTING:
199
- await self.execute(ctx, req, lang_hint, current_user, max_steps,
200
- model_id=ctx.executing_model)
201
- elif ctx.state == AgentState.VERIFYING:
202
- await self.verify(ctx, req, lang_hint, current_user, max_retry,
203
- model_id=ctx.reviewing_model)
204
- elif ctx.state == AgentState.ROLLBACK:
205
- self.rollback(ctx, current_user)
206
- else:
207
- ctx.state = AgentState.FAILED
208
-
209
- ctx.state_history.append(ctx.state.value)
210
- self._emit_step(ctx, "terminal", "state", state=ctx.state.value)