marvisx-cli 0.1.0__py3-none-any.whl
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.
- core/api/__init__.py +0 -0
- core/api/agents/__init__.py +0 -0
- core/api/agents/session_health.py +59 -0
- core/api/agents/session_manager.py +206 -0
- core/api/bin/marvisx-state-hook.py +182 -0
- core/api/config.py +533 -0
- core/api/db.py +1516 -0
- core/api/dependencies/__init__.py +0 -0
- core/api/dependencies/tenant.py +34 -0
- core/api/main.py +1641 -0
- core/api/mcp/__init__.py +8 -0
- core/api/mcp/_adapter.py +184 -0
- core/api/mcp/server.py +58 -0
- core/api/mcp/tools/__init__.py +59 -0
- core/api/mcp/tools/brain.py +599 -0
- core/api/mcp/tools/graph.py +380 -0
- core/api/mcp/tools/handoffs.py +112 -0
- core/api/mcp/tools/ingest.py +326 -0
- core/api/mcp/tools/learnings.py +144 -0
- core/api/mcp/tools/projects.py +99 -0
- core/api/mcp/tools/pull_requests.py +173 -0
- core/api/mcp/tools/safety.py +111 -0
- core/api/mcp/tools/search.py +79 -0
- core/api/mcp/tools/tasks.py +258 -0
- core/api/middleware/__init__.py +0 -0
- core/api/middleware/tool_call_audit.py +111 -0
- core/api/models/__init__.py +346 -0
- core/api/models/auth.py +48 -0
- core/api/models/brain.py +1006 -0
- core/api/models/common.py +76 -0
- core/api/models/costs.py +91 -0
- core/api/models/graph.py +66 -0
- core/api/models/graph_cosmo.py +125 -0
- core/api/models/graph_pr_impact.py +257 -0
- core/api/models/graph_ux.py +141 -0
- core/api/models/inbox.py +230 -0
- core/api/models/ingest_keys.py +108 -0
- core/api/models/kg.py +41 -0
- core/api/models/llm_config.py +56 -0
- core/api/models/monitoring.py +234 -0
- core/api/models/projects.py +161 -0
- core/api/models/search.py +42 -0
- core/api/models/sessions.py +322 -0
- core/api/models/tasks.py +184 -0
- core/api/models/teams.py +63 -0
- core/api/models/users.py +184 -0
- core/api/observability/__init__.py +0 -0
- core/api/observability/tracing.py +92 -0
- core/api/paths.py +26 -0
- core/api/rate_limit.py +24 -0
- core/api/rbac.py +112 -0
- core/api/routers/__init__.py +0 -0
- core/api/routers/_adapter.py +24 -0
- core/api/routers/admin_pr_impact.py +230 -0
- core/api/routers/admin_settings.py +147 -0
- core/api/routers/agent.py +1079 -0
- core/api/routers/agent_tokens.py +276 -0
- core/api/routers/app_settings.py +112 -0
- core/api/routers/audit.py +89 -0
- core/api/routers/auth.py +586 -0
- core/api/routers/bench.py +161 -0
- core/api/routers/brain.py +881 -0
- core/api/routers/brain_directions.py +527 -0
- core/api/routers/ci_checks.py +140 -0
- core/api/routers/comments.py +273 -0
- core/api/routers/costs.py +148 -0
- core/api/routers/docs_coverage.py +217 -0
- core/api/routers/docs_governance.py +63 -0
- core/api/routers/documents.py +318 -0
- core/api/routers/files.py +163 -0
- core/api/routers/finder.py +987 -0
- core/api/routers/graph.py +836 -0
- core/api/routers/handoffs.py +156 -0
- core/api/routers/inbox.py +496 -0
- core/api/routers/ingest_api_keys.py +205 -0
- core/api/routers/ingest_triage.py +1227 -0
- core/api/routers/judge.py +306 -0
- core/api/routers/kg.py +336 -0
- core/api/routers/learnings.py +253 -0
- core/api/routers/llm_config.py +130 -0
- core/api/routers/monitoring.py +347 -0
- core/api/routers/notifications.py +125 -0
- core/api/routers/pr_impact.py +315 -0
- core/api/routers/projects.py +1061 -0
- core/api/routers/pull_requests.py +312 -0
- core/api/routers/push.py +67 -0
- core/api/routers/raci.py +228 -0
- core/api/routers/search.py +125 -0
- core/api/routers/sessions.py +3100 -0
- core/api/routers/settings.py +90 -0
- core/api/routers/share_repo.py +68 -0
- core/api/routers/status_updates.py +96 -0
- core/api/routers/tags.py +45 -0
- core/api/routers/tasks.py +526 -0
- core/api/routers/teams.py +425 -0
- core/api/routers/terminal.py +105 -0
- core/api/routers/users.py +331 -0
- core/api/routers/webhooks.py +330 -0
- core/api/runtime_settings.py +84 -0
- core/api/security.py +652 -0
- core/api/services/__init__.py +0 -0
- core/api/services/audit.py +58 -0
- core/api/services/auto_approval.py +82 -0
- core/api/services/brain/__init__.py +52 -0
- core/api/services/brain/baseline.py +230 -0
- core/api/services/brain/capabilities.py +75 -0
- core/api/services/brain/cascade_rollup.py +388 -0
- core/api/services/brain/compound_bridge.py +215 -0
- core/api/services/brain/cycle.py +1242 -0
- core/api/services/brain/cycle_snapshot.py +371 -0
- core/api/services/brain/digest_collector.py +147 -0
- core/api/services/brain/direction.py +421 -0
- core/api/services/brain/drift.py +356 -0
- core/api/services/brain/drift_router.py +409 -0
- core/api/services/brain/edge_metrics.py +79 -0
- core/api/services/brain/events_reader.py +222 -0
- core/api/services/brain/findings.py +1379 -0
- core/api/services/brain/findings_reader.py +1006 -0
- core/api/services/brain/jobs.py +733 -0
- core/api/services/brain/journal.py +206 -0
- core/api/services/brain/knowledge_forms.py +92 -0
- core/api/services/brain/llm/__init__.py +37 -0
- core/api/services/brain/llm/_runner.py +62 -0
- core/api/services/brain/llm/base.py +70 -0
- core/api/services/brain/llm/cache.py +99 -0
- core/api/services/brain/llm/constants.py +46 -0
- core/api/services/brain/llm/direction_alignment.py +289 -0
- core/api/services/brain/llm/factory.py +132 -0
- core/api/services/brain/llm/finding_reasoning.py +98 -0
- core/api/services/brain/llm/finding_summary.py +92 -0
- core/api/services/brain/llm/grounding.py +46 -0
- core/api/services/brain/llm/journal_polish.py +96 -0
- core/api/services/brain/llm/local_gateway.py +426 -0
- core/api/services/brain/llm/parsers.py +71 -0
- core/api/services/brain/llm/router_glue.py +422 -0
- core/api/services/brain/memory_ops.py +1677 -0
- core/api/services/brain/models.py +140 -0
- core/api/services/brain/owner_hint.py +211 -0
- core/api/services/brain/recap.py +307 -0
- core/api/services/brain/rules/__init__.py +65 -0
- core/api/services/brain/rules/_signals.py +205 -0
- core/api/services/brain/rules/dr1_activity_without_status.py +108 -0
- core/api/services/brain/rules/dr2_decision_without_adr.py +110 -0
- core/api/services/brain/rules/dr3_stale_open_loop.py +127 -0
- core/api/services/brain/rules/dr4_docs_governance_drift.py +79 -0
- core/api/services/brain/rules/dr5_playbook_changed.py +99 -0
- core/api/services/brain/rules/dr6_external_update_unpropagated.py +100 -0
- core/api/services/brain/rules/dr7_claimed_decision_gap.py +123 -0
- core/api/services/brain/rules/dr8_direction_misalignment.py +230 -0
- core/api/services/brain/runs_reader.py +485 -0
- core/api/services/brain/scope.py +79 -0
- core/api/services/brain/sources/__init__.py +46 -0
- core/api/services/brain/sources/base.py +86 -0
- core/api/services/brain/sources/git_kg.py +393 -0
- core/api/services/brain/sources/handoffs.py +157 -0
- core/api/services/brain/sources/ingestor.py +130 -0
- core/api/services/brain/sources/learnings.py +121 -0
- core/api/services/brain/sources/pir_tasks.py +245 -0
- core/api/services/brain/watermarks.py +147 -0
- core/api/services/brain/ws_emitter.py +170 -0
- core/api/services/cc_tasks_reader.py +76 -0
- core/api/services/ci_service.py +263 -0
- core/api/services/claude_metrics.py +796 -0
- core/api/services/codex_metrics.py +364 -0
- core/api/services/conversation_reader.py +102 -0
- core/api/services/cost_service.py +243 -0
- core/api/services/crypto.py +147 -0
- core/api/services/docs_governance/__init__.py +1 -0
- core/api/services/docs_governance/confidence.py +230 -0
- core/api/services/docs_governance/config.py +87 -0
- core/api/services/docs_governance/enrichment.py +65 -0
- core/api/services/docs_governance/frontmatter_validator.py +83 -0
- core/api/services/docs_governance/hard_gates.py +221 -0
- core/api/services/docs_governance/triage_orchestrator.py +98 -0
- core/api/services/embedding_internal.py +395 -0
- core/api/services/embedding_service.py +832 -0
- core/api/services/event_dispatcher.py +167 -0
- core/api/services/events.py +70 -0
- core/api/services/git_ops.py +621 -0
- core/api/services/graph_cosmo_service.py +440 -0
- core/api/services/graph_ranker.py +306 -0
- core/api/services/graph_service.py +1589 -0
- core/api/services/inbox.py +800 -0
- core/api/services/inbox_digest.py +221 -0
- core/api/services/inbox_digest_deep_research.py +80 -0
- core/api/services/inbox_digest_jobs.py +595 -0
- core/api/services/inbox_gmail_sync.py +167 -0
- core/api/services/inbox_llm_classifier.py +906 -0
- core/api/services/inbox_source_identity.py +116 -0
- core/api/services/inbox_sources.py +456 -0
- core/api/services/inbox_taxonomy.py +195 -0
- core/api/services/inbox_tldr.py +1079 -0
- core/api/services/inbox_triage.py +899 -0
- core/api/services/ingest/__init__.py +13 -0
- core/api/services/ingest/api_key_auth.py +136 -0
- core/api/services/ingest/auto_approve.py +120 -0
- core/api/services/ingest/classifier.py +138 -0
- core/api/services/ingest/confidence.py +173 -0
- core/api/services/ingest/dispatch.py +88 -0
- core/api/services/ingest/embedding_router.py +272 -0
- core/api/services/ingest/events.py +33 -0
- core/api/services/ingest/ignore_patterns.py +79 -0
- core/api/services/ingest/image_probe.py +218 -0
- core/api/services/ingest/ingress.py +263 -0
- core/api/services/ingest/insert_saga.py +793 -0
- core/api/services/ingest/llm/__init__.py +13 -0
- core/api/services/ingest/llm/anthropic_haiku.py +23 -0
- core/api/services/ingest/llm/base.py +59 -0
- core/api/services/ingest/llm/byok_provider.py +130 -0
- core/api/services/ingest/llm/classification_context.py +301 -0
- core/api/services/ingest/llm/config_store.py +246 -0
- core/api/services/ingest/llm/factory.py +24 -0
- core/api/services/ingest/llm/kg_enricher.py +306 -0
- core/api/services/ingest/llm/local_gateway.py +821 -0
- core/api/services/ingest/llm/local_vllm.py +23 -0
- core/api/services/ingest/llm/openai_nano.py +349 -0
- core/api/services/ingest/lock_advisory.py +57 -0
- core/api/services/ingest/parser_router.py +1756 -0
- core/api/services/ingest/parsers/__init__.py +1 -0
- core/api/services/ingest/parsers/docling_parser.py +142 -0
- core/api/services/ingest/parsers/docparse_gateway.py +178 -0
- core/api/services/ingest/parsers/docx_parser.py +127 -0
- core/api/services/ingest/parsers/folder_unpacker.py +85 -0
- core/api/services/ingest/parsers/gateway_aux.py +147 -0
- core/api/services/ingest/parsers/image_parser.py +251 -0
- core/api/services/ingest/parsers/internal_markdown.py +89 -0
- core/api/services/ingest/parsers/ocr_gateway.py +117 -0
- core/api/services/ingest/parsers/ocr_pdf_parser.py +112 -0
- core/api/services/ingest/parsers/pdf_types.py +13 -0
- core/api/services/ingest/parsers/transcript_parser.py +445 -0
- core/api/services/ingest/parsers/vision_gateway.py +186 -0
- core/api/services/ingest/parsers/xlsx_parser.py +91 -0
- core/api/services/ingest/parsers/zip_unpacker.py +126 -0
- core/api/services/ingest/preflight.py +393 -0
- core/api/services/ingest/retry_voyage.py +88 -0
- core/api/services/ingest/routing_policy.py +307 -0
- core/api/services/ingest/serializers/__init__.py +1 -0
- core/api/services/ingest/serializers/xlsx_to_markdown.py +80 -0
- core/api/services/ingest/skip_log.py +74 -0
- core/api/services/ingest/watcher.py +637 -0
- core/api/services/kg/__init__.py +0 -0
- core/api/services/kg/audit.py +49 -0
- core/api/services/kg/hybrid_search.py +691 -0
- core/api/services/kg/lens.py +339 -0
- core/api/services/kg/pr_impact.py +770 -0
- core/api/services/kg/queries.py +152 -0
- core/api/services/kg/ranking.py +89 -0
- core/api/services/kg/rrf.py +143 -0
- core/api/services/kg_watcher_control.py +161 -0
- core/api/services/local_llm/__init__.py +19 -0
- core/api/services/local_llm/async_client.py +385 -0
- core/api/services/local_llm/client.py +173 -0
- core/api/services/local_llm/url_validator.py +44 -0
- core/api/services/metrics_collector.py +646 -0
- core/api/services/metrics_providers.py +65 -0
- core/api/services/model_registry.py +266 -0
- core/api/services/model_router.py +137 -0
- core/api/services/n8n_client.py +77 -0
- core/api/services/newsletter_llm_gateway.py +66 -0
- core/api/services/notification_service.py +134 -0
- core/api/services/openai_responses.py +55 -0
- core/api/services/opencode_metrics.py +375 -0
- core/api/services/opencode_sessions.py +173 -0
- core/api/services/pii_redactor.py +138 -0
- core/api/services/pr_impact_pipeline/__init__.py +21 -0
- core/api/services/pr_impact_pipeline/differ.py +421 -0
- core/api/services/pr_impact_pipeline/dispatcher.py +415 -0
- core/api/services/pr_impact_pipeline/gc.py +93 -0
- core/api/services/pr_impact_pipeline/languages.py +192 -0
- core/api/services/pr_impact_pipeline/parser.py +178 -0
- core/api/services/pr_impact_pipeline/writer.py +394 -0
- core/api/services/pr_service.py +1393 -0
- core/api/services/project_paths.py +70 -0
- core/api/services/project_status_updates.py +265 -0
- core/api/services/providers.py +276 -0
- core/api/services/push_service.py +170 -0
- core/api/services/reminder_service.py +89 -0
- core/api/services/runas.py +41 -0
- core/api/services/salience_service.py +69 -0
- core/api/services/security_collector.py +281 -0
- core/api/services/session_catalog.py +385 -0
- core/api/services/session_metrics_service.py +301 -0
- core/api/services/session_ops.py +272 -0
- core/api/services/session_state.py +173 -0
- core/api/services/share_links.py +222 -0
- core/api/services/task_transitions.py +146 -0
- core/api/services/terminal_metrics.py +462 -0
- core/api/services/terminal_metrics_dump.py +203 -0
- core/api/services/tmux.py +1205 -0
- core/api/services/webhook_service.py +422 -0
- core/api/services/workspace_sync.py +164 -0
- core/api/templates/__init__.py +1 -0
- core/api/templates/markdown_share.py +164 -0
- core/api/terminal.py +1031 -0
- core/api/tests/__init__.py +0 -0
- core/api/tests/test_agent_facing_auth_dependencies.py +132 -0
- core/api/tests/test_audit_permissions.py +133 -0
- core/api/tests/test_backfill_session_conversations.py +90 -0
- core/api/tests/test_backfill_working_seconds_msg.py +129 -0
- core/api/tests/test_claude_metrics.py +326 -0
- core/api/tests/test_codex_metrics.py +189 -0
- core/api/tests/test_finder_paths.py +74 -0
- core/api/tests/test_git_ops_merge.py +155 -0
- core/api/tests/test_learnings_check_search.py +81 -0
- core/api/tests/test_metrics_providers.py +133 -0
- core/api/tests/test_migration_087.py +164 -0
- core/api/tests/test_migration_088.py +94 -0
- core/api/tests/test_migration_089.py +116 -0
- core/api/tests/test_openai_responses.py +24 -0
- core/api/tests/test_opencode_metrics.py +740 -0
- core/api/tests/test_opencode_sessions.py +321 -0
- core/api/tests/test_pr_workflow_e2e.py +457 -0
- core/api/tests/test_projects_handoffs.py +31 -0
- core/api/tests/test_providers.py +138 -0
- core/api/tests/test_safety_bridge.py +347 -0
- core/api/tests/test_session_catalog.py +142 -0
- core/api/tests/test_session_conversations.py +512 -0
- core/api/tests/test_session_metrics_service.py +270 -0
- core/api/tests/test_session_resume_paths.py +548 -0
- core/api/tests/test_session_theme_mode_migration.py +56 -0
- core/api/tests/test_sessions_rbac.py +131 -0
- core/api/tests/test_share_edit.py +398 -0
- core/api/tests/test_share_repo.py +200 -0
- core/api/tests/test_terminal_session_manager.py +98 -0
- core/api/tests/test_terminal_upload.py +34 -0
- core/api/tests/test_tmux.py +272 -0
- core/api/tests/test_workspace_sync.py +186 -0
- core/api/tests/test_ws_ticket_in_memory.py +73 -0
- core/api/use_cases/__init__.py +11 -0
- core/api/use_cases/_context.py +89 -0
- core/api/use_cases/_errors.py +62 -0
- core/api/use_cases/_roles.py +16 -0
- core/api/use_cases/audit.py +171 -0
- core/api/use_cases/brain.py +1232 -0
- core/api/use_cases/costs.py +249 -0
- core/api/use_cases/graph.py +1153 -0
- core/api/use_cases/handoffs.py +506 -0
- core/api/use_cases/ingest_triage.py +1229 -0
- core/api/use_cases/learnings.py +538 -0
- core/api/use_cases/projects.py +705 -0
- core/api/use_cases/pull_requests.py +415 -0
- core/api/use_cases/search.py +926 -0
- core/api/use_cases/tasks.py +1495 -0
- core/api/visibility.py +141 -0
- core/cli/__init__.py +5 -0
- core/cli/_index_source.py +632 -0
- core/cli/_runtime_ctx.py +160 -0
- core/cli/_transmute.py +241 -0
- core/cli/marvis_doctor.py +704 -0
- core/cli/marvis_feedback.py +396 -0
- core/cli/marvis_governance.py +315 -0
- core/cli/marvis_hooks.py +515 -0
- core/cli/marvis_init.py +757 -0
- core/cli/marvis_mcp.py +401 -0
- core/cli/marvis_runtime.py +855 -0
- core/cli/marvis_telemetry.py +228 -0
- core/scripts/_drift_check.py +716 -0
- core/scripts/_frontmatter.py +66 -0
- core/scripts/_graph_writer.py +189 -0
- core/scripts/ast_parser.py +1553 -0
- core/scripts/install_hooks/__init__.py +1 -0
- core/scripts/install_hooks/_config.sh +109 -0
- core/scripts/install_hooks/block-dangerous-bash.sh +23 -0
- core/scripts/install_hooks/block-db-direct-write.sh +23 -0
- core/scripts/install_hooks/block-push-no-task.sh +23 -0
- core/scripts/install_hooks/block-staging-to-prod.sh +23 -0
- core/scripts/install_hooks/block-subtree-push.sh +23 -0
- core/scripts/install_hooks/config.json +53 -0
- core/scripts/install_hooks/enforce-no-merge-main.sh +23 -0
- core/scripts/install_hooks/enforce-worktree.sh +23 -0
- core/scripts/install_hooks/quality-gate.sh +170 -0
- core/scripts/install_hooks/safety_bridge.py +968 -0
- core/scripts/install_hooks/secret-scan.sh +23 -0
- core/scripts/migrate_spike_node_ids.py +122 -0
- core/scripts/populate_artifacts.py +2198 -0
- core/scripts/populate_cross_project.py +2457 -0
- core/scripts/populate_inbox_nodes.py +357 -0
- core/scripts/populate_pr_impact.py +267 -0
- core/scripts/populate_project_nodes.py +603 -0
- core/scripts/populate_touch_counter.py +337 -0
- core/scripts/reparse_failed.py +57 -0
- core/scripts/safety_bridge.py +968 -0
- core/telemetry/__init__.py +9 -0
- core/telemetry/client.py +405 -0
- core/telemetry/schema.py +122 -0
- core/wizard/__init__.py +65 -0
- core/wizard/byok_vault.py +147 -0
- core/wizard/defaults.py +58 -0
- core/wizard/state.py +117 -0
- core/wizard/steps.py +70 -0
- core/wizard/validation.py +136 -0
- marvisx_cli-0.1.0.dist-info/METADATA +201 -0
- marvisx_cli-0.1.0.dist-info/RECORD +587 -0
- marvisx_cli-0.1.0.dist-info/WHEEL +5 -0
- marvisx_cli-0.1.0.dist-info/entry_points.txt +3 -0
- marvisx_cli-0.1.0.dist-info/licenses/LICENSE +98 -0
- marvisx_cli-0.1.0.dist-info/top_level.txt +3 -0
- migrations/001_initial.sql +33 -0
- migrations/002_tasks.sql +30 -0
- migrations/003_session_management.sql +7 -0
- migrations/004_projects_comments.sql +65 -0
- migrations/005_session_intelligence.sql +15 -0
- migrations/006_settings.sql +12 -0
- migrations/007_task_scoring.sql +12 -0
- migrations/008_cost_tracking.sql +31 -0
- migrations/009_session_card_metrics.sql +3 -0
- migrations/010_monitoring.sql +55 -0
- migrations/012_agent_api.sql +21 -0
- migrations/013_session_complete.sql +8 -0
- migrations/015_pull_requests.sql +43 -0
- migrations/015_pull_requests_down.sql +5 -0
- migrations/016_users_raci.sql +116 -0
- migrations/017_task_cost_entries.sql +87 -0
- migrations/018_agents.sql +73 -0
- migrations/018_agents_down.sql +13 -0
- migrations/019_review_feedback.sql +18 -0
- migrations/020_pr_commit_sha.sql +4 -0
- migrations/021_webhook_events.sql +18 -0
- migrations/022_devx_agent_managed.sql +11 -0
- migrations/022_devx_agent_managed_down.sql +6 -0
- migrations/023_devx_p1_gate.sql +7 -0
- migrations/023_devx_p1_gate_down.sql +3 -0
- migrations/024_chat_messages.sql +16 -0
- migrations/024_pr_conversation_id.sql +8 -0
- migrations/024_task_indexes.sql +21 -0
- migrations/024_task_indexes_down.sql +7 -0
- migrations/025_audit_log.sql +17 -0
- migrations/026_agent_tokens.sql +20 -0
- migrations/027_teams_auth_phase_b.sql +35 -0
- migrations/028_learnings.sql +23 -0
- migrations/029_team_roles.sql +14 -0
- migrations/030_finder_pins.sql +10 -0
- migrations/031_pr_deploy_status.sql +9 -0
- migrations/032_task_reminders.sql +7 -0
- migrations/033_events_retry_count.sql +6 -0
- migrations/033_session_owner.sql +9 -0
- migrations/034_notifications.sql +38 -0
- migrations/035_shared_links.sql +15 -0
- migrations/036_session_index_upgrade.sql +29 -0
- migrations/037_pr_approval.sql +15 -0
- migrations/038_pr_submitted_by.sql +6 -0
- migrations/039_push_subscriptions.sql +17 -0
- migrations/040_semantic_search.sql +16 -0
- migrations/041_workspaces.sql +63 -0
- migrations/042_oidc_providers.sql +24 -0
- migrations/043_ci_checks.sql +31 -0
- migrations/044_agent_metrics.sql +30 -0
- migrations/045_documents_doc_type.sql +5 -0
- migrations/046_salience.sql +13 -0
- migrations/047_seed_missing_agents.sql +6 -0
- migrations/048_fix_agent_paths_roles.sql +5 -0
- migrations/049_agent_role_and_learnings_schema.sql +3 -0
- migrations/050_session_provider.sql +2 -0
- migrations/051_session_launch_profile.sql +4 -0
- migrations/052_session_theme_mode.sql +2 -0
- migrations/052_task_kind.sql +4 -0
- migrations/053_inbox_items.sql +31 -0
- migrations/054_inbox_triage_contract.sql +30 -0
- migrations/055_inbox_topic_treatment.sql +12 -0
- migrations/056_inbox_treatment_read_save.sql +57 -0
- migrations/057_session_theme_mode_backfill.sql +4 -0
- migrations/058_inbox_item_status_lifecycle.sql +13 -0
- migrations/059_inbox_tldr_and_source_scores.sql +18 -0
- migrations/060_newsletter.sql +16 -0
- migrations/061_inbox_redesign.sql +69 -0
- migrations/062_fix_inbox_sources_backfill.sql +37 -0
- migrations/063_task_completion_mode.sql +23 -0
- migrations/064_judge_mode_setting.sql +4 -0
- migrations/065_knowledge_graph_spike.sql +40 -0
- migrations/066_digest_ranking_inputs.sql +10 -0
- migrations/066_kg_artifact_nodes.sql +129 -0
- migrations/067_inbox_digest_selections.sql +28 -0
- migrations/067_kg_temporal.sql +53 -0
- migrations/068_inbox_digest_app_settings.sql +9 -0
- migrations/068_kg_touch_counter.sql +52 -0
- migrations/069_kg_doc_types.sql +117 -0
- migrations/070_digest_ranking_inputs_recovery.sql +3 -0
- migrations/071_inbox_digest_selections_recovery.sql +3 -0
- migrations/072_inbox_digest_app_settings_recovery.sql +3 -0
- migrations/073_kg_cross_project.sql +216 -0
- migrations/073_kg_cross_project_down.sql +77 -0
- migrations/074_kg_infra_types.sql +208 -0
- migrations/074_kg_infra_types_down.sql +80 -0
- migrations/075_kg_file_state_recovery.sql +35 -0
- migrations/075_kg_file_state_recovery_down.sql +5 -0
- migrations/076_kg_watcher_state.sql +33 -0
- migrations/076_kg_watcher_state_down.sql +3 -0
- migrations/077_kg_doc_types_extend.sql +226 -0
- migrations/077_kg_doc_types_extend_down.sql +80 -0
- migrations/078_kg_fts5.sql +102 -0
- migrations/078_kg_fts5_down.sql +14 -0
- migrations/079_kg_missing_indexes.sql +31 -0
- migrations/079_kg_missing_indexes_down.sql +10 -0
- migrations/080_kg_fts5_extended.sql +232 -0
- migrations/080_kg_fts5_extended_down.sql +25 -0
- migrations/081_kg_lens_indexes.sql +9 -0
- migrations/081_kg_lens_indexes_down.sql +3 -0
- migrations/082_kg_pins.sql +26 -0
- migrations/082_kg_pins_down.sql +14 -0
- migrations/083_kg_graph_nodes_degree.sql +20 -0
- migrations/083_kg_graph_nodes_degree_down.sql +15 -0
- migrations/084_drop_legacy_scheduler_tables.sql +58 -0
- migrations/084_drop_legacy_scheduler_tables_down.sql +112 -0
- migrations/085_kg_edge_resolves_to.sql +142 -0
- migrations/085_kg_edge_resolves_to_down.sql +66 -0
- migrations/086_project_status_updates_feed.sql +20 -0
- migrations/086_project_status_updates_feed_down.sql +36 -0
- migrations/087_session_metrics_dual.sql +50 -0
- migrations/087_session_metrics_dual_down.sql +21 -0
- migrations/088_rename_context_pct_legacy.sql +23 -0
- migrations/088_rename_context_pct_legacy_down.sql +8 -0
- migrations/089_session_metrics_equivalent_cost.sql +26 -0
- migrations/089_session_metrics_equivalent_cost_down.sql +11 -0
- migrations/090_kg_inbox_node_type.sql +26 -0
- migrations/090_kg_inbox_node_type_down.sql +20 -0
- migrations/091_kg_inbox_node_type_check.sql +265 -0
- migrations/091_kg_inbox_node_type_check_down.sql +129 -0
- migrations/092_sessions_activity_state_ts.sql +29 -0
- migrations/092_sessions_activity_state_ts_down.sql +14 -0
- migrations/093_sessions_activity_state_column.sql +29 -0
- migrations/093_sessions_activity_state_column_down.sql +10 -0
- migrations/094_ingest_pending.sql +55 -0
- migrations/094_ingest_pending_down.sql +15 -0
- migrations/095_kg_intent_first.sql +77 -0
- migrations/095_kg_intent_first_down.sql +25 -0
- migrations/096_kg_xlsx_artifact_prefix.sql +17 -0
- migrations/096_kg_xlsx_artifact_prefix_down.sql +11 -0
- migrations/097_ingest_change_history.sql +37 -0
- migrations/097_ingest_change_history_down.sql +13 -0
- migrations/098_kg_node_type_business.sql +254 -0
- migrations/098_kg_node_type_business_down.sql +195 -0
- migrations/099_kg_edges_restore_weight.sql +58 -0
- migrations/099_kg_edges_restore_weight_down.sql +12 -0
- migrations/100_kg_enriched_at.sql +25 -0
- migrations/100_kg_enriched_at_down.sql +12 -0
- migrations/101_local_llm_shadow_comparisons.sql +66 -0
- migrations/101_local_llm_shadow_comparisons_down.sql +15 -0
- migrations/102_promote_llm_costs.sql +69 -0
- migrations/102_promote_llm_costs_down.sql +19 -0
- migrations/103_ingest_skipped_log.sql +46 -0
- migrations/103_ingest_skipped_log_down.sql +15 -0
- migrations/120_docs_governance.sql +50 -0
- migrations/120_docs_governance_down.sql +11 -0
- migrations/121_notification_event_fk_cleanup.sql +21 -0
- migrations/121_notification_event_fk_cleanup_down.sql +10 -0
- migrations/122_docs_drift_history.sql +34 -0
- migrations/122_docs_drift_history_down.sql +15 -0
- migrations/123_ingest_parser_waiting_status.sql +69 -0
- migrations/123_ingest_parser_waiting_status_down.sql +69 -0
- migrations/124_heypocket_recordings.sql +63 -0
- migrations/124_heypocket_recordings_down.sql +13 -0
- migrations/125_kg_node_type_record.sql +219 -0
- migrations/125_kg_node_type_record_down.sql +205 -0
- migrations/126_ingest_terminal_upload_source_kind.sql +69 -0
- migrations/126_ingest_terminal_upload_source_kind_down.sql +69 -0
- migrations/127_brain_v1_substrate.sql +200 -0
- migrations/127_brain_v1_substrate_down.sql +32 -0
- migrations/128_brain_drift_signals.sql +157 -0
- migrations/128_brain_drift_signals_down.sql +23 -0
- migrations/129_brain_memory_operations.sql +232 -0
- migrations/129_brain_memory_operations_down.sql +27 -0
- migrations/130_brain_findings.sql +258 -0
- migrations/130_brain_findings_down.sql +29 -0
- migrations/132_kg_pr_modifies.sql +242 -0
- migrations/132_kg_pr_modifies_down.sql +99 -0
- migrations/133_brain_v1_2_direction_schema.sql +476 -0
- migrations/133_brain_v1_2_direction_schema_down.sql +273 -0
- migrations/134_brain_journal_narrative_polished.sql +8 -0
- migrations/134_brain_journal_narrative_polished_down.sql +6 -0
- migrations/135_kg_edges_provider.sql +21 -0
- migrations/136_documents_fts.sql +56 -0
- migrations/137_promote_llm_costs.sql +59 -0
- migrations/137_promote_llm_costs_down.sql +19 -0
- migrations/138_ingest_api_keys.sql +39 -0
- migrations/138_ingest_api_keys_down.sql +8 -0
- migrations/139_ingest_pending_ingress.sql +91 -0
- migrations/139_ingest_pending_ingress_down.sql +73 -0
- migrations/140_ingest_idempotency_quota.sql +45 -0
- migrations/140_ingest_idempotency_quota_down.sql +9 -0
- migrations/141_ingest_pending_metadata.sql +16 -0
- migrations/141_ingest_pending_metadata_down.sql +7 -0
- migrations/142_llm_function_config.sql +36 -0
- migrations/142_llm_function_config_down.sql +8 -0
- migrations/143_kg_code_embeddings.sql +25 -0
- migrations/143_kg_code_embeddings_down.sql +5 -0
- migrations/__init__.py +4 -0
- projects/_template/project.yaml +46 -0
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# v1.0.0 - 2026-03-03 - Audit logging for privileged human operations
|
|
2
|
+
"""
|
|
3
|
+
Audit logging service.
|
|
4
|
+
|
|
5
|
+
Writes immutable records to the audit_log table for human-only privileged
|
|
6
|
+
operations: PR merge/revert, task approve/complete, delete operations, user
|
|
7
|
+
management actions.
|
|
8
|
+
|
|
9
|
+
Non-critical: failures are logged as warnings but never block the caller.
|
|
10
|
+
"""
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
import json
|
|
14
|
+
import logging
|
|
15
|
+
from typing import Any
|
|
16
|
+
|
|
17
|
+
import aiosqlite
|
|
18
|
+
|
|
19
|
+
logger = logging.getLogger(__name__)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
async def log_audit(
|
|
23
|
+
db: aiosqlite.Connection,
|
|
24
|
+
action: str,
|
|
25
|
+
user: str,
|
|
26
|
+
resource_type: str,
|
|
27
|
+
resource_id: str,
|
|
28
|
+
details: dict[str, Any] | None = None,
|
|
29
|
+
) -> None:
|
|
30
|
+
"""Write one audit record to the audit_log table.
|
|
31
|
+
|
|
32
|
+
Args:
|
|
33
|
+
db: Active aiosqlite connection (request-scoped or fresh).
|
|
34
|
+
action: Verb describing the privileged operation, e.g.
|
|
35
|
+
"pr.merge", "task.approve", "task.delete", "user.delete".
|
|
36
|
+
user: Username (or agent slug) performing the action.
|
|
37
|
+
resource_type: Entity type: "pull_request", "task", "user", "raci", etc.
|
|
38
|
+
resource_id: Primary key of the affected resource.
|
|
39
|
+
details: Optional free-form dict serialised to JSON for extra context.
|
|
40
|
+
|
|
41
|
+
Non-critical: any exception is caught and logged as a warning so the
|
|
42
|
+
caller's business logic is never interrupted by an audit failure.
|
|
43
|
+
"""
|
|
44
|
+
try:
|
|
45
|
+
await db.execute(
|
|
46
|
+
"INSERT INTO audit_log (action, user, resource_type, resource_id, details_json) "
|
|
47
|
+
"VALUES (?, ?, ?, ?, ?)",
|
|
48
|
+
(
|
|
49
|
+
action,
|
|
50
|
+
user,
|
|
51
|
+
resource_type,
|
|
52
|
+
resource_id,
|
|
53
|
+
json.dumps(details) if details is not None else None,
|
|
54
|
+
),
|
|
55
|
+
)
|
|
56
|
+
await db.commit()
|
|
57
|
+
except Exception as exc: # noqa: BLE001
|
|
58
|
+
logger.warning("log_audit failed (non-critical): %s", exc)
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# v1.3.0 - 2026-05-28 - Self-improvement veto agent list is config-driven (SELF_IMPROVEMENT_AGENTS)
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
import logging
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
from enum import Enum
|
|
7
|
+
|
|
8
|
+
from core.api.config import settings
|
|
9
|
+
|
|
10
|
+
logger = logging.getLogger(__name__)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class ApprovalDecision(str, Enum):
|
|
14
|
+
AUTO_APPROVED = "auto_approved"
|
|
15
|
+
PENDING_HUMAN = "pending"
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@dataclass
|
|
19
|
+
class ApprovalPolicy:
|
|
20
|
+
"""Evaluate whether a new task should be auto-approved or require human review.
|
|
21
|
+
|
|
22
|
+
Safety valves:
|
|
23
|
+
- Impact veto: high-impact tasks always need human review
|
|
24
|
+
- Confidence floor: low-confidence scores need human review
|
|
25
|
+
- Daily cap: checked externally (not in this class)
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
agent_ease_threshold: int = 6
|
|
29
|
+
hybrid_ease_threshold: int = 7
|
|
30
|
+
impact_veto_threshold: int = 9
|
|
31
|
+
min_confidence: int = 5
|
|
32
|
+
|
|
33
|
+
def evaluate(
|
|
34
|
+
self,
|
|
35
|
+
delegation: str | None,
|
|
36
|
+
ease: int | None,
|
|
37
|
+
impact: int | None,
|
|
38
|
+
confidence: int | None,
|
|
39
|
+
scored_by: str | None,
|
|
40
|
+
created_by: str | None,
|
|
41
|
+
) -> tuple[ApprovalDecision, str]:
|
|
42
|
+
"""Return (decision, reason) for a task's initial status."""
|
|
43
|
+
|
|
44
|
+
# Unscored tasks always need human review
|
|
45
|
+
if delegation is None or ease is None:
|
|
46
|
+
return ApprovalDecision.PENDING_HUMAN, "unscored_task"
|
|
47
|
+
|
|
48
|
+
# Self-improvement hybrid proposals always require human review.
|
|
49
|
+
# The agent list is config-driven (SELF_IMPROVEMENT_AGENTS) so OSS core
|
|
50
|
+
# ships no internal agent names; our deploy .env arms the veto. Read
|
|
51
|
+
# settings live so tests can override via monkeypatch.
|
|
52
|
+
if (
|
|
53
|
+
created_by is not None
|
|
54
|
+
and created_by in settings.self_improvement_agents
|
|
55
|
+
and delegation == "hybrid"
|
|
56
|
+
):
|
|
57
|
+
return ApprovalDecision.PENDING_HUMAN, "agent_self_improvement_hybrid_veto"
|
|
58
|
+
|
|
59
|
+
# Human delegation = human IS the gate, auto-approve
|
|
60
|
+
if delegation == "human":
|
|
61
|
+
return ApprovalDecision.AUTO_APPROVED, "delegation_human"
|
|
62
|
+
|
|
63
|
+
# Safety valve: high impact always needs human eyes
|
|
64
|
+
if impact is not None and impact >= self.impact_veto_threshold:
|
|
65
|
+
return ApprovalDecision.PENDING_HUMAN, "high_impact_veto"
|
|
66
|
+
|
|
67
|
+
# Safety valve: low confidence = uncertain, needs human
|
|
68
|
+
if confidence is not None and confidence < self.min_confidence:
|
|
69
|
+
return ApprovalDecision.PENDING_HUMAN, "low_confidence"
|
|
70
|
+
|
|
71
|
+
# Threshold-based auto-approval
|
|
72
|
+
if delegation == "agent" and ease >= self.agent_ease_threshold:
|
|
73
|
+
return ApprovalDecision.AUTO_APPROVED, "agent_ease_threshold"
|
|
74
|
+
|
|
75
|
+
if delegation == "hybrid" and ease >= self.hybrid_ease_threshold:
|
|
76
|
+
return ApprovalDecision.AUTO_APPROVED, "hybrid_ease_threshold"
|
|
77
|
+
|
|
78
|
+
return ApprovalDecision.PENDING_HUMAN, "below_threshold"
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
# Singleton — can be overridden per-project via project.yaml thresholds
|
|
82
|
+
DEFAULT_POLICY = ApprovalPolicy()
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# Brain v1 — Digest + Journal subpackage (sub-01).
|
|
2
|
+
# Layering: api/models/brain.py owns cross-boundary contracts.
|
|
3
|
+
# Service-private dataclasses live in api/services/brain/models.py.
|
|
4
|
+
# No LLM imports allowed at this layer (parent §9.3 invariant).
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
from core.api.services.brain.cycle import (
|
|
8
|
+
canonical_evidence,
|
|
9
|
+
cycle_cutoff_at,
|
|
10
|
+
current_brain_cycle_key,
|
|
11
|
+
derive_event_id,
|
|
12
|
+
evidence_hash as compute_evidence_hash,
|
|
13
|
+
make_event_id,
|
|
14
|
+
persist_event,
|
|
15
|
+
polish_pending_journals,
|
|
16
|
+
polish_run_journals,
|
|
17
|
+
publish_run_journals,
|
|
18
|
+
update_run_status,
|
|
19
|
+
)
|
|
20
|
+
from core.api.services.brain.capabilities import KNOWLEDGE_GLYPHS, get_capabilities
|
|
21
|
+
from core.api.services.brain.events_reader import list_events_for_cycle
|
|
22
|
+
from core.api.services.brain.jobs import recompute_brain_cycle, run_brain_jobs_if_due
|
|
23
|
+
from core.api.services.brain.runs_reader import (
|
|
24
|
+
fetch_single_run,
|
|
25
|
+
get_pipeline_counters,
|
|
26
|
+
list_runs,
|
|
27
|
+
)
|
|
28
|
+
from core.api.services.brain.ws_emitter import emit_phase_complete, get_hub
|
|
29
|
+
|
|
30
|
+
__all__ = [
|
|
31
|
+
"KNOWLEDGE_GLYPHS",
|
|
32
|
+
"canonical_evidence",
|
|
33
|
+
"compute_evidence_hash",
|
|
34
|
+
"current_brain_cycle_key",
|
|
35
|
+
"cycle_cutoff_at",
|
|
36
|
+
"derive_event_id",
|
|
37
|
+
"emit_phase_complete",
|
|
38
|
+
"fetch_single_run",
|
|
39
|
+
"get_capabilities",
|
|
40
|
+
"get_hub",
|
|
41
|
+
"get_pipeline_counters",
|
|
42
|
+
"list_events_for_cycle",
|
|
43
|
+
"list_runs",
|
|
44
|
+
"make_event_id",
|
|
45
|
+
"persist_event",
|
|
46
|
+
"polish_pending_journals",
|
|
47
|
+
"polish_run_journals",
|
|
48
|
+
"publish_run_journals",
|
|
49
|
+
"recompute_brain_cycle",
|
|
50
|
+
"run_brain_jobs_if_due",
|
|
51
|
+
"update_run_status",
|
|
52
|
+
]
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
# Brain v1 — Direction baseline resolver (sub-02 C2).
|
|
2
|
+
# Priority chain: latest journal entry → latest project_status_update → latest
|
|
3
|
+
# handoff (auto-handoff). Conflict detection across the three sources.
|
|
4
|
+
# Never returns None — uses BaselineReference(source='none', ref=None) sentinel.
|
|
5
|
+
#
|
|
6
|
+
# Reads ONLY from L2 outputs (journal API) and read-only substrate. Drift rules
|
|
7
|
+
# consume the precomputed baselines from CycleSnapshot.baselines.
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import logging
|
|
11
|
+
from datetime import datetime, timezone
|
|
12
|
+
from typing import Any
|
|
13
|
+
|
|
14
|
+
from core.api.db import acquire_db
|
|
15
|
+
from core.api.models.brain import BaselineReference, DirectionSource, ScopeType
|
|
16
|
+
from core.api.services.brain.journal import get_latest_entry
|
|
17
|
+
|
|
18
|
+
logger = logging.getLogger(__name__)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def _parse_iso(value: str | None) -> datetime | None:
|
|
22
|
+
if not value:
|
|
23
|
+
return None
|
|
24
|
+
try:
|
|
25
|
+
dt = datetime.fromisoformat(str(value).replace("Z", "+00:00"))
|
|
26
|
+
except ValueError:
|
|
27
|
+
return None
|
|
28
|
+
if dt.tzinfo is None:
|
|
29
|
+
dt = dt.replace(tzinfo=timezone.utc)
|
|
30
|
+
return dt.astimezone(timezone.utc)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
async def _resolve_project_direction(
|
|
34
|
+
*, scope_type: ScopeType, scope_key: str, as_of: datetime
|
|
35
|
+
) -> tuple[str | None, datetime | None]:
|
|
36
|
+
"""Return (ref, last_updated_at) of the project_directions row for a slug.
|
|
37
|
+
|
|
38
|
+
Brain v1.2: this is the priority-1 source of baseline truth for project
|
|
39
|
+
scope when present. Falls through silently when the table is empty
|
|
40
|
+
(cold start before bootstrap rollout) or the project lacks a direction.
|
|
41
|
+
"""
|
|
42
|
+
if scope_type != "project":
|
|
43
|
+
return (None, None)
|
|
44
|
+
try:
|
|
45
|
+
async with acquire_db() as db:
|
|
46
|
+
row = await (
|
|
47
|
+
await db.execute(
|
|
48
|
+
"SELECT project_slug, last_updated_at FROM project_directions"
|
|
49
|
+
" WHERE project_slug = ?",
|
|
50
|
+
(scope_key,),
|
|
51
|
+
)
|
|
52
|
+
).fetchone()
|
|
53
|
+
except Exception as exc: # noqa: BLE001 — table may not exist on cold DB
|
|
54
|
+
logger.debug("baseline: project_directions lookup skipped (%s)", exc)
|
|
55
|
+
return (None, None)
|
|
56
|
+
if row is None:
|
|
57
|
+
return (None, None)
|
|
58
|
+
slug = row[0] if not hasattr(row, "keys") else row["project_slug"]
|
|
59
|
+
state_at = row[1] if not hasattr(row, "keys") else row["last_updated_at"]
|
|
60
|
+
return (f"project_directions:{slug}", _parse_iso(state_at))
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
async def _resolve_status_update(
|
|
64
|
+
*, scope_type: ScopeType, scope_key: str, as_of: datetime, workspace_id: str
|
|
65
|
+
) -> tuple[str | None, datetime | None]:
|
|
66
|
+
"""Return (ref, state_at) of the latest project_status_update for the scope.
|
|
67
|
+
|
|
68
|
+
Restricted to project scope (status updates are project-only). For company
|
|
69
|
+
or program scope returns (None, None) — falls through to handoff.
|
|
70
|
+
"""
|
|
71
|
+
if scope_type != "project":
|
|
72
|
+
return (None, None)
|
|
73
|
+
cutoff = as_of.astimezone(timezone.utc).isoformat()
|
|
74
|
+
try:
|
|
75
|
+
async with acquire_db() as db:
|
|
76
|
+
row = await (
|
|
77
|
+
await db.execute(
|
|
78
|
+
"SELECT update_id, created_at FROM project_status_updates "
|
|
79
|
+
"WHERE project = ? AND created_at <= ? "
|
|
80
|
+
"AND COALESCE(derived, 0) = 0 "
|
|
81
|
+
"ORDER BY created_at DESC LIMIT 1",
|
|
82
|
+
(scope_key, cutoff),
|
|
83
|
+
)
|
|
84
|
+
).fetchone()
|
|
85
|
+
except Exception as exc: # noqa: BLE001 — table may not exist in test DB
|
|
86
|
+
logger.debug("baseline: status update lookup skipped (%s)", exc)
|
|
87
|
+
return (None, None)
|
|
88
|
+
if row is None:
|
|
89
|
+
return (None, None)
|
|
90
|
+
ref = row[0] if not hasattr(row, "keys") else row["update_id"]
|
|
91
|
+
state_at = row[1] if not hasattr(row, "keys") else row["created_at"]
|
|
92
|
+
return (f"project_status_update:{ref}", _parse_iso(state_at))
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
async def _resolve_handoff(
|
|
96
|
+
*, scope_type: ScopeType, scope_key: str, as_of: datetime, workspace_id: str
|
|
97
|
+
) -> tuple[str | None, datetime | None]:
|
|
98
|
+
"""Return (ref, state_at) of the latest auto_handoff for the scope."""
|
|
99
|
+
cutoff = as_of.astimezone(timezone.utc).isoformat()
|
|
100
|
+
try:
|
|
101
|
+
async with acquire_db() as db:
|
|
102
|
+
row = await (
|
|
103
|
+
await db.execute(
|
|
104
|
+
"SELECT handoff_id, created_at FROM handoffs "
|
|
105
|
+
"WHERE project = ? AND created_at <= ? "
|
|
106
|
+
"AND COALESCE(kind, '') IN ('auto_handoff','handoff') "
|
|
107
|
+
"ORDER BY created_at DESC LIMIT 1",
|
|
108
|
+
(scope_key,) if scope_type == "project" else (scope_key,),
|
|
109
|
+
)
|
|
110
|
+
).fetchone()
|
|
111
|
+
except Exception as exc: # noqa: BLE001 — table may not exist
|
|
112
|
+
logger.debug("baseline: handoff lookup skipped (%s)", exc)
|
|
113
|
+
return (None, None)
|
|
114
|
+
if row is None:
|
|
115
|
+
return (None, None)
|
|
116
|
+
ref = row[0] if not hasattr(row, "keys") else row["handoff_id"]
|
|
117
|
+
state_at = row[1] if not hasattr(row, "keys") else row["created_at"]
|
|
118
|
+
return (f"handoff:{ref}", _parse_iso(state_at))
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def _direction_marker(payload: Any) -> str | None:
|
|
122
|
+
if isinstance(payload, dict):
|
|
123
|
+
marker = payload.get("direction_marker") or payload.get("direction")
|
|
124
|
+
if isinstance(marker, str):
|
|
125
|
+
return marker.strip().lower() or None
|
|
126
|
+
return None
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
async def resolve_baseline(
|
|
130
|
+
scope_type: ScopeType,
|
|
131
|
+
scope_key: str,
|
|
132
|
+
*,
|
|
133
|
+
cycle_key: str,
|
|
134
|
+
as_of: datetime,
|
|
135
|
+
workspace_id: str = "ws_default",
|
|
136
|
+
visible_projects: set[str] | None = None,
|
|
137
|
+
) -> BaselineReference:
|
|
138
|
+
"""Resolve the expected direction baseline for the scope.
|
|
139
|
+
|
|
140
|
+
Priority chain (sub-02 C2):
|
|
141
|
+
1. Latest Journal entry within cycle window.
|
|
142
|
+
2. Latest project status update within 7d (derived=False).
|
|
143
|
+
3. Latest handoff within 7d (auto_handoff or handoff kind).
|
|
144
|
+
|
|
145
|
+
Returns BaselineReference(source='none') sentinel when nothing matches.
|
|
146
|
+
Conflict detection compares direction markers across resolved sources.
|
|
147
|
+
"""
|
|
148
|
+
# Visibility short-circuit: if caller can see only some projects and this
|
|
149
|
+
# one isn't visible, refuse to leak existence.
|
|
150
|
+
if (
|
|
151
|
+
scope_type == "project"
|
|
152
|
+
and visible_projects is not None
|
|
153
|
+
and scope_key not in visible_projects
|
|
154
|
+
):
|
|
155
|
+
return BaselineReference(source="none", ref=None, confidence=0.7)
|
|
156
|
+
|
|
157
|
+
# Brain v1.2 (2026-05-18): project_directions becomes the priority-1
|
|
158
|
+
# source for project scope. Falls back to journal -> status -> handoff
|
|
159
|
+
# when no direction is present (bootstrap not yet applied).
|
|
160
|
+
direction_ref, direction_state_at = await _resolve_project_direction(
|
|
161
|
+
scope_type=scope_type, scope_key=scope_key, as_of=as_of
|
|
162
|
+
)
|
|
163
|
+
|
|
164
|
+
journal = await get_latest_entry(
|
|
165
|
+
scope_type, scope_key, before=cycle_key, workspace_id=workspace_id
|
|
166
|
+
)
|
|
167
|
+
journal_ref: str | None = None
|
|
168
|
+
journal_state_at: datetime | None = None
|
|
169
|
+
journal_marker: str | None = None
|
|
170
|
+
if journal is not None:
|
|
171
|
+
journal_ref = f"journal_entry:{journal.entry_id}"
|
|
172
|
+
journal_state_at = journal.published_at
|
|
173
|
+
journal_marker = _direction_marker(journal.body.model_dump())
|
|
174
|
+
|
|
175
|
+
status_ref, status_state_at = await _resolve_status_update(
|
|
176
|
+
scope_type=scope_type, scope_key=scope_key, as_of=as_of, workspace_id=workspace_id
|
|
177
|
+
)
|
|
178
|
+
handoff_ref, handoff_state_at = await _resolve_handoff(
|
|
179
|
+
scope_type=scope_type, scope_key=scope_key, as_of=as_of, workspace_id=workspace_id
|
|
180
|
+
)
|
|
181
|
+
|
|
182
|
+
chain: list[tuple[DirectionSource, str | None, datetime | None]] = [
|
|
183
|
+
("doc", direction_ref, direction_state_at),
|
|
184
|
+
("journal", journal_ref, journal_state_at),
|
|
185
|
+
("project_status", status_ref, status_state_at),
|
|
186
|
+
("handoff", handoff_ref, handoff_state_at),
|
|
187
|
+
]
|
|
188
|
+
primary: tuple[DirectionSource, str | None, datetime | None] | None = None
|
|
189
|
+
secondary: list[tuple[DirectionSource, str | None, datetime | None]] = []
|
|
190
|
+
for entry in chain:
|
|
191
|
+
if entry[1]:
|
|
192
|
+
if primary is None:
|
|
193
|
+
primary = entry
|
|
194
|
+
else:
|
|
195
|
+
secondary.append(entry)
|
|
196
|
+
if primary is None:
|
|
197
|
+
return BaselineReference(source="none", ref=None, confidence=0.7)
|
|
198
|
+
|
|
199
|
+
secondary_refs = [s[1] for s in secondary if s[1]]
|
|
200
|
+
# Conflict: at least two sources present and direction markers disagree.
|
|
201
|
+
# Without direction_marker on status/handoff substrate we only flag conflict
|
|
202
|
+
# if journal_marker exists AND a secondary source disagrees explicitly.
|
|
203
|
+
conflict = False
|
|
204
|
+
if journal_marker and secondary_refs:
|
|
205
|
+
# Best-effort: load secondary direction markers from handoff_summary etc.
|
|
206
|
+
# In v1 we mark conflict if multiple secondaries exist (proxy for
|
|
207
|
+
# divergence pressure). Future v1.1 will compare typed direction tags.
|
|
208
|
+
conflict = len(secondary_refs) >= 2
|
|
209
|
+
|
|
210
|
+
source: DirectionSource = primary[0]
|
|
211
|
+
confidence_base = {
|
|
212
|
+
"doc": 1.0, # project_directions = founder-declared
|
|
213
|
+
"journal": 1.0,
|
|
214
|
+
"project_status": 0.9,
|
|
215
|
+
"handoff": 0.8,
|
|
216
|
+
}.get(source, 0.5)
|
|
217
|
+
if conflict:
|
|
218
|
+
confidence_base = max(0.3, confidence_base - 0.1)
|
|
219
|
+
|
|
220
|
+
return BaselineReference(
|
|
221
|
+
source=source,
|
|
222
|
+
ref=primary[1],
|
|
223
|
+
state_at=primary[2],
|
|
224
|
+
confidence=confidence_base,
|
|
225
|
+
conflict=conflict,
|
|
226
|
+
secondary_refs=secondary_refs,
|
|
227
|
+
)
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
__all__ = ["resolve_baseline"]
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# Brain v1 — Capabilities (sub-05 §3.1, OD-11).
|
|
2
|
+
# Schema metadata for agent cold-start. Mirrors graph_capabilities precedent.
|
|
3
|
+
# Returns the Literal members enforced at the Pydantic boundary so agents can
|
|
4
|
+
# discover enums without hardcoding constants.
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
from typing import get_args
|
|
8
|
+
|
|
9
|
+
from core.api.models.brain import (
|
|
10
|
+
BrainCapabilities,
|
|
11
|
+
ConfidenceTier,
|
|
12
|
+
DriftAxis,
|
|
13
|
+
EventType,
|
|
14
|
+
FindingApprovalState,
|
|
15
|
+
FindingType,
|
|
16
|
+
KnowledgeForm,
|
|
17
|
+
RunStatus,
|
|
18
|
+
RunTrigger,
|
|
19
|
+
ScopeType,
|
|
20
|
+
Severity,
|
|
21
|
+
SignalState,
|
|
22
|
+
SignalType,
|
|
23
|
+
SourceSystem,
|
|
24
|
+
SuggestedArtifact,
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
# Note: ApprovalState, OperationType, ClosureConditionKind are imported lazily
|
|
28
|
+
# to keep this module a leaf (no cycle with models.brain).
|
|
29
|
+
|
|
30
|
+
KNOWLEDGE_GLYPHS: dict[str, str] = {
|
|
31
|
+
"adr": "▣",
|
|
32
|
+
"spec": "▦",
|
|
33
|
+
"playbook": "⌘",
|
|
34
|
+
"tribal_memory": "✶",
|
|
35
|
+
"external_update": "↘",
|
|
36
|
+
"claimed_decision": "!",
|
|
37
|
+
"unknown": "?",
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def get_capabilities() -> BrainCapabilities:
|
|
42
|
+
"""Snapshot of every Literal enum exposed by the Brain v1 surface."""
|
|
43
|
+
from core.api.models.brain import (
|
|
44
|
+
ApprovalState,
|
|
45
|
+
ClosureConditionKind,
|
|
46
|
+
OperationType,
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
def _literal_members(tp: object) -> list[str]:
|
|
50
|
+
return [str(m) for m in get_args(tp)]
|
|
51
|
+
|
|
52
|
+
return BrainCapabilities(
|
|
53
|
+
schema_version=1,
|
|
54
|
+
event_types=_literal_members(EventType),
|
|
55
|
+
source_systems=_literal_members(SourceSystem),
|
|
56
|
+
signal_types=_literal_members(SignalType),
|
|
57
|
+
knowledge_forms=_literal_members(KnowledgeForm),
|
|
58
|
+
operation_types=_literal_members(OperationType),
|
|
59
|
+
finding_types=_literal_members(FindingType),
|
|
60
|
+
severities=_literal_members(Severity),
|
|
61
|
+
confidence_tiers=_literal_members(ConfidenceTier),
|
|
62
|
+
drift_axes=_literal_members(DriftAxis),
|
|
63
|
+
approval_states=_literal_members(ApprovalState),
|
|
64
|
+
finding_approval_states=_literal_members(FindingApprovalState),
|
|
65
|
+
signal_states=_literal_members(SignalState),
|
|
66
|
+
run_statuses=_literal_members(RunStatus),
|
|
67
|
+
run_triggers=_literal_members(RunTrigger),
|
|
68
|
+
scope_types=_literal_members(ScopeType),
|
|
69
|
+
suggested_artifacts=_literal_members(SuggestedArtifact),
|
|
70
|
+
closure_condition_kinds=_literal_members(ClosureConditionKind),
|
|
71
|
+
knowledge_glyphs=dict(KNOWLEDGE_GLYPHS),
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
__all__ = ["KNOWLEDGE_GLYPHS", "get_capabilities"]
|