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
package/latticeai/api/memory.py
DELETED
|
@@ -1,270 +0,0 @@
|
|
|
1
|
-
"""Memory platform + Memory Manager API router (v3.2.0).
|
|
2
|
-
|
|
3
|
-
Exposes :class:`~latticeai.services.memory_service.MemoryService` so the /app
|
|
4
|
-
Memory view can inspect every memory tier, recall across them, and run manager
|
|
5
|
-
operations (prune / compact / rebuild / clear). Full paths in decorators.
|
|
6
|
-
|
|
7
|
-
v11.1.0 adds the Self-Model surface (Track 4) on the same factory: the profile
|
|
8
|
-
the Brain holds about its owner is *memory*, so it is read, edited and deleted
|
|
9
|
-
here rather than behind a router of its own. Ownership is the whole point —
|
|
10
|
-
extraction only ever proposes (``/self-model/propose`` → Review Center →
|
|
11
|
-
``/self-model/apply``), while the user's own edits and deletions write
|
|
12
|
-
directly.
|
|
13
|
-
"""
|
|
14
|
-
|
|
15
|
-
from __future__ import annotations
|
|
16
|
-
|
|
17
|
-
from typing import Any, Callable, List, Optional
|
|
18
|
-
|
|
19
|
-
from fastapi import APIRouter, HTTPException, Request
|
|
20
|
-
from pydantic import BaseModel
|
|
21
|
-
|
|
22
|
-
from lattice_brain.self_model import SelfModelError
|
|
23
|
-
from latticeai.core.messages import http_error, resolve_language
|
|
24
|
-
from latticeai.services.memory_service import MemoryService
|
|
25
|
-
from latticeai.services.self_model_service import SelfModelService
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
class RecallRequest(BaseModel):
|
|
29
|
-
query: str = ""
|
|
30
|
-
limit: int = 20
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
class PruneRequest(BaseModel):
|
|
34
|
-
ids: List[str] = []
|
|
35
|
-
kind: Optional[str] = None
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
class RebuildRequest(BaseModel):
|
|
39
|
-
target: str = "vector"
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
class ClearRequest(BaseModel):
|
|
43
|
-
scope: str
|
|
44
|
-
confirm: bool = False
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
#: Self-Model failure codes → catalog ids. Brain Core raises codes (it cannot
|
|
48
|
-
#: import the catalog); the answer a person reads is chosen here, in their
|
|
49
|
-
#: language. An unknown code degrades to the generic entry rather than leaking
|
|
50
|
-
#: an English developer sentence.
|
|
51
|
-
SELF_MODEL_MESSAGES = {
|
|
52
|
-
"invalid_kind": "self_model.invalid_kind",
|
|
53
|
-
"text_required": "self_model.text_required",
|
|
54
|
-
"not_found": "self_model.not_found",
|
|
55
|
-
"not_self_model": "self_model.not_self_model",
|
|
56
|
-
"not_a_proposal": "self_model.not_a_proposal",
|
|
57
|
-
"empty_proposal": "self_model.empty_proposal",
|
|
58
|
-
"graph_unavailable": "self_model.graph_unavailable",
|
|
59
|
-
"queue_unavailable": "self_model.queue_unavailable",
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
def _self_model_error(exc: SelfModelError, request: Request) -> HTTPException:
|
|
64
|
-
status = 404 if exc.code == "not_found" else 400
|
|
65
|
-
message_id = SELF_MODEL_MESSAGES.get(exc.code, "self_model.invalid")
|
|
66
|
-
return http_error(status, message_id, resolve_language(request))
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
class SelfModelFactRequest(BaseModel):
|
|
70
|
-
"""A fact the user states about themselves (user-initiated direct write)."""
|
|
71
|
-
|
|
72
|
-
kind: str
|
|
73
|
-
text: str
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
class SelfModelProposeRequest(BaseModel):
|
|
77
|
-
"""Text to read for candidate facts. Output is proposals, never writes."""
|
|
78
|
-
|
|
79
|
-
text: str = ""
|
|
80
|
-
source: str = ""
|
|
81
|
-
max_proposals: int = 5
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
class SelfModelApplyRequest(BaseModel):
|
|
85
|
-
"""Approve one Self-Model proposal and write the fact it carries."""
|
|
86
|
-
|
|
87
|
-
item_id: str
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
def create_memory_router(
|
|
91
|
-
*,
|
|
92
|
-
service: MemoryService,
|
|
93
|
-
require_user: Callable[[Request], str],
|
|
94
|
-
get_current_user: Callable[[Request], Optional[str]],
|
|
95
|
-
gate_read: Callable[[Request], Optional[str]],
|
|
96
|
-
gate_write: Callable[[Request], Optional[str]],
|
|
97
|
-
append_audit_event: Callable[..., None],
|
|
98
|
-
active_model_getter: Callable[[], str] | None = None,
|
|
99
|
-
self_model: Any = None,
|
|
100
|
-
) -> APIRouter:
|
|
101
|
-
router = APIRouter()
|
|
102
|
-
# Derived from the memory service by default (graph + review queue), so the
|
|
103
|
-
# Self-Model routes exist on every build and report honestly when this
|
|
104
|
-
# Brain has no graph rather than disappearing from the API surface.
|
|
105
|
-
profile = self_model or SelfModelService(memory_service=service)
|
|
106
|
-
|
|
107
|
-
@router.get("/api/memory/manager")
|
|
108
|
-
async def memory_manager(request: Request):
|
|
109
|
-
user = require_user(request)
|
|
110
|
-
scope = gate_read(request)
|
|
111
|
-
return service.manager(user_email=user, workspace_id=scope)
|
|
112
|
-
|
|
113
|
-
@router.get("/api/memory/brain-quality")
|
|
114
|
-
async def brain_quality_summary(request: Request):
|
|
115
|
-
user = require_user(request)
|
|
116
|
-
scope = gate_read(request)
|
|
117
|
-
return service.brain_quality_summary(user_email=user, workspace_id=scope)
|
|
118
|
-
|
|
119
|
-
@router.get("/api/memory/brain-brief")
|
|
120
|
-
async def brain_brief(request: Request, q: str = "", limit: int = 3):
|
|
121
|
-
user = require_user(request)
|
|
122
|
-
scope = gate_read(request)
|
|
123
|
-
active_model = active_model_getter() if active_model_getter else ""
|
|
124
|
-
return service.brain_brief(
|
|
125
|
-
user_email=user,
|
|
126
|
-
workspace_id=scope,
|
|
127
|
-
active_model=active_model,
|
|
128
|
-
recall_query=q,
|
|
129
|
-
limit=limit,
|
|
130
|
-
)
|
|
131
|
-
|
|
132
|
-
@router.get("/api/memory/brain-proof")
|
|
133
|
-
async def brain_proof(request: Request, q: str = "", limit: int = 3):
|
|
134
|
-
user = require_user(request)
|
|
135
|
-
scope = gate_read(request)
|
|
136
|
-
active_model = active_model_getter() if active_model_getter else ""
|
|
137
|
-
return service.brain_proof(
|
|
138
|
-
user_email=user,
|
|
139
|
-
workspace_id=scope,
|
|
140
|
-
active_model=active_model,
|
|
141
|
-
recall_query=q,
|
|
142
|
-
limit=limit,
|
|
143
|
-
)
|
|
144
|
-
|
|
145
|
-
@router.get("/api/memory/tiers")
|
|
146
|
-
async def memory_tiers(request: Request):
|
|
147
|
-
require_user(request)
|
|
148
|
-
return service.tiers()
|
|
149
|
-
|
|
150
|
-
@router.get("/api/memory/inspect")
|
|
151
|
-
async def memory_inspect(request: Request, source: str, limit: int = 50):
|
|
152
|
-
user = require_user(request)
|
|
153
|
-
scope = gate_read(request)
|
|
154
|
-
try:
|
|
155
|
-
return service.inspect(source, user_email=user, workspace_id=scope, limit=limit)
|
|
156
|
-
except KeyError as exc:
|
|
157
|
-
raise http_error(404, "memory.unknown_source", resolve_language(request), source=source) from exc
|
|
158
|
-
|
|
159
|
-
@router.post("/api/memory/recall")
|
|
160
|
-
async def memory_recall(req: RecallRequest, request: Request):
|
|
161
|
-
user = require_user(request)
|
|
162
|
-
scope = gate_read(request)
|
|
163
|
-
return service.recall(req.query, user_email=user, workspace_id=scope, limit=req.limit)
|
|
164
|
-
|
|
165
|
-
@router.post("/api/memory/prune")
|
|
166
|
-
async def memory_prune(req: PruneRequest, request: Request):
|
|
167
|
-
user = require_user(request)
|
|
168
|
-
scope = gate_write(request)
|
|
169
|
-
result = service.prune(ids=req.ids, kind=req.kind, user_email=user, workspace_id=scope)
|
|
170
|
-
append_audit_event("memory_prune", user_email=user, count=result.get("count", 0))
|
|
171
|
-
return result
|
|
172
|
-
|
|
173
|
-
@router.post("/api/memory/compact")
|
|
174
|
-
async def memory_compact(request: Request):
|
|
175
|
-
user = require_user(request)
|
|
176
|
-
scope = gate_write(request)
|
|
177
|
-
result = service.compact(user_email=user, workspace_id=scope)
|
|
178
|
-
append_audit_event("memory_compact", user_email=user, compacted=result.get("compacted", 0))
|
|
179
|
-
return result
|
|
180
|
-
|
|
181
|
-
@router.post("/api/memory/rebuild")
|
|
182
|
-
async def memory_rebuild(req: RebuildRequest, request: Request):
|
|
183
|
-
user = require_user(request)
|
|
184
|
-
gate_write(request)
|
|
185
|
-
result = service.rebuild(req.target)
|
|
186
|
-
append_audit_event("memory_rebuild", user_email=user, target=req.target, status=result.get("status"))
|
|
187
|
-
return result
|
|
188
|
-
|
|
189
|
-
@router.post("/api/memory/clear")
|
|
190
|
-
async def memory_clear(req: ClearRequest, request: Request):
|
|
191
|
-
user = require_user(request)
|
|
192
|
-
scope = gate_write(request)
|
|
193
|
-
try:
|
|
194
|
-
result = service.clear(scope=req.scope, confirm=req.confirm, user_email=user, workspace_id=scope)
|
|
195
|
-
except ValueError as exc:
|
|
196
|
-
raise HTTPException(status_code=400, detail=str(exc)) from exc
|
|
197
|
-
append_audit_event("memory_clear", user_email=user, scope=req.scope)
|
|
198
|
-
return result
|
|
199
|
-
|
|
200
|
-
# ── Self-Model (v11.1.0) ─────────────────────────────────────────────
|
|
201
|
-
|
|
202
|
-
@router.get("/api/memory/self-model")
|
|
203
|
-
async def self_model_profile(request: Request):
|
|
204
|
-
"""What the Brain believes about its owner, and the injected summary."""
|
|
205
|
-
user = require_user(request)
|
|
206
|
-
scope = gate_read(request)
|
|
207
|
-
return profile.profile(user_email=user, workspace_id=scope)
|
|
208
|
-
|
|
209
|
-
@router.post("/api/memory/self-model")
|
|
210
|
-
async def self_model_upsert(req: SelfModelFactRequest, request: Request):
|
|
211
|
-
"""Add or correct one fact directly — the user owns their profile."""
|
|
212
|
-
user = require_user(request)
|
|
213
|
-
scope = gate_write(request)
|
|
214
|
-
try:
|
|
215
|
-
fact = profile.upsert(kind=req.kind, text=req.text, workspace_id=scope)
|
|
216
|
-
except SelfModelError as exc:
|
|
217
|
-
raise _self_model_error(exc, request) from exc
|
|
218
|
-
append_audit_event(
|
|
219
|
-
"self_model_upsert", user_email=user, node_id=fact["id"], kind=fact["kind"]
|
|
220
|
-
)
|
|
221
|
-
return fact
|
|
222
|
-
|
|
223
|
-
@router.delete("/api/memory/self-model/{node_id}")
|
|
224
|
-
async def self_model_delete(node_id: str, request: Request):
|
|
225
|
-
"""Forget one fact about the user, permanently."""
|
|
226
|
-
user = require_user(request)
|
|
227
|
-
gate_write(request)
|
|
228
|
-
try:
|
|
229
|
-
result = profile.delete(node_id)
|
|
230
|
-
except SelfModelError as exc:
|
|
231
|
-
raise _self_model_error(exc, request) from exc
|
|
232
|
-
append_audit_event("self_model_delete", user_email=user, node_id=node_id)
|
|
233
|
-
return result
|
|
234
|
-
|
|
235
|
-
@router.post("/api/memory/self-model/propose")
|
|
236
|
-
async def self_model_propose(req: SelfModelProposeRequest, request: Request):
|
|
237
|
-
"""Read text for candidate facts; every hit becomes a review proposal."""
|
|
238
|
-
user = require_user(request)
|
|
239
|
-
scope = gate_write(request)
|
|
240
|
-
result = profile.propose(
|
|
241
|
-
req.text,
|
|
242
|
-
source=req.source or None,
|
|
243
|
-
user_email=user,
|
|
244
|
-
workspace_id=scope,
|
|
245
|
-
max_proposals=req.max_proposals,
|
|
246
|
-
)
|
|
247
|
-
append_audit_event(
|
|
248
|
-
"self_model_proposed",
|
|
249
|
-
user_email=user,
|
|
250
|
-
proposed=result.get("proposed_count", 0),
|
|
251
|
-
)
|
|
252
|
-
return result
|
|
253
|
-
|
|
254
|
-
@router.post("/api/memory/self-model/apply")
|
|
255
|
-
async def self_model_apply(req: SelfModelApplyRequest, request: Request):
|
|
256
|
-
"""Approve a proposal and write the fact it carries into the graph."""
|
|
257
|
-
user = require_user(request)
|
|
258
|
-
scope = gate_write(request)
|
|
259
|
-
try:
|
|
260
|
-
result = profile.apply(req.item_id, workspace_id=scope)
|
|
261
|
-
except SelfModelError as exc:
|
|
262
|
-
raise _self_model_error(exc, request) from exc
|
|
263
|
-
except (KeyError, FileNotFoundError) as exc:
|
|
264
|
-
raise http_error(404, "review.item_not_found", resolve_language(request)) from exc
|
|
265
|
-
append_audit_event(
|
|
266
|
-
"self_model_applied", user_email=user, item_id=req.item_id
|
|
267
|
-
)
|
|
268
|
-
return result
|
|
269
|
-
|
|
270
|
-
return router
|
package/latticeai/api/network.py
DELETED
|
@@ -1,81 +0,0 @@
|
|
|
1
|
-
"""Brain Network API — device identity, peer pairing, knowledge exchange.
|
|
2
|
-
|
|
3
|
-
The /network/receive endpoint authenticates PEERS (signed device requests),
|
|
4
|
-
not user sessions; everything else requires a logged-in user, and pairing /
|
|
5
|
-
pushing are deliberate owner actions.
|
|
6
|
-
"""
|
|
7
|
-
|
|
8
|
-
from __future__ import annotations
|
|
9
|
-
|
|
10
|
-
from typing import Optional
|
|
11
|
-
|
|
12
|
-
from fastapi import APIRouter, HTTPException, Request
|
|
13
|
-
from pydantic import BaseModel
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
class PeerPairRequest(BaseModel):
|
|
17
|
-
name: str
|
|
18
|
-
base_url: str
|
|
19
|
-
public_key: str
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
class PeerPushRequest(BaseModel):
|
|
23
|
-
workspace_id: Optional[str] = None
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
def create_network_router(*, network, identity, require_user, require_admin) -> APIRouter:
|
|
27
|
-
router = APIRouter()
|
|
28
|
-
|
|
29
|
-
@router.get("/network/identity")
|
|
30
|
-
async def network_identity(request: Request):
|
|
31
|
-
require_user(request)
|
|
32
|
-
return identity.describe()
|
|
33
|
-
|
|
34
|
-
@router.get("/network/peers")
|
|
35
|
-
async def network_peers(request: Request):
|
|
36
|
-
require_admin(request)
|
|
37
|
-
return {"peers": network.list_peers()}
|
|
38
|
-
|
|
39
|
-
@router.post("/network/peers")
|
|
40
|
-
async def network_pair(req: PeerPairRequest, request: Request):
|
|
41
|
-
require_admin(request)
|
|
42
|
-
try:
|
|
43
|
-
return {"status": "paired", "peer": network.add_peer(
|
|
44
|
-
name=req.name, base_url=req.base_url, public_key=req.public_key,
|
|
45
|
-
)}
|
|
46
|
-
except ValueError as exc:
|
|
47
|
-
raise HTTPException(status_code=400, detail=str(exc)) from exc
|
|
48
|
-
|
|
49
|
-
@router.delete("/network/peers/{peer_id}")
|
|
50
|
-
async def network_unpair(peer_id: str, request: Request):
|
|
51
|
-
require_admin(request)
|
|
52
|
-
try:
|
|
53
|
-
return network.remove_peer(peer_id)
|
|
54
|
-
except FileNotFoundError as exc:
|
|
55
|
-
raise HTTPException(status_code=404, detail=f"Unknown peer: {exc}") from exc
|
|
56
|
-
|
|
57
|
-
@router.post("/network/push/{peer_id}")
|
|
58
|
-
async def network_push(peer_id: str, req: PeerPushRequest, request: Request):
|
|
59
|
-
require_admin(request)
|
|
60
|
-
try:
|
|
61
|
-
return network.push_to_peer(peer_id, workspace_id=req.workspace_id)
|
|
62
|
-
except FileNotFoundError as exc:
|
|
63
|
-
raise HTTPException(status_code=404, detail=f"Unknown peer: {exc}") from exc
|
|
64
|
-
except Exception as exc:
|
|
65
|
-
raise HTTPException(status_code=502, detail=f"Push failed: {exc}") from exc
|
|
66
|
-
|
|
67
|
-
@router.post("/network/receive")
|
|
68
|
-
async def network_receive(request: Request):
|
|
69
|
-
# Peer-authenticated: a paired device's signature replaces the session.
|
|
70
|
-
body = await request.body()
|
|
71
|
-
try:
|
|
72
|
-
return network.receive(dict(request.headers), body)
|
|
73
|
-
except PermissionError as exc:
|
|
74
|
-
raise HTTPException(status_code=403, detail=str(exc)) from exc
|
|
75
|
-
except ValueError as exc:
|
|
76
|
-
raise HTTPException(status_code=400, detail=str(exc)) from exc
|
|
77
|
-
|
|
78
|
-
return router
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
__all__ = ["create_network_router"]
|
|
@@ -1,225 +0,0 @@
|
|
|
1
|
-
"""Network boundary API — local_only / cloud_allowed dial + Phase 3 policy/UI."""
|
|
2
|
-
|
|
3
|
-
from __future__ import annotations
|
|
4
|
-
|
|
5
|
-
from typing import Any, Callable, Dict, Optional
|
|
6
|
-
|
|
7
|
-
from fastapi import APIRouter, HTTPException, Request
|
|
8
|
-
from pydantic import BaseModel, Field
|
|
9
|
-
|
|
10
|
-
from latticeai.core.messages import http_error, resolve_language, translate
|
|
11
|
-
from latticeai.core.network_boundary import network_mode_catalog, normalize_network_mode
|
|
12
|
-
from latticeai.services.cloud_egress_audit import record_cloud_egress
|
|
13
|
-
from latticeai.services.cloud_token_guard import budget_for
|
|
14
|
-
from latticeai.services.hybrid_context import build_minimal_context
|
|
15
|
-
from latticeai.services.hybrid_policy import HybridPolicyService
|
|
16
|
-
from latticeai.services.network_boundary_service import NetworkBoundaryService
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
class SetNetworkBoundaryRequest(BaseModel):
|
|
20
|
-
mode: str = Field(..., description="local_only | cloud_allowed")
|
|
21
|
-
workspace_id: Optional[str] = None
|
|
22
|
-
acknowledge_risk: bool = False
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
class PreviewRequest(BaseModel):
|
|
26
|
-
message: str
|
|
27
|
-
workspace_id: Optional[str] = None
|
|
28
|
-
top_k: int = 6
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
class SetNodeSensitivityRequest(BaseModel):
|
|
32
|
-
node_id: str
|
|
33
|
-
local_only: bool = True
|
|
34
|
-
reason: Optional[str] = None
|
|
35
|
-
workspace_id: Optional[str] = None
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
class SetHybridPolicyRequest(BaseModel):
|
|
39
|
-
workspace_id: Optional[str] = None
|
|
40
|
-
blocked_node_types: Optional[list[str]] = None
|
|
41
|
-
blocked_metadata_flags: Optional[list[str]] = None
|
|
42
|
-
auto_commit: Optional[bool] = None
|
|
43
|
-
allow_multimodal: Optional[bool] = None
|
|
44
|
-
min_extraction_confidence: Optional[float] = None
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
def create_network_boundary_router(
|
|
48
|
-
*,
|
|
49
|
-
service: NetworkBoundaryService,
|
|
50
|
-
require_user: Callable[..., str],
|
|
51
|
-
knowledge_graph: Any = None,
|
|
52
|
-
policy_service: Optional[HybridPolicyService] = None,
|
|
53
|
-
) -> APIRouter:
|
|
54
|
-
router = APIRouter(tags=["network-boundary"])
|
|
55
|
-
|
|
56
|
-
def _scope(request: Request, workspace_id: Optional[str] = None) -> tuple[str, Optional[str]]:
|
|
57
|
-
user = require_user(request)
|
|
58
|
-
header_ws = request.headers.get("X-Workspace-Id")
|
|
59
|
-
scope = workspace_id or (header_ws.strip() if header_ws else None)
|
|
60
|
-
return user, scope
|
|
61
|
-
|
|
62
|
-
@router.get("/api/network-boundary")
|
|
63
|
-
async def get_network_boundary(
|
|
64
|
-
request: Request,
|
|
65
|
-
workspace_id: Optional[str] = None,
|
|
66
|
-
):
|
|
67
|
-
user, scope = _scope(request, workspace_id)
|
|
68
|
-
payload = service.get(user_email=user, workspace_id=scope)
|
|
69
|
-
scope_key = f"{user or 'anon'}|{scope or 'global'}"
|
|
70
|
-
payload["token_budget"] = budget_for(scope_key).snapshot()
|
|
71
|
-
if policy_service is not None:
|
|
72
|
-
payload["policy"] = policy_service.resolve(
|
|
73
|
-
user_email=user, workspace_id=scope
|
|
74
|
-
)
|
|
75
|
-
return payload
|
|
76
|
-
|
|
77
|
-
@router.get("/api/network-boundary/catalog")
|
|
78
|
-
async def network_boundary_catalog(request: Request):
|
|
79
|
-
require_user(request)
|
|
80
|
-
return {"modes": network_mode_catalog()}
|
|
81
|
-
|
|
82
|
-
@router.get("/api/network-boundary/ui-state")
|
|
83
|
-
async def network_boundary_ui_state(
|
|
84
|
-
request: Request,
|
|
85
|
-
workspace_id: Optional[str] = None,
|
|
86
|
-
):
|
|
87
|
-
"""Compact payload for the progressive-enhancement toggle panel."""
|
|
88
|
-
user, scope = _scope(request, workspace_id)
|
|
89
|
-
mode_payload = service.get(user_email=user, workspace_id=scope)
|
|
90
|
-
policy = (
|
|
91
|
-
policy_service.resolve(user_email=user, workspace_id=scope)
|
|
92
|
-
if policy_service is not None
|
|
93
|
-
else {}
|
|
94
|
-
)
|
|
95
|
-
scope_key = f"{user or 'anon'}|{scope or 'global'}"
|
|
96
|
-
return {
|
|
97
|
-
"mode": mode_payload.get("mode"),
|
|
98
|
-
"label": mode_payload.get("label"),
|
|
99
|
-
"label_ko": mode_payload.get("label_ko"),
|
|
100
|
-
"allows_cloud": mode_payload.get("allows_cloud"),
|
|
101
|
-
"requires_ack": mode_payload.get("requires_ack"),
|
|
102
|
-
"warning_ko": next(
|
|
103
|
-
(
|
|
104
|
-
m.get("warning_ko")
|
|
105
|
-
for m in (mode_payload.get("catalog") or [])
|
|
106
|
-
if m.get("id") == mode_payload.get("mode")
|
|
107
|
-
),
|
|
108
|
-
None,
|
|
109
|
-
),
|
|
110
|
-
"policy": policy,
|
|
111
|
-
"token_budget": budget_for(scope_key).snapshot(),
|
|
112
|
-
"catalog": network_mode_catalog(),
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
@router.post("/api/network-boundary")
|
|
116
|
-
async def set_network_boundary(body: SetNetworkBoundaryRequest, request: Request):
|
|
117
|
-
user, scope = _scope(request, body.workspace_id)
|
|
118
|
-
try:
|
|
119
|
-
return service.set_mode(
|
|
120
|
-
normalize_network_mode(body.mode),
|
|
121
|
-
user_email=user,
|
|
122
|
-
workspace_id=scope,
|
|
123
|
-
acknowledge_risk=body.acknowledge_risk,
|
|
124
|
-
source="api",
|
|
125
|
-
)
|
|
126
|
-
except PermissionError as exc:
|
|
127
|
-
raise HTTPException(status_code=400, detail=str(exc)) from exc
|
|
128
|
-
|
|
129
|
-
@router.post("/api/network-boundary/preview")
|
|
130
|
-
async def preview_cloud_context(body: PreviewRequest, request: Request):
|
|
131
|
-
user, scope = _scope(request, body.workspace_id)
|
|
132
|
-
mode = service.resolve(user_email=user, workspace_id=scope)
|
|
133
|
-
minimal = build_minimal_context(
|
|
134
|
-
body.message,
|
|
135
|
-
store=knowledge_graph,
|
|
136
|
-
mode=mode,
|
|
137
|
-
top_k=max(1, min(int(body.top_k or 6), 12)),
|
|
138
|
-
allowed_workspaces={scope} if scope else None,
|
|
139
|
-
)
|
|
140
|
-
scope_key = f"{user or 'anon'}|{scope or 'global'}"
|
|
141
|
-
budget = budget_for(scope_key)
|
|
142
|
-
refusal = budget.check_turn(minimal.token_estimate)
|
|
143
|
-
return {
|
|
144
|
-
"mode": mode.value,
|
|
145
|
-
"allows_cloud": mode.value == "cloud_allowed",
|
|
146
|
-
"node_ids": minimal.node_ids,
|
|
147
|
-
"keywords": minimal.keywords,
|
|
148
|
-
"titles": [str(n.get("title") or n.get("id") or "") for n in minimal.nodes],
|
|
149
|
-
"types": [str(n.get("type") or "") for n in minimal.nodes],
|
|
150
|
-
"token_estimate": minimal.token_estimate,
|
|
151
|
-
"quality": minimal.quality,
|
|
152
|
-
"compact_preview": minimal.compact_text[:1200],
|
|
153
|
-
"token_budget": budget.snapshot(),
|
|
154
|
-
"would_block": refusal,
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
@router.post("/api/network-boundary/node-sensitivity")
|
|
158
|
-
async def set_node_sensitivity(body: SetNodeSensitivityRequest, request: Request):
|
|
159
|
-
"""Mark one memory as never-leaving (or clear the mark).
|
|
160
|
-
|
|
161
|
-
The cloud filter has always looked for this flag. Until 10.2.0 nothing
|
|
162
|
-
in the product could set it, so the guard could not fire. Ingestion
|
|
163
|
-
stamps secret-bearing paths automatically; this covers what a path
|
|
164
|
-
cannot tell you — a note whose *content* is private.
|
|
165
|
-
"""
|
|
166
|
-
user, scope = _scope(request, body.workspace_id)
|
|
167
|
-
if knowledge_graph is None or not hasattr(knowledge_graph, "set_node_sensitivity"):
|
|
168
|
-
raise http_error(501, "common.graph_unavailable", resolve_language(request))
|
|
169
|
-
result = knowledge_graph.set_node_sensitivity(
|
|
170
|
-
body.node_id, local_only=bool(body.local_only), reason=body.reason
|
|
171
|
-
)
|
|
172
|
-
if not result.get("ok"):
|
|
173
|
-
raise HTTPException(
|
|
174
|
-
status_code=404,
|
|
175
|
-
detail=result.get("reason")
|
|
176
|
-
or translate("graph.node_not_found", resolve_language(request)),
|
|
177
|
-
)
|
|
178
|
-
record_cloud_egress(
|
|
179
|
-
node_ids=[body.node_id], token_estimate=0, mode="(policy)",
|
|
180
|
-
provider="(local)", user_email=user, workspace_id=scope,
|
|
181
|
-
outcome="marked_local_only" if body.local_only else "cleared_local_only",
|
|
182
|
-
detail=body.reason,
|
|
183
|
-
)
|
|
184
|
-
return result
|
|
185
|
-
|
|
186
|
-
@router.get("/api/network-boundary/policy")
|
|
187
|
-
async def get_hybrid_policy(
|
|
188
|
-
request: Request,
|
|
189
|
-
workspace_id: Optional[str] = None,
|
|
190
|
-
):
|
|
191
|
-
if policy_service is None:
|
|
192
|
-
raise http_error(501, "boundary.policy_not_configured", resolve_language(request))
|
|
193
|
-
user, scope = _scope(request, workspace_id)
|
|
194
|
-
return policy_service.resolve(user_email=user, workspace_id=scope)
|
|
195
|
-
|
|
196
|
-
@router.post("/api/network-boundary/policy")
|
|
197
|
-
async def set_hybrid_policy(body: SetHybridPolicyRequest, request: Request):
|
|
198
|
-
if policy_service is None:
|
|
199
|
-
raise http_error(501, "boundary.policy_not_configured", resolve_language(request))
|
|
200
|
-
user, scope = _scope(request, body.workspace_id)
|
|
201
|
-
patch: Dict[str, Any] = {}
|
|
202
|
-
for key in (
|
|
203
|
-
"blocked_node_types",
|
|
204
|
-
"blocked_metadata_flags",
|
|
205
|
-
"auto_commit",
|
|
206
|
-
"allow_multimodal",
|
|
207
|
-
"min_extraction_confidence",
|
|
208
|
-
):
|
|
209
|
-
val = getattr(body, key, None)
|
|
210
|
-
if val is not None:
|
|
211
|
-
patch[key] = val
|
|
212
|
-
return policy_service.set_policy(
|
|
213
|
-
patch, user_email=user, workspace_id=scope, source="api"
|
|
214
|
-
)
|
|
215
|
-
|
|
216
|
-
return router
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
__all__ = [
|
|
220
|
-
"create_network_boundary_router",
|
|
221
|
-
"SetNetworkBoundaryRequest",
|
|
222
|
-
"PreviewRequest",
|
|
223
|
-
"SetHybridPolicyRequest",
|
|
224
|
-
"SetNodeSensitivityRequest",
|
|
225
|
-
]
|
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
"""Permission mode API — strict / trusted / bypass dial (v9.9.8)."""
|
|
2
|
-
|
|
3
|
-
from __future__ import annotations
|
|
4
|
-
|
|
5
|
-
from typing import Callable, Optional
|
|
6
|
-
|
|
7
|
-
from fastapi import APIRouter, HTTPException, Request
|
|
8
|
-
from pydantic import BaseModel, Field
|
|
9
|
-
|
|
10
|
-
from latticeai.core.permission_mode import mode_catalog, normalize_mode
|
|
11
|
-
from latticeai.services.permission_mode_service import PermissionModeService
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
class SetPermissionModeRequest(BaseModel):
|
|
15
|
-
mode: str = Field(..., description="strict | trusted | bypass")
|
|
16
|
-
workspace_id: Optional[str] = None
|
|
17
|
-
acknowledge_risk: bool = False
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
def create_permission_mode_router(
|
|
21
|
-
*,
|
|
22
|
-
service: PermissionModeService,
|
|
23
|
-
require_user: Callable[..., str],
|
|
24
|
-
) -> APIRouter:
|
|
25
|
-
router = APIRouter(tags=["permission-mode"])
|
|
26
|
-
|
|
27
|
-
@router.get("/api/permission-mode")
|
|
28
|
-
async def get_permission_mode(
|
|
29
|
-
request: Request,
|
|
30
|
-
workspace_id: Optional[str] = None,
|
|
31
|
-
):
|
|
32
|
-
user = require_user(request)
|
|
33
|
-
header_ws = request.headers.get("X-Workspace-Id")
|
|
34
|
-
scope = workspace_id or (header_ws.strip() if header_ws else None)
|
|
35
|
-
return service.get(user_email=user, workspace_id=scope)
|
|
36
|
-
|
|
37
|
-
@router.get("/api/permission-mode/catalog")
|
|
38
|
-
async def permission_mode_catalog(request: Request):
|
|
39
|
-
require_user(request)
|
|
40
|
-
return {"modes": mode_catalog()}
|
|
41
|
-
|
|
42
|
-
@router.post("/api/permission-mode")
|
|
43
|
-
async def set_permission_mode(body: SetPermissionModeRequest, request: Request):
|
|
44
|
-
user = require_user(request)
|
|
45
|
-
header_ws = request.headers.get("X-Workspace-Id")
|
|
46
|
-
scope = body.workspace_id or (header_ws.strip() if header_ws else None)
|
|
47
|
-
try:
|
|
48
|
-
return service.set_mode(
|
|
49
|
-
normalize_mode(body.mode),
|
|
50
|
-
user_email=user,
|
|
51
|
-
workspace_id=scope,
|
|
52
|
-
acknowledge_risk=body.acknowledge_risk,
|
|
53
|
-
source="api",
|
|
54
|
-
)
|
|
55
|
-
except PermissionError as exc:
|
|
56
|
-
raise HTTPException(status_code=400, detail=str(exc)) from exc
|
|
57
|
-
|
|
58
|
-
return router
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
__all__ = ["create_permission_mode_router", "SetPermissionModeRequest"]
|