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/hooks.py
DELETED
|
@@ -1,165 +0,0 @@
|
|
|
1
|
-
"""Hooks platform API router (v3.2.0).
|
|
2
|
-
|
|
3
|
-
Exposes the lifecycle :class:`~lattice_brain.runtime.hooks.HooksRegistry` over HTTP so
|
|
4
|
-
the /app Hooks view can list, inspect, enable/disable, reorder, and register
|
|
5
|
-
hooks. Full paths live in the decorators (no ``prefix=``), matching the rest of
|
|
6
|
-
the API.
|
|
7
|
-
"""
|
|
8
|
-
|
|
9
|
-
from __future__ import annotations
|
|
10
|
-
|
|
11
|
-
from typing import Any, Callable, List, Optional
|
|
12
|
-
|
|
13
|
-
from fastapi import APIRouter, HTTPException, Request
|
|
14
|
-
from pydantic import BaseModel
|
|
15
|
-
|
|
16
|
-
from lattice_brain.runtime.hooks import HooksRegistry
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
class HookToggleRequest(BaseModel):
|
|
20
|
-
hook_id: str
|
|
21
|
-
enabled: bool = True
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
class HookReorderRequest(BaseModel):
|
|
25
|
-
kind: str
|
|
26
|
-
ordered_ids: List[str] = []
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
class HookRegisterRequest(BaseModel):
|
|
30
|
-
name: str
|
|
31
|
-
kind: str
|
|
32
|
-
description: str = ""
|
|
33
|
-
command: str = ""
|
|
34
|
-
order: Optional[int] = None
|
|
35
|
-
enabled: bool = True
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
class HookRunRequest(BaseModel):
|
|
39
|
-
kind: Optional[str] = None
|
|
40
|
-
hook_id: Optional[str] = None
|
|
41
|
-
event: str = ""
|
|
42
|
-
payload: dict = {}
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
def create_hooks_router(
|
|
46
|
-
*,
|
|
47
|
-
registry: HooksRegistry,
|
|
48
|
-
require_user: Callable[[Request], str],
|
|
49
|
-
# Production returns ``(email, users)``; small adapters return just the
|
|
50
|
-
# identity, which ``_require_admin_email`` below handles.
|
|
51
|
-
require_admin: Callable[[Request], Any],
|
|
52
|
-
append_audit_event: Callable[..., None],
|
|
53
|
-
) -> APIRouter:
|
|
54
|
-
router = APIRouter()
|
|
55
|
-
|
|
56
|
-
def _require_admin_email(request: Request) -> str:
|
|
57
|
-
"""Authorize a hook mutation and return the acting administrator."""
|
|
58
|
-
result = require_admin(request)
|
|
59
|
-
if isinstance(result, tuple):
|
|
60
|
-
return str(result[0] or "")
|
|
61
|
-
# Keep the router tolerant of small test/runtime adapters that return
|
|
62
|
-
# only the identity while the production access runtime returns
|
|
63
|
-
# ``(email, users)``.
|
|
64
|
-
return str(result or "")
|
|
65
|
-
|
|
66
|
-
@router.get("/api/hooks")
|
|
67
|
-
async def list_hooks(request: Request, kind: Optional[str] = None):
|
|
68
|
-
require_user(request)
|
|
69
|
-
return registry.list(kind=kind)
|
|
70
|
-
|
|
71
|
-
# NOTE: declared before the ``/{hook_id:path}`` catch-all so "runs" is not
|
|
72
|
-
# captured as a hook id.
|
|
73
|
-
@router.get("/api/hooks/runs")
|
|
74
|
-
async def hook_runs(request: Request, limit: int = 50, kind: Optional[str] = None):
|
|
75
|
-
require_user(request)
|
|
76
|
-
return registry.recent_runs(limit=limit, kind=kind)
|
|
77
|
-
|
|
78
|
-
@router.post("/api/hooks/run")
|
|
79
|
-
async def run_hooks(req: HookRunRequest, request: Request):
|
|
80
|
-
"""Execute hooks now — by ``kind`` (all enabled hooks of that kind) or a
|
|
81
|
-
single ``hook_id``. Returns the dispatch record so callers can see what
|
|
82
|
-
ran, in what order, and whether the action was blocked."""
|
|
83
|
-
user = _require_admin_email(request)
|
|
84
|
-
try:
|
|
85
|
-
if req.hook_id:
|
|
86
|
-
result = registry.run_hook(req.hook_id, event=req.event or None, payload=req.payload, user_email=user)
|
|
87
|
-
elif req.kind:
|
|
88
|
-
result = registry.run_hooks(req.kind, event=req.event or None, payload=req.payload, user_email=user)
|
|
89
|
-
else:
|
|
90
|
-
raise HTTPException(status_code=400, detail="Provide a 'kind' or a 'hook_id' to run.")
|
|
91
|
-
except KeyError as exc:
|
|
92
|
-
raise HTTPException(status_code=404, detail=f"Hook not found: {exc}") from exc
|
|
93
|
-
except ValueError as exc:
|
|
94
|
-
raise HTTPException(status_code=400, detail=str(exc)) from exc
|
|
95
|
-
append_audit_event("hook_run", user_email=user, hook_id=req.hook_id, kind=req.kind, event=req.event)
|
|
96
|
-
return result
|
|
97
|
-
|
|
98
|
-
# Alias for the same dispatch action (fire == run).
|
|
99
|
-
@router.post("/api/hooks/fire")
|
|
100
|
-
async def fire_hooks(req: HookRunRequest, request: Request):
|
|
101
|
-
return await run_hooks(req, request)
|
|
102
|
-
|
|
103
|
-
@router.get("/api/hooks/{hook_id:path}")
|
|
104
|
-
async def inspect_hook(hook_id: str, request: Request):
|
|
105
|
-
require_user(request)
|
|
106
|
-
try:
|
|
107
|
-
return {"hook": registry.inspect(hook_id)}
|
|
108
|
-
except KeyError as exc:
|
|
109
|
-
raise HTTPException(status_code=404, detail=f"Hook not found: {hook_id}") from exc
|
|
110
|
-
|
|
111
|
-
@router.post("/api/hooks/enable")
|
|
112
|
-
async def enable_hook(req: HookToggleRequest, request: Request):
|
|
113
|
-
user = _require_admin_email(request)
|
|
114
|
-
try:
|
|
115
|
-
hook = registry.set_enabled(req.hook_id, req.enabled)
|
|
116
|
-
except KeyError as exc:
|
|
117
|
-
raise HTTPException(status_code=404, detail=f"Hook not found: {req.hook_id}") from exc
|
|
118
|
-
append_audit_event("hook_toggle", user_email=user, hook_id=req.hook_id, enabled=req.enabled)
|
|
119
|
-
return {"hook": hook}
|
|
120
|
-
|
|
121
|
-
@router.post("/api/hooks/disable")
|
|
122
|
-
async def disable_hook(req: HookToggleRequest, request: Request):
|
|
123
|
-
user = _require_admin_email(request)
|
|
124
|
-
try:
|
|
125
|
-
hook = registry.set_enabled(req.hook_id, False)
|
|
126
|
-
except KeyError as exc:
|
|
127
|
-
raise HTTPException(status_code=404, detail=f"Hook not found: {req.hook_id}") from exc
|
|
128
|
-
append_audit_event("hook_toggle", user_email=user, hook_id=req.hook_id, enabled=False)
|
|
129
|
-
return {"hook": hook}
|
|
130
|
-
|
|
131
|
-
@router.post("/api/hooks/reorder")
|
|
132
|
-
async def reorder_hooks(req: HookReorderRequest, request: Request):
|
|
133
|
-
_require_admin_email(request)
|
|
134
|
-
return registry.reorder(req.kind, req.ordered_ids)
|
|
135
|
-
|
|
136
|
-
@router.post("/api/hooks/register")
|
|
137
|
-
async def register_hook(req: HookRegisterRequest, request: Request):
|
|
138
|
-
user = _require_admin_email(request)
|
|
139
|
-
try:
|
|
140
|
-
entry = registry.register(
|
|
141
|
-
name=req.name,
|
|
142
|
-
kind=req.kind,
|
|
143
|
-
description=req.description,
|
|
144
|
-
command=req.command,
|
|
145
|
-
order=req.order,
|
|
146
|
-
enabled=req.enabled,
|
|
147
|
-
)
|
|
148
|
-
except ValueError as exc:
|
|
149
|
-
raise HTTPException(status_code=400, detail=str(exc)) from exc
|
|
150
|
-
append_audit_event("hook_register", user_email=user, hook_id=entry["id"], kind=entry["kind"])
|
|
151
|
-
return {"hook": entry}
|
|
152
|
-
|
|
153
|
-
@router.delete("/api/hooks/{hook_id:path}")
|
|
154
|
-
async def remove_hook(hook_id: str, request: Request):
|
|
155
|
-
user = _require_admin_email(request)
|
|
156
|
-
try:
|
|
157
|
-
result = registry.remove(hook_id)
|
|
158
|
-
except KeyError as exc:
|
|
159
|
-
raise HTTPException(status_code=404, detail=f"Hook not found: {hook_id}") from exc
|
|
160
|
-
except ValueError as exc:
|
|
161
|
-
raise HTTPException(status_code=400, detail=str(exc)) from exc
|
|
162
|
-
append_audit_event("hook_remove", user_email=user, hook_id=hook_id)
|
|
163
|
-
return result
|
|
164
|
-
|
|
165
|
-
return router
|
|
@@ -1,145 +0,0 @@
|
|
|
1
|
-
"""Index jobs API (v11.5.0, plan §3a) — the trigger the embed queue never had.
|
|
2
|
-
|
|
3
|
-
``vector_jobs`` has been a durable backlog since v11.1.0: an ingest whose
|
|
4
|
-
inline embedding fails lands anyway, the node is queued, and
|
|
5
|
-
``VectorEmbedQueue.tick`` will embed it *when someone runs a tick*. Nobody did.
|
|
6
|
-
The only drain in the product was
|
|
7
|
-
:meth:`~lattice_brain.ingestion.IngestionPipeline.drain_vector_queue`, callable
|
|
8
|
-
from a test or a REPL and from nothing that runs on its own — which is what
|
|
9
|
-
FEATURE_STATUS.md:179-185 says out loud.
|
|
10
|
-
|
|
11
|
-
This router is the missing half: an HTTP surface a scheduler can call.
|
|
12
|
-
|
|
13
|
-
* ``POST /api/index/drain`` — run one tick now, answer with what it did and
|
|
14
|
-
what is left.
|
|
15
|
-
* ``GET /api/index/queue`` — the backlog counts, read-only.
|
|
16
|
-
|
|
17
|
-
Their two siblings under the same prefix live in ``search.py``
|
|
18
|
-
(``/api/index/status``, ``/api/index/rebuild``): those answer *is the index
|
|
19
|
-
complete* and *rebuild it from scratch*, which is the operator's hammer. This
|
|
20
|
-
pair is the small, repeatable step a timer can take every minute.
|
|
21
|
-
|
|
22
|
-
Two honest boundaries, stated here so the payloads are not read as more than
|
|
23
|
-
they are:
|
|
24
|
-
|
|
25
|
-
* **The backlog is machine-wide.** One SQLite queue serves every workspace, so
|
|
26
|
-
a drain embeds whatever is owed regardless of who asked, and the counts are
|
|
27
|
-
totals for this Brain. The workspace gate still runs on both paths — it is
|
|
28
|
-
the authorization check, not a filter — and the drain payload says
|
|
29
|
-
``scope: "machine"`` rather than implying a scoped number.
|
|
30
|
-
* **Draining is not indexing.** The tick delegates to the store's own indexer;
|
|
31
|
-
a node that fails goes back to ``pending`` until its retry budget runs out.
|
|
32
|
-
The response reports ``claimed``/``indexed``/``retried``/``failed`` verbatim
|
|
33
|
-
from the queue instead of summarising them into a success flag.
|
|
34
|
-
"""
|
|
35
|
-
|
|
36
|
-
from __future__ import annotations
|
|
37
|
-
|
|
38
|
-
import asyncio
|
|
39
|
-
from typing import Any, Callable, Dict, Optional
|
|
40
|
-
|
|
41
|
-
from fastapi import APIRouter, Request
|
|
42
|
-
from pydantic import BaseModel
|
|
43
|
-
|
|
44
|
-
from lattice_brain.graph.vector_index import DEFAULT_TICK_LIMIT, VECTOR_JOB_STATUSES
|
|
45
|
-
from latticeai.core.messages import http_error, resolve_language
|
|
46
|
-
|
|
47
|
-
#: Bounds on one drain. The floor keeps a caller from asking for a no-op tick
|
|
48
|
-
#: that still claims the queue's lock; the ceiling keeps one HTTP request from
|
|
49
|
-
#: turning into an unbounded embedding run behind a client that will time out.
|
|
50
|
-
MIN_DRAIN_LIMIT = 1
|
|
51
|
-
MAX_DRAIN_LIMIT = 100
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
class DrainRequest(BaseModel):
|
|
55
|
-
"""How many queued nodes one drain may claim.
|
|
56
|
-
|
|
57
|
-
Omitted (or a body-less POST) means the queue's own tick size, so a caller
|
|
58
|
-
with no opinion inherits the number the queue was designed around.
|
|
59
|
-
"""
|
|
60
|
-
|
|
61
|
-
limit: Optional[int] = None
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
def create_index_jobs_router(
|
|
65
|
-
*,
|
|
66
|
-
pipeline: Any,
|
|
67
|
-
knowledge_graph: Any,
|
|
68
|
-
require_user: Callable[[Request], Any],
|
|
69
|
-
gate_read: Callable[[Request], Optional[str]],
|
|
70
|
-
gate_write: Callable[[Request], Optional[str]],
|
|
71
|
-
) -> APIRouter:
|
|
72
|
-
router = APIRouter()
|
|
73
|
-
|
|
74
|
-
def _require_pipeline(request: Request) -> Any:
|
|
75
|
-
"""The pipeline, or 503 — a drain without ingestion is not a 500."""
|
|
76
|
-
if pipeline is None or not pipeline.available():
|
|
77
|
-
raise http_error(503, "capture.ingestion_disabled", resolve_language(request))
|
|
78
|
-
return pipeline
|
|
79
|
-
|
|
80
|
-
def _queue_state() -> Dict[str, Any]:
|
|
81
|
-
"""Backlog counts, or an explicit "not tracked" — never a fake zero.
|
|
82
|
-
|
|
83
|
-
A store without a durable queue reports ``available: false`` alongside
|
|
84
|
-
its zeros, because "0 pending" and "nothing is counting" are different
|
|
85
|
-
answers and a scheduler polling this would otherwise read the second as
|
|
86
|
-
the first.
|
|
87
|
-
"""
|
|
88
|
-
queue = getattr(knowledge_graph, "vector_queue", None)
|
|
89
|
-
if queue is None or not queue.available:
|
|
90
|
-
return {
|
|
91
|
-
"available": False,
|
|
92
|
-
"counts": dict.fromkeys(VECTOR_JOB_STATUSES, 0),
|
|
93
|
-
"pending": 0,
|
|
94
|
-
}
|
|
95
|
-
return {
|
|
96
|
-
"available": True,
|
|
97
|
-
"counts": dict(queue.snapshot()),
|
|
98
|
-
"pending": int(queue.pending_count()),
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
@router.post("/api/index/drain")
|
|
102
|
-
async def drain_index_queue(request: Request, req: Optional[DrainRequest] = None):
|
|
103
|
-
"""Run one background-embedding tick and report what it did.
|
|
104
|
-
|
|
105
|
-
Off the event loop: the tick opens SQLite and calls the embedder, and
|
|
106
|
-
this server has one loop for every user (10.9.0).
|
|
107
|
-
"""
|
|
108
|
-
require_user(request)
|
|
109
|
-
gate_write(request)
|
|
110
|
-
active = _require_pipeline(request)
|
|
111
|
-
requested = req.limit if req is not None else None
|
|
112
|
-
limit = DEFAULT_TICK_LIMIT if requested is None else requested
|
|
113
|
-
if limit < MIN_DRAIN_LIMIT or limit > MAX_DRAIN_LIMIT:
|
|
114
|
-
raise http_error(
|
|
115
|
-
422,
|
|
116
|
-
"index.limit_out_of_range",
|
|
117
|
-
resolve_language(request),
|
|
118
|
-
min=MIN_DRAIN_LIMIT,
|
|
119
|
-
max=MAX_DRAIN_LIMIT,
|
|
120
|
-
)
|
|
121
|
-
tick = await asyncio.to_thread(active.drain_vector_queue, limit)
|
|
122
|
-
return {
|
|
123
|
-
**dict(tick),
|
|
124
|
-
"limit": limit,
|
|
125
|
-
"scope": "machine",
|
|
126
|
-
"queue": _queue_state(),
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
@router.get("/api/index/queue")
|
|
130
|
-
async def index_queue(request: Request):
|
|
131
|
-
"""The embed backlog, counted. Reads nothing else and writes nothing."""
|
|
132
|
-
require_user(request)
|
|
133
|
-
gate_read(request)
|
|
134
|
-
_require_pipeline(request)
|
|
135
|
-
return _queue_state()
|
|
136
|
-
|
|
137
|
-
return router
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
__all__ = [
|
|
141
|
-
"MAX_DRAIN_LIMIT",
|
|
142
|
-
"MIN_DRAIN_LIMIT",
|
|
143
|
-
"DrainRequest",
|
|
144
|
-
"create_index_jobs_router",
|
|
145
|
-
]
|
|
@@ -1,100 +0,0 @@
|
|
|
1
|
-
"""Invitation API: create, list, and accept workspace invitations."""
|
|
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
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
class InvitationCreateRequest(BaseModel):
|
|
12
|
-
email: Optional[str] = None
|
|
13
|
-
workspace_id: Optional[str] = None
|
|
14
|
-
role: str = "member"
|
|
15
|
-
expires_hours: int = 168
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
def create_invitations_router(
|
|
19
|
-
*,
|
|
20
|
-
invitation_store,
|
|
21
|
-
workspace_service,
|
|
22
|
-
require_admin: Callable,
|
|
23
|
-
require_user: Callable[[Request], str],
|
|
24
|
-
user_id_for_email: Callable[[Optional[str]], Optional[str]],
|
|
25
|
-
append_audit_event: Callable[..., None],
|
|
26
|
-
) -> APIRouter:
|
|
27
|
-
router = APIRouter()
|
|
28
|
-
|
|
29
|
-
@router.get("/invitations")
|
|
30
|
-
async def list_invitations(request: Request):
|
|
31
|
-
require_admin(request)
|
|
32
|
-
return {"invitations": invitation_store.list()}
|
|
33
|
-
|
|
34
|
-
@router.post("/invitations")
|
|
35
|
-
async def create_invitation(req: InvitationCreateRequest, request: Request):
|
|
36
|
-
admin_email, _ = require_admin(request)
|
|
37
|
-
actor_id = user_id_for_email(admin_email)
|
|
38
|
-
if req.workspace_id:
|
|
39
|
-
try:
|
|
40
|
-
workspace_service.store._require_permission(
|
|
41
|
-
workspace_service.store._load_org(workspace_service.store.load_state(), req.workspace_id),
|
|
42
|
-
actor_id,
|
|
43
|
-
"manage_members",
|
|
44
|
-
)
|
|
45
|
-
except FileNotFoundError as exc:
|
|
46
|
-
raise HTTPException(status_code=404, detail=f"Workspace not found: {req.workspace_id}") from exc
|
|
47
|
-
except ValueError as exc:
|
|
48
|
-
raise HTTPException(status_code=400, detail=str(exc)) from exc
|
|
49
|
-
except PermissionError as exc:
|
|
50
|
-
raise HTTPException(status_code=403, detail=str(exc)) from exc
|
|
51
|
-
if req.role not in {"owner", "admin", "member", "viewer"}:
|
|
52
|
-
raise HTTPException(status_code=400, detail="unknown invitation role")
|
|
53
|
-
invitation = invitation_store.create(
|
|
54
|
-
email=req.email,
|
|
55
|
-
workspace_id=req.workspace_id,
|
|
56
|
-
role=req.role,
|
|
57
|
-
created_by=actor_id,
|
|
58
|
-
expires_hours=req.expires_hours,
|
|
59
|
-
)
|
|
60
|
-
append_audit_event(
|
|
61
|
-
"invitation_created",
|
|
62
|
-
user_email=admin_email,
|
|
63
|
-
invitation_id=invitation.get("id"),
|
|
64
|
-
workspace_id=req.workspace_id,
|
|
65
|
-
role=req.role,
|
|
66
|
-
)
|
|
67
|
-
return {"invitation": invitation}
|
|
68
|
-
|
|
69
|
-
@router.post("/invitations/{token}/accept")
|
|
70
|
-
async def accept_invitation(token: str, request: Request):
|
|
71
|
-
email = require_user(request)
|
|
72
|
-
user_id = user_id_for_email(email)
|
|
73
|
-
if not user_id:
|
|
74
|
-
raise HTTPException(status_code=401, detail="Authentication required")
|
|
75
|
-
try:
|
|
76
|
-
invitation = invitation_store.accept(token, accepted_by=user_id, email=email or None)
|
|
77
|
-
except FileNotFoundError as exc:
|
|
78
|
-
raise HTTPException(status_code=404, detail="Invitation not found") from exc
|
|
79
|
-
except PermissionError as exc:
|
|
80
|
-
raise HTTPException(status_code=403, detail=str(exc)) from exc
|
|
81
|
-
workspace_id = invitation.get("workspace_id")
|
|
82
|
-
if workspace_id:
|
|
83
|
-
try:
|
|
84
|
-
workspace_service.add_member(
|
|
85
|
-
workspace_id,
|
|
86
|
-
user_id=user_id,
|
|
87
|
-
role=invitation.get("role") or "member",
|
|
88
|
-
actor=invitation.get("created_by"),
|
|
89
|
-
)
|
|
90
|
-
except Exception as exc:
|
|
91
|
-
raise HTTPException(status_code=409, detail=str(exc)) from exc
|
|
92
|
-
append_audit_event(
|
|
93
|
-
"invitation_accepted",
|
|
94
|
-
user_email=email,
|
|
95
|
-
invitation_id=invitation.get("id"),
|
|
96
|
-
workspace_id=workspace_id,
|
|
97
|
-
)
|
|
98
|
-
return {"invitation": invitation}
|
|
99
|
-
|
|
100
|
-
return router
|