ltcai 11.5.2 → 11.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +93 -148
- package/bin/ltcai.js +234 -24
- package/docs/CHANGELOG.md +119 -0
- package/docs/COMMUNITY_AND_PLUGINS.md +1 -1
- package/docs/DEVELOPMENT.md +8 -6
- package/docs/ENTERPRISE.md +2 -1
- package/docs/MULTI_AGENT_RUNTIME.md +12 -5
- package/docs/ONBOARDING.md +1 -1
- package/docs/OPERATIONS.md +6 -3
- package/docs/REALTIME_COLLABORATION.md +1 -1
- package/docs/TRUST_MODEL.md +1 -1
- package/docs/WHY_LATTICE.md +1 -1
- package/docs/WORKFLOW_DESIGNER.md +3 -2
- package/docs/kg-schema.md +1 -1
- package/docs/v11.6.0_ONE_DOOR_PLAN.md +170 -0
- package/lattice_brain/__init__.py +42 -79
- package/lattice_brain/graph/__init__.py +9 -24
- package/lattice_brain/graph/_kg_common/__init__.py +13 -24
- package/lattice_brain/ingestion/__init__.py +19 -58
- package/lattice_brain/ingestion/pipeline.py +20 -398
- package/lattice_brain/multimodal/__init__.py +7 -18
- package/lattice_brain/multimodal/images.py +6 -264
- package/lattice_brain/multimodal/video.py +8 -247
- package/lattice_brain/runtime/__init__.py +8 -79
- package/lattice_brain/runtime/hooks.py +22 -584
- package/latticeai/__init__.py +1 -1
- package/latticeai/api/agent_worker_seam.py +7 -80
- package/latticeai/api/health.py +6 -20
- package/latticeai/api/local_files.py +16 -613
- package/latticeai/api/models.py +12 -87
- package/latticeai/api/search.py +17 -255
- package/latticeai/api/tools.py +69 -750
- package/latticeai/api/voice_capture.py +8 -70
- package/latticeai/api/worker_compute.py +842 -0
- package/latticeai/api/worker_seams.py +216 -0
- package/latticeai/app_factory.py +23 -232
- package/latticeai/cli/entrypoint.py +36 -249
- package/latticeai/core/agent_permission.py +16 -91
- package/latticeai/core/messages.py +89 -531
- package/latticeai/runtime/access_runtime.py +0 -14
- package/latticeai/runtime/bootstrap.py +10 -21
- package/latticeai/runtime/brain_runtime.py +35 -43
- package/latticeai/runtime/build_phases/__init__.py +21 -37
- package/latticeai/runtime/build_phases/features.py +99 -389
- package/latticeai/runtime/build_phases/foundation.py +82 -392
- package/latticeai/runtime/build_phases/web.py +91 -408
- package/latticeai/runtime/build_phases/worker_profile.py +244 -0
- package/latticeai/runtime/lifespan_runtime.py +7 -16
- package/latticeai/runtime/platform_services_runtime.py +1 -15
- package/latticeai/runtime/runtime_context.py +13 -130
- package/latticeai/runtime/security_runtime.py +10 -103
- package/latticeai/services/architecture_readiness.py +82 -43
- package/latticeai/services/model_runtime/__init__.py +2 -11
- package/latticeai/services/model_runtime/service.py +4 -28
- package/latticeai/services/p_reinforce.py +10 -261
- package/latticeai/services/product_readiness.py +31 -38
- package/latticeai/services/search_service.py +37 -795
- package/latticeai/services/tool_dispatch.py +30 -386
- package/latticeai/services/voice_capture.py +13 -107
- package/latticeai/tools/__init__.py +22 -49
- package/latticeai/tools/commands.py +0 -163
- package/latticeai/tools/computer.py +0 -39
- package/latticeai/tools/documents.py +1 -134
- package/latticeai/tools/filesystem.py +1 -247
- package/latticeai/tools/knowledge.py +1 -52
- package/latticeai/tools/local_files.py +0 -20
- package/latticeai/worker_app.py +75 -0
- package/package.json +2 -3
- package/requirements.txt +0 -5
- package/scripts/agent_eval.py +16 -28
- package/scripts/brain_quality_eval.py +20 -183
- package/scripts/bump_version.py +5 -5
- package/scripts/check_current_release_docs.mjs +7 -4
- package/scripts/check_openapi_drift.mjs +10 -0
- package/scripts/check_server_i18n.mjs +2 -18
- package/scripts/compose_openapi.py +377 -0
- package/scripts/export_openapi.py +32 -4
- package/scripts/gen_messages_catalog_fixture.py +302 -0
- package/scripts/gen_openapi_fragments.py +365 -0
- package/scripts/gen_redact_fixture.py +196 -0
- package/scripts/gen_worker_allowlist_fixture.py +115 -0
- package/scripts/generate_agent_parity_fixtures.py +33 -14
- package/scripts/openapi_route_families.json +2161 -0
- package/scripts/release_screen_claims.json +54 -0
- package/scripts/run_integration_tests.mjs +134 -29
- package/scripts/run_sidecar_e2e.mjs +104 -11
- package/scripts/wheel_smoke.py +32 -22
- package/src-tauri/Cargo.lock +375 -9
- package/src-tauri/Cargo.toml +1 -1
- package/src-tauri/src/backend.rs +151 -45
- package/src-tauri/src/main.rs +18 -18
- package/src-tauri/src/topology.rs +58 -88
- package/src-tauri/tauri.conf.json +1 -2
- package/static/app/asset-manifest.json +41 -41
- package/static/app/assets/{Act-DcQizkl1.js → Act-BPcVAbOL.js} +1 -1
- package/static/app/assets/AdminConsole-Bw1ATQL0.js +1 -0
- package/static/app/assets/{Brain-3VCSHFcn.js → Brain-CT92Kos0.js} +2 -2
- package/static/app/assets/{BrainHome-Qm8eaztx.js → BrainHome-CFBkt1K_.js} +1 -1
- package/static/app/assets/{BrainSignals-DS9BtKOW.js → BrainSignals-ReLWF2H8.js} +1 -1
- package/static/app/assets/Capture-BsTokYkk.js +1 -0
- package/static/app/assets/{Chronicle-BGvuAchH.js → Chronicle-B6f0T9id.js} +1 -1
- package/static/app/assets/{CommandPalette-Bqhm0Urn.js → CommandPalette-CuvjTv1u.js} +1 -1
- package/static/app/assets/{Library-BV6NnF0a.js → Library-BGJbG9Hd.js} +1 -1
- package/static/app/assets/{LivingBrain-GzenJchP.js → LivingBrain-DGYK_Jsa.js} +1 -1
- package/static/app/assets/{ProductFlow-DEP6-vML.js → ProductFlow-DXBC6brE.js} +1 -1
- package/static/app/assets/{ReviewCard-CNZ7XjWG.js → ReviewCard-HXRle3qq.js} +2 -2
- package/static/app/assets/System-CMHSO9qM.js +1 -0
- package/static/app/assets/arrow-left-BfmkskWx.js +1 -0
- package/static/app/assets/{bot-B_K1Tdmw.js → bot-Cn8bWRuq.js} +1 -1
- package/static/app/assets/{brain-DWyaV1L1.js → brain-CQJberbE.js} +1 -1
- package/static/app/assets/{button-aTn4s84A.js → button-Ct9f2_oT.js} +1 -1
- package/static/app/assets/circle-check-DruOxB-4.js +1 -0
- package/static/app/assets/{circle-pause-xKgeGXkT.js → circle-pause-CmzC_apg.js} +1 -1
- package/static/app/assets/{circle-play-DkT6tYPX.js → circle-play-D8mW2aQ7.js} +1 -1
- package/static/app/assets/{cpu-85xYObUC.js → cpu-DZcdd0PZ.js} +1 -1
- package/static/app/assets/{download-B5Fm7YXo.js → download-bv1KEPGQ.js} +1 -1
- package/static/app/assets/{folder-open-kk2Xa52u.js → folder-open-d-Pip5gr.js} +1 -1
- package/static/app/assets/{hard-drive-DkA3zBW_.js → hard-drive-D20iavUb.js} +1 -1
- package/static/app/assets/index-D9x-kSNy.css +2 -0
- package/static/app/assets/{index-BMPdTmlY.js → index-Do83hDzJ.js} +3 -3
- package/static/app/assets/{input-B0nRf2jO.js → input-BLXVNmj1.js} +1 -1
- package/static/app/assets/{link-2-Dwb4gnTc.js → link-2-BPJOFlAy.js} +1 -1
- package/static/app/assets/{permissionCopy-CQDUBrOZ.js → permissionCopy-ChdJd493.js} +1 -1
- package/static/app/assets/primitives-Cv5tbZBY.js +1 -0
- package/static/app/assets/search-CT9aho2j.js +1 -0
- package/static/app/assets/{share-2-BsrxFglO.js → share-2-YNX_NtMU.js} +1 -1
- package/static/app/assets/{shield-alert-5BStfp2_.js → shield-alert-DuQ3zrVL.js} +1 -1
- package/static/app/assets/{textarea-Cg8IUA-k.js → textarea-DqwLnli4.js} +1 -1
- package/static/app/assets/{useFocusTrap-CYKvE46M.js → useFocusTrap-ZVI98jaW.js} +1 -1
- package/static/app/assets/{useMutation-CSn9t1op.js → useMutation-CVC4qv_D.js} +1 -1
- package/static/app/assets/{useQuery-CY2OI2uy.js → useQuery-C7BeG4HU.js} +1 -1
- package/static/app/assets/{utils-Ddol2RWD.js → utils-CiFtIdZq.js} +1 -1
- package/static/app/assets/{workspace-BqDwOz_p.js → workspace-DQz9vIId.js} +1 -1
- package/static/app/index.html +4 -4
- package/static/sw.js +1 -1
- package/lattice_brain/archive.py +0 -522
- package/lattice_brain/context.py +0 -325
- package/lattice_brain/conversations.py +0 -382
- package/lattice_brain/core.py +0 -82
- package/lattice_brain/graph/_kg_contract.py +0 -249
- package/lattice_brain/graph/curator.py +0 -676
- package/lattice_brain/graph/discovery.py +0 -595
- package/lattice_brain/graph/discovery_index/__init__.py +0 -35
- package/lattice_brain/graph/discovery_index/cleanup.py +0 -182
- package/lattice_brain/graph/discovery_index/extract.py +0 -137
- package/lattice_brain/graph/discovery_index/scan.py +0 -411
- package/lattice_brain/graph/discovery_index/upsert.py +0 -495
- package/lattice_brain/graph/documents.py +0 -380
- package/lattice_brain/graph/fusion.py +0 -395
- package/lattice_brain/graph/identity.py +0 -175
- package/lattice_brain/graph/image_vectors.py +0 -230
- package/lattice_brain/graph/ingest.py +0 -829
- package/lattice_brain/graph/network.py +0 -205
- package/lattice_brain/graph/proactive.py +0 -724
- package/lattice_brain/graph/projection/__init__.py +0 -42
- package/lattice_brain/graph/projection/curation.py +0 -500
- package/lattice_brain/graph/projection/v2_schema.py +0 -518
- package/lattice_brain/graph/provenance.py +0 -524
- package/lattice_brain/graph/rerank.py +0 -163
- package/lattice_brain/graph/retrieval/__init__.py +0 -54
- package/lattice_brain/graph/retrieval/context.py +0 -197
- package/lattice_brain/graph/retrieval/graph_view.py +0 -319
- package/lattice_brain/graph/retrieval/hybrid.py +0 -488
- package/lattice_brain/graph/retrieval/maintenance.py +0 -121
- package/lattice_brain/graph/retrieval/signals.py +0 -95
- package/lattice_brain/graph/retrieval_docgen.py +0 -253
- package/lattice_brain/graph/retrieval_policy.py +0 -180
- package/lattice_brain/graph/retrieval_reads.py +0 -769
- package/lattice_brain/graph/retrieval_vector/__init__.py +0 -42
- package/lattice_brain/graph/retrieval_vector/fingerprint.py +0 -97
- package/lattice_brain/graph/retrieval_vector/indexing.py +0 -347
- package/lattice_brain/graph/retrieval_vector/search.py +0 -560
- package/lattice_brain/graph/retrieval_vector/status.py +0 -374
- package/lattice_brain/graph/schema.py +0 -792
- package/lattice_brain/graph/store.py +0 -268
- package/lattice_brain/graph/vector_index/__init__.py +0 -85
- package/lattice_brain/graph/vector_index/base.py +0 -167
- package/lattice_brain/graph/vector_index/brute_force.py +0 -110
- package/lattice_brain/graph/vector_index/hnsw.py +0 -290
- package/lattice_brain/graph/vector_index/jobs.py +0 -287
- package/lattice_brain/graph/vector_index/quantized.py +0 -148
- package/lattice_brain/graph/vector_index/selector.py +0 -161
- package/lattice_brain/graph/write_master.py +0 -308
- package/lattice_brain/ingestion/_contract.py +0 -90
- package/lattice_brain/ingestion/folder_scan.py +0 -57
- package/lattice_brain/ingestion/folders.py +0 -258
- package/lattice_brain/ingestion/jobs_api.py +0 -107
- package/lattice_brain/ingestion/routing.py +0 -295
- package/lattice_brain/ingestion_jobs.py +0 -380
- package/lattice_brain/memory.py +0 -75
- package/lattice_brain/portability/__init__.py +0 -90
- package/lattice_brain/portability/_contract.py +0 -42
- package/lattice_brain/portability/backups.py +0 -338
- package/lattice_brain/portability/bundles.py +0 -136
- package/lattice_brain/portability/constants.py +0 -93
- package/lattice_brain/portability/fsops.py +0 -138
- package/lattice_brain/portability/service.py +0 -41
- package/lattice_brain/portability/sharing.py +0 -710
- package/lattice_brain/quality.py +0 -543
- package/lattice_brain/retrieval_benchmark_fixtures.py +0 -92
- package/lattice_brain/runtime/agent_runtime.py +0 -859
- package/lattice_brain/runtime/contracts.py +0 -460
- package/lattice_brain/runtime/multi_agent.py +0 -942
- package/lattice_brain/runtime/statuses.py +0 -10
- package/lattice_brain/sealed_box.py +0 -240
- package/lattice_brain/self_model.py +0 -675
- package/lattice_brain/sensitivity.py +0 -94
- package/lattice_brain/storage/__init__.py +0 -22
- package/lattice_brain/storage/base.py +0 -100
- package/lattice_brain/storage/docker.py +0 -105
- package/lattice_brain/storage/factory.py +0 -31
- package/lattice_brain/storage/migration.py +0 -191
- package/lattice_brain/storage/postgres.py +0 -123
- package/lattice_brain/storage/sqlite.py +0 -143
- package/lattice_brain/synthesis.py +0 -824
- package/lattice_brain/workflow.py +0 -497
- package/latticeai/api/admin.py +0 -471
- package/latticeai/api/agent_registry.py +0 -105
- package/latticeai/api/agents.py +0 -228
- package/latticeai/api/auth.py +0 -383
- package/latticeai/api/automation_intelligence.py +0 -401
- package/latticeai/api/brain_intelligence.py +0 -199
- package/latticeai/api/browser.py +0 -493
- package/latticeai/api/change_proposals.py +0 -89
- package/latticeai/api/chat.py +0 -572
- package/latticeai/api/chat_agent_http.py +0 -892
- package/latticeai/api/chat_contracts.py +0 -73
- package/latticeai/api/chat_documents.py +0 -276
- package/latticeai/api/chat_helpers.py +0 -460
- package/latticeai/api/chat_history.py +0 -101
- package/latticeai/api/chat_hybrid.py +0 -113
- package/latticeai/api/chat_intents.py +0 -646
- package/latticeai/api/chat_stream.py +0 -216
- package/latticeai/api/chronicle.py +0 -63
- package/latticeai/api/command_center.py +0 -51
- package/latticeai/api/computer_use.py +0 -474
- package/latticeai/api/evidence_actions.py +0 -48
- package/latticeai/api/features.py +0 -70
- package/latticeai/api/funnel_metrics.py +0 -31
- package/latticeai/api/garden.py +0 -34
- package/latticeai/api/hooks.py +0 -165
- package/latticeai/api/index_jobs.py +0 -145
- package/latticeai/api/invitations.py +0 -100
- package/latticeai/api/knowledge_graph.py +0 -536
- package/latticeai/api/marketplace.py +0 -105
- package/latticeai/api/mcp.py +0 -482
- package/latticeai/api/memory.py +0 -270
- package/latticeai/api/network.py +0 -81
- package/latticeai/api/network_boundary.py +0 -225
- package/latticeai/api/permission_mode.py +0 -61
- package/latticeai/api/permissions.py +0 -436
- package/latticeai/api/plugins.py +0 -126
- package/latticeai/api/portability.py +0 -391
- package/latticeai/api/project_sessions.py +0 -114
- package/latticeai/api/realtime.py +0 -118
- package/latticeai/api/review_queue.py +0 -364
- package/latticeai/api/security_dashboard.py +0 -604
- package/latticeai/api/setup.py +0 -319
- package/latticeai/api/static_routes.py +0 -354
- package/latticeai/api/ui_redirects.py +0 -26
- package/latticeai/api/workflow_designer.py +0 -394
- package/latticeai/api/workspace.py +0 -856
- package/latticeai/api/workspace_scope.py +0 -125
- package/latticeai/core/agent/__init__.py +0 -93
- package/latticeai/core/agent/_contract.py +0 -79
- package/latticeai/core/agent/context.py +0 -57
- package/latticeai/core/agent/deps.py +0 -125
- package/latticeai/core/agent/execution.py +0 -622
- package/latticeai/core/agent/planning.py +0 -145
- package/latticeai/core/agent/recovery.py +0 -157
- package/latticeai/core/agent/runtime.py +0 -210
- package/latticeai/core/agent/verification.py +0 -231
- package/latticeai/core/agent_eval.py +0 -739
- package/latticeai/core/agent_helpers.py +0 -493
- package/latticeai/core/agent_profiles.py +0 -110
- package/latticeai/core/agent_prompts.py +0 -171
- package/latticeai/core/agent_registry.py +0 -232
- package/latticeai/core/agent_state.py +0 -41
- package/latticeai/core/agent_trace.py +0 -104
- package/latticeai/core/artifact_ledger.py +0 -109
- package/latticeai/core/audit.py +0 -260
- package/latticeai/core/builtin_hooks.py +0 -105
- package/latticeai/core/context_builder.py +0 -394
- package/latticeai/core/document_generator.py +0 -103
- package/latticeai/core/enterprise.py +0 -154
- package/latticeai/core/enterprise_admin.py +0 -158
- package/latticeai/core/file_generation/__init__.py +0 -115
- package/latticeai/core/file_generation/bundles.py +0 -76
- package/latticeai/core/file_generation/extraction.py +0 -154
- package/latticeai/core/file_generation/inference.py +0 -235
- package/latticeai/core/file_generation/orchestration.py +0 -152
- package/latticeai/core/file_generation/prompting.py +0 -117
- package/latticeai/core/file_generation/repair.py +0 -114
- package/latticeai/core/file_generation/sanitize.py +0 -61
- package/latticeai/core/file_generation/validation.py +0 -201
- package/latticeai/core/invitations.py +0 -132
- package/latticeai/core/legacy_compatibility.py +0 -243
- package/latticeai/core/logging_safety.py +0 -46
- package/latticeai/core/marketplace.py +0 -293
- package/latticeai/core/mcp_catalog.py +0 -452
- package/latticeai/core/mcp_registry.py +0 -506
- package/latticeai/core/network_boundary.py +0 -168
- package/latticeai/core/oidc.py +0 -208
- package/latticeai/core/plugins.py +0 -432
- package/latticeai/core/product_hardening.py +0 -218
- package/latticeai/core/project_sessions.py +0 -337
- package/latticeai/core/realtime.py +0 -238
- package/latticeai/core/run_explain.py +0 -426
- package/latticeai/core/run_store.py +0 -252
- package/latticeai/core/timezones.py +0 -80
- package/latticeai/core/workspace_computer_memory.py +0 -84
- package/latticeai/core/workspace_graph_trace.py +0 -155
- package/latticeai/core/workspace_indexing.py +0 -102
- package/latticeai/core/workspace_memory.py +0 -77
- package/latticeai/core/workspace_onboarding.py +0 -104
- package/latticeai/core/workspace_os.py +0 -978
- package/latticeai/core/workspace_os_constants.py +0 -126
- package/latticeai/core/workspace_os_state.py +0 -180
- package/latticeai/core/workspace_os_utils.py +0 -103
- package/latticeai/core/workspace_permissions.py +0 -101
- package/latticeai/core/workspace_plugins.py +0 -97
- package/latticeai/core/workspace_relationships.py +0 -99
- package/latticeai/core/workspace_reorganization.py +0 -335
- package/latticeai/core/workspace_review_items.py +0 -112
- package/latticeai/core/workspace_runs.py +0 -726
- package/latticeai/core/workspace_skills.py +0 -109
- package/latticeai/core/workspace_snapshots.py +0 -198
- package/latticeai/core/workspace_timeline.py +0 -110
- package/latticeai/integrations/__init__.py +0 -0
- package/latticeai/integrations/telegram_bot/__init__.py +0 -123
- package/latticeai/integrations/telegram_bot/__main__.py +0 -17
- package/latticeai/integrations/telegram_bot/config.py +0 -86
- package/latticeai/integrations/telegram_bot/dispatch.py +0 -311
- package/latticeai/integrations/telegram_bot/flows.py +0 -478
- package/latticeai/integrations/telegram_bot/helpers.py +0 -322
- package/latticeai/integrations/telegram_bot/screens.py +0 -394
- package/latticeai/runtime/audit_runtime.py +0 -76
- package/latticeai/runtime/automation_runtime.py +0 -81
- package/latticeai/runtime/chat_wiring.py +0 -141
- package/latticeai/runtime/context_runtime.py +0 -66
- package/latticeai/runtime/feature_toggle_wiring.py +0 -157
- package/latticeai/runtime/history_runtime.py +0 -163
- package/latticeai/runtime/history_writer.py +0 -138
- package/latticeai/runtime/hooks_runtime.py +0 -77
- package/latticeai/runtime/model_wiring.py +0 -68
- package/latticeai/runtime/namespace_runtime.py +0 -163
- package/latticeai/runtime/network_boundary_wiring.py +0 -117
- package/latticeai/runtime/network_config_runtime.py +0 -56
- package/latticeai/runtime/permission_mode_wiring.py +0 -112
- package/latticeai/runtime/persistence_runtime.py +0 -159
- package/latticeai/runtime/platform_runtime_wiring.py +0 -89
- package/latticeai/runtime/review_wiring.py +0 -42
- package/latticeai/runtime/router_registration.py +0 -693
- package/latticeai/runtime/service_singletons.py +0 -55
- package/latticeai/runtime/sso_config_runtime.py +0 -128
- package/latticeai/runtime/user_key_runtime.py +0 -106
- package/latticeai/runtime/web_runtime.py +0 -92
- package/latticeai/server_app.py +0 -51
- package/latticeai/services/app_context.py +0 -130
- package/latticeai/services/automation_execution.py +0 -266
- package/latticeai/services/automation_intelligence.py +0 -614
- package/latticeai/services/brain_automation.py +0 -191
- package/latticeai/services/brain_intelligence/__init__.py +0 -58
- package/latticeai/services/brain_intelligence/_contract.py +0 -71
- package/latticeai/services/brain_intelligence/consistency.py +0 -193
- package/latticeai/services/brain_intelligence/constants.py +0 -47
- package/latticeai/services/brain_intelligence/digest.py +0 -258
- package/latticeai/services/brain_intelligence/health.py +0 -331
- package/latticeai/services/brain_intelligence/proposals.py +0 -259
- package/latticeai/services/brain_intelligence/sampling.py +0 -84
- package/latticeai/services/brain_intelligence/service.py +0 -48
- package/latticeai/services/change_proposals.py +0 -471
- package/latticeai/services/chat_service.py +0 -243
- package/latticeai/services/chronicle.py +0 -555
- package/latticeai/services/cloud_egress_audit.py +0 -85
- package/latticeai/services/cloud_extraction.py +0 -129
- package/latticeai/services/cloud_streaming.py +0 -268
- package/latticeai/services/cloud_token_guard.py +0 -84
- package/latticeai/services/command_center.py +0 -548
- package/latticeai/services/evidence_actions.py +0 -258
- package/latticeai/services/feature_toggles.py +0 -502
- package/latticeai/services/folder_watch.py +0 -520
- package/latticeai/services/funnel_metrics.py +0 -307
- package/latticeai/services/hybrid_chat.py +0 -316
- package/latticeai/services/hybrid_context.py +0 -228
- package/latticeai/services/hybrid_policy.py +0 -129
- package/latticeai/services/interop_bridges.py +0 -978
- package/latticeai/services/local_knowledge.py +0 -465
- package/latticeai/services/memory_service/__init__.py +0 -52
- package/latticeai/services/memory_service/_contract.py +0 -100
- package/latticeai/services/memory_service/brief.py +0 -431
- package/latticeai/services/memory_service/constants.py +0 -57
- package/latticeai/services/memory_service/maintenance.py +0 -138
- package/latticeai/services/memory_service/manager.py +0 -186
- package/latticeai/services/memory_service/proof.py +0 -136
- package/latticeai/services/memory_service/recall.py +0 -225
- package/latticeai/services/memory_service/service.py +0 -48
- package/latticeai/services/memory_service/stores.py +0 -110
- package/latticeai/services/mode_store.py +0 -132
- package/latticeai/services/model_recommendation.py +0 -224
- package/latticeai/services/model_runtime/cloud.py +0 -87
- package/latticeai/services/network_boundary_service.py +0 -117
- package/latticeai/services/obsidian_bridge.py +0 -609
- package/latticeai/services/openai_compatible_adapter.py +0 -101
- package/latticeai/services/permission_mode_service.py +0 -122
- package/latticeai/services/platform_runtime.py +0 -366
- package/latticeai/services/review_queue.py +0 -380
- package/latticeai/services/router_context.py +0 -59
- package/latticeai/services/run_executor.py +0 -387
- package/latticeai/services/self_model_service.py +0 -171
- package/latticeai/services/setup_detection.py +0 -147
- package/latticeai/services/triggers.py +0 -378
- package/latticeai/services/upload_service.py +0 -172
- package/latticeai/services/workspace_service.py +0 -165
- package/latticeai/setup/__init__.py +0 -25
- package/latticeai/setup/auto_setup.py +0 -846
- package/latticeai/setup/demo_corpus.py +0 -98
- package/latticeai/setup/wizard/__init__.py +0 -126
- package/latticeai/setup/wizard/catalog.py +0 -175
- package/latticeai/setup/wizard/detect.py +0 -302
- package/latticeai/setup/wizard/install.py +0 -348
- package/latticeai/setup/wizard/paths.py +0 -165
- package/latticeai/setup/wizard/plans.py +0 -74
- package/latticeai/setup/wizard/recommend.py +0 -320
- package/scripts/bench_agent_smoke.py +0 -409
- package/scripts/bench_models.py +0 -540
- package/scripts/bench_vector_index.py +0 -295
- package/scripts/funnel_soft_gate.py +0 -192
- package/scripts/generate_agent_loop_fixtures.py +0 -994
- package/scripts/generate_rust_parity_fixtures.py +0 -908
- package/scripts/migrate_brain_storage.py +0 -57
- package/scripts/parity_fixture_corpus_context.py +0 -162
- package/scripts/parity_fixture_corpus_docgen.py +0 -341
- package/scripts/profile_kg.py +0 -355
- package/server.py +0 -30
- package/static/app/assets/AdminConsole-cf4npybT.js +0 -1
- package/static/app/assets/Capture-DiQ219jW.js +0 -1
- package/static/app/assets/System-CieofHQa.js +0 -1
- package/static/app/assets/arrow-left-kfsrk0mv.js +0 -1
- package/static/app/assets/circle-check-qqLug9nU.js +0 -1
- package/static/app/assets/index-DxmOfNRi.css +0 -2
- package/static/app/assets/primitives-SNp0LRJz.js +0 -1
- package/static/app/assets/search-BcHqkjoy.js +0 -1
|
@@ -1,391 +0,0 @@
|
|
|
1
|
-
"""Knowledge Graph portability routes — local export / import / backup / restore.
|
|
2
|
-
|
|
3
|
-
Status reads require a signed-in user. Whole-graph exports, provenance, and all
|
|
4
|
-
mutating operations require admin because the graph is machine-global, not
|
|
5
|
-
workspace-scoped. Nothing here touches a cloud service.
|
|
6
|
-
|
|
7
|
-
The ``/share`` family (v11.1.0) adds the selective Brain Network prototype:
|
|
8
|
-
export a *chosen* subgraph signed by this device, and receive one as review
|
|
9
|
-
proposals rather than a merge. It is gated on ``LATTICEAI_BRAIN_NETWORK`` and
|
|
10
|
-
answers 403 with the reason while that flag is off — except ``GET
|
|
11
|
-
/api/knowledge-graph/share``, which reports ``enabled: false`` so a UI can say
|
|
12
|
-
why the feature is absent instead of guessing.
|
|
13
|
-
"""
|
|
14
|
-
|
|
15
|
-
from __future__ import annotations
|
|
16
|
-
|
|
17
|
-
from typing import Any, Callable, Dict, List, Optional
|
|
18
|
-
|
|
19
|
-
from fastapi import APIRouter, HTTPException, Request
|
|
20
|
-
from pydantic import BaseModel
|
|
21
|
-
|
|
22
|
-
from latticeai.core.messages import http_error, resolve_language, translate
|
|
23
|
-
from latticeai.services.review_queue import InvalidReviewTransition
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
class ImportRequest(BaseModel):
|
|
27
|
-
artifact: dict
|
|
28
|
-
mode: str = "merge"
|
|
29
|
-
dry_run: bool = False
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
class BackupRequest(BaseModel):
|
|
33
|
-
path: Optional[str] = None
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
class RestoreRequest(BaseModel):
|
|
37
|
-
path: str
|
|
38
|
-
verify: bool = True
|
|
39
|
-
dry_run: bool = False
|
|
40
|
-
confirm: bool = False
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
class EncryptedArchiveRequest(BaseModel):
|
|
44
|
-
path: Optional[str] = None
|
|
45
|
-
passphrase: str
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
class EncryptedRestoreRequest(BaseModel):
|
|
49
|
-
path: str
|
|
50
|
-
passphrase: str
|
|
51
|
-
dry_run: bool = False
|
|
52
|
-
confirm: bool = False
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
class EncryptedInspectRequest(BaseModel):
|
|
56
|
-
path: str
|
|
57
|
-
passphrase: Optional[str] = None
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
class EncryptedVerifyRequest(BaseModel):
|
|
61
|
-
path: str
|
|
62
|
-
passphrase: str
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
class DockerPostgresRequest(BaseModel):
|
|
66
|
-
consent: bool = False
|
|
67
|
-
dry_run: bool = False
|
|
68
|
-
port: int = 5432
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
class SQLiteToPostgresRequest(BaseModel):
|
|
72
|
-
dsn: str
|
|
73
|
-
schema_name: str = "lattice_brain"
|
|
74
|
-
dry_run: bool = True
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
class SubgraphSelection(BaseModel):
|
|
78
|
-
"""What to share. At least one selector is required by the service."""
|
|
79
|
-
|
|
80
|
-
node_ids: List[str] = []
|
|
81
|
-
node_types: List[str] = []
|
|
82
|
-
source_types: List[str] = []
|
|
83
|
-
workspace_id: Optional[str] = None
|
|
84
|
-
include_legacy_global: bool = False
|
|
85
|
-
include_neighbors: bool = False
|
|
86
|
-
redact_provenance: bool = True
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
class SubgraphArchiveRequest(SubgraphSelection):
|
|
90
|
-
"""Exactly one recipient mechanism — a passphrase, or a public key.
|
|
91
|
-
|
|
92
|
-
Both optional at the schema level and validated in the service, so the
|
|
93
|
-
error a caller gets names the rule ("choose one") instead of a field.
|
|
94
|
-
"""
|
|
95
|
-
|
|
96
|
-
path: Optional[str] = None
|
|
97
|
-
passphrase: Optional[str] = None
|
|
98
|
-
#: The recipient's X25519 public key, from their ``/share/recipient-key``.
|
|
99
|
-
recipient_public_key: Optional[str] = None
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
class SubgraphImportRequest(BaseModel):
|
|
103
|
-
"""Either an inline ``artifact`` or a ``path`` (+ passphrase) on disk."""
|
|
104
|
-
|
|
105
|
-
artifact: Optional[Dict[str, Any]] = None
|
|
106
|
-
path: Optional[str] = None
|
|
107
|
-
passphrase: Optional[str] = None
|
|
108
|
-
workspace_id: Optional[str] = None
|
|
109
|
-
dry_run: bool = False
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
class SubgraphAcceptRequest(BaseModel):
|
|
113
|
-
workspace_id: Optional[str] = None
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
def create_portability_router(
|
|
117
|
-
*,
|
|
118
|
-
service: Any,
|
|
119
|
-
require_user: Callable[[Request], str],
|
|
120
|
-
require_admin: Callable[[Request], Any],
|
|
121
|
-
review_queue: Any = None,
|
|
122
|
-
) -> APIRouter:
|
|
123
|
-
router = APIRouter()
|
|
124
|
-
|
|
125
|
-
def _require_service(request: Request):
|
|
126
|
-
if service is None or not service.available():
|
|
127
|
-
raise http_error(503, "common.graph_disabled", resolve_language(request))
|
|
128
|
-
|
|
129
|
-
def _require_review_queue(request: Request):
|
|
130
|
-
if review_queue is None:
|
|
131
|
-
raise http_error(503, "portability.review_queue_unavailable", resolve_language(request))
|
|
132
|
-
|
|
133
|
-
@router.get("/api/knowledge-graph/portability")
|
|
134
|
-
async def portability_status(request: Request):
|
|
135
|
-
require_user(request)
|
|
136
|
-
_require_service(request)
|
|
137
|
-
return service.snapshot_metadata()
|
|
138
|
-
|
|
139
|
-
@router.get("/api/brain/storage")
|
|
140
|
-
async def brain_storage_status(request: Request):
|
|
141
|
-
require_user(request)
|
|
142
|
-
_require_service(request)
|
|
143
|
-
return service.storage_status()
|
|
144
|
-
|
|
145
|
-
@router.get("/api/knowledge-graph/backup-health")
|
|
146
|
-
async def backup_health(request: Request):
|
|
147
|
-
require_user(request)
|
|
148
|
-
_require_service(request)
|
|
149
|
-
return service.backup_health()
|
|
150
|
-
|
|
151
|
-
@router.get("/api/knowledge-graph/provenance")
|
|
152
|
-
async def recent_provenance(request: Request, limit: int = 50, source_type: Optional[str] = None):
|
|
153
|
-
"""Recent ingestions (provenance trail) for the ingestion-sources UI."""
|
|
154
|
-
require_admin(request)
|
|
155
|
-
_require_service(request)
|
|
156
|
-
return service.recent_ingestions(limit=limit, source_type=source_type)
|
|
157
|
-
|
|
158
|
-
@router.post("/api/knowledge-graph/export")
|
|
159
|
-
async def export_graph(request: Request):
|
|
160
|
-
"""Logical JSON export of the whole graph (read-only)."""
|
|
161
|
-
require_admin(request)
|
|
162
|
-
_require_service(request)
|
|
163
|
-
return service.export()
|
|
164
|
-
|
|
165
|
-
@router.post("/api/knowledge-graph/export-file")
|
|
166
|
-
async def export_graph_file(request: Request):
|
|
167
|
-
require_admin(request)
|
|
168
|
-
_require_service(request)
|
|
169
|
-
return service.export_to_file()
|
|
170
|
-
|
|
171
|
-
@router.post("/api/knowledge-graph/import")
|
|
172
|
-
async def import_graph(req: ImportRequest, request: Request):
|
|
173
|
-
require_admin(request)
|
|
174
|
-
_require_service(request)
|
|
175
|
-
try:
|
|
176
|
-
return service.import_data(req.artifact, mode=req.mode, dry_run=req.dry_run)
|
|
177
|
-
except ValueError as exc:
|
|
178
|
-
raise HTTPException(status_code=400, detail=str(exc))
|
|
179
|
-
|
|
180
|
-
@router.post("/api/knowledge-graph/backup")
|
|
181
|
-
async def backup_graph(req: BackupRequest, request: Request):
|
|
182
|
-
require_admin(request)
|
|
183
|
-
_require_service(request)
|
|
184
|
-
return service.backup(req.path)
|
|
185
|
-
|
|
186
|
-
@router.post("/api/knowledge-graph/restore")
|
|
187
|
-
async def restore_graph(req: RestoreRequest, request: Request):
|
|
188
|
-
require_admin(request)
|
|
189
|
-
_require_service(request)
|
|
190
|
-
try:
|
|
191
|
-
return service.restore(req.path, verify=req.verify, dry_run=req.dry_run, confirm=req.confirm)
|
|
192
|
-
except (ValueError, FileNotFoundError) as exc:
|
|
193
|
-
raise HTTPException(status_code=400, detail=str(exc))
|
|
194
|
-
|
|
195
|
-
@router.post("/api/knowledge-graph/archive")
|
|
196
|
-
async def encrypted_archive(req: EncryptedArchiveRequest, request: Request):
|
|
197
|
-
require_admin(request)
|
|
198
|
-
_require_service(request)
|
|
199
|
-
try:
|
|
200
|
-
return service.encrypted_archive(req.path, passphrase=req.passphrase)
|
|
201
|
-
except (ValueError, FileNotFoundError) as exc:
|
|
202
|
-
raise HTTPException(status_code=400, detail=str(exc))
|
|
203
|
-
|
|
204
|
-
@router.post("/api/knowledge-graph/archive/inspect")
|
|
205
|
-
async def inspect_encrypted_archive(req: EncryptedInspectRequest, request: Request):
|
|
206
|
-
require_admin(request)
|
|
207
|
-
_require_service(request)
|
|
208
|
-
try:
|
|
209
|
-
return service.inspect_encrypted_archive(req.path, passphrase=req.passphrase)
|
|
210
|
-
except (ValueError, FileNotFoundError) as exc:
|
|
211
|
-
raise HTTPException(status_code=400, detail=str(exc))
|
|
212
|
-
|
|
213
|
-
@router.post("/api/knowledge-graph/archive/verify")
|
|
214
|
-
async def verify_encrypted_archive(req: EncryptedVerifyRequest, request: Request):
|
|
215
|
-
require_admin(request)
|
|
216
|
-
_require_service(request)
|
|
217
|
-
result = service.verify_encrypted_archive(req.path, passphrase=req.passphrase)
|
|
218
|
-
if not result.get("ok"):
|
|
219
|
-
raise HTTPException(
|
|
220
|
-
status_code=400,
|
|
221
|
-
detail="; ".join(
|
|
222
|
-
result.get("errors")
|
|
223
|
-
or [translate("portability.verification_failed", resolve_language(request))]
|
|
224
|
-
),
|
|
225
|
-
)
|
|
226
|
-
return result
|
|
227
|
-
|
|
228
|
-
@router.post("/api/knowledge-graph/archive/import")
|
|
229
|
-
async def import_encrypted_archive(req: EncryptedRestoreRequest, request: Request):
|
|
230
|
-
require_admin(request)
|
|
231
|
-
_require_service(request)
|
|
232
|
-
try:
|
|
233
|
-
return service.import_encrypted_archive(
|
|
234
|
-
req.path,
|
|
235
|
-
passphrase=req.passphrase,
|
|
236
|
-
dry_run=req.dry_run,
|
|
237
|
-
confirm=req.confirm,
|
|
238
|
-
)
|
|
239
|
-
except (ValueError, FileNotFoundError) as exc:
|
|
240
|
-
raise HTTPException(status_code=400, detail=str(exc))
|
|
241
|
-
|
|
242
|
-
@router.post("/api/knowledge-graph/archive/restore")
|
|
243
|
-
async def restore_encrypted_archive(req: EncryptedRestoreRequest, request: Request):
|
|
244
|
-
require_admin(request)
|
|
245
|
-
_require_service(request)
|
|
246
|
-
try:
|
|
247
|
-
return service.restore_encrypted_archive(
|
|
248
|
-
req.path,
|
|
249
|
-
passphrase=req.passphrase,
|
|
250
|
-
dry_run=req.dry_run,
|
|
251
|
-
confirm=req.confirm,
|
|
252
|
-
)
|
|
253
|
-
except (ValueError, FileNotFoundError) as exc:
|
|
254
|
-
raise HTTPException(status_code=400, detail=str(exc))
|
|
255
|
-
|
|
256
|
-
# ── selective subgraph share (opt-in; 403 with the reason while off) ─────
|
|
257
|
-
@router.get("/api/knowledge-graph/share")
|
|
258
|
-
async def share_status(request: Request):
|
|
259
|
-
"""Reports whether sharing is on, and names the flag when it is not."""
|
|
260
|
-
require_user(request)
|
|
261
|
-
status = service.share_status()
|
|
262
|
-
if not status.get("enabled"):
|
|
263
|
-
status["detail"] = translate(
|
|
264
|
-
"portability.brain_network_disabled", resolve_language(request)
|
|
265
|
-
)
|
|
266
|
-
return status
|
|
267
|
-
|
|
268
|
-
@router.post("/api/knowledge-graph/share/export")
|
|
269
|
-
async def share_export(req: SubgraphSelection, request: Request):
|
|
270
|
-
"""Signed JSON bundle of the selected subgraph (no file written)."""
|
|
271
|
-
require_admin(request)
|
|
272
|
-
_require_service(request)
|
|
273
|
-
try:
|
|
274
|
-
return service.export_subgraph(**req.model_dump())
|
|
275
|
-
except PermissionError:
|
|
276
|
-
raise http_error(403, "portability.brain_network_disabled", resolve_language(request))
|
|
277
|
-
except (ValueError, RuntimeError) as exc:
|
|
278
|
-
raise HTTPException(status_code=400, detail=str(exc))
|
|
279
|
-
|
|
280
|
-
@router.get("/api/knowledge-graph/share/recipient-key")
|
|
281
|
-
async def share_recipient_key(request: Request):
|
|
282
|
-
"""This Brain's X25519 receiving key, for a sender to seal a bundle to.
|
|
283
|
-
|
|
284
|
-
Public by construction — a public key encrypts, it does not decrypt —
|
|
285
|
-
so handing it out is the whole point. Still admin-gated and still
|
|
286
|
-
behind the share flag, because minting the key is a decision to
|
|
287
|
-
participate in sharing at all.
|
|
288
|
-
"""
|
|
289
|
-
require_admin(request)
|
|
290
|
-
_require_service(request)
|
|
291
|
-
try:
|
|
292
|
-
return service.recipient_key()
|
|
293
|
-
except PermissionError:
|
|
294
|
-
raise http_error(403, "portability.brain_network_disabled", resolve_language(request))
|
|
295
|
-
|
|
296
|
-
@router.post("/api/knowledge-graph/share/archive")
|
|
297
|
-
async def share_archive(req: SubgraphArchiveRequest, request: Request):
|
|
298
|
-
"""Same bundle, written as an encrypted ``.latticebrain`` file."""
|
|
299
|
-
require_admin(request)
|
|
300
|
-
_require_service(request)
|
|
301
|
-
body = req.model_dump()
|
|
302
|
-
path = body.pop("path")
|
|
303
|
-
passphrase = body.pop("passphrase")
|
|
304
|
-
recipient_public_key = body.pop("recipient_public_key")
|
|
305
|
-
try:
|
|
306
|
-
return service.export_subgraph_archive(
|
|
307
|
-
path,
|
|
308
|
-
passphrase=passphrase,
|
|
309
|
-
recipient_public_key=recipient_public_key,
|
|
310
|
-
**body,
|
|
311
|
-
)
|
|
312
|
-
except PermissionError:
|
|
313
|
-
raise http_error(403, "portability.brain_network_disabled", resolve_language(request))
|
|
314
|
-
except (ValueError, RuntimeError) as exc:
|
|
315
|
-
raise HTTPException(status_code=400, detail=str(exc))
|
|
316
|
-
|
|
317
|
-
@router.post("/api/knowledge-graph/share/import")
|
|
318
|
-
async def share_import(req: SubgraphImportRequest, request: Request):
|
|
319
|
-
"""Receive a bundle as review proposals. Never merges on its own."""
|
|
320
|
-
require_admin(request)
|
|
321
|
-
_require_service(request)
|
|
322
|
-
user = require_user(request)
|
|
323
|
-
_require_review_queue(request)
|
|
324
|
-
try:
|
|
325
|
-
artifact = req.artifact
|
|
326
|
-
if artifact is None:
|
|
327
|
-
if not req.path:
|
|
328
|
-
raise ValueError(
|
|
329
|
-
"Provide an artifact, or the path of a bundle on disk."
|
|
330
|
-
)
|
|
331
|
-
# A sealed bundle needs no passphrase — the envelope says which
|
|
332
|
-
# mechanism it used and the service refuses if the wrong one is
|
|
333
|
-
# supplied, so the route does not have to guess here.
|
|
334
|
-
artifact = service.read_subgraph_archive(req.path, passphrase=req.passphrase)
|
|
335
|
-
return service.import_subgraph_proposals(
|
|
336
|
-
artifact,
|
|
337
|
-
review_sink=review_queue,
|
|
338
|
-
workspace_id=req.workspace_id,
|
|
339
|
-
user_email=user or None,
|
|
340
|
-
dry_run=req.dry_run,
|
|
341
|
-
)
|
|
342
|
-
except PermissionError:
|
|
343
|
-
raise http_error(403, "portability.brain_network_disabled", resolve_language(request))
|
|
344
|
-
except FileNotFoundError as exc:
|
|
345
|
-
raise HTTPException(status_code=404, detail=str(exc))
|
|
346
|
-
except ValueError as exc:
|
|
347
|
-
raise HTTPException(status_code=400, detail=str(exc))
|
|
348
|
-
|
|
349
|
-
@router.post("/api/knowledge-graph/share/proposals/{item_id}/accept")
|
|
350
|
-
async def share_accept(item_id: str, req: SubgraphAcceptRequest, request: Request):
|
|
351
|
-
"""Merge one reviewed proposal into this Brain and approve the item."""
|
|
352
|
-
require_admin(request)
|
|
353
|
-
_require_service(request)
|
|
354
|
-
_require_review_queue(request)
|
|
355
|
-
try:
|
|
356
|
-
return service.accept_subgraph_proposal(
|
|
357
|
-
item_id, review_sink=review_queue, workspace_id=req.workspace_id,
|
|
358
|
-
)
|
|
359
|
-
except PermissionError:
|
|
360
|
-
raise http_error(403, "portability.brain_network_disabled", resolve_language(request))
|
|
361
|
-
except FileNotFoundError:
|
|
362
|
-
raise http_error(404, "review.item_not_found", resolve_language(request))
|
|
363
|
-
except InvalidReviewTransition as exc:
|
|
364
|
-
raise HTTPException(status_code=409, detail=str(exc))
|
|
365
|
-
except ValueError as exc:
|
|
366
|
-
raise HTTPException(status_code=400, detail=str(exc))
|
|
367
|
-
|
|
368
|
-
@router.post("/api/brain/storage/postgres/docker")
|
|
369
|
-
async def setup_postgres_docker(req: DockerPostgresRequest, request: Request):
|
|
370
|
-
require_admin(request)
|
|
371
|
-
_require_service(request)
|
|
372
|
-
return service.postgres_docker_setup(
|
|
373
|
-
consent=req.consent,
|
|
374
|
-
dry_run=req.dry_run,
|
|
375
|
-
port=req.port,
|
|
376
|
-
)
|
|
377
|
-
|
|
378
|
-
@router.post("/api/brain/storage/migrate-postgres")
|
|
379
|
-
async def migrate_sqlite_to_postgres(req: SQLiteToPostgresRequest, request: Request):
|
|
380
|
-
require_admin(request)
|
|
381
|
-
_require_service(request)
|
|
382
|
-
try:
|
|
383
|
-
return service.migrate_sqlite_to_postgres(
|
|
384
|
-
dsn=req.dsn,
|
|
385
|
-
schema=req.schema_name,
|
|
386
|
-
dry_run=req.dry_run,
|
|
387
|
-
)
|
|
388
|
-
except (ValueError, FileNotFoundError, RuntimeError) as exc:
|
|
389
|
-
raise HTTPException(status_code=400, detail=str(exc))
|
|
390
|
-
|
|
391
|
-
return router
|
|
@@ -1,114 +0,0 @@
|
|
|
1
|
-
"""Project session router — the multi-turn project loop (v9.9.6).
|
|
2
|
-
|
|
3
|
-
CRUD over :class:`~latticeai.core.project_sessions.ProjectSessionStore`. A
|
|
4
|
-
project session carries what a single agent run cannot: the files the project
|
|
5
|
-
already produced, what is still open, and the last honest verification result.
|
|
6
|
-
Runs fold themselves in through the agent HTTP layer; this surface is what the
|
|
7
|
-
user drives.
|
|
8
|
-
"""
|
|
9
|
-
|
|
10
|
-
from __future__ import annotations
|
|
11
|
-
|
|
12
|
-
from typing import Any, Callable, List, Optional
|
|
13
|
-
|
|
14
|
-
from fastapi import APIRouter, Request
|
|
15
|
-
from pydantic import BaseModel, Field
|
|
16
|
-
|
|
17
|
-
from latticeai.core.messages import http_error, resolve_language
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
class CreateProjectRequest(BaseModel):
|
|
21
|
-
title: str = Field(..., min_length=1, max_length=200)
|
|
22
|
-
goal: str = ""
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
class UpdateProjectRequest(BaseModel):
|
|
26
|
-
title: Optional[str] = None
|
|
27
|
-
goal: Optional[str] = None
|
|
28
|
-
status: Optional[str] = None
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
class TodosRequest(BaseModel):
|
|
32
|
-
todos: List[Any] = Field(default_factory=list)
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
def create_project_sessions_router(
|
|
36
|
-
*,
|
|
37
|
-
store: Any,
|
|
38
|
-
require_user: Callable[[Request], Any],
|
|
39
|
-
gate_read: Optional[Callable[[Request], Optional[str]]] = None,
|
|
40
|
-
gate_write: Optional[Callable[[Request], Optional[str]]] = None,
|
|
41
|
-
) -> APIRouter:
|
|
42
|
-
router = APIRouter()
|
|
43
|
-
|
|
44
|
-
def _scope(request: Request, write: bool = False):
|
|
45
|
-
gate = gate_write if write else gate_read
|
|
46
|
-
return gate(request) if gate is not None else None
|
|
47
|
-
|
|
48
|
-
@router.get("/api/projects")
|
|
49
|
-
async def list_projects(request: Request, status: str = "active"):
|
|
50
|
-
user = require_user(request)
|
|
51
|
-
return store.list(
|
|
52
|
-
user_email=user, workspace_id=_scope(request), status=status
|
|
53
|
-
)
|
|
54
|
-
|
|
55
|
-
@router.post("/api/projects")
|
|
56
|
-
async def create_project(req: CreateProjectRequest, request: Request):
|
|
57
|
-
user = require_user(request)
|
|
58
|
-
return store.create(
|
|
59
|
-
title=req.title,
|
|
60
|
-
goal=req.goal,
|
|
61
|
-
user_email=user,
|
|
62
|
-
workspace_id=_scope(request, write=True),
|
|
63
|
-
)
|
|
64
|
-
|
|
65
|
-
@router.get("/api/projects/{session_id}")
|
|
66
|
-
async def get_project(session_id: str, request: Request):
|
|
67
|
-
user = require_user(request)
|
|
68
|
-
record = store.get(session_id, user_email=user, workspace_id=_scope(request))
|
|
69
|
-
if record is None:
|
|
70
|
-
raise http_error(404, "project.not_found", resolve_language(request))
|
|
71
|
-
return record
|
|
72
|
-
|
|
73
|
-
@router.patch("/api/projects/{session_id}")
|
|
74
|
-
async def update_project(session_id: str, req: UpdateProjectRequest, request: Request):
|
|
75
|
-
user = require_user(request)
|
|
76
|
-
record = store.update(
|
|
77
|
-
session_id,
|
|
78
|
-
title=req.title,
|
|
79
|
-
goal=req.goal,
|
|
80
|
-
status=req.status,
|
|
81
|
-
user_email=user,
|
|
82
|
-
workspace_id=_scope(request, write=True),
|
|
83
|
-
)
|
|
84
|
-
if record is None:
|
|
85
|
-
raise http_error(404, "project.not_found", resolve_language(request))
|
|
86
|
-
return record
|
|
87
|
-
|
|
88
|
-
@router.put("/api/projects/{session_id}/todos")
|
|
89
|
-
async def set_todos(session_id: str, req: TodosRequest, request: Request):
|
|
90
|
-
user = require_user(request)
|
|
91
|
-
record = store.set_todos(
|
|
92
|
-
session_id,
|
|
93
|
-
req.todos,
|
|
94
|
-
user_email=user,
|
|
95
|
-
workspace_id=_scope(request, write=True),
|
|
96
|
-
)
|
|
97
|
-
if record is None:
|
|
98
|
-
raise http_error(404, "project.not_found", resolve_language(request))
|
|
99
|
-
return record
|
|
100
|
-
|
|
101
|
-
@router.delete("/api/projects/{session_id}")
|
|
102
|
-
async def delete_project(session_id: str, request: Request):
|
|
103
|
-
user = require_user(request)
|
|
104
|
-
removed = store.delete(
|
|
105
|
-
session_id, user_email=user, workspace_id=_scope(request, write=True)
|
|
106
|
-
)
|
|
107
|
-
if not removed:
|
|
108
|
-
raise http_error(404, "project.not_found", resolve_language(request))
|
|
109
|
-
return {"status": "deleted", "id": session_id}
|
|
110
|
-
|
|
111
|
-
return router
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
__all__ = ["create_project_sessions_router"]
|
|
@@ -1,118 +0,0 @@
|
|
|
1
|
-
"""Realtime Collaboration API router (v2).
|
|
2
|
-
|
|
3
|
-
Server-Sent-Events stream + presence + activity feed over
|
|
4
|
-
:class:`latticeai.core.realtime.RealtimeBus`. Workspace isolation is enforced by
|
|
5
|
-
resolving each caller's allowed workspace scope before subscribing; single-user
|
|
6
|
-
local mode works with no scope restriction.
|
|
7
|
-
"""
|
|
8
|
-
|
|
9
|
-
from __future__ import annotations
|
|
10
|
-
|
|
11
|
-
import secrets
|
|
12
|
-
from pathlib import Path
|
|
13
|
-
from typing import Any, Callable, Optional, Set
|
|
14
|
-
|
|
15
|
-
from fastapi import APIRouter, HTTPException, Request
|
|
16
|
-
from fastapi.responses import StreamingResponse
|
|
17
|
-
from pydantic import BaseModel
|
|
18
|
-
|
|
19
|
-
from lattice_brain.runtime.contracts import contract_views
|
|
20
|
-
from latticeai.api.ui_redirects import app_redirect
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
class PresenceRequest(BaseModel):
|
|
24
|
-
client_id: Optional[str] = None
|
|
25
|
-
workspace_id: Optional[str] = None
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
def create_realtime_router(
|
|
29
|
-
*,
|
|
30
|
-
bus,
|
|
31
|
-
require_user: Callable[[Request], str],
|
|
32
|
-
get_current_user: Callable[[Request], Optional[str]],
|
|
33
|
-
allowed_scopes: Callable[[Optional[str]], Optional[Set[str]]],
|
|
34
|
-
ui_file_response: Optional[Callable[[Path], Any]] = None,
|
|
35
|
-
static_dir: Optional[Path] = None,
|
|
36
|
-
) -> APIRouter:
|
|
37
|
-
router = APIRouter()
|
|
38
|
-
|
|
39
|
-
@router.get("/activity")
|
|
40
|
-
async def activity_page(request: Request):
|
|
41
|
-
require_user(request)
|
|
42
|
-
return app_redirect("activity", request)
|
|
43
|
-
|
|
44
|
-
@router.get("/realtime/stream")
|
|
45
|
-
async def realtime_stream(request: Request):
|
|
46
|
-
user = require_user(request)
|
|
47
|
-
scope = allowed_scopes(user or None)
|
|
48
|
-
sub_id = secrets.token_urlsafe(12)
|
|
49
|
-
sub = bus.add_subscriber(sub_id, workspace_scope=scope, user=user or None)
|
|
50
|
-
|
|
51
|
-
def refresh_authorization(current_sub) -> bool:
|
|
52
|
-
try:
|
|
53
|
-
refreshed_user = require_user(request)
|
|
54
|
-
except Exception:
|
|
55
|
-
return False
|
|
56
|
-
if refreshed_user != user:
|
|
57
|
-
return False
|
|
58
|
-
current_sub.workspace_scope = allowed_scopes(refreshed_user or None)
|
|
59
|
-
return True
|
|
60
|
-
|
|
61
|
-
async def event_gen():
|
|
62
|
-
async for frame in bus.stream(
|
|
63
|
-
sub,
|
|
64
|
-
refresh_authorization=refresh_authorization,
|
|
65
|
-
):
|
|
66
|
-
if await request.is_disconnected():
|
|
67
|
-
break
|
|
68
|
-
yield frame
|
|
69
|
-
|
|
70
|
-
return StreamingResponse(
|
|
71
|
-
event_gen(),
|
|
72
|
-
media_type="text/event-stream",
|
|
73
|
-
headers={"Cache-Control": "no-cache", "X-Accel-Buffering": "no", "Connection": "keep-alive"},
|
|
74
|
-
)
|
|
75
|
-
|
|
76
|
-
@router.get("/realtime/feed")
|
|
77
|
-
async def realtime_feed(request: Request, limit: int = 50):
|
|
78
|
-
user = require_user(request)
|
|
79
|
-
scope = allowed_scopes(user or None)
|
|
80
|
-
events = bus.recent(limit=limit, workspace_scope=scope)
|
|
81
|
-
return {"events": events, "contracts": contract_views(events), "stats": bus.stats()}
|
|
82
|
-
|
|
83
|
-
@router.get("/realtime/presence")
|
|
84
|
-
async def realtime_presence(request: Request):
|
|
85
|
-
user = require_user(request)
|
|
86
|
-
scope = allowed_scopes(user or None)
|
|
87
|
-
return {"presence": bus.presence(workspace_scope=scope), "stats": bus.stats()}
|
|
88
|
-
|
|
89
|
-
@router.post("/realtime/presence/join")
|
|
90
|
-
async def realtime_join(req: PresenceRequest, request: Request):
|
|
91
|
-
user = require_user(request)
|
|
92
|
-
scope = allowed_scopes(user or None)
|
|
93
|
-
workspace_id = req.workspace_id
|
|
94
|
-
if scope is not None:
|
|
95
|
-
if workspace_id is None:
|
|
96
|
-
if not scope:
|
|
97
|
-
raise HTTPException(status_code=403, detail="No accessible workspace for presence.")
|
|
98
|
-
workspace_id = "personal" if "personal" in scope else sorted(scope)[0]
|
|
99
|
-
elif workspace_id not in scope:
|
|
100
|
-
raise HTTPException(status_code=403, detail="Workspace presence access denied.")
|
|
101
|
-
client_id = req.client_id or secrets.token_urlsafe(8)
|
|
102
|
-
try:
|
|
103
|
-
record = bus.join(client_id, user=user or None, workspace_id=workspace_id)
|
|
104
|
-
except PermissionError as exc:
|
|
105
|
-
raise HTTPException(status_code=403, detail=str(exc)) from exc
|
|
106
|
-
return {"presence": record}
|
|
107
|
-
|
|
108
|
-
@router.post("/realtime/presence/leave")
|
|
109
|
-
async def realtime_leave(req: PresenceRequest, request: Request):
|
|
110
|
-
user = require_user(request)
|
|
111
|
-
if req.client_id:
|
|
112
|
-
try:
|
|
113
|
-
bus.leave(req.client_id, user=user or None)
|
|
114
|
-
except PermissionError as exc:
|
|
115
|
-
raise HTTPException(status_code=403, detail=str(exc)) from exc
|
|
116
|
-
return {"status": "ok"}
|
|
117
|
-
|
|
118
|
-
return router
|