ltcai 11.5.1 → 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.
- package/README.md +92 -117
- package/bin/ltcai.js +234 -24
- package/docs/CHANGELOG.md +129 -0
- package/docs/COMMUNITY_AND_PLUGINS.md +1 -1
- package/docs/DEVELOPMENT.md +8 -6
- package/docs/ENTERPRISE.md +2 -1
- package/docs/HYBRID_CLOUD_KG_STREAMING.md +8 -4
- 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/architecture.md +4 -4
- package/docs/kg-schema.md +1 -1
- package/docs/v11.5.2_TIGHT_SHIP_PLAN.md +144 -0
- 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 -597
- package/latticeai/__init__.py +1 -1
- package/latticeai/api/agent_worker_seam.py +7 -80
- package/latticeai/api/health.py +26 -20
- package/latticeai/api/local_files.py +25 -608
- package/latticeai/api/models.py +12 -87
- package/latticeai/api/search.py +17 -231
- 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/cli/runtime.py +7 -3
- package/latticeai/core/agent_permission.py +16 -91
- package/latticeai/core/config.py +47 -3
- package/latticeai/core/csrf.py +28 -2
- package/latticeai/core/embedding_providers/__init__.py +3 -5
- package/latticeai/core/embedding_providers/base.py +1 -1
- package/latticeai/core/embedding_providers/text.py +1 -1
- package/latticeai/core/http_origin.py +146 -0
- package/latticeai/core/io_utils.py +2 -11
- package/latticeai/core/messages.py +94 -531
- package/latticeai/core/model_compat.py +2 -8
- package/latticeai/core/module_probe.py +37 -0
- package/latticeai/core/security.py +13 -0
- package/latticeai/core/sessions.py +3 -2
- package/latticeai/core/sse.py +25 -0
- package/latticeai/core/users.py +0 -9
- package/latticeai/models/router/documents.py +0 -35
- package/latticeai/models/router/registry.py +0 -4
- package/latticeai/runtime/access_runtime.py +16 -18
- 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 +101 -389
- package/latticeai/runtime/build_phases/foundation.py +82 -392
- package/latticeai/runtime/build_phases/web.py +91 -406
- 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 +14 -143
- package/latticeai/runtime/security_runtime.py +10 -103
- package/latticeai/services/architecture_readiness.py +82 -43
- package/latticeai/services/model_capability_registry.py +0 -15
- package/latticeai/services/model_catalog.py +0 -7
- package/latticeai/services/model_loading.py +3 -2
- package/latticeai/services/model_runtime/__init__.py +2 -68
- package/latticeai/services/model_runtime/engines.py +27 -128
- package/latticeai/services/model_runtime/loading.py +2 -2
- 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 -390
- 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 +3 -6
- 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 +19 -6
- package/scripts/check_current_release_docs.mjs +7 -4
- package/scripts/check_legacy_debt.mjs +1 -1
- 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 +31 -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 +223 -43
- package/src-tauri/src/main.rs +27 -17
- package/src-tauri/src/topology.rs +58 -88
- package/src-tauri/tauri.conf.json +2 -3
- package/static/app/asset-manifest.json +41 -41
- package/static/app/assets/{Act-Drs_jd-O.js → Act-CToZnOHz.js} +1 -1
- package/static/app/assets/{AdminConsole-BqNx5oF6.js → AdminConsole-D5kbrBu8.js} +1 -1
- package/static/app/assets/{Brain-C7bdNmz-.js → Brain-C6zCdv4S.js} +1 -1
- package/static/app/assets/{BrainHome-DcTnINcI.js → BrainHome-D4n3EDUt.js} +1 -1
- package/static/app/assets/{BrainSignals-Df1MRIPD.js → BrainSignals-CzeZ-vQ9.js} +1 -1
- package/static/app/assets/{Capture-Dgsxvnbx.js → Capture-DPcyTczq.js} +1 -1
- package/static/app/assets/{Chronicle-BrkTxJK1.js → Chronicle-D3Nx-oWS.js} +1 -1
- package/static/app/assets/{CommandPalette-DDziw7Oj.js → CommandPalette-DPPvpp28.js} +1 -1
- package/static/app/assets/{Library-CYxeuraA.js → Library-Co2qTu3I.js} +1 -1
- package/static/app/assets/{LivingBrain-CNp6mvCm.js → LivingBrain-Bsmzwy8_.js} +1 -1
- package/static/app/assets/{ProductFlow-BVsowu3Z.js → ProductFlow-CBLdmesT.js} +1 -1
- package/static/app/assets/{ReviewCard-D1LbOfJS.js → ReviewCard-CfR6DLs7.js} +1 -1
- package/static/app/assets/{System-DcRAq7wj.js → System-Ce-P2YL6.js} +1 -1
- package/static/app/assets/arrow-left-DD5jFGHV.js +1 -0
- package/static/app/assets/{bot-BK2xQ9mN.js → bot-C5-XIww2.js} +1 -1
- package/static/app/assets/{brain-BZNztFBb.js → brain-CFRJUvdX.js} +1 -1
- package/static/app/assets/{button-CjueubVZ.js → button-DI1KcQP-.js} +1 -1
- package/static/app/assets/circle-check-CY20XMCh.js +1 -0
- package/static/app/assets/{circle-pause-HRjSmpyY.js → circle-pause-C8HHyood.js} +1 -1
- package/static/app/assets/{circle-play-CMcKDVwu.js → circle-play-Cufc235E.js} +1 -1
- package/static/app/assets/{cpu-B_MyTfYr.js → cpu-DW2_JVsv.js} +1 -1
- package/static/app/assets/{download-CTy0EByV.js → download-gbdKTBb8.js} +1 -1
- package/static/app/assets/{folder-open-DF6bLhTg.js → folder-open-C9ydyX33.js} +1 -1
- package/static/app/assets/{hard-drive-CpWHG77C.js → hard-drive-BxNMzBM3.js} +1 -1
- package/static/app/assets/index-BovSWRmQ.css +2 -0
- package/static/app/assets/{index-Dd6abJHX.js → index-q9XyDY7A.js} +3 -3
- package/static/app/assets/{input-bVgIRN3s.js → input-GLD94Y_K.js} +1 -1
- package/static/app/assets/{link-2-bg6CG5Vk.js → link-2-B4Y0gz1S.js} +1 -1
- package/static/app/assets/{permissionCopy-DFTf1HDZ.js → permissionCopy-CxrV0fvU.js} +1 -1
- package/static/app/assets/{primitives-D7D-sQ3T.js → primitives-2ANdAsu2.js} +1 -1
- package/static/app/assets/search-DTxtjQ07.js +1 -0
- package/static/app/assets/{share-2-BLzo7U4L.js → share-2-DxArfiuI.js} +1 -1
- package/static/app/assets/{shield-alert-BmngqnsJ.js → shield-alert-CgftuFFs.js} +1 -1
- package/static/app/assets/{textarea-DOSlAQQ3.js → textarea-BHVBAoCr.js} +1 -1
- package/static/app/assets/{useFocusTrap-pZhgeee4.js → useFocusTrap-Dzq2j_OG.js} +1 -1
- package/static/app/assets/{useMutation-BMDwNk4I.js → useMutation-BcqfMLNq.js} +1 -1
- package/static/app/assets/{useQuery-CSjKttRo.js → useQuery-DBzLKCOv.js} +1 -1
- package/static/app/assets/{utils-D3u_yv7B.js → utils-B-Aah1bO.js} +1 -1
- package/static/app/assets/{workspace-H3bMjYXC.js → workspace-B6fFhoko.js} +1 -1
- package/static/app/index.html +4 -4
- package/static/sw.js +1 -1
- package/desktop/electron/README.md +0 -9
- package/desktop/electron/main.cjs +0 -58
- package/desktop/electron/preload.cjs +0 -5
- 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 -546
- 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 -170
- package/lattice_brain/graph/vector_index/brute_force.py +0 -114
- package/lattice_brain/graph/vector_index/hnsw.py +0 -293
- package/lattice_brain/graph/vector_index/jobs.py +0 -287
- package/lattice_brain/graph/vector_index/quantized.py +0 -151
- 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 -383
- package/lattice_brain/memory.py +0 -102
- 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 -714
- package/lattice_brain/quality.py +0 -592
- 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 -244
- 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 -360
- 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 -901
- package/latticeai/api/chat_contracts.py +0 -73
- package/latticeai/api/chat_documents.py +0 -276
- package/latticeai/api/chat_helpers.py +0 -463
- 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 -485
- 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 -410
- 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 -351
- 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 -157
- 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/graph_curator.py +0 -11
- package/latticeai/core/hooks.py +0 -11
- package/latticeai/core/invitations.py +0 -131
- package/latticeai/core/legacy_compatibility.py +0 -243
- package/latticeai/core/local_embeddings.py +0 -104
- 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/multi_agent.py +0 -11
- 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 -423
- package/latticeai/core/run_store.py +0 -252
- package/latticeai/core/timezones.py +0 -80
- package/latticeai/core/workflow_engine.py +0 -11
- package/latticeai/core/workspace_computer_memory.py +0 -84
- package/latticeai/core/workspace_graph_trace.py +0 -161
- 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 -988
- 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 -69
- 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 -118
- package/latticeai/core/workspace_runs.py +0 -732
- package/latticeai/core/workspace_skills.py +0 -114
- 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 -163
- 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 -128
- package/latticeai/runtime/network_config_runtime.py +0 -56
- package/latticeai/runtime/permission_mode_wiring.py +0 -119
- 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 -689
- 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 -264
- 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 -505
- package/latticeai/services/chat_service.py +0 -243
- package/latticeai/services/chronicle.py +0 -557
- 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 -255
- 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 -178
- package/latticeai/services/ingestion.py +0 -11
- package/latticeai/services/interop_bridges.py +0 -978
- package/latticeai/services/kg_portability.py +0 -11
- 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/model_recommendation.py +0 -224
- package/latticeai/services/model_runtime/cloud.py +0 -87
- package/latticeai/services/multimodal_streaming.py +0 -129
- package/latticeai/services/network_boundary_service.py +0 -154
- 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 -170
- 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 -81
- package/latticeai/services/triggers.py +0 -378
- package/latticeai/services/upload_service.py +0 -185
- package/latticeai/services/workspace_service.py +0 -171
- package/latticeai/setup/__init__.py +0 -25
- package/latticeai/setup/auto_setup.py +0 -857
- package/latticeai/setup/demo_corpus.py +0 -98
- package/latticeai/setup/wizard/__init__.py +0 -126
- package/latticeai/setup/wizard/catalog.py +0 -172
- package/latticeai/setup/wizard/detect.py +0 -323
- package/latticeai/setup/wizard/install.py +0 -348
- package/latticeai/setup/wizard/paths.py +0 -168
- 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 -907
- package/scripts/generate_rust_parity_fixtures.py +0 -1000
- package/scripts/measure_brain_home_fill.mjs +0 -141
- package/scripts/migrate_brain_storage.py +0 -57
- 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/arrow-left-BHYmOTWU.js +0 -1
- package/static/app/assets/circle-check-CLeWJr67.js +0 -1
- package/static/app/assets/index-DxmOfNRi.css +0 -2
- package/static/app/assets/search-BgSVX6NG.js +0 -1
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
"""Compatibility shim: physically moved to ``lattice_brain.portability``.
|
|
2
|
-
|
|
3
|
-
Aliases itself to the physical module so identity, module-level state, and
|
|
4
|
-
monkeypatching keep working through the old import path.
|
|
5
|
-
"""
|
|
6
|
-
|
|
7
|
-
import sys
|
|
8
|
-
|
|
9
|
-
import lattice_brain.portability as _impl
|
|
10
|
-
|
|
11
|
-
sys.modules[__name__] = _impl
|
|
@@ -1,465 +0,0 @@
|
|
|
1
|
-
"""Local folder knowledge-source API and optional filesystem watcher."""
|
|
2
|
-
|
|
3
|
-
import logging
|
|
4
|
-
import threading
|
|
5
|
-
from pathlib import Path
|
|
6
|
-
from typing import Any, Callable, Dict, Optional
|
|
7
|
-
|
|
8
|
-
from fastapi import APIRouter, HTTPException, Request
|
|
9
|
-
from pydantic import BaseModel, Field
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
class LocalTreeRequest(BaseModel):
|
|
13
|
-
path: str
|
|
14
|
-
max_items: int = 200
|
|
15
|
-
approved: bool = False
|
|
16
|
-
approval_token: Optional[str] = None
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
class LocalKnowledgeAuditRequest(BaseModel):
|
|
20
|
-
path: str
|
|
21
|
-
include_ocr: bool = False
|
|
22
|
-
max_files: int = 50_000
|
|
23
|
-
approved: bool = False
|
|
24
|
-
approval_token: Optional[str] = None
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
class LocalKnowledgeIndexRequest(BaseModel):
|
|
28
|
-
path: str
|
|
29
|
-
include_ocr: bool = False
|
|
30
|
-
watch_enabled: bool = False
|
|
31
|
-
max_files: int = 5_000
|
|
32
|
-
consent: Dict[str, Any] = Field(default_factory=dict)
|
|
33
|
-
approved: bool = False
|
|
34
|
-
approval_token: Optional[str] = None
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
class LocalKnowledgeWatchRequest(BaseModel):
|
|
38
|
-
source_id: str
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
class _LocalWatchHandler:
|
|
42
|
-
def __init__(self, schedule_change: Callable[[], None]):
|
|
43
|
-
self._schedule_change = schedule_change
|
|
44
|
-
|
|
45
|
-
def on_any_event(self, event): # pragma: no cover - exercised by OS watcher
|
|
46
|
-
if getattr(event, "is_directory", False):
|
|
47
|
-
return
|
|
48
|
-
self._schedule_change()
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
class LocalKnowledgeWatcher:
|
|
52
|
-
"""Debounced watchdog wrapper for approved local knowledge sources."""
|
|
53
|
-
|
|
54
|
-
def __init__(self, get_graph: Callable[[], Any], *, debounce_seconds: float = 5.0, hooks: Any = None):
|
|
55
|
-
self._get_graph = get_graph
|
|
56
|
-
self._debounce_seconds = debounce_seconds
|
|
57
|
-
self._hooks = hooks
|
|
58
|
-
self._lock = threading.Lock()
|
|
59
|
-
self._watched: Dict[str, Dict[str, Any]] = {}
|
|
60
|
-
# watchdog is optional; these stay None when it is not installed.
|
|
61
|
-
self._observer_cls: Optional[Any] = None
|
|
62
|
-
self._event_handler_base: Optional[Any] = None
|
|
63
|
-
self._import_error = ""
|
|
64
|
-
try:
|
|
65
|
-
from watchdog.events import FileSystemEventHandler
|
|
66
|
-
from watchdog.observers import Observer
|
|
67
|
-
|
|
68
|
-
self._observer_cls = Observer
|
|
69
|
-
self._event_handler_base = FileSystemEventHandler
|
|
70
|
-
except Exception as exc: # pragma: no cover - depends on optional dependency
|
|
71
|
-
self._import_error = str(exc)
|
|
72
|
-
|
|
73
|
-
@property
|
|
74
|
-
def available(self) -> bool:
|
|
75
|
-
return self._observer_cls is not None and self._event_handler_base is not None
|
|
76
|
-
|
|
77
|
-
def status(self) -> Dict[str, Any]:
|
|
78
|
-
with self._lock:
|
|
79
|
-
active = {
|
|
80
|
-
source_id: {
|
|
81
|
-
"root_path": item["source"].get("root_path"),
|
|
82
|
-
"last_event_at": item.get("last_event_at"),
|
|
83
|
-
"last_indexed_at": item.get("last_indexed_at"),
|
|
84
|
-
"last_error": item.get("last_error"),
|
|
85
|
-
}
|
|
86
|
-
for source_id, item in self._watched.items()
|
|
87
|
-
}
|
|
88
|
-
return {
|
|
89
|
-
"available": self.available,
|
|
90
|
-
"error": "" if self.available else self._import_error or "watchdog is not installed",
|
|
91
|
-
"debounce_seconds": self._debounce_seconds,
|
|
92
|
-
"active": active,
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
def restore_enabled_sources(self) -> Dict[str, Any]:
|
|
96
|
-
graph = self._get_graph()
|
|
97
|
-
if graph is None:
|
|
98
|
-
return {"restored": 0, "available": self.available}
|
|
99
|
-
restored = 0
|
|
100
|
-
try:
|
|
101
|
-
for source in graph.local_sources().get("sources", []):
|
|
102
|
-
if source.get("watch_enabled"):
|
|
103
|
-
consent = source.get("consent") or {}
|
|
104
|
-
restored_source = {
|
|
105
|
-
**source,
|
|
106
|
-
# Older sources had no explicit scope. Their local
|
|
107
|
-
# knowledge belongs to the personal Brain, never every
|
|
108
|
-
# workspace.
|
|
109
|
-
"workspace_id": source.get("workspace_id") or consent.get("workspace_id") or "personal",
|
|
110
|
-
}
|
|
111
|
-
result = self.start_source(restored_source)
|
|
112
|
-
if result.get("watching"):
|
|
113
|
-
restored += 1
|
|
114
|
-
except Exception as exc:
|
|
115
|
-
logging.warning("local knowledge watcher restore failed: %s", exc)
|
|
116
|
-
return {"restored": restored, "available": self.available}
|
|
117
|
-
|
|
118
|
-
def start_source(self, source: Dict[str, Any]) -> Dict[str, Any]:
|
|
119
|
-
source_id = str(source.get("id") or "")
|
|
120
|
-
root_path = str(source.get("root_path") or "")
|
|
121
|
-
if not source_id or not root_path:
|
|
122
|
-
return {"watching": False, "error": "source_id and root_path are required"}
|
|
123
|
-
if not self.available:
|
|
124
|
-
return {"watching": False, "source_id": source_id, "error": self._import_error or "watchdog is not installed"}
|
|
125
|
-
root = Path(root_path).expanduser().resolve()
|
|
126
|
-
if not root.exists() or not root.is_dir():
|
|
127
|
-
return {"watching": False, "source_id": source_id, "error": "source folder is not available"}
|
|
128
|
-
|
|
129
|
-
self.stop_source(source_id)
|
|
130
|
-
|
|
131
|
-
handler_base = self._event_handler_base
|
|
132
|
-
observer_cls = self._observer_cls
|
|
133
|
-
if handler_base is None or observer_cls is None:
|
|
134
|
-
return {
|
|
135
|
-
"watching": False,
|
|
136
|
-
"source_id": source_id,
|
|
137
|
-
"error": self._import_error or "watchdog is not installed",
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
class Handler(_LocalWatchHandler, handler_base): # type: ignore[misc, valid-type]
|
|
141
|
-
def __init__(handler_self):
|
|
142
|
-
handler_base.__init__(handler_self) # type: ignore[misc]
|
|
143
|
-
_LocalWatchHandler.__init__(handler_self, lambda: self._schedule(source_id))
|
|
144
|
-
|
|
145
|
-
observer = observer_cls()
|
|
146
|
-
try:
|
|
147
|
-
observer.schedule(Handler(), str(root), recursive=True)
|
|
148
|
-
observer.start()
|
|
149
|
-
except Exception as exc:
|
|
150
|
-
logging.warning("local knowledge watcher start failed for %s: %s", root, exc)
|
|
151
|
-
return {"watching": False, "source_id": source_id, "error": str(exc)}
|
|
152
|
-
|
|
153
|
-
with self._lock:
|
|
154
|
-
self._watched[source_id] = {
|
|
155
|
-
"observer": observer,
|
|
156
|
-
"timer": None,
|
|
157
|
-
"source": dict(source),
|
|
158
|
-
"last_event_at": None,
|
|
159
|
-
"last_indexed_at": None,
|
|
160
|
-
"last_error": None,
|
|
161
|
-
}
|
|
162
|
-
return {"watching": True, "source_id": source_id, "root_path": str(root)}
|
|
163
|
-
|
|
164
|
-
def stop_source(self, source_id: str) -> Dict[str, Any]:
|
|
165
|
-
with self._lock:
|
|
166
|
-
item = self._watched.pop(source_id, None)
|
|
167
|
-
if not item:
|
|
168
|
-
return {"stopped": False, "source_id": source_id}
|
|
169
|
-
timer = item.get("timer")
|
|
170
|
-
if timer:
|
|
171
|
-
timer.cancel()
|
|
172
|
-
observer = item.get("observer")
|
|
173
|
-
try:
|
|
174
|
-
if observer is None:
|
|
175
|
-
raise RuntimeError("watch entry has no observer")
|
|
176
|
-
observer.stop()
|
|
177
|
-
observer.join(timeout=3)
|
|
178
|
-
except Exception as exc:
|
|
179
|
-
logging.warning("local knowledge watcher stop failed for %s: %s", source_id, exc)
|
|
180
|
-
return {"stopped": True, "source_id": source_id}
|
|
181
|
-
|
|
182
|
-
def stop_all(self) -> None:
|
|
183
|
-
for source_id in list(self.status().get("active", {}).keys()):
|
|
184
|
-
self.stop_source(source_id)
|
|
185
|
-
|
|
186
|
-
def _schedule(self, source_id: str) -> None:
|
|
187
|
-
with self._lock:
|
|
188
|
-
item = self._watched.get(source_id)
|
|
189
|
-
if not item:
|
|
190
|
-
return
|
|
191
|
-
timer = item.get("timer")
|
|
192
|
-
if timer:
|
|
193
|
-
timer.cancel()
|
|
194
|
-
item["last_event_at"] = _now_seconds()
|
|
195
|
-
timer = threading.Timer(self._debounce_seconds, self._run_index, args=(source_id,))
|
|
196
|
-
timer.daemon = True
|
|
197
|
-
item["timer"] = timer
|
|
198
|
-
timer.start()
|
|
199
|
-
|
|
200
|
-
def _run_index(self, source_id: str) -> None:
|
|
201
|
-
with self._lock:
|
|
202
|
-
item = self._watched.get(source_id)
|
|
203
|
-
if not item:
|
|
204
|
-
return
|
|
205
|
-
source = dict(item["source"])
|
|
206
|
-
item["timer"] = None
|
|
207
|
-
graph = self._get_graph()
|
|
208
|
-
if graph is None:
|
|
209
|
-
return
|
|
210
|
-
consent = source.get("consent") or {}
|
|
211
|
-
root = source.get("root_path")
|
|
212
|
-
if self._hooks is not None:
|
|
213
|
-
self._hooks.fire_hook("pre_index", "folder.reindex",
|
|
214
|
-
payload={"source_id": source_id, "root_path": root, "trigger": "watch"})
|
|
215
|
-
try:
|
|
216
|
-
result = graph.index_local_folder(
|
|
217
|
-
Path(source["root_path"]),
|
|
218
|
-
include_ocr=bool(source.get("include_ocr")),
|
|
219
|
-
watch_enabled=True,
|
|
220
|
-
user_email=consent.get("approved_by"),
|
|
221
|
-
workspace_id=source.get("workspace_id") or consent.get("workspace_id") or "personal",
|
|
222
|
-
consent=consent,
|
|
223
|
-
source_id_override=source_id,
|
|
224
|
-
)
|
|
225
|
-
with self._lock:
|
|
226
|
-
if source_id in self._watched:
|
|
227
|
-
self._watched[source_id]["last_indexed_at"] = _now_seconds()
|
|
228
|
-
self._watched[source_id]["last_error"] = None
|
|
229
|
-
if self._hooks is not None:
|
|
230
|
-
self._hooks.fire_hook("post_index", "folder.reindex",
|
|
231
|
-
payload={"source_id": source_id, "root_path": root, "trigger": "watch", "status": "ok"})
|
|
232
|
-
counts = (result.get("counts") or {}) if isinstance(result, dict) else {}
|
|
233
|
-
if int(counts.get("indexed") or 0) + int(counts.get("deleted") or 0) > 0:
|
|
234
|
-
self._hooks.fire_hook(
|
|
235
|
-
"post_tool",
|
|
236
|
-
"tool.kg_ingest.local_folder",
|
|
237
|
-
payload={
|
|
238
|
-
"tool": "kg_ingest.local_folder",
|
|
239
|
-
"status": "ok",
|
|
240
|
-
"source": "ingestion",
|
|
241
|
-
"source_type": "local_folder",
|
|
242
|
-
"source_id": source_id,
|
|
243
|
-
},
|
|
244
|
-
user_email=consent.get("approved_by"),
|
|
245
|
-
workspace_id=source.get("workspace_id"),
|
|
246
|
-
)
|
|
247
|
-
except Exception as exc:
|
|
248
|
-
logging.warning("local knowledge watcher reindex failed for %s: %s", source_id, exc)
|
|
249
|
-
with self._lock:
|
|
250
|
-
if source_id in self._watched:
|
|
251
|
-
self._watched[source_id]["last_error"] = str(exc)
|
|
252
|
-
if self._hooks is not None:
|
|
253
|
-
self._hooks.fire_hook("post_index", "folder.reindex",
|
|
254
|
-
payload={"source_id": source_id, "root_path": root, "trigger": "watch", "status": "error", "error": str(exc)})
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
def _now_seconds() -> float:
|
|
258
|
-
import time
|
|
259
|
-
|
|
260
|
-
return time.time()
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
def create_local_knowledge_router(
|
|
264
|
-
*,
|
|
265
|
-
get_graph: Callable[[], Any],
|
|
266
|
-
require_graph: Callable[[], None],
|
|
267
|
-
require_user: Callable[[Request], str],
|
|
268
|
-
require_local_user: Callable[[Request], str],
|
|
269
|
-
local_permission_response: Callable[..., dict],
|
|
270
|
-
require_local_approval: Callable[..., None],
|
|
271
|
-
watcher: Optional[LocalKnowledgeWatcher] = None,
|
|
272
|
-
hooks: Any = None,
|
|
273
|
-
workspace_service: Any = None,
|
|
274
|
-
) -> APIRouter:
|
|
275
|
-
router = APIRouter()
|
|
276
|
-
|
|
277
|
-
def graph():
|
|
278
|
-
require_graph()
|
|
279
|
-
return get_graph()
|
|
280
|
-
|
|
281
|
-
def write_workspace(request: Request, user: str) -> Optional[str]:
|
|
282
|
-
requested = request.headers.get("X-Workspace-Id")
|
|
283
|
-
requested = requested.strip() if requested and requested.strip() else None
|
|
284
|
-
if workspace_service is None:
|
|
285
|
-
return requested
|
|
286
|
-
try:
|
|
287
|
-
return workspace_service.resolve_write_scope(requested, user or None)
|
|
288
|
-
except PermissionError as exc:
|
|
289
|
-
raise HTTPException(status_code=403, detail=str(exc)) from exc
|
|
290
|
-
|
|
291
|
-
@router.get("/knowledge-graph/local/roots")
|
|
292
|
-
async def knowledge_graph_local_roots(request: Request):
|
|
293
|
-
require_user(request)
|
|
294
|
-
return graph().discover_local_roots()
|
|
295
|
-
|
|
296
|
-
@router.get("/knowledge-graph/local/sources")
|
|
297
|
-
async def knowledge_graph_local_sources(request: Request):
|
|
298
|
-
require_user(request)
|
|
299
|
-
payload = graph().local_sources()
|
|
300
|
-
watch_status: Dict[str, Any] = (
|
|
301
|
-
watcher.status() if watcher else {"available": False, "active": {}}
|
|
302
|
-
)
|
|
303
|
-
active: Dict[str, Any] = dict(watch_status.get("active") or {})
|
|
304
|
-
for source in payload.get("sources", []):
|
|
305
|
-
source["watch_active"] = source.get("id") in active
|
|
306
|
-
source["watch_status"] = active.get(source.get("id"))
|
|
307
|
-
payload["watch"] = watch_status
|
|
308
|
-
return payload
|
|
309
|
-
|
|
310
|
-
@router.get("/knowledge-graph/local/health")
|
|
311
|
-
async def knowledge_graph_local_health(request: Request, error_samples: int = 3):
|
|
312
|
-
"""Per-folder memory state (v9.9.7): indexing coverage, failures with
|
|
313
|
-
their stored reasons, and watch state.
|
|
314
|
-
|
|
315
|
-
Vector freshness rides along as an explicitly **global** figure — the
|
|
316
|
-
vector index is not per-folder, and claiming otherwise would invent a
|
|
317
|
-
number.
|
|
318
|
-
"""
|
|
319
|
-
require_user(request)
|
|
320
|
-
kg = graph()
|
|
321
|
-
payload = kg.local_source_health(error_samples=error_samples)
|
|
322
|
-
watch_status: Dict[str, Any] = (
|
|
323
|
-
watcher.status() if watcher else {"available": False, "active": {}}
|
|
324
|
-
)
|
|
325
|
-
active = dict(watch_status.get("active") or {})
|
|
326
|
-
for folder in payload.get("folders", []):
|
|
327
|
-
folder["watch_active"] = folder.get("id") in active
|
|
328
|
-
payload["watch"] = watch_status
|
|
329
|
-
try:
|
|
330
|
-
payload["vector_freshness_global"] = kg.vector_freshness()
|
|
331
|
-
except Exception as exc: # noqa: BLE001 — one missing signal, not a 500
|
|
332
|
-
payload["vector_freshness_global"] = {
|
|
333
|
-
"status": "unavailable", "detail": str(exc),
|
|
334
|
-
"pending_items": 0, "total_items": 0,
|
|
335
|
-
}
|
|
336
|
-
return payload
|
|
337
|
-
|
|
338
|
-
@router.get("/knowledge-graph/local/watch/status")
|
|
339
|
-
async def knowledge_graph_local_watch_status(request: Request):
|
|
340
|
-
require_user(request)
|
|
341
|
-
graph()
|
|
342
|
-
return watcher.status() if watcher else {"available": False, "active": {}, "error": "watcher unavailable"}
|
|
343
|
-
|
|
344
|
-
@router.post("/knowledge-graph/local/watch/stop")
|
|
345
|
-
async def knowledge_graph_local_watch_stop(req: LocalKnowledgeWatchRequest, request: Request):
|
|
346
|
-
require_user(request)
|
|
347
|
-
kg = graph()
|
|
348
|
-
try:
|
|
349
|
-
kg.set_local_source_watch(req.source_id, False)
|
|
350
|
-
except ValueError as exc:
|
|
351
|
-
raise HTTPException(status_code=404, detail=str(exc)) from exc
|
|
352
|
-
result = watcher.stop_source(req.source_id) if watcher else {"stopped": False, "source_id": req.source_id}
|
|
353
|
-
return {"status": "ok", "watch": result}
|
|
354
|
-
|
|
355
|
-
@router.post("/knowledge-graph/local/tree")
|
|
356
|
-
async def knowledge_graph_local_tree(req: LocalTreeRequest, request: Request):
|
|
357
|
-
current_user = require_local_user(request)
|
|
358
|
-
kg = graph()
|
|
359
|
-
if not req.approved:
|
|
360
|
-
return local_permission_response(req.path, "list", current_user)
|
|
361
|
-
require_local_approval(token=req.approval_token, path=req.path, action="list", user_email=current_user)
|
|
362
|
-
try:
|
|
363
|
-
return kg.preview_local_tree(Path(req.path), max_items=req.max_items)
|
|
364
|
-
except ValueError as exc:
|
|
365
|
-
raise HTTPException(status_code=400, detail=str(exc)) from exc
|
|
366
|
-
|
|
367
|
-
@router.post("/knowledge-graph/local/audit")
|
|
368
|
-
async def knowledge_graph_local_audit(req: LocalKnowledgeAuditRequest, request: Request):
|
|
369
|
-
current_user = require_local_user(request)
|
|
370
|
-
kg = graph()
|
|
371
|
-
if not req.approved:
|
|
372
|
-
return local_permission_response(req.path, "list", current_user)
|
|
373
|
-
require_local_approval(token=req.approval_token, path=req.path, action="list", user_email=current_user)
|
|
374
|
-
try:
|
|
375
|
-
return kg.audit_local_folder(Path(req.path), include_ocr=req.include_ocr, max_files=req.max_files)
|
|
376
|
-
except ValueError as exc:
|
|
377
|
-
raise HTTPException(status_code=400, detail=str(exc)) from exc
|
|
378
|
-
|
|
379
|
-
@router.post("/knowledge-graph/local/index")
|
|
380
|
-
async def knowledge_graph_local_index(req: LocalKnowledgeIndexRequest, request: Request):
|
|
381
|
-
current_user = require_local_user(request)
|
|
382
|
-
workspace_id = write_workspace(request, current_user)
|
|
383
|
-
kg = graph()
|
|
384
|
-
if not req.approved:
|
|
385
|
-
return local_permission_response(req.path, "read", current_user)
|
|
386
|
-
require_local_approval(token=req.approval_token, path=req.path, action="read", user_email=current_user)
|
|
387
|
-
consent = {
|
|
388
|
-
**(req.consent or {}),
|
|
389
|
-
"approved_by": current_user,
|
|
390
|
-
"workspace_id": workspace_id or "personal",
|
|
391
|
-
}
|
|
392
|
-
source_id_override = None
|
|
393
|
-
try:
|
|
394
|
-
target_root = Path(req.path).expanduser().resolve()
|
|
395
|
-
target_scope = workspace_id or "personal"
|
|
396
|
-
for source in kg.local_sources().get("sources", []):
|
|
397
|
-
source_consent = source.get("consent") or {}
|
|
398
|
-
source_scope = source_consent.get("workspace_id") or "personal"
|
|
399
|
-
source_root_value = str(source.get("root_path") or "").strip()
|
|
400
|
-
if not source_root_value:
|
|
401
|
-
continue
|
|
402
|
-
source_root = Path(source_root_value).expanduser().resolve()
|
|
403
|
-
if source_scope == target_scope and source_root == target_root:
|
|
404
|
-
source_id_override = source.get("id")
|
|
405
|
-
break
|
|
406
|
-
except Exception:
|
|
407
|
-
# Source reuse is a compatibility optimization; indexing still has
|
|
408
|
-
# a deterministic workspace-scoped ID when discovery is unavailable.
|
|
409
|
-
source_id_override = None
|
|
410
|
-
if hooks is not None:
|
|
411
|
-
hooks.fire_hook("pre_index", "folder.index",
|
|
412
|
-
payload={"root_path": req.path, "trigger": "connect", "watch": req.watch_enabled},
|
|
413
|
-
user_email=current_user, workspace_id=workspace_id)
|
|
414
|
-
try:
|
|
415
|
-
result = kg.index_local_folder(
|
|
416
|
-
Path(req.path),
|
|
417
|
-
include_ocr=req.include_ocr,
|
|
418
|
-
watch_enabled=req.watch_enabled,
|
|
419
|
-
user_email=current_user,
|
|
420
|
-
workspace_id=workspace_id or "personal",
|
|
421
|
-
consent=consent,
|
|
422
|
-
max_files=req.max_files,
|
|
423
|
-
source_id_override=source_id_override,
|
|
424
|
-
)
|
|
425
|
-
except ValueError as exc:
|
|
426
|
-
if hooks is not None:
|
|
427
|
-
hooks.fire_hook("post_index", "folder.index",
|
|
428
|
-
payload={"root_path": req.path, "trigger": "connect", "status": "error", "error": str(exc)},
|
|
429
|
-
user_email=current_user, workspace_id=workspace_id)
|
|
430
|
-
raise HTTPException(status_code=400, detail=str(exc)) from exc
|
|
431
|
-
if hooks is not None:
|
|
432
|
-
_idx = (result.get("index") or {}) if isinstance(result, dict) else {}
|
|
433
|
-
hooks.fire_hook("post_index", "folder.index",
|
|
434
|
-
payload={"root_path": req.path, "trigger": "connect", "status": "ok",
|
|
435
|
-
"indexed": _idx.get("indexed") or (result or {}).get("indexed")},
|
|
436
|
-
user_email=current_user, workspace_id=workspace_id)
|
|
437
|
-
counts = (result.get("counts") or {}) if isinstance(result, dict) else {}
|
|
438
|
-
if int(counts.get("indexed") or 0) + int(counts.get("deleted") or 0) > 0:
|
|
439
|
-
hooks.fire_hook(
|
|
440
|
-
"post_tool",
|
|
441
|
-
"tool.kg_ingest.local_folder",
|
|
442
|
-
payload={
|
|
443
|
-
"tool": "kg_ingest.local_folder",
|
|
444
|
-
"status": "ok",
|
|
445
|
-
"source": "ingestion",
|
|
446
|
-
"source_type": "local_folder",
|
|
447
|
-
"source_id": (result.get("source") or {}).get("id") if isinstance(result, dict) else None,
|
|
448
|
-
},
|
|
449
|
-
user_email=current_user,
|
|
450
|
-
workspace_id=workspace_id,
|
|
451
|
-
)
|
|
452
|
-
|
|
453
|
-
if watcher:
|
|
454
|
-
if req.watch_enabled:
|
|
455
|
-
source_payload = {
|
|
456
|
-
**result.get("source", {}),
|
|
457
|
-
"workspace_id": workspace_id or "personal",
|
|
458
|
-
"consent": consent,
|
|
459
|
-
}
|
|
460
|
-
result["watch"] = watcher.start_source(source_payload)
|
|
461
|
-
else:
|
|
462
|
-
result["watch"] = watcher.stop_source(result.get("source", {}).get("id", ""))
|
|
463
|
-
return result
|
|
464
|
-
|
|
465
|
-
return router
|
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
"""Long-Term Memory platform + Memory Manager (v3.2.0).
|
|
2
|
-
|
|
3
|
-
Parts 7, 8 and 13. Lattice AI already persists memory in several real stores;
|
|
4
|
-
before this service they were unrelated. ``MemoryService`` unifies them behind
|
|
5
|
-
one façade and adds a Memory Manager that reports usage / sources / health /
|
|
6
|
-
size / type and supports recall / inspect / prune / compact / rebuild / clear.
|
|
7
|
-
|
|
8
|
-
Memory tiers and their real backing store (nothing is fabricated — a tier with
|
|
9
|
-
no backing reports ``unavailable``):
|
|
10
|
-
|
|
11
|
-
* **workspace** — personal workspace memories (``WorkspaceOS`` memories)
|
|
12
|
-
* **project** — memories scoped to a non-personal (organization) workspace
|
|
13
|
-
* **agent** — agent memory snapshots captured during runs
|
|
14
|
-
* **conversation** — chat history conversations
|
|
15
|
-
* **graph** — Knowledge Graph nodes (entities + relations)
|
|
16
|
-
* **vector** — local embedding vector index
|
|
17
|
-
|
|
18
|
-
The service never invents counts or health: every number is read from the
|
|
19
|
-
underlying store, and missing stores surface as ``unavailable``.
|
|
20
|
-
|
|
21
|
-
Split into cohesive submodules in v11.3.0 (no behaviour change): ``constants``
|
|
22
|
-
(tier vocabulary + the visual passthrough), ``stores`` (reads over the real
|
|
23
|
-
backends), ``manager`` (the cross-tier report), ``brief`` and ``proof`` (the
|
|
24
|
-
home screen's briefing and its evidence), ``recall`` (unified retrieval and
|
|
25
|
-
per-tier inspection), ``maintenance`` (the mutating half), and ``service`` (the
|
|
26
|
-
composed class). This module re-exports every name the single file exposed, so
|
|
27
|
-
``latticeai.services.memory_service.X`` keeps working.
|
|
28
|
-
"""
|
|
29
|
-
|
|
30
|
-
from __future__ import annotations
|
|
31
|
-
|
|
32
|
-
# The single file had no ``__all__``, so its public surface was "every module
|
|
33
|
-
# global" — including the names it imported for its own use. The redundant-alias
|
|
34
|
-
# form reproduces exactly that surface and marks each name as a deliberate
|
|
35
|
-
# re-export rather than a leftover import.
|
|
36
|
-
#
|
|
37
|
-
# Stubbing note: rebinding one of these *here* changes only this module's name.
|
|
38
|
-
# The submodule that calls it holds its own reference, so a test standing in for
|
|
39
|
-
# ``LOGGER`` or ``_now`` patches the submodule that uses it.
|
|
40
|
-
# noqa on the next line, not a redundant alias: the single file exposed this
|
|
41
|
-
# name *renamed* (``memory_service._now``), and reproducing that surface exactly
|
|
42
|
-
# is the point — importing it as ``now_iso`` would add a name the module never had.
|
|
43
|
-
from latticeai.core.timeutil import now_iso as _now # noqa: F401
|
|
44
|
-
from latticeai.core.workspace_os_utils import _file_size as _file_size
|
|
45
|
-
|
|
46
|
-
from .constants import LOGGER as LOGGER
|
|
47
|
-
from .constants import MAX_RECALL_THUMBNAIL_CHARS as MAX_RECALL_THUMBNAIL_CHARS
|
|
48
|
-
from .constants import TIERS as TIERS
|
|
49
|
-
from .constants import WORKSPACE_KINDS as WORKSPACE_KINDS
|
|
50
|
-
from .constants import MemoryServiceError as MemoryServiceError
|
|
51
|
-
from .constants import _visual_fields as _visual_fields
|
|
52
|
-
from .service import MemoryService as MemoryService
|
|
@@ -1,100 +0,0 @@
|
|
|
1
|
-
"""The seam the memory mixins share.
|
|
2
|
-
|
|
3
|
-
``MemoryService`` is assembled from the store-read, manager, brief, proof,
|
|
4
|
-
recall and maintenance mixins. Every one of them reads constructor state it
|
|
5
|
-
does not own, and most of them call methods another mixin implements. That
|
|
6
|
-
contract existed as an unwritten convention inside one class; this module
|
|
7
|
-
writes it down.
|
|
8
|
-
|
|
9
|
-
Typing-only, exactly like :mod:`lattice_brain.portability._contract`: the
|
|
10
|
-
declarations below are never executed as behaviour — each body is a bare
|
|
11
|
-
``raise NotImplementedError`` that the composed class always overrides — so the
|
|
12
|
-
MRO and every method resolution stay what the single-file class had. Adding a
|
|
13
|
-
cross-mixin call without declaring it here is a type error.
|
|
14
|
-
"""
|
|
15
|
-
|
|
16
|
-
from __future__ import annotations
|
|
17
|
-
|
|
18
|
-
from pathlib import Path
|
|
19
|
-
from typing import Any, Dict, List, Optional
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
class MemoryCore:
|
|
23
|
-
"""What any memory mixin may assume about ``self``.
|
|
24
|
-
|
|
25
|
-
Members are declared, not implemented: the implementation lives in
|
|
26
|
-
``service.py`` (constructor state) or in whichever mixin owns the method.
|
|
27
|
-
"""
|
|
28
|
-
|
|
29
|
-
# ── State owned by MemoryService.__init__ ────────────────────────────────
|
|
30
|
-
_store: Any
|
|
31
|
-
_kg: Any
|
|
32
|
-
_enable_graph: bool
|
|
33
|
-
_data_dir: Path
|
|
34
|
-
_history_file: Path
|
|
35
|
-
_conversation_store: Any
|
|
36
|
-
|
|
37
|
-
# ── stores.py: the reads every other surface is built on ─────────────────
|
|
38
|
-
def _workspace_memories(
|
|
39
|
-
self, *, user_email: Optional[str], workspace_id: Optional[str]
|
|
40
|
-
) -> List[Dict[str, Any]]:
|
|
41
|
-
raise NotImplementedError
|
|
42
|
-
|
|
43
|
-
def _all_memories(self) -> List[Dict[str, Any]]:
|
|
44
|
-
raise NotImplementedError
|
|
45
|
-
|
|
46
|
-
def _snapshots(self, *, workspace_id: Optional[str]) -> List[Dict[str, Any]]:
|
|
47
|
-
raise NotImplementedError
|
|
48
|
-
|
|
49
|
-
def _conversations(self) -> List[Dict[str, Any]]:
|
|
50
|
-
raise NotImplementedError
|
|
51
|
-
|
|
52
|
-
def _scoped_conversations(
|
|
53
|
-
self, *, user_email: Optional[str], workspace_id: Optional[str]
|
|
54
|
-
) -> List[Dict[str, Any]]:
|
|
55
|
-
raise NotImplementedError
|
|
56
|
-
|
|
57
|
-
def _kg_stats(self) -> Optional[Dict[str, Any]]:
|
|
58
|
-
raise NotImplementedError
|
|
59
|
-
|
|
60
|
-
def _kg_index(self) -> Optional[Dict[str, Any]]:
|
|
61
|
-
raise NotImplementedError
|
|
62
|
-
|
|
63
|
-
# ── manager.py: the cross-tier report the brief and the proof rest on ────
|
|
64
|
-
def manager(
|
|
65
|
-
self, *, user_email: Optional[str] = None, workspace_id: Optional[str] = None
|
|
66
|
-
) -> Dict[str, Any]:
|
|
67
|
-
raise NotImplementedError
|
|
68
|
-
|
|
69
|
-
# ── proof.py / recall.py: what the brief composes ────────────────────────
|
|
70
|
-
def brain_proof(
|
|
71
|
-
self,
|
|
72
|
-
*,
|
|
73
|
-
user_email: Optional[str] = None,
|
|
74
|
-
workspace_id: Optional[str] = None,
|
|
75
|
-
active_model: Optional[str] = None,
|
|
76
|
-
recall_query: str = "",
|
|
77
|
-
limit: int = 3,
|
|
78
|
-
) -> Dict[str, Any]:
|
|
79
|
-
raise NotImplementedError
|
|
80
|
-
|
|
81
|
-
def recall(
|
|
82
|
-
self,
|
|
83
|
-
query: str,
|
|
84
|
-
*,
|
|
85
|
-
user_email: Optional[str] = None,
|
|
86
|
-
workspace_id: Optional[str] = None,
|
|
87
|
-
limit: int = 20,
|
|
88
|
-
) -> Dict[str, Any]:
|
|
89
|
-
raise NotImplementedError
|
|
90
|
-
|
|
91
|
-
# ── maintenance.py: clear() delegates every scope to prune() ─────────────
|
|
92
|
-
def prune(
|
|
93
|
-
self,
|
|
94
|
-
*,
|
|
95
|
-
ids: Optional[List[str]] = None,
|
|
96
|
-
kind: Optional[str] = None,
|
|
97
|
-
user_email: Optional[str] = None,
|
|
98
|
-
workspace_id: Optional[str] = None,
|
|
99
|
-
) -> Dict[str, Any]:
|
|
100
|
-
raise NotImplementedError
|