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,218 +0,0 @@
|
|
|
1
|
-
"""Product hardening and privacy status helpers.
|
|
2
|
-
|
|
3
|
-
These helpers are read-only and must not perform network probes. They describe
|
|
4
|
-
the local-first startup posture and distinguish available credentials from
|
|
5
|
-
enabled outbound communication.
|
|
6
|
-
"""
|
|
7
|
-
|
|
8
|
-
from __future__ import annotations
|
|
9
|
-
|
|
10
|
-
import os
|
|
11
|
-
import shutil
|
|
12
|
-
from pathlib import Path
|
|
13
|
-
from typing import Any, Dict, Mapping, Optional
|
|
14
|
-
|
|
15
|
-
from latticeai import __version__
|
|
16
|
-
from latticeai.core.config import Config
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
def _bool(env: Mapping[str, str], key: str, default: bool = False) -> bool:
|
|
20
|
-
raw = env.get(key)
|
|
21
|
-
if raw is None:
|
|
22
|
-
return default
|
|
23
|
-
return raw.strip().lower() in {"1", "true", "yes", "on"}
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
def _present(env: Mapping[str, str], *keys: str) -> bool:
|
|
27
|
-
return any(bool(str(env.get(key) or "").strip()) for key in keys)
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
def external_integration_status(
|
|
31
|
-
config: Config,
|
|
32
|
-
*,
|
|
33
|
-
env: Optional[Mapping[str, str]] = None,
|
|
34
|
-
) -> Dict[str, Any]:
|
|
35
|
-
if env is None:
|
|
36
|
-
env = os.environ
|
|
37
|
-
telegram_credentials = _present(env, "LATTICEAI_TELEGRAM_BOT_TOKEN", "TELEGRAM_BOT_TOKEN")
|
|
38
|
-
brain_network_auto_push = _bool(env, "LATTICEAI_BRAIN_NETWORK_AUTO_PUSH", default=False)
|
|
39
|
-
updater_enabled = _bool(env, "LATTICEAI_ENABLE_UPDATES", default=False)
|
|
40
|
-
model_downloads_enabled = _bool(env, "LATTICEAI_ALLOW_MODEL_DOWNLOADS", default=False) or bool(config.autoload_models)
|
|
41
|
-
docker_auto_start = _bool(env, "LATTICEAI_DOCKER_AUTO_START", default=False)
|
|
42
|
-
external_connectors_enabled = _bool(env, "LATTICEAI_ENABLE_EXTERNAL_CONNECTORS", default=False)
|
|
43
|
-
postgres_enabled = config.storage_engine == "postgres" and bool(config.postgres_dsn)
|
|
44
|
-
return {
|
|
45
|
-
"local_only_default": default_startup_local_only(config, env=env),
|
|
46
|
-
"integrations": {
|
|
47
|
-
"telegram": {
|
|
48
|
-
"enabled": bool(config.enable_telegram),
|
|
49
|
-
"credential_present": telegram_credentials,
|
|
50
|
-
"opt_in_required": True,
|
|
51
|
-
"automatic_egress": bool(config.enable_telegram),
|
|
52
|
-
"detail": (
|
|
53
|
-
"enabled by LATTICEAI_ENABLE_TELEGRAM"
|
|
54
|
-
if config.enable_telegram
|
|
55
|
-
else "disabled; token presence alone does not start Telegram"
|
|
56
|
-
),
|
|
57
|
-
},
|
|
58
|
-
"brain_network": {
|
|
59
|
-
"enabled": brain_network_auto_push,
|
|
60
|
-
"credential_present": False,
|
|
61
|
-
"opt_in_required": True,
|
|
62
|
-
"automatic_egress": brain_network_auto_push,
|
|
63
|
-
"detail": "peer pushes are user/admin initiated; no automatic peer sync by default",
|
|
64
|
-
},
|
|
65
|
-
"updates": {
|
|
66
|
-
"enabled": updater_enabled,
|
|
67
|
-
"credential_present": False,
|
|
68
|
-
"opt_in_required": True,
|
|
69
|
-
"automatic_egress": updater_enabled,
|
|
70
|
-
"detail": "desktop updater checks are disabled unless LATTICEAI_ENABLE_UPDATES is true",
|
|
71
|
-
},
|
|
72
|
-
"model_downloads": {
|
|
73
|
-
"enabled": model_downloads_enabled,
|
|
74
|
-
"credential_present": _present(env, "HF_TOKEN", "HUGGINGFACEHUB_API_TOKEN"),
|
|
75
|
-
"opt_in_required": True,
|
|
76
|
-
"automatic_egress": bool(config.autoload_models),
|
|
77
|
-
"detail": "model downloads require an explicit load/autoload setting",
|
|
78
|
-
},
|
|
79
|
-
"docker": {
|
|
80
|
-
"enabled": docker_auto_start,
|
|
81
|
-
"credential_present": False,
|
|
82
|
-
"opt_in_required": True,
|
|
83
|
-
"automatic_egress": docker_auto_start,
|
|
84
|
-
"detail": "Docker setup requires explicit runtime consent; auto-start is disabled by default",
|
|
85
|
-
},
|
|
86
|
-
"postgres": {
|
|
87
|
-
"enabled": postgres_enabled,
|
|
88
|
-
"credential_present": bool(config.postgres_dsn),
|
|
89
|
-
"opt_in_required": True,
|
|
90
|
-
"automatic_egress": postgres_enabled,
|
|
91
|
-
"detail": "Postgres scale mode is used only when storage engine and DSN are explicitly configured",
|
|
92
|
-
},
|
|
93
|
-
"external_connectors": {
|
|
94
|
-
"enabled": external_connectors_enabled,
|
|
95
|
-
"credential_present": _present(
|
|
96
|
-
env,
|
|
97
|
-
"OPENAI_API_KEY",
|
|
98
|
-
"ANTHROPIC_API_KEY",
|
|
99
|
-
"GITHUB_TOKEN",
|
|
100
|
-
"SLACK_BOT_TOKEN",
|
|
101
|
-
"DISCORD_BOT_TOKEN",
|
|
102
|
-
),
|
|
103
|
-
"opt_in_required": True,
|
|
104
|
-
"automatic_egress": external_connectors_enabled,
|
|
105
|
-
"detail": "connector credentials are inert until the connector is explicitly enabled and invoked",
|
|
106
|
-
},
|
|
107
|
-
},
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
def default_startup_local_only(
|
|
112
|
-
config: Config,
|
|
113
|
-
*,
|
|
114
|
-
env: Optional[Mapping[str, str]] = None,
|
|
115
|
-
) -> bool:
|
|
116
|
-
if env is None:
|
|
117
|
-
env = os.environ
|
|
118
|
-
local_embedding = config.embedding_provider in {"", "hash", "local", "fallback", "sqlite"}
|
|
119
|
-
external = external_integration_status_no_recurse(config, env=env)
|
|
120
|
-
return (
|
|
121
|
-
not config.network_exposed
|
|
122
|
-
and not config.cors_allow_network
|
|
123
|
-
and not config.enable_telegram
|
|
124
|
-
and not config.autoload_models
|
|
125
|
-
and config.storage_engine == "sqlite"
|
|
126
|
-
and local_embedding
|
|
127
|
-
and not any(item["automatic_egress"] for item in external.values())
|
|
128
|
-
)
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
def external_integration_status_no_recurse(
|
|
132
|
-
config: Config,
|
|
133
|
-
*,
|
|
134
|
-
env: Mapping[str, str],
|
|
135
|
-
) -> Dict[str, Dict[str, Any]]:
|
|
136
|
-
return {
|
|
137
|
-
"brain_network": {"automatic_egress": _bool(env, "LATTICEAI_BRAIN_NETWORK_AUTO_PUSH", default=False)},
|
|
138
|
-
"updates": {"automatic_egress": _bool(env, "LATTICEAI_ENABLE_UPDATES", default=False)},
|
|
139
|
-
"docker": {"automatic_egress": _bool(env, "LATTICEAI_DOCKER_AUTO_START", default=False)},
|
|
140
|
-
"postgres": {"automatic_egress": config.storage_engine == "postgres" and bool(config.postgres_dsn)},
|
|
141
|
-
"external_connectors": {"automatic_egress": _bool(env, "LATTICEAI_ENABLE_EXTERNAL_CONNECTORS", default=False)},
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
def build_product_hardening_status(
|
|
146
|
-
*,
|
|
147
|
-
config: Config,
|
|
148
|
-
portability: Any = None,
|
|
149
|
-
device_identity: Any = None,
|
|
150
|
-
env: Optional[Mapping[str, str]] = None,
|
|
151
|
-
) -> Dict[str, Any]:
|
|
152
|
-
if env is None:
|
|
153
|
-
env = os.environ
|
|
154
|
-
storage = {"available": False}
|
|
155
|
-
backup = {"available": False}
|
|
156
|
-
if portability is not None and getattr(portability, "available", lambda: False)():
|
|
157
|
-
storage = portability.storage_status()
|
|
158
|
-
backup = portability.backup_health()
|
|
159
|
-
identity = {}
|
|
160
|
-
if device_identity is not None:
|
|
161
|
-
identity = device_identity.describe()
|
|
162
|
-
data_dir = Path(config.data_dir)
|
|
163
|
-
return {
|
|
164
|
-
"version": __version__,
|
|
165
|
-
"startup": {
|
|
166
|
-
"local_only_default": default_startup_local_only(config, env=env),
|
|
167
|
-
"host": config.host,
|
|
168
|
-
"port": config.port,
|
|
169
|
-
"network_exposed": config.network_exposed,
|
|
170
|
-
"auth_required": config.require_auth,
|
|
171
|
-
"cors_network_allowed": config.cors_allow_network,
|
|
172
|
-
},
|
|
173
|
-
"desktop": {
|
|
174
|
-
"sidecar_lifecycle": "managed",
|
|
175
|
-
"restart_supported": True,
|
|
176
|
-
"shutdown_supported": True,
|
|
177
|
-
"updater": {
|
|
178
|
-
"enabled": _bool(env, "LATTICEAI_ENABLE_UPDATES", default=False),
|
|
179
|
-
"limitation": "No external update checks run unless explicitly enabled by policy.",
|
|
180
|
-
},
|
|
181
|
-
},
|
|
182
|
-
"first_run": {
|
|
183
|
-
"data_dir": str(data_dir),
|
|
184
|
-
"data_dir_exists": data_dir.exists(),
|
|
185
|
-
"python_available": shutil.which("python3") is not None or shutil.which("python") is not None,
|
|
186
|
-
"docker_available": shutil.which("docker") is not None,
|
|
187
|
-
"docker_required": False,
|
|
188
|
-
"postgres_required": False,
|
|
189
|
-
},
|
|
190
|
-
"privacy": external_integration_status(config, env=env),
|
|
191
|
-
"storage": storage,
|
|
192
|
-
"backup": backup,
|
|
193
|
-
"device_identity": identity,
|
|
194
|
-
"permissions": {
|
|
195
|
-
"export_requires_admin": True,
|
|
196
|
-
"import_requires_admin": True,
|
|
197
|
-
"restore_requires_admin": True,
|
|
198
|
-
"destructive_restore_requires_confirmation": True,
|
|
199
|
-
"workspace_isolation_enforced": True,
|
|
200
|
-
"audit_log_visible_to_admin": True,
|
|
201
|
-
},
|
|
202
|
-
"failure_policy": {
|
|
203
|
-
"archive_corruption": "fail_closed",
|
|
204
|
-
"partial_archive": "fail_closed",
|
|
205
|
-
"signature_mismatch": "fail_closed",
|
|
206
|
-
"unsupported_version": "fail_closed",
|
|
207
|
-
"missing_docker": "honest_unavailable",
|
|
208
|
-
"missing_postgres": "honest_unavailable",
|
|
209
|
-
"permission_denied": "honest_error",
|
|
210
|
-
},
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
__all__ = [
|
|
215
|
-
"build_product_hardening_status",
|
|
216
|
-
"default_startup_local_only",
|
|
217
|
-
"external_integration_status",
|
|
218
|
-
]
|
|
@@ -1,337 +0,0 @@
|
|
|
1
|
-
"""Persistent project sessions — the multi-turn project loop (v9.9.6).
|
|
2
|
-
|
|
3
|
-
A single agent run is well modelled: PLAN → EXECUTE → VERIFY, with a durable
|
|
4
|
-
approval pause and a transcript. What was missing is the loop *above* it —
|
|
5
|
-
"만들고 → 고치고 → 검증하고 → 다시 고치는" work that spans several runs and
|
|
6
|
-
several days. Each run started from a blank workspace picture, so the second
|
|
7
|
-
turn could not see what the first produced, what still failed verification, or
|
|
8
|
-
what was left to do.
|
|
9
|
-
|
|
10
|
-
A project session is that missing state:
|
|
11
|
-
|
|
12
|
-
* ``files`` — every artifact the project's runs actually produced;
|
|
13
|
-
* ``todos`` — what is still open (explicit, user- or agent-authored);
|
|
14
|
-
* ``last_verification`` — the most recent honest outcome (never upgraded);
|
|
15
|
-
* ``runs`` — the run history that got there.
|
|
16
|
-
|
|
17
|
-
Storage mirrors :mod:`latticeai.core.run_store`: one JSON file per session
|
|
18
|
-
under the data dir, atomic replace on write, no new dependencies. Reads are
|
|
19
|
-
fail-open (a corrupt file is skipped, not fatal); writes never break a run —
|
|
20
|
-
a project session is context, not a gate.
|
|
21
|
-
"""
|
|
22
|
-
|
|
23
|
-
from __future__ import annotations
|
|
24
|
-
|
|
25
|
-
import logging
|
|
26
|
-
import re
|
|
27
|
-
import secrets
|
|
28
|
-
from pathlib import Path
|
|
29
|
-
from typing import Any, Dict, List, Optional
|
|
30
|
-
|
|
31
|
-
from latticeai.core.io_utils import atomic_write_json
|
|
32
|
-
from latticeai.core.timeutil import now_iso
|
|
33
|
-
|
|
34
|
-
LOGGER = logging.getLogger(__name__)
|
|
35
|
-
|
|
36
|
-
__all__ = ["ProjectSessionStore", "PROJECT_STATUSES"]
|
|
37
|
-
|
|
38
|
-
# Terminal-ish project states. "active" is the default; "archived" hides a
|
|
39
|
-
# project from the default listing without deleting its history.
|
|
40
|
-
PROJECT_STATUSES = ("active", "archived")
|
|
41
|
-
|
|
42
|
-
_SESSION_ID_RE = re.compile(r"^[A-Za-z0-9_-]{8,64}$")
|
|
43
|
-
_MAX_FILES = 200
|
|
44
|
-
_MAX_TODOS = 100
|
|
45
|
-
_MAX_RUNS = 100
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
def _clean(value: Any, limit: int) -> str:
|
|
49
|
-
return str(value or "").strip()[:limit]
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
class ProjectSessionStore:
|
|
53
|
-
"""JSON-file store for long-running project sessions."""
|
|
54
|
-
|
|
55
|
-
def __init__(self, root: Path) -> None:
|
|
56
|
-
self._root = Path(root)
|
|
57
|
-
|
|
58
|
-
# ── paths ────────────────────────────────────────────────────────────
|
|
59
|
-
def _path(self, session_id: str) -> Optional[Path]:
|
|
60
|
-
if not _SESSION_ID_RE.match(str(session_id or "")):
|
|
61
|
-
return None
|
|
62
|
-
return self._root / f"{session_id}.json"
|
|
63
|
-
|
|
64
|
-
def _ensure_root(self) -> None:
|
|
65
|
-
self._root.mkdir(parents=True, exist_ok=True)
|
|
66
|
-
|
|
67
|
-
# ── read ─────────────────────────────────────────────────────────────
|
|
68
|
-
def _load(self, session_id: str) -> Optional[Dict[str, Any]]:
|
|
69
|
-
path = self._path(session_id)
|
|
70
|
-
if path is None or not path.exists():
|
|
71
|
-
return None
|
|
72
|
-
try:
|
|
73
|
-
import json
|
|
74
|
-
|
|
75
|
-
with path.open(encoding="utf-8") as handle:
|
|
76
|
-
data = json.load(handle)
|
|
77
|
-
return data if isinstance(data, dict) else None
|
|
78
|
-
except Exception as exc: # noqa: BLE001 — a bad file is skipped, not fatal
|
|
79
|
-
LOGGER.warning("project session load failed for %s: %s", session_id, exc)
|
|
80
|
-
return None
|
|
81
|
-
|
|
82
|
-
def get(
|
|
83
|
-
self,
|
|
84
|
-
session_id: str,
|
|
85
|
-
*,
|
|
86
|
-
user_email: Optional[str] = None,
|
|
87
|
-
workspace_id: Optional[str] = None,
|
|
88
|
-
) -> Optional[Dict[str, Any]]:
|
|
89
|
-
"""One session, scoped. Returns None when it does not exist or is
|
|
90
|
-
outside the caller's scope — never a partial record."""
|
|
91
|
-
record = self._load(session_id)
|
|
92
|
-
if record is None:
|
|
93
|
-
return None
|
|
94
|
-
if user_email is not None and record.get("user_email") != user_email:
|
|
95
|
-
return None
|
|
96
|
-
if workspace_id is not None and record.get("workspace_id") != workspace_id:
|
|
97
|
-
return None
|
|
98
|
-
return record
|
|
99
|
-
|
|
100
|
-
def list(
|
|
101
|
-
self,
|
|
102
|
-
*,
|
|
103
|
-
user_email: Optional[str] = None,
|
|
104
|
-
workspace_id: Optional[str] = None,
|
|
105
|
-
status: str = "active",
|
|
106
|
-
) -> Dict[str, Any]:
|
|
107
|
-
"""Sessions for a caller, newest first. ``status="all"`` includes archived."""
|
|
108
|
-
if not self._root.exists():
|
|
109
|
-
return {"projects": [], "count": 0}
|
|
110
|
-
sessions: List[Dict[str, Any]] = []
|
|
111
|
-
for path in self._root.glob("*.json"):
|
|
112
|
-
record = self._load(path.stem)
|
|
113
|
-
if record is None:
|
|
114
|
-
continue
|
|
115
|
-
if user_email is not None and record.get("user_email") != user_email:
|
|
116
|
-
continue
|
|
117
|
-
if workspace_id is not None and record.get("workspace_id") != workspace_id:
|
|
118
|
-
continue
|
|
119
|
-
if status != "all" and record.get("status") != status:
|
|
120
|
-
continue
|
|
121
|
-
sessions.append(record)
|
|
122
|
-
sessions.sort(key=lambda item: str(item.get("updated_at") or ""), reverse=True)
|
|
123
|
-
return {"projects": sessions, "count": len(sessions)}
|
|
124
|
-
|
|
125
|
-
# ── write ────────────────────────────────────────────────────────────
|
|
126
|
-
def _save(self, record: Dict[str, Any]) -> Dict[str, Any]:
|
|
127
|
-
record["updated_at"] = now_iso()
|
|
128
|
-
path = self._path(str(record.get("id") or ""))
|
|
129
|
-
if path is None:
|
|
130
|
-
raise ValueError("invalid project session id")
|
|
131
|
-
try:
|
|
132
|
-
self._ensure_root()
|
|
133
|
-
atomic_write_json(path, record)
|
|
134
|
-
except Exception as exc: # noqa: BLE001 — persistence never breaks a run
|
|
135
|
-
LOGGER.warning("project session save failed for %s: %s", record.get("id"), exc)
|
|
136
|
-
return record
|
|
137
|
-
|
|
138
|
-
def create(
|
|
139
|
-
self,
|
|
140
|
-
*,
|
|
141
|
-
title: str,
|
|
142
|
-
goal: str = "",
|
|
143
|
-
user_email: Optional[str] = None,
|
|
144
|
-
workspace_id: Optional[str] = None,
|
|
145
|
-
) -> Dict[str, Any]:
|
|
146
|
-
session_id = secrets.token_urlsafe(12)
|
|
147
|
-
record: Dict[str, Any] = {
|
|
148
|
-
"id": session_id,
|
|
149
|
-
"title": _clean(title, 200) or "프로젝트",
|
|
150
|
-
"goal": _clean(goal, 2000),
|
|
151
|
-
"status": "active",
|
|
152
|
-
"user_email": user_email,
|
|
153
|
-
"workspace_id": workspace_id,
|
|
154
|
-
"created_at": now_iso(),
|
|
155
|
-
"updated_at": now_iso(),
|
|
156
|
-
"files": [],
|
|
157
|
-
"todos": [],
|
|
158
|
-
"runs": [],
|
|
159
|
-
"last_verification": None,
|
|
160
|
-
}
|
|
161
|
-
return self._save(record)
|
|
162
|
-
|
|
163
|
-
def update(
|
|
164
|
-
self,
|
|
165
|
-
session_id: str,
|
|
166
|
-
*,
|
|
167
|
-
title: Optional[str] = None,
|
|
168
|
-
goal: Optional[str] = None,
|
|
169
|
-
status: Optional[str] = None,
|
|
170
|
-
user_email: Optional[str] = None,
|
|
171
|
-
workspace_id: Optional[str] = None,
|
|
172
|
-
) -> Optional[Dict[str, Any]]:
|
|
173
|
-
record = self.get(session_id, user_email=user_email, workspace_id=workspace_id)
|
|
174
|
-
if record is None:
|
|
175
|
-
return None
|
|
176
|
-
if title is not None:
|
|
177
|
-
record["title"] = _clean(title, 200) or record["title"]
|
|
178
|
-
if goal is not None:
|
|
179
|
-
record["goal"] = _clean(goal, 2000)
|
|
180
|
-
if status is not None and status in PROJECT_STATUSES:
|
|
181
|
-
record["status"] = status
|
|
182
|
-
return self._save(record)
|
|
183
|
-
|
|
184
|
-
def delete(
|
|
185
|
-
self,
|
|
186
|
-
session_id: str,
|
|
187
|
-
*,
|
|
188
|
-
user_email: Optional[str] = None,
|
|
189
|
-
workspace_id: Optional[str] = None,
|
|
190
|
-
) -> bool:
|
|
191
|
-
record = self.get(session_id, user_email=user_email, workspace_id=workspace_id)
|
|
192
|
-
if record is None:
|
|
193
|
-
return False
|
|
194
|
-
path = self._path(session_id)
|
|
195
|
-
try:
|
|
196
|
-
if path is not None and path.exists():
|
|
197
|
-
path.unlink()
|
|
198
|
-
return True
|
|
199
|
-
except OSError as exc:
|
|
200
|
-
LOGGER.warning("project session delete failed for %s: %s", session_id, exc)
|
|
201
|
-
return False
|
|
202
|
-
|
|
203
|
-
def set_todos(
|
|
204
|
-
self,
|
|
205
|
-
session_id: str,
|
|
206
|
-
todos: List[Any],
|
|
207
|
-
*,
|
|
208
|
-
user_email: Optional[str] = None,
|
|
209
|
-
workspace_id: Optional[str] = None,
|
|
210
|
-
) -> Optional[Dict[str, Any]]:
|
|
211
|
-
"""Replace the open-work list. Accepts strings or ``{text, done}`` dicts."""
|
|
212
|
-
record = self.get(session_id, user_email=user_email, workspace_id=workspace_id)
|
|
213
|
-
if record is None:
|
|
214
|
-
return None
|
|
215
|
-
normalized: List[Dict[str, Any]] = []
|
|
216
|
-
for raw in list(todos or [])[:_MAX_TODOS]:
|
|
217
|
-
if isinstance(raw, dict):
|
|
218
|
-
text = _clean(raw.get("text"), 300)
|
|
219
|
-
done = bool(raw.get("done"))
|
|
220
|
-
else:
|
|
221
|
-
text = _clean(raw, 300)
|
|
222
|
-
done = False
|
|
223
|
-
if text:
|
|
224
|
-
normalized.append({"text": text, "done": done})
|
|
225
|
-
record["todos"] = normalized
|
|
226
|
-
return self._save(record)
|
|
227
|
-
|
|
228
|
-
def record_run(
|
|
229
|
-
self,
|
|
230
|
-
session_id: str,
|
|
231
|
-
*,
|
|
232
|
-
run_id: str = "",
|
|
233
|
-
goal: str = "",
|
|
234
|
-
status: str = "",
|
|
235
|
-
final_state: str = "",
|
|
236
|
-
files: Optional[List[Any]] = None,
|
|
237
|
-
explanation: Optional[Dict[str, Any]] = None,
|
|
238
|
-
user_email: Optional[str] = None,
|
|
239
|
-
workspace_id: Optional[str] = None,
|
|
240
|
-
) -> Optional[Dict[str, Any]]:
|
|
241
|
-
"""Fold one finished agent run into the project's state.
|
|
242
|
-
|
|
243
|
-
The verification result is recorded exactly as the run reported it —
|
|
244
|
-
a NEEDS_REVIEW run never becomes a project's "done".
|
|
245
|
-
"""
|
|
246
|
-
record = self.get(session_id, user_email=user_email, workspace_id=workspace_id)
|
|
247
|
-
if record is None:
|
|
248
|
-
return None
|
|
249
|
-
paths: List[str] = []
|
|
250
|
-
for entry in list(files or []):
|
|
251
|
-
path = entry if isinstance(entry, str) else (entry or {}).get("path")
|
|
252
|
-
path = _clean(path, 400)
|
|
253
|
-
if path and path not in paths:
|
|
254
|
-
paths.append(path)
|
|
255
|
-
known = list(record.get("files") or [])
|
|
256
|
-
for path in paths:
|
|
257
|
-
if path not in known:
|
|
258
|
-
known.append(path)
|
|
259
|
-
record["files"] = known[-_MAX_FILES:]
|
|
260
|
-
headline = ""
|
|
261
|
-
next_step = ""
|
|
262
|
-
if isinstance(explanation, dict):
|
|
263
|
-
headline = _clean((explanation.get("headline") or {}).get("ko"), 400)
|
|
264
|
-
next_step = _clean((explanation.get("next_step") or {}).get("ko"), 400)
|
|
265
|
-
entry = {
|
|
266
|
-
"run_id": _clean(run_id, 120),
|
|
267
|
-
"goal": _clean(goal, 400),
|
|
268
|
-
"status": _clean(status, 40),
|
|
269
|
-
"final_state": _clean(final_state, 40),
|
|
270
|
-
"files": paths,
|
|
271
|
-
"at": now_iso(),
|
|
272
|
-
**({"headline": headline} if headline else {}),
|
|
273
|
-
**({"next_step": next_step} if next_step else {}),
|
|
274
|
-
}
|
|
275
|
-
runs = list(record.get("runs") or [])
|
|
276
|
-
runs.append(entry)
|
|
277
|
-
record["runs"] = runs[-_MAX_RUNS:]
|
|
278
|
-
record["last_verification"] = {
|
|
279
|
-
"final_state": entry["final_state"],
|
|
280
|
-
"ok": bool(isinstance(explanation, dict) and explanation.get("ok")),
|
|
281
|
-
"at": entry["at"],
|
|
282
|
-
**({"headline": headline} if headline else {}),
|
|
283
|
-
# Failure-learning loop (review 루프 §3): what the *last* attempt
|
|
284
|
-
# says to do differently travels into the next run's plan context
|
|
285
|
-
# instead of dying with the run.
|
|
286
|
-
**({"next_step": next_step} if next_step else {}),
|
|
287
|
-
}
|
|
288
|
-
return self._save(record)
|
|
289
|
-
|
|
290
|
-
# ── prompt context ───────────────────────────────────────────────────
|
|
291
|
-
def summary(
|
|
292
|
-
self,
|
|
293
|
-
session_id: str,
|
|
294
|
-
*,
|
|
295
|
-
user_email: Optional[str] = None,
|
|
296
|
-
workspace_id: Optional[str] = None,
|
|
297
|
-
max_files: int = 15,
|
|
298
|
-
max_todos: int = 10,
|
|
299
|
-
) -> str:
|
|
300
|
-
"""Prompt block describing where this project stands.
|
|
301
|
-
|
|
302
|
-
Injected into the planner/executor context so a later run sees the
|
|
303
|
-
files that already exist, what is still open, and whether the last
|
|
304
|
-
attempt actually passed. Empty string when there is nothing to say —
|
|
305
|
-
an empty project must not pad the prompt.
|
|
306
|
-
"""
|
|
307
|
-
record = self.get(session_id, user_email=user_email, workspace_id=workspace_id)
|
|
308
|
-
if record is None:
|
|
309
|
-
return ""
|
|
310
|
-
lines: List[str] = []
|
|
311
|
-
title = record.get("title") or ""
|
|
312
|
-
goal = record.get("goal") or ""
|
|
313
|
-
lines.append(f"Project: {title}" + (f" — {goal}" if goal else ""))
|
|
314
|
-
files = list(record.get("files") or [])[-max_files:]
|
|
315
|
-
if files:
|
|
316
|
-
lines.append("Files this project has already produced (they exist now):")
|
|
317
|
-
lines.extend(f"- {path}" for path in files)
|
|
318
|
-
open_todos = [
|
|
319
|
-
todo for todo in (record.get("todos") or []) if not todo.get("done")
|
|
320
|
-
][:max_todos]
|
|
321
|
-
if open_todos:
|
|
322
|
-
lines.append("Still open:")
|
|
323
|
-
lines.extend(f"- {todo['text']}" for todo in open_todos)
|
|
324
|
-
verification = record.get("last_verification")
|
|
325
|
-
if isinstance(verification, dict) and verification.get("final_state"):
|
|
326
|
-
verdict = verification["final_state"]
|
|
327
|
-
headline = verification.get("headline") or ""
|
|
328
|
-
lines.append(
|
|
329
|
-
f"Last verification: {verdict}" + (f" — {headline}" if headline else "")
|
|
330
|
-
)
|
|
331
|
-
next_step = verification.get("next_step") or ""
|
|
332
|
-
if next_step and not verification.get("ok"):
|
|
333
|
-
# The previous attempt's own diagnosis, so this plan does not
|
|
334
|
-
# repeat the same failure.
|
|
335
|
-
lines.append(f"What the last attempt said to do differently: {next_step}")
|
|
336
|
-
# Only the header line means nothing worth injecting.
|
|
337
|
-
return "\n".join(lines) if len(lines) > 1 else ""
|