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.
- package/README.md +93 -148
- package/bin/ltcai.js +234 -24
- package/docs/CHANGELOG.md +119 -0
- package/docs/COMMUNITY_AND_PLUGINS.md +1 -1
- package/docs/DEVELOPMENT.md +8 -6
- package/docs/ENTERPRISE.md +2 -1
- package/docs/MULTI_AGENT_RUNTIME.md +12 -5
- package/docs/ONBOARDING.md +1 -1
- package/docs/OPERATIONS.md +6 -3
- package/docs/REALTIME_COLLABORATION.md +1 -1
- package/docs/TRUST_MODEL.md +1 -1
- package/docs/WHY_LATTICE.md +1 -1
- package/docs/WORKFLOW_DESIGNER.md +3 -2
- package/docs/kg-schema.md +1 -1
- package/docs/v11.6.0_ONE_DOOR_PLAN.md +170 -0
- package/lattice_brain/__init__.py +42 -79
- package/lattice_brain/graph/__init__.py +9 -24
- package/lattice_brain/graph/_kg_common/__init__.py +13 -24
- package/lattice_brain/ingestion/__init__.py +19 -58
- package/lattice_brain/ingestion/pipeline.py +20 -398
- package/lattice_brain/multimodal/__init__.py +7 -18
- package/lattice_brain/multimodal/images.py +6 -264
- package/lattice_brain/multimodal/video.py +8 -247
- package/lattice_brain/runtime/__init__.py +8 -79
- package/lattice_brain/runtime/hooks.py +22 -584
- package/latticeai/__init__.py +1 -1
- package/latticeai/api/agent_worker_seam.py +7 -80
- package/latticeai/api/health.py +6 -20
- package/latticeai/api/local_files.py +16 -613
- package/latticeai/api/models.py +12 -87
- package/latticeai/api/search.py +17 -255
- package/latticeai/api/tools.py +69 -750
- package/latticeai/api/voice_capture.py +8 -70
- package/latticeai/api/worker_compute.py +842 -0
- package/latticeai/api/worker_seams.py +216 -0
- package/latticeai/app_factory.py +23 -232
- package/latticeai/cli/entrypoint.py +36 -249
- package/latticeai/core/agent_permission.py +16 -91
- package/latticeai/core/messages.py +89 -531
- package/latticeai/runtime/access_runtime.py +0 -14
- package/latticeai/runtime/bootstrap.py +10 -21
- package/latticeai/runtime/brain_runtime.py +35 -43
- package/latticeai/runtime/build_phases/__init__.py +21 -37
- package/latticeai/runtime/build_phases/features.py +99 -389
- package/latticeai/runtime/build_phases/foundation.py +82 -392
- package/latticeai/runtime/build_phases/web.py +91 -408
- package/latticeai/runtime/build_phases/worker_profile.py +244 -0
- package/latticeai/runtime/lifespan_runtime.py +7 -16
- package/latticeai/runtime/platform_services_runtime.py +1 -15
- package/latticeai/runtime/runtime_context.py +13 -130
- package/latticeai/runtime/security_runtime.py +10 -103
- package/latticeai/services/architecture_readiness.py +82 -43
- package/latticeai/services/model_runtime/__init__.py +2 -11
- package/latticeai/services/model_runtime/service.py +4 -28
- package/latticeai/services/p_reinforce.py +10 -261
- package/latticeai/services/product_readiness.py +31 -38
- package/latticeai/services/search_service.py +37 -795
- package/latticeai/services/tool_dispatch.py +30 -386
- package/latticeai/services/voice_capture.py +13 -107
- package/latticeai/tools/__init__.py +22 -49
- package/latticeai/tools/commands.py +0 -163
- package/latticeai/tools/computer.py +0 -39
- package/latticeai/tools/documents.py +1 -134
- package/latticeai/tools/filesystem.py +1 -247
- package/latticeai/tools/knowledge.py +1 -52
- package/latticeai/tools/local_files.py +0 -20
- package/latticeai/worker_app.py +75 -0
- package/package.json +2 -3
- package/requirements.txt +0 -5
- package/scripts/agent_eval.py +16 -28
- package/scripts/brain_quality_eval.py +20 -183
- package/scripts/bump_version.py +5 -5
- package/scripts/check_current_release_docs.mjs +7 -4
- package/scripts/check_openapi_drift.mjs +10 -0
- package/scripts/check_server_i18n.mjs +2 -18
- package/scripts/compose_openapi.py +377 -0
- package/scripts/export_openapi.py +32 -4
- package/scripts/gen_messages_catalog_fixture.py +302 -0
- package/scripts/gen_openapi_fragments.py +365 -0
- package/scripts/gen_redact_fixture.py +196 -0
- package/scripts/gen_worker_allowlist_fixture.py +115 -0
- package/scripts/generate_agent_parity_fixtures.py +33 -14
- package/scripts/openapi_route_families.json +2161 -0
- package/scripts/release_screen_claims.json +54 -0
- package/scripts/run_integration_tests.mjs +134 -29
- package/scripts/run_sidecar_e2e.mjs +104 -11
- package/scripts/wheel_smoke.py +32 -22
- package/src-tauri/Cargo.lock +375 -9
- package/src-tauri/Cargo.toml +1 -1
- package/src-tauri/src/backend.rs +151 -45
- package/src-tauri/src/main.rs +18 -18
- package/src-tauri/src/topology.rs +58 -88
- package/src-tauri/tauri.conf.json +1 -2
- package/static/app/asset-manifest.json +41 -41
- package/static/app/assets/{Act-DcQizkl1.js → Act-BPcVAbOL.js} +1 -1
- package/static/app/assets/AdminConsole-Bw1ATQL0.js +1 -0
- package/static/app/assets/{Brain-3VCSHFcn.js → Brain-CT92Kos0.js} +2 -2
- package/static/app/assets/{BrainHome-Qm8eaztx.js → BrainHome-CFBkt1K_.js} +1 -1
- package/static/app/assets/{BrainSignals-DS9BtKOW.js → BrainSignals-ReLWF2H8.js} +1 -1
- package/static/app/assets/Capture-BsTokYkk.js +1 -0
- package/static/app/assets/{Chronicle-BGvuAchH.js → Chronicle-B6f0T9id.js} +1 -1
- package/static/app/assets/{CommandPalette-Bqhm0Urn.js → CommandPalette-CuvjTv1u.js} +1 -1
- package/static/app/assets/{Library-BV6NnF0a.js → Library-BGJbG9Hd.js} +1 -1
- package/static/app/assets/{LivingBrain-GzenJchP.js → LivingBrain-DGYK_Jsa.js} +1 -1
- package/static/app/assets/{ProductFlow-DEP6-vML.js → ProductFlow-DXBC6brE.js} +1 -1
- package/static/app/assets/{ReviewCard-CNZ7XjWG.js → ReviewCard-HXRle3qq.js} +2 -2
- package/static/app/assets/System-CMHSO9qM.js +1 -0
- package/static/app/assets/arrow-left-BfmkskWx.js +1 -0
- package/static/app/assets/{bot-B_K1Tdmw.js → bot-Cn8bWRuq.js} +1 -1
- package/static/app/assets/{brain-DWyaV1L1.js → brain-CQJberbE.js} +1 -1
- package/static/app/assets/{button-aTn4s84A.js → button-Ct9f2_oT.js} +1 -1
- package/static/app/assets/circle-check-DruOxB-4.js +1 -0
- package/static/app/assets/{circle-pause-xKgeGXkT.js → circle-pause-CmzC_apg.js} +1 -1
- package/static/app/assets/{circle-play-DkT6tYPX.js → circle-play-D8mW2aQ7.js} +1 -1
- package/static/app/assets/{cpu-85xYObUC.js → cpu-DZcdd0PZ.js} +1 -1
- package/static/app/assets/{download-B5Fm7YXo.js → download-bv1KEPGQ.js} +1 -1
- package/static/app/assets/{folder-open-kk2Xa52u.js → folder-open-d-Pip5gr.js} +1 -1
- package/static/app/assets/{hard-drive-DkA3zBW_.js → hard-drive-D20iavUb.js} +1 -1
- package/static/app/assets/index-D9x-kSNy.css +2 -0
- package/static/app/assets/{index-BMPdTmlY.js → index-Do83hDzJ.js} +3 -3
- package/static/app/assets/{input-B0nRf2jO.js → input-BLXVNmj1.js} +1 -1
- package/static/app/assets/{link-2-Dwb4gnTc.js → link-2-BPJOFlAy.js} +1 -1
- package/static/app/assets/{permissionCopy-CQDUBrOZ.js → permissionCopy-ChdJd493.js} +1 -1
- package/static/app/assets/primitives-Cv5tbZBY.js +1 -0
- package/static/app/assets/search-CT9aho2j.js +1 -0
- package/static/app/assets/{share-2-BsrxFglO.js → share-2-YNX_NtMU.js} +1 -1
- package/static/app/assets/{shield-alert-5BStfp2_.js → shield-alert-DuQ3zrVL.js} +1 -1
- package/static/app/assets/{textarea-Cg8IUA-k.js → textarea-DqwLnli4.js} +1 -1
- package/static/app/assets/{useFocusTrap-CYKvE46M.js → useFocusTrap-ZVI98jaW.js} +1 -1
- package/static/app/assets/{useMutation-CSn9t1op.js → useMutation-CVC4qv_D.js} +1 -1
- package/static/app/assets/{useQuery-CY2OI2uy.js → useQuery-C7BeG4HU.js} +1 -1
- package/static/app/assets/{utils-Ddol2RWD.js → utils-CiFtIdZq.js} +1 -1
- package/static/app/assets/{workspace-BqDwOz_p.js → workspace-DQz9vIId.js} +1 -1
- package/static/app/index.html +4 -4
- package/static/sw.js +1 -1
- package/lattice_brain/archive.py +0 -522
- package/lattice_brain/context.py +0 -325
- package/lattice_brain/conversations.py +0 -382
- package/lattice_brain/core.py +0 -82
- package/lattice_brain/graph/_kg_contract.py +0 -249
- package/lattice_brain/graph/curator.py +0 -676
- package/lattice_brain/graph/discovery.py +0 -595
- package/lattice_brain/graph/discovery_index/__init__.py +0 -35
- package/lattice_brain/graph/discovery_index/cleanup.py +0 -182
- package/lattice_brain/graph/discovery_index/extract.py +0 -137
- package/lattice_brain/graph/discovery_index/scan.py +0 -411
- package/lattice_brain/graph/discovery_index/upsert.py +0 -495
- package/lattice_brain/graph/documents.py +0 -380
- package/lattice_brain/graph/fusion.py +0 -395
- package/lattice_brain/graph/identity.py +0 -175
- package/lattice_brain/graph/image_vectors.py +0 -230
- package/lattice_brain/graph/ingest.py +0 -829
- package/lattice_brain/graph/network.py +0 -205
- package/lattice_brain/graph/proactive.py +0 -724
- package/lattice_brain/graph/projection/__init__.py +0 -42
- package/lattice_brain/graph/projection/curation.py +0 -500
- package/lattice_brain/graph/projection/v2_schema.py +0 -518
- package/lattice_brain/graph/provenance.py +0 -524
- package/lattice_brain/graph/rerank.py +0 -163
- package/lattice_brain/graph/retrieval/__init__.py +0 -54
- package/lattice_brain/graph/retrieval/context.py +0 -197
- package/lattice_brain/graph/retrieval/graph_view.py +0 -319
- package/lattice_brain/graph/retrieval/hybrid.py +0 -488
- package/lattice_brain/graph/retrieval/maintenance.py +0 -121
- package/lattice_brain/graph/retrieval/signals.py +0 -95
- package/lattice_brain/graph/retrieval_docgen.py +0 -253
- package/lattice_brain/graph/retrieval_policy.py +0 -180
- package/lattice_brain/graph/retrieval_reads.py +0 -769
- package/lattice_brain/graph/retrieval_vector/__init__.py +0 -42
- package/lattice_brain/graph/retrieval_vector/fingerprint.py +0 -97
- package/lattice_brain/graph/retrieval_vector/indexing.py +0 -347
- package/lattice_brain/graph/retrieval_vector/search.py +0 -560
- package/lattice_brain/graph/retrieval_vector/status.py +0 -374
- package/lattice_brain/graph/schema.py +0 -792
- package/lattice_brain/graph/store.py +0 -268
- package/lattice_brain/graph/vector_index/__init__.py +0 -85
- package/lattice_brain/graph/vector_index/base.py +0 -167
- package/lattice_brain/graph/vector_index/brute_force.py +0 -110
- package/lattice_brain/graph/vector_index/hnsw.py +0 -290
- package/lattice_brain/graph/vector_index/jobs.py +0 -287
- package/lattice_brain/graph/vector_index/quantized.py +0 -148
- package/lattice_brain/graph/vector_index/selector.py +0 -161
- package/lattice_brain/graph/write_master.py +0 -308
- package/lattice_brain/ingestion/_contract.py +0 -90
- package/lattice_brain/ingestion/folder_scan.py +0 -57
- package/lattice_brain/ingestion/folders.py +0 -258
- package/lattice_brain/ingestion/jobs_api.py +0 -107
- package/lattice_brain/ingestion/routing.py +0 -295
- package/lattice_brain/ingestion_jobs.py +0 -380
- package/lattice_brain/memory.py +0 -75
- package/lattice_brain/portability/__init__.py +0 -90
- package/lattice_brain/portability/_contract.py +0 -42
- package/lattice_brain/portability/backups.py +0 -338
- package/lattice_brain/portability/bundles.py +0 -136
- package/lattice_brain/portability/constants.py +0 -93
- package/lattice_brain/portability/fsops.py +0 -138
- package/lattice_brain/portability/service.py +0 -41
- package/lattice_brain/portability/sharing.py +0 -710
- package/lattice_brain/quality.py +0 -543
- package/lattice_brain/retrieval_benchmark_fixtures.py +0 -92
- package/lattice_brain/runtime/agent_runtime.py +0 -859
- package/lattice_brain/runtime/contracts.py +0 -460
- package/lattice_brain/runtime/multi_agent.py +0 -942
- package/lattice_brain/runtime/statuses.py +0 -10
- package/lattice_brain/sealed_box.py +0 -240
- package/lattice_brain/self_model.py +0 -675
- package/lattice_brain/sensitivity.py +0 -94
- package/lattice_brain/storage/__init__.py +0 -22
- package/lattice_brain/storage/base.py +0 -100
- package/lattice_brain/storage/docker.py +0 -105
- package/lattice_brain/storage/factory.py +0 -31
- package/lattice_brain/storage/migration.py +0 -191
- package/lattice_brain/storage/postgres.py +0 -123
- package/lattice_brain/storage/sqlite.py +0 -143
- package/lattice_brain/synthesis.py +0 -824
- package/lattice_brain/workflow.py +0 -497
- package/latticeai/api/admin.py +0 -471
- package/latticeai/api/agent_registry.py +0 -105
- package/latticeai/api/agents.py +0 -228
- package/latticeai/api/auth.py +0 -383
- package/latticeai/api/automation_intelligence.py +0 -401
- package/latticeai/api/brain_intelligence.py +0 -199
- package/latticeai/api/browser.py +0 -493
- package/latticeai/api/change_proposals.py +0 -89
- package/latticeai/api/chat.py +0 -572
- package/latticeai/api/chat_agent_http.py +0 -892
- package/latticeai/api/chat_contracts.py +0 -73
- package/latticeai/api/chat_documents.py +0 -276
- package/latticeai/api/chat_helpers.py +0 -460
- package/latticeai/api/chat_history.py +0 -101
- package/latticeai/api/chat_hybrid.py +0 -113
- package/latticeai/api/chat_intents.py +0 -646
- package/latticeai/api/chat_stream.py +0 -216
- package/latticeai/api/chronicle.py +0 -63
- package/latticeai/api/command_center.py +0 -51
- package/latticeai/api/computer_use.py +0 -474
- package/latticeai/api/evidence_actions.py +0 -48
- package/latticeai/api/features.py +0 -70
- package/latticeai/api/funnel_metrics.py +0 -31
- package/latticeai/api/garden.py +0 -34
- package/latticeai/api/hooks.py +0 -165
- package/latticeai/api/index_jobs.py +0 -145
- package/latticeai/api/invitations.py +0 -100
- package/latticeai/api/knowledge_graph.py +0 -536
- package/latticeai/api/marketplace.py +0 -105
- package/latticeai/api/mcp.py +0 -482
- package/latticeai/api/memory.py +0 -270
- package/latticeai/api/network.py +0 -81
- package/latticeai/api/network_boundary.py +0 -225
- package/latticeai/api/permission_mode.py +0 -61
- package/latticeai/api/permissions.py +0 -436
- package/latticeai/api/plugins.py +0 -126
- package/latticeai/api/portability.py +0 -391
- package/latticeai/api/project_sessions.py +0 -114
- package/latticeai/api/realtime.py +0 -118
- package/latticeai/api/review_queue.py +0 -364
- package/latticeai/api/security_dashboard.py +0 -604
- package/latticeai/api/setup.py +0 -319
- package/latticeai/api/static_routes.py +0 -354
- package/latticeai/api/ui_redirects.py +0 -26
- package/latticeai/api/workflow_designer.py +0 -394
- package/latticeai/api/workspace.py +0 -856
- package/latticeai/api/workspace_scope.py +0 -125
- package/latticeai/core/agent/__init__.py +0 -93
- package/latticeai/core/agent/_contract.py +0 -79
- package/latticeai/core/agent/context.py +0 -57
- package/latticeai/core/agent/deps.py +0 -125
- package/latticeai/core/agent/execution.py +0 -622
- package/latticeai/core/agent/planning.py +0 -145
- package/latticeai/core/agent/recovery.py +0 -157
- package/latticeai/core/agent/runtime.py +0 -210
- package/latticeai/core/agent/verification.py +0 -231
- package/latticeai/core/agent_eval.py +0 -739
- package/latticeai/core/agent_helpers.py +0 -493
- package/latticeai/core/agent_profiles.py +0 -110
- package/latticeai/core/agent_prompts.py +0 -171
- package/latticeai/core/agent_registry.py +0 -232
- package/latticeai/core/agent_state.py +0 -41
- package/latticeai/core/agent_trace.py +0 -104
- package/latticeai/core/artifact_ledger.py +0 -109
- package/latticeai/core/audit.py +0 -260
- package/latticeai/core/builtin_hooks.py +0 -105
- package/latticeai/core/context_builder.py +0 -394
- package/latticeai/core/document_generator.py +0 -103
- package/latticeai/core/enterprise.py +0 -154
- package/latticeai/core/enterprise_admin.py +0 -158
- package/latticeai/core/file_generation/__init__.py +0 -115
- package/latticeai/core/file_generation/bundles.py +0 -76
- package/latticeai/core/file_generation/extraction.py +0 -154
- package/latticeai/core/file_generation/inference.py +0 -235
- package/latticeai/core/file_generation/orchestration.py +0 -152
- package/latticeai/core/file_generation/prompting.py +0 -117
- package/latticeai/core/file_generation/repair.py +0 -114
- package/latticeai/core/file_generation/sanitize.py +0 -61
- package/latticeai/core/file_generation/validation.py +0 -201
- package/latticeai/core/invitations.py +0 -132
- package/latticeai/core/legacy_compatibility.py +0 -243
- package/latticeai/core/logging_safety.py +0 -46
- package/latticeai/core/marketplace.py +0 -293
- package/latticeai/core/mcp_catalog.py +0 -452
- package/latticeai/core/mcp_registry.py +0 -506
- package/latticeai/core/network_boundary.py +0 -168
- package/latticeai/core/oidc.py +0 -208
- package/latticeai/core/plugins.py +0 -432
- package/latticeai/core/product_hardening.py +0 -218
- package/latticeai/core/project_sessions.py +0 -337
- package/latticeai/core/realtime.py +0 -238
- package/latticeai/core/run_explain.py +0 -426
- package/latticeai/core/run_store.py +0 -252
- package/latticeai/core/timezones.py +0 -80
- package/latticeai/core/workspace_computer_memory.py +0 -84
- package/latticeai/core/workspace_graph_trace.py +0 -155
- package/latticeai/core/workspace_indexing.py +0 -102
- package/latticeai/core/workspace_memory.py +0 -77
- package/latticeai/core/workspace_onboarding.py +0 -104
- package/latticeai/core/workspace_os.py +0 -978
- package/latticeai/core/workspace_os_constants.py +0 -126
- package/latticeai/core/workspace_os_state.py +0 -180
- package/latticeai/core/workspace_os_utils.py +0 -103
- package/latticeai/core/workspace_permissions.py +0 -101
- package/latticeai/core/workspace_plugins.py +0 -97
- package/latticeai/core/workspace_relationships.py +0 -99
- package/latticeai/core/workspace_reorganization.py +0 -335
- package/latticeai/core/workspace_review_items.py +0 -112
- package/latticeai/core/workspace_runs.py +0 -726
- package/latticeai/core/workspace_skills.py +0 -109
- package/latticeai/core/workspace_snapshots.py +0 -198
- package/latticeai/core/workspace_timeline.py +0 -110
- package/latticeai/integrations/__init__.py +0 -0
- package/latticeai/integrations/telegram_bot/__init__.py +0 -123
- package/latticeai/integrations/telegram_bot/__main__.py +0 -17
- package/latticeai/integrations/telegram_bot/config.py +0 -86
- package/latticeai/integrations/telegram_bot/dispatch.py +0 -311
- package/latticeai/integrations/telegram_bot/flows.py +0 -478
- package/latticeai/integrations/telegram_bot/helpers.py +0 -322
- package/latticeai/integrations/telegram_bot/screens.py +0 -394
- package/latticeai/runtime/audit_runtime.py +0 -76
- package/latticeai/runtime/automation_runtime.py +0 -81
- package/latticeai/runtime/chat_wiring.py +0 -141
- package/latticeai/runtime/context_runtime.py +0 -66
- package/latticeai/runtime/feature_toggle_wiring.py +0 -157
- package/latticeai/runtime/history_runtime.py +0 -163
- package/latticeai/runtime/history_writer.py +0 -138
- package/latticeai/runtime/hooks_runtime.py +0 -77
- package/latticeai/runtime/model_wiring.py +0 -68
- package/latticeai/runtime/namespace_runtime.py +0 -163
- package/latticeai/runtime/network_boundary_wiring.py +0 -117
- package/latticeai/runtime/network_config_runtime.py +0 -56
- package/latticeai/runtime/permission_mode_wiring.py +0 -112
- package/latticeai/runtime/persistence_runtime.py +0 -159
- package/latticeai/runtime/platform_runtime_wiring.py +0 -89
- package/latticeai/runtime/review_wiring.py +0 -42
- package/latticeai/runtime/router_registration.py +0 -693
- package/latticeai/runtime/service_singletons.py +0 -55
- package/latticeai/runtime/sso_config_runtime.py +0 -128
- package/latticeai/runtime/user_key_runtime.py +0 -106
- package/latticeai/runtime/web_runtime.py +0 -92
- package/latticeai/server_app.py +0 -51
- package/latticeai/services/app_context.py +0 -130
- package/latticeai/services/automation_execution.py +0 -266
- package/latticeai/services/automation_intelligence.py +0 -614
- package/latticeai/services/brain_automation.py +0 -191
- package/latticeai/services/brain_intelligence/__init__.py +0 -58
- package/latticeai/services/brain_intelligence/_contract.py +0 -71
- package/latticeai/services/brain_intelligence/consistency.py +0 -193
- package/latticeai/services/brain_intelligence/constants.py +0 -47
- package/latticeai/services/brain_intelligence/digest.py +0 -258
- package/latticeai/services/brain_intelligence/health.py +0 -331
- package/latticeai/services/brain_intelligence/proposals.py +0 -259
- package/latticeai/services/brain_intelligence/sampling.py +0 -84
- package/latticeai/services/brain_intelligence/service.py +0 -48
- package/latticeai/services/change_proposals.py +0 -471
- package/latticeai/services/chat_service.py +0 -243
- package/latticeai/services/chronicle.py +0 -555
- package/latticeai/services/cloud_egress_audit.py +0 -85
- package/latticeai/services/cloud_extraction.py +0 -129
- package/latticeai/services/cloud_streaming.py +0 -268
- package/latticeai/services/cloud_token_guard.py +0 -84
- package/latticeai/services/command_center.py +0 -548
- package/latticeai/services/evidence_actions.py +0 -258
- package/latticeai/services/feature_toggles.py +0 -502
- package/latticeai/services/folder_watch.py +0 -520
- package/latticeai/services/funnel_metrics.py +0 -307
- package/latticeai/services/hybrid_chat.py +0 -316
- package/latticeai/services/hybrid_context.py +0 -228
- package/latticeai/services/hybrid_policy.py +0 -129
- package/latticeai/services/interop_bridges.py +0 -978
- package/latticeai/services/local_knowledge.py +0 -465
- package/latticeai/services/memory_service/__init__.py +0 -52
- package/latticeai/services/memory_service/_contract.py +0 -100
- package/latticeai/services/memory_service/brief.py +0 -431
- package/latticeai/services/memory_service/constants.py +0 -57
- package/latticeai/services/memory_service/maintenance.py +0 -138
- package/latticeai/services/memory_service/manager.py +0 -186
- package/latticeai/services/memory_service/proof.py +0 -136
- package/latticeai/services/memory_service/recall.py +0 -225
- package/latticeai/services/memory_service/service.py +0 -48
- package/latticeai/services/memory_service/stores.py +0 -110
- package/latticeai/services/mode_store.py +0 -132
- package/latticeai/services/model_recommendation.py +0 -224
- package/latticeai/services/model_runtime/cloud.py +0 -87
- package/latticeai/services/network_boundary_service.py +0 -117
- package/latticeai/services/obsidian_bridge.py +0 -609
- package/latticeai/services/openai_compatible_adapter.py +0 -101
- package/latticeai/services/permission_mode_service.py +0 -122
- package/latticeai/services/platform_runtime.py +0 -366
- package/latticeai/services/review_queue.py +0 -380
- package/latticeai/services/router_context.py +0 -59
- package/latticeai/services/run_executor.py +0 -387
- package/latticeai/services/self_model_service.py +0 -171
- package/latticeai/services/setup_detection.py +0 -147
- package/latticeai/services/triggers.py +0 -378
- package/latticeai/services/upload_service.py +0 -172
- package/latticeai/services/workspace_service.py +0 -165
- package/latticeai/setup/__init__.py +0 -25
- package/latticeai/setup/auto_setup.py +0 -846
- package/latticeai/setup/demo_corpus.py +0 -98
- package/latticeai/setup/wizard/__init__.py +0 -126
- package/latticeai/setup/wizard/catalog.py +0 -175
- package/latticeai/setup/wizard/detect.py +0 -302
- package/latticeai/setup/wizard/install.py +0 -348
- package/latticeai/setup/wizard/paths.py +0 -165
- package/latticeai/setup/wizard/plans.py +0 -74
- package/latticeai/setup/wizard/recommend.py +0 -320
- package/scripts/bench_agent_smoke.py +0 -409
- package/scripts/bench_models.py +0 -540
- package/scripts/bench_vector_index.py +0 -295
- package/scripts/funnel_soft_gate.py +0 -192
- package/scripts/generate_agent_loop_fixtures.py +0 -994
- package/scripts/generate_rust_parity_fixtures.py +0 -908
- package/scripts/migrate_brain_storage.py +0 -57
- package/scripts/parity_fixture_corpus_context.py +0 -162
- package/scripts/parity_fixture_corpus_docgen.py +0 -341
- package/scripts/profile_kg.py +0 -355
- package/server.py +0 -30
- package/static/app/assets/AdminConsole-cf4npybT.js +0 -1
- package/static/app/assets/Capture-DiQ219jW.js +0 -1
- package/static/app/assets/System-CieofHQa.js +0 -1
- package/static/app/assets/arrow-left-kfsrk0mv.js +0 -1
- package/static/app/assets/circle-check-qqLug9nU.js +0 -1
- package/static/app/assets/index-DxmOfNRi.css +0 -2
- package/static/app/assets/primitives-SNp0LRJz.js +0 -1
- package/static/app/assets/search-BcHqkjoy.js +0 -1
|
@@ -1,401 +0,0 @@
|
|
|
1
|
-
"""Question-driven everyday automation API router (v9.4.0).
|
|
2
|
-
|
|
3
|
-
Exposes :class:`~latticeai.services.automation_intelligence.AutomationIntelligenceService`:
|
|
4
|
-
recurring-question patterns with evidence, automation suggestions (from the
|
|
5
|
-
user's own questions and connected knowledge folders), one-click consent-first
|
|
6
|
-
install, and a combined overview for the automation surface.
|
|
7
|
-
|
|
8
|
-
Installs follow the same consent contract as the starter recipes: the
|
|
9
|
-
workflow is created as a disabled draft (unless the user explicitly asks to
|
|
10
|
-
enable), review-queue gated, local-only, and idempotent per suggestion.
|
|
11
|
-
"""
|
|
12
|
-
|
|
13
|
-
from __future__ import annotations
|
|
14
|
-
|
|
15
|
-
import asyncio
|
|
16
|
-
import os
|
|
17
|
-
import time
|
|
18
|
-
from typing import Any, Callable, Optional
|
|
19
|
-
|
|
20
|
-
from fastapi import APIRouter, HTTPException, Request
|
|
21
|
-
from pydantic import BaseModel
|
|
22
|
-
|
|
23
|
-
from latticeai.core.quiet import quiet
|
|
24
|
-
from latticeai.services.automation_execution import (
|
|
25
|
-
build_last_execution,
|
|
26
|
-
dry_run_report,
|
|
27
|
-
enqueue_failed_execution,
|
|
28
|
-
is_automation_workflow,
|
|
29
|
-
summarize_workflow_run,
|
|
30
|
-
)
|
|
31
|
-
from latticeai.services.automation_intelligence import AutomationIntelligenceService
|
|
32
|
-
from latticeai.services.brain_automation import find_installed_recipe_workflow
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
class SuggestionInstallRequest(BaseModel):
|
|
36
|
-
suggestion_id: str
|
|
37
|
-
enabled: bool = False
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
class AutomationRunNowRequest(BaseModel):
|
|
41
|
-
workflow_id: str
|
|
42
|
-
# Dry-run first: the default reports what WOULD happen without side
|
|
43
|
-
# effects; an explicit dry_run=false runs the automation once for real.
|
|
44
|
-
dry_run: bool = True
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
def _ensure_activity_run_source(run: Any) -> dict[str, Any]:
|
|
48
|
-
"""Guarantee the Act feed ``source`` field for 레시피/목표 badge split.
|
|
49
|
-
|
|
50
|
-
Capture 09 (``frontend/src/pages/Act.tsx``) distinguishes workflow vs agent
|
|
51
|
-
with ``run.source === "workflow" || Boolean(run.workflow_id)``. A store that
|
|
52
|
-
omits both collapses every row to "목표" and silently undoes the merged
|
|
53
|
-
timeline redesign. Prefer an explicit ``source``; otherwise infer from
|
|
54
|
-
``workflow_id`` / ``agent_id``.
|
|
55
|
-
"""
|
|
56
|
-
if not isinstance(run, dict):
|
|
57
|
-
return {"source": "agent", "id": None, "title": "", "status": ""}
|
|
58
|
-
row = dict(run)
|
|
59
|
-
source = str(row.get("source") or "").strip().lower()
|
|
60
|
-
if source in {"workflow", "agent"}:
|
|
61
|
-
row["source"] = source
|
|
62
|
-
return row
|
|
63
|
-
if row.get("workflow_id"):
|
|
64
|
-
row["source"] = "workflow"
|
|
65
|
-
return row
|
|
66
|
-
if row.get("agent_id"):
|
|
67
|
-
row["source"] = "agent"
|
|
68
|
-
return row
|
|
69
|
-
# Last resort: keep any non-empty legacy value, else agent.
|
|
70
|
-
row["source"] = source if source else "agent"
|
|
71
|
-
return row
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
def _with_run_truncation_meta(payload: Any, *, capped: int) -> dict[str, Any]:
|
|
75
|
-
"""Guarantee ``total`` / ``truncated`` on combined-run payloads.
|
|
76
|
-
|
|
77
|
-
Stores that already implement :meth:`list_combined_runs` may return only
|
|
78
|
-
``runs``. The Act feed needs to say how many rows are hidden when the
|
|
79
|
-
server slices agent+workflow history (default cap 20).
|
|
80
|
-
|
|
81
|
-
Every run is also passed through :func:`_ensure_activity_run_source` so the
|
|
82
|
-
레시피/목표 badge never loses its discriminator.
|
|
83
|
-
"""
|
|
84
|
-
if not isinstance(payload, dict):
|
|
85
|
-
return {"runs": [], "total": 0, "truncated": False}
|
|
86
|
-
runs = [_ensure_activity_run_source(run) for run in (payload.get("runs") or [])]
|
|
87
|
-
if "total" in payload:
|
|
88
|
-
try:
|
|
89
|
-
total = max(0, int(payload.get("total") or 0))
|
|
90
|
-
except (TypeError, ValueError):
|
|
91
|
-
total = len(runs)
|
|
92
|
-
else:
|
|
93
|
-
total = len(runs)
|
|
94
|
-
if "truncated" in payload:
|
|
95
|
-
truncated = bool(payload.get("truncated"))
|
|
96
|
-
else:
|
|
97
|
-
truncated = total > len(runs) or total > capped
|
|
98
|
-
# Prefer the larger of declared total and visible rows so a buggy store
|
|
99
|
-
# never claims fewer total than the list it just returned.
|
|
100
|
-
total = max(total, len(runs))
|
|
101
|
-
if total > len(runs):
|
|
102
|
-
truncated = True
|
|
103
|
-
return {**payload, "runs": runs, "total": total, "truncated": truncated}
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
def _run_now_wait_seconds() -> float:
|
|
107
|
-
raw = os.environ.get("LATTICEAI_AUTOMATION_RUN_NOW_WAIT", "30")
|
|
108
|
-
try:
|
|
109
|
-
return max(0.0, min(float(raw), 300.0))
|
|
110
|
-
except (TypeError, ValueError):
|
|
111
|
-
return 30.0
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
def create_automation_intelligence_router(
|
|
115
|
-
*,
|
|
116
|
-
service: AutomationIntelligenceService,
|
|
117
|
-
store: Any,
|
|
118
|
-
require_user: Callable[[Request], str],
|
|
119
|
-
gate_read: Callable[[Request], Optional[str]],
|
|
120
|
-
gate_write: Callable[[Request], Optional[str]],
|
|
121
|
-
append_audit_event: Callable[..., None],
|
|
122
|
-
workspace_graph: Callable[[], Any],
|
|
123
|
-
run_executor: Any = None,
|
|
124
|
-
review_queue: Any = None,
|
|
125
|
-
) -> APIRouter:
|
|
126
|
-
from lattice_brain.runtime.statuses import RUN_TERMINAL_STATUSES
|
|
127
|
-
from lattice_brain.workflow import legacy_steps_from_nodes, validate_definition
|
|
128
|
-
|
|
129
|
-
router = APIRouter()
|
|
130
|
-
|
|
131
|
-
@router.get("/api/automation/patterns")
|
|
132
|
-
async def automation_patterns(request: Request):
|
|
133
|
-
user = require_user(request)
|
|
134
|
-
scope = gate_read(request)
|
|
135
|
-
return service.question_patterns(user_email=user, workspace_id=scope)
|
|
136
|
-
|
|
137
|
-
@router.get("/api/automation/suggestions")
|
|
138
|
-
async def automation_suggestions(request: Request):
|
|
139
|
-
user = require_user(request)
|
|
140
|
-
scope = gate_read(request)
|
|
141
|
-
return service.suggestions(user_email=user, workspace_id=scope)
|
|
142
|
-
|
|
143
|
-
@router.get("/api/automation/overview")
|
|
144
|
-
async def automation_overview(request: Request):
|
|
145
|
-
user = require_user(request)
|
|
146
|
-
scope = gate_read(request)
|
|
147
|
-
return service.overview(user_email=user, workspace_id=scope)
|
|
148
|
-
|
|
149
|
-
@router.post("/api/automation/install")
|
|
150
|
-
async def automation_install(req: SuggestionInstallRequest, request: Request):
|
|
151
|
-
user = require_user(request)
|
|
152
|
-
scope = gate_write(request)
|
|
153
|
-
suggestion = service.find_suggestion(
|
|
154
|
-
req.suggestion_id, user_email=user, workspace_id=scope
|
|
155
|
-
)
|
|
156
|
-
if suggestion is None:
|
|
157
|
-
raise HTTPException(
|
|
158
|
-
status_code=404,
|
|
159
|
-
detail=f"Automation suggestion not found: {req.suggestion_id}",
|
|
160
|
-
)
|
|
161
|
-
definition = service.build_suggestion_workflow(suggestion, enabled=req.enabled)
|
|
162
|
-
|
|
163
|
-
# Idempotent per suggestion: reuse the workflow provenance match used
|
|
164
|
-
# for recipes, keyed on suggestion_id instead of recipe_id.
|
|
165
|
-
existing = None
|
|
166
|
-
for workflow in store.list_workflows(workspace_id=scope).get("workflows") or []:
|
|
167
|
-
metadata = (workflow or {}).get("metadata") or {}
|
|
168
|
-
if (
|
|
169
|
-
metadata.get("created_from") == "automation_suggestion"
|
|
170
|
-
and metadata.get("suggestion_id") == req.suggestion_id
|
|
171
|
-
):
|
|
172
|
-
existing = workflow
|
|
173
|
-
break
|
|
174
|
-
if existing is None and suggestion.get("recipe_id"):
|
|
175
|
-
existing = find_installed_recipe_workflow(
|
|
176
|
-
store.list_workflows(workspace_id=scope).get("workflows"),
|
|
177
|
-
suggestion["recipe_id"],
|
|
178
|
-
)
|
|
179
|
-
if existing is not None:
|
|
180
|
-
return {
|
|
181
|
-
"workflow": existing,
|
|
182
|
-
"suggestion": suggestion,
|
|
183
|
-
"enabled": bool((existing.get("metadata") or {}).get("automation_state") == "enabled"),
|
|
184
|
-
"already_installed": True,
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
errors = validate_definition({"name": definition["name"], "nodes": definition["nodes"]})
|
|
188
|
-
if errors:
|
|
189
|
-
raise HTTPException(status_code=400, detail={"validation_errors": errors})
|
|
190
|
-
workflow = store.create_workflow(
|
|
191
|
-
name=definition["name"],
|
|
192
|
-
steps=legacy_steps_from_nodes(definition["nodes"]),
|
|
193
|
-
nodes=definition["nodes"],
|
|
194
|
-
metadata=definition["metadata"],
|
|
195
|
-
user_email=user or None,
|
|
196
|
-
graph=workspace_graph(),
|
|
197
|
-
workspace_id=scope,
|
|
198
|
-
)
|
|
199
|
-
append_audit_event(
|
|
200
|
-
"automation_suggestion_installed",
|
|
201
|
-
user_email=user,
|
|
202
|
-
workflow_id=workflow.get("id"),
|
|
203
|
-
suggestion_id=req.suggestion_id,
|
|
204
|
-
suggestion_kind=suggestion.get("kind"),
|
|
205
|
-
enabled=bool(req.enabled),
|
|
206
|
-
)
|
|
207
|
-
return {
|
|
208
|
-
"workflow": workflow,
|
|
209
|
-
"suggestion": suggestion,
|
|
210
|
-
"enabled": bool(req.enabled),
|
|
211
|
-
"already_installed": False,
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
def _stamp_last_execution(
|
|
215
|
-
workflow_id: str, last_execution: dict, scope: Optional[str]
|
|
216
|
-
) -> None:
|
|
217
|
-
"""Persist the execution record on the workflow's metadata (merge)."""
|
|
218
|
-
try:
|
|
219
|
-
store.update_workflow_definition(
|
|
220
|
-
workflow_id,
|
|
221
|
-
metadata={"last_execution": last_execution},
|
|
222
|
-
workspace_id=scope,
|
|
223
|
-
)
|
|
224
|
-
except Exception: # noqa: BLE001 — surfacing must never undo the run
|
|
225
|
-
quiet()
|
|
226
|
-
|
|
227
|
-
@router.post("/api/automation/run-now")
|
|
228
|
-
async def automation_run_now(req: AutomationRunNowRequest, request: Request):
|
|
229
|
-
"""Run an installed automation once — dry-run by default.
|
|
230
|
-
|
|
231
|
-
* ``dry_run=true``: deterministic report of what WOULD happen; no
|
|
232
|
-
runner executes and nothing but the ``last_execution`` stamp moves.
|
|
233
|
-
* ``dry_run=false``: one real execution through the async run
|
|
234
|
-
executor; the request waits briefly for completion, stamps the
|
|
235
|
-
result, and enqueues failed executions into the review inbox.
|
|
236
|
-
"""
|
|
237
|
-
user = require_user(request)
|
|
238
|
-
scope = gate_write(request)
|
|
239
|
-
try:
|
|
240
|
-
workflow = store.get_workflow(req.workflow_id, workspace_id=scope)
|
|
241
|
-
except FileNotFoundError as exc:
|
|
242
|
-
raise HTTPException(
|
|
243
|
-
status_code=404, detail=f"Automation not found: {req.workflow_id}"
|
|
244
|
-
) from exc
|
|
245
|
-
if not is_automation_workflow(workflow):
|
|
246
|
-
raise HTTPException(
|
|
247
|
-
status_code=404,
|
|
248
|
-
detail=f"Workflow is not an installed automation: {req.workflow_id}",
|
|
249
|
-
)
|
|
250
|
-
|
|
251
|
-
if req.dry_run:
|
|
252
|
-
report = dry_run_report(workflow)
|
|
253
|
-
last_execution = build_last_execution(
|
|
254
|
-
mode="dry_run",
|
|
255
|
-
status=report["status"],
|
|
256
|
-
summary=report["summary"],
|
|
257
|
-
)
|
|
258
|
-
_stamp_last_execution(req.workflow_id, last_execution, scope)
|
|
259
|
-
append_audit_event(
|
|
260
|
-
"automation_run_now",
|
|
261
|
-
user_email=user,
|
|
262
|
-
workflow_id=req.workflow_id,
|
|
263
|
-
dry_run=True,
|
|
264
|
-
status=report["status"],
|
|
265
|
-
)
|
|
266
|
-
return {
|
|
267
|
-
"workflow_id": req.workflow_id,
|
|
268
|
-
"dry_run": True,
|
|
269
|
-
"status": report["status"],
|
|
270
|
-
"report": report,
|
|
271
|
-
"last_execution": last_execution,
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
if run_executor is None:
|
|
275
|
-
raise HTTPException(
|
|
276
|
-
status_code=503,
|
|
277
|
-
detail="Automation execution runtime is not available.",
|
|
278
|
-
)
|
|
279
|
-
started = await run_executor.start_workflow(
|
|
280
|
-
workflow,
|
|
281
|
-
workflow_id=req.workflow_id,
|
|
282
|
-
user_email=user or None,
|
|
283
|
-
scope=scope,
|
|
284
|
-
inputs={"trigger": "run_now"},
|
|
285
|
-
)
|
|
286
|
-
run_id = str(((started or {}).get("run") or {}).get("id") or "")
|
|
287
|
-
|
|
288
|
-
# Bounded, non-cancelling wait: poll the durable run row so a slow
|
|
289
|
-
# execution keeps running in the background instead of being aborted.
|
|
290
|
-
run = (started or {}).get("run") or {}
|
|
291
|
-
deadline = time.monotonic() + _run_now_wait_seconds()
|
|
292
|
-
while time.monotonic() < deadline:
|
|
293
|
-
try:
|
|
294
|
-
run = store.get_workflow_run(run_id, workspace_id=scope)
|
|
295
|
-
except FileNotFoundError:
|
|
296
|
-
break
|
|
297
|
-
if str(run.get("status") or "") in RUN_TERMINAL_STATUSES:
|
|
298
|
-
break
|
|
299
|
-
await asyncio.sleep(0.05)
|
|
300
|
-
|
|
301
|
-
status = str(run.get("status") or "running")
|
|
302
|
-
completed = status in RUN_TERMINAL_STATUSES
|
|
303
|
-
summary = (
|
|
304
|
-
summarize_workflow_run(run)
|
|
305
|
-
if completed
|
|
306
|
-
else "started — still running in the background"
|
|
307
|
-
)
|
|
308
|
-
last_execution = build_last_execution(
|
|
309
|
-
mode="live",
|
|
310
|
-
status=status if completed else "running",
|
|
311
|
-
summary=summary,
|
|
312
|
-
run_id=run_id or None,
|
|
313
|
-
)
|
|
314
|
-
_stamp_last_execution(req.workflow_id, last_execution, scope)
|
|
315
|
-
|
|
316
|
-
review_item = None
|
|
317
|
-
if status == "failed":
|
|
318
|
-
review_item = enqueue_failed_execution(
|
|
319
|
-
review_queue,
|
|
320
|
-
workflow=workflow,
|
|
321
|
-
run_id=run_id or None,
|
|
322
|
-
error=summary,
|
|
323
|
-
user_email=user or None,
|
|
324
|
-
workspace_id=scope,
|
|
325
|
-
)
|
|
326
|
-
append_audit_event(
|
|
327
|
-
"automation_run_now",
|
|
328
|
-
user_email=user,
|
|
329
|
-
workflow_id=req.workflow_id,
|
|
330
|
-
dry_run=False,
|
|
331
|
-
run_id=run_id or None,
|
|
332
|
-
status=last_execution["status"],
|
|
333
|
-
)
|
|
334
|
-
payload = {
|
|
335
|
-
"workflow_id": req.workflow_id,
|
|
336
|
-
"dry_run": False,
|
|
337
|
-
"status": last_execution["status"],
|
|
338
|
-
"run_id": run_id or None,
|
|
339
|
-
"last_execution": last_execution,
|
|
340
|
-
}
|
|
341
|
-
if review_item is not None:
|
|
342
|
-
payload["review_item_id"] = review_item.get("id")
|
|
343
|
-
return payload
|
|
344
|
-
|
|
345
|
-
def _combined_runs(request: Request, limit: int = 20) -> dict[str, Any]:
|
|
346
|
-
require_user(request)
|
|
347
|
-
scope = gate_read(request)
|
|
348
|
-
capped = max(1, min(int(limit or 20), 100))
|
|
349
|
-
if hasattr(store, "list_combined_runs"):
|
|
350
|
-
payload = store.list_combined_runs(limit=capped, workspace_id=scope)
|
|
351
|
-
return _with_run_truncation_meta(payload, capped=capped)
|
|
352
|
-
# Store without the helper: merge the two existing listings in place.
|
|
353
|
-
agent_listing = (
|
|
354
|
-
store.list_agents(workspace_id=scope)
|
|
355
|
-
if hasattr(store, "list_agents")
|
|
356
|
-
else {"runs": []}
|
|
357
|
-
)
|
|
358
|
-
# Pull a wide window so agent+workflow merge can still fill ``capped``
|
|
359
|
-
# after sort; true total is the unscoped merge size before slice.
|
|
360
|
-
workflow_listing = (
|
|
361
|
-
store.list_workflow_runs(limit=max(capped, 100), workspace_id=scope)
|
|
362
|
-
if hasattr(store, "list_workflow_runs")
|
|
363
|
-
else {"runs": []}
|
|
364
|
-
)
|
|
365
|
-
from latticeai.core.workspace_runs import WorkspaceRuns
|
|
366
|
-
|
|
367
|
-
rows = []
|
|
368
|
-
for run in agent_listing.get("runs") or []:
|
|
369
|
-
rows.append(
|
|
370
|
-
_ensure_activity_run_source(
|
|
371
|
-
WorkspaceRuns.activity_run_row(run, source="agent")
|
|
372
|
-
)
|
|
373
|
-
)
|
|
374
|
-
for run in workflow_listing.get("runs") or []:
|
|
375
|
-
rows.append(
|
|
376
|
-
_ensure_activity_run_source(
|
|
377
|
-
WorkspaceRuns.activity_run_row(run, source="workflow")
|
|
378
|
-
)
|
|
379
|
-
)
|
|
380
|
-
rows.sort(key=lambda row: str(row.get("started_at") or ""), reverse=True)
|
|
381
|
-
total = len(rows)
|
|
382
|
-
return {
|
|
383
|
-
"runs": rows[:capped],
|
|
384
|
-
"total": total,
|
|
385
|
-
"truncated": total > capped,
|
|
386
|
-
}
|
|
387
|
-
|
|
388
|
-
@router.get("/api/activity/runs")
|
|
389
|
-
async def activity_runs(request: Request, limit: int = 20):
|
|
390
|
-
"""Unified agent + workflow run timeline (layout rebuild screen 09)."""
|
|
391
|
-
return _combined_runs(request, limit=limit)
|
|
392
|
-
|
|
393
|
-
@router.get("/automations/runs/combined")
|
|
394
|
-
async def automations_runs_combined(request: Request, limit: int = 20):
|
|
395
|
-
"""Alias used by the frontend handoff for the same combined timeline."""
|
|
396
|
-
return _combined_runs(request, limit=limit)
|
|
397
|
-
|
|
398
|
-
return router
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
__all__ = ["create_automation_intelligence_router"]
|
|
@@ -1,199 +0,0 @@
|
|
|
1
|
-
"""Proactive Brain Intelligence API router (v9.3.0).
|
|
2
|
-
|
|
3
|
-
Exposes :class:`~latticeai.services.brain_intelligence.BrainIntelligenceService`:
|
|
4
|
-
health diagnosis, proactive insights, contradiction surfacing, and consent-first
|
|
5
|
-
consolidation. Read endpoints use the workspace read gate; consolidation with
|
|
6
|
-
``apply=true`` is a write and is audited.
|
|
7
|
-
"""
|
|
8
|
-
|
|
9
|
-
from __future__ import annotations
|
|
10
|
-
|
|
11
|
-
from typing import Callable, Optional
|
|
12
|
-
|
|
13
|
-
from fastapi import APIRouter, HTTPException, Request
|
|
14
|
-
from pydantic import BaseModel
|
|
15
|
-
|
|
16
|
-
from latticeai.services.brain_intelligence import BrainIntelligenceService
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
class ResolveContradictionRequest(BaseModel):
|
|
20
|
-
"""Settle one contradiction proposal (v11.1.0).
|
|
21
|
-
|
|
22
|
-
``item_id`` is a Review Center item raised by synthesis; ``resolution`` is
|
|
23
|
-
one of ``keep_old`` / ``replace`` / ``keep_both_temporal``. Approval and
|
|
24
|
-
the temporal stamping happen together — there is no way to stamp the graph
|
|
25
|
-
without approving the proposal first.
|
|
26
|
-
"""
|
|
27
|
-
|
|
28
|
-
item_id: str
|
|
29
|
-
resolution: str = "keep_both_temporal"
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
class ConsolidateRequest(BaseModel):
|
|
33
|
-
apply: bool = False
|
|
34
|
-
# v9.6.x additive alias: dry_run=true means apply=false. When provided it
|
|
35
|
-
# takes precedence over ``apply`` (explicit intent wins); omitted keeps the
|
|
36
|
-
# v9.3.0 contract unchanged. Default behaviour is always a dry run.
|
|
37
|
-
dry_run: Optional[bool] = None
|
|
38
|
-
|
|
39
|
-
def effective_apply(self) -> bool:
|
|
40
|
-
if self.dry_run is not None:
|
|
41
|
-
return not self.dry_run
|
|
42
|
-
return self.apply
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
def create_brain_intelligence_router(
|
|
46
|
-
*,
|
|
47
|
-
service: BrainIntelligenceService,
|
|
48
|
-
require_user: Callable[[Request], str],
|
|
49
|
-
gate_read: Callable[[Request], Optional[str]],
|
|
50
|
-
gate_write: Callable[[Request], Optional[str]],
|
|
51
|
-
append_audit_event: Callable[..., None],
|
|
52
|
-
) -> APIRouter:
|
|
53
|
-
router = APIRouter()
|
|
54
|
-
|
|
55
|
-
@router.get("/api/brain/health")
|
|
56
|
-
async def brain_health(request: Request):
|
|
57
|
-
user = require_user(request)
|
|
58
|
-
scope = gate_read(request)
|
|
59
|
-
return service.health_report(user_email=user, workspace_id=scope)
|
|
60
|
-
|
|
61
|
-
@router.get("/api/brain/insights")
|
|
62
|
-
async def brain_insights(request: Request):
|
|
63
|
-
user = require_user(request)
|
|
64
|
-
scope = gate_read(request)
|
|
65
|
-
return service.insights(user_email=user, workspace_id=scope)
|
|
66
|
-
|
|
67
|
-
@router.get("/api/brain/contradictions")
|
|
68
|
-
async def brain_contradictions(request: Request):
|
|
69
|
-
user = require_user(request)
|
|
70
|
-
scope = gate_read(request)
|
|
71
|
-
return service.contradictions(user_email=user, workspace_id=scope)
|
|
72
|
-
|
|
73
|
-
@router.get("/api/brain/garden")
|
|
74
|
-
async def brain_garden(request: Request, limit: int = 8):
|
|
75
|
-
"""Knowledge garden overview (v9.9.7): recent / contradictions /
|
|
76
|
-
stale / frequent, read-only and workspace-scoped."""
|
|
77
|
-
user = require_user(request)
|
|
78
|
-
scope = gate_read(request)
|
|
79
|
-
return service.garden_overview(user_email=user, workspace_id=scope, limit=limit)
|
|
80
|
-
|
|
81
|
-
@router.get("/api/brain/vector-freshness")
|
|
82
|
-
async def brain_vector_freshness(request: Request):
|
|
83
|
-
"""Vector index freshness summary (read-only, never raises).
|
|
84
|
-
|
|
85
|
-
Fixed contract consumed by the frontend:
|
|
86
|
-
``{"status": "ready"|"pending"|"unavailable", "pending_items": int,
|
|
87
|
-
"total_items": int, "detail": str}``.
|
|
88
|
-
|
|
89
|
-
Additive since v11.2.0: ``breakdown`` splits the backlog into
|
|
90
|
-
``embedded``/``missing``/``stale``/``queued`` when the store can. The
|
|
91
|
-
four keys above are unchanged, so existing readers see no difference.
|
|
92
|
-
"""
|
|
93
|
-
user = require_user(request)
|
|
94
|
-
scope = gate_read(request)
|
|
95
|
-
return service.vector_freshness(user_email=user, workspace_id=scope)
|
|
96
|
-
|
|
97
|
-
@router.get("/api/brain/duplicates")
|
|
98
|
-
async def brain_duplicates(request: Request):
|
|
99
|
-
"""Graph-layer duplicate node candidates (read-only)."""
|
|
100
|
-
user = require_user(request)
|
|
101
|
-
scope = gate_read(request)
|
|
102
|
-
return service.graph_duplicates(user_email=user, workspace_id=scope)
|
|
103
|
-
|
|
104
|
-
@router.get("/api/brain/quality-report")
|
|
105
|
-
async def brain_quality_report(request: Request):
|
|
106
|
-
"""Combined graph quality report: duplicates, contradictions, stale
|
|
107
|
-
nodes, edge quality (read-only)."""
|
|
108
|
-
user = require_user(request)
|
|
109
|
-
scope = gate_read(request)
|
|
110
|
-
return service.quality_report(user_email=user, workspace_id=scope)
|
|
111
|
-
|
|
112
|
-
@router.get("/api/brain/proactive-brief")
|
|
113
|
-
async def brain_proactive_brief(request: Request):
|
|
114
|
-
"""Brain Brief proactive section (v11.1.0): what the Brain noticed.
|
|
115
|
-
|
|
116
|
-
Read-only — it reports the proposals already waiting in the Review
|
|
117
|
-
Center and never raises new ones.
|
|
118
|
-
"""
|
|
119
|
-
user = require_user(request)
|
|
120
|
-
scope = gate_read(request)
|
|
121
|
-
return service.proactive_brief(user_email=user, workspace_id=scope)
|
|
122
|
-
|
|
123
|
-
@router.get("/api/brain/importance")
|
|
124
|
-
async def brain_importance(request: Request):
|
|
125
|
-
"""Importance + decay report (read-only): what has gone quiet."""
|
|
126
|
-
user = require_user(request)
|
|
127
|
-
scope = gate_read(request)
|
|
128
|
-
return service.importance_report(user_email=user, workspace_id=scope)
|
|
129
|
-
|
|
130
|
-
@router.post("/api/brain/synthesize")
|
|
131
|
-
async def brain_synthesize(request: Request):
|
|
132
|
-
"""Run one synthesis pass. Output is review proposals, never writes."""
|
|
133
|
-
user = require_user(request)
|
|
134
|
-
scope = gate_write(request)
|
|
135
|
-
result = service.synthesize(user_email=user, workspace_id=scope)
|
|
136
|
-
append_audit_event(
|
|
137
|
-
"brain_synthesize",
|
|
138
|
-
user_email=user,
|
|
139
|
-
proposed=result.get("proposed_total", 0),
|
|
140
|
-
suppressed=result.get("suppressed", 0),
|
|
141
|
-
)
|
|
142
|
-
return result
|
|
143
|
-
|
|
144
|
-
@router.post("/api/brain/contradictions/propose")
|
|
145
|
-
async def brain_propose_contradictions(request: Request):
|
|
146
|
-
"""Raise a review proposal for each contradicting pair of memories."""
|
|
147
|
-
user = require_user(request)
|
|
148
|
-
scope = gate_write(request)
|
|
149
|
-
result = service.propose_contradictions(user_email=user, workspace_id=scope)
|
|
150
|
-
append_audit_event(
|
|
151
|
-
"brain_contradiction_proposals",
|
|
152
|
-
user_email=user,
|
|
153
|
-
proposed=result.get("proposed_count", 0),
|
|
154
|
-
suppressed=result.get("suppressed", 0),
|
|
155
|
-
)
|
|
156
|
-
return result
|
|
157
|
-
|
|
158
|
-
@router.post("/api/brain/contradictions/resolve")
|
|
159
|
-
async def brain_resolve_contradiction(
|
|
160
|
-
req: ResolveContradictionRequest, request: Request
|
|
161
|
-
):
|
|
162
|
-
"""Approve a contradiction proposal and stamp the graph accordingly."""
|
|
163
|
-
user = require_user(request)
|
|
164
|
-
scope = gate_write(request)
|
|
165
|
-
try:
|
|
166
|
-
result = service.resolve_contradiction(
|
|
167
|
-
req.item_id, resolution=req.resolution, workspace_id=scope
|
|
168
|
-
)
|
|
169
|
-
except ValueError as exc:
|
|
170
|
-
raise HTTPException(status_code=400, detail=str(exc))
|
|
171
|
-
except FileNotFoundError:
|
|
172
|
-
raise HTTPException(status_code=404, detail=req.item_id)
|
|
173
|
-
append_audit_event(
|
|
174
|
-
"brain_contradiction_resolved",
|
|
175
|
-
user_email=user,
|
|
176
|
-
item_id=req.item_id,
|
|
177
|
-
resolution=req.resolution,
|
|
178
|
-
)
|
|
179
|
-
return result
|
|
180
|
-
|
|
181
|
-
@router.post("/api/brain/consolidate")
|
|
182
|
-
async def brain_consolidate(req: ConsolidateRequest, request: Request):
|
|
183
|
-
user = require_user(request)
|
|
184
|
-
apply = req.effective_apply()
|
|
185
|
-
scope = gate_write(request) if apply else gate_read(request)
|
|
186
|
-
result = service.consolidate(apply=apply, user_email=user, workspace_id=scope)
|
|
187
|
-
append_audit_event(
|
|
188
|
-
"brain_consolidate",
|
|
189
|
-
user_email=user,
|
|
190
|
-
mode=result.get("mode"),
|
|
191
|
-
duplicate_memories=result.get("duplicate_memory_count", 0),
|
|
192
|
-
pruned=result.get("pruned", 0),
|
|
193
|
-
)
|
|
194
|
-
return result
|
|
195
|
-
|
|
196
|
-
return router
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
__all__ = ["create_brain_intelligence_router"]
|