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.
Files changed (587) hide show
  1. core/api/__init__.py +0 -0
  2. core/api/agents/__init__.py +0 -0
  3. core/api/agents/session_health.py +59 -0
  4. core/api/agents/session_manager.py +206 -0
  5. core/api/bin/marvisx-state-hook.py +182 -0
  6. core/api/config.py +533 -0
  7. core/api/db.py +1516 -0
  8. core/api/dependencies/__init__.py +0 -0
  9. core/api/dependencies/tenant.py +34 -0
  10. core/api/main.py +1641 -0
  11. core/api/mcp/__init__.py +8 -0
  12. core/api/mcp/_adapter.py +184 -0
  13. core/api/mcp/server.py +58 -0
  14. core/api/mcp/tools/__init__.py +59 -0
  15. core/api/mcp/tools/brain.py +599 -0
  16. core/api/mcp/tools/graph.py +380 -0
  17. core/api/mcp/tools/handoffs.py +112 -0
  18. core/api/mcp/tools/ingest.py +326 -0
  19. core/api/mcp/tools/learnings.py +144 -0
  20. core/api/mcp/tools/projects.py +99 -0
  21. core/api/mcp/tools/pull_requests.py +173 -0
  22. core/api/mcp/tools/safety.py +111 -0
  23. core/api/mcp/tools/search.py +79 -0
  24. core/api/mcp/tools/tasks.py +258 -0
  25. core/api/middleware/__init__.py +0 -0
  26. core/api/middleware/tool_call_audit.py +111 -0
  27. core/api/models/__init__.py +346 -0
  28. core/api/models/auth.py +48 -0
  29. core/api/models/brain.py +1006 -0
  30. core/api/models/common.py +76 -0
  31. core/api/models/costs.py +91 -0
  32. core/api/models/graph.py +66 -0
  33. core/api/models/graph_cosmo.py +125 -0
  34. core/api/models/graph_pr_impact.py +257 -0
  35. core/api/models/graph_ux.py +141 -0
  36. core/api/models/inbox.py +230 -0
  37. core/api/models/ingest_keys.py +108 -0
  38. core/api/models/kg.py +41 -0
  39. core/api/models/llm_config.py +56 -0
  40. core/api/models/monitoring.py +234 -0
  41. core/api/models/projects.py +161 -0
  42. core/api/models/search.py +42 -0
  43. core/api/models/sessions.py +322 -0
  44. core/api/models/tasks.py +184 -0
  45. core/api/models/teams.py +63 -0
  46. core/api/models/users.py +184 -0
  47. core/api/observability/__init__.py +0 -0
  48. core/api/observability/tracing.py +92 -0
  49. core/api/paths.py +26 -0
  50. core/api/rate_limit.py +24 -0
  51. core/api/rbac.py +112 -0
  52. core/api/routers/__init__.py +0 -0
  53. core/api/routers/_adapter.py +24 -0
  54. core/api/routers/admin_pr_impact.py +230 -0
  55. core/api/routers/admin_settings.py +147 -0
  56. core/api/routers/agent.py +1079 -0
  57. core/api/routers/agent_tokens.py +276 -0
  58. core/api/routers/app_settings.py +112 -0
  59. core/api/routers/audit.py +89 -0
  60. core/api/routers/auth.py +586 -0
  61. core/api/routers/bench.py +161 -0
  62. core/api/routers/brain.py +881 -0
  63. core/api/routers/brain_directions.py +527 -0
  64. core/api/routers/ci_checks.py +140 -0
  65. core/api/routers/comments.py +273 -0
  66. core/api/routers/costs.py +148 -0
  67. core/api/routers/docs_coverage.py +217 -0
  68. core/api/routers/docs_governance.py +63 -0
  69. core/api/routers/documents.py +318 -0
  70. core/api/routers/files.py +163 -0
  71. core/api/routers/finder.py +987 -0
  72. core/api/routers/graph.py +836 -0
  73. core/api/routers/handoffs.py +156 -0
  74. core/api/routers/inbox.py +496 -0
  75. core/api/routers/ingest_api_keys.py +205 -0
  76. core/api/routers/ingest_triage.py +1227 -0
  77. core/api/routers/judge.py +306 -0
  78. core/api/routers/kg.py +336 -0
  79. core/api/routers/learnings.py +253 -0
  80. core/api/routers/llm_config.py +130 -0
  81. core/api/routers/monitoring.py +347 -0
  82. core/api/routers/notifications.py +125 -0
  83. core/api/routers/pr_impact.py +315 -0
  84. core/api/routers/projects.py +1061 -0
  85. core/api/routers/pull_requests.py +312 -0
  86. core/api/routers/push.py +67 -0
  87. core/api/routers/raci.py +228 -0
  88. core/api/routers/search.py +125 -0
  89. core/api/routers/sessions.py +3100 -0
  90. core/api/routers/settings.py +90 -0
  91. core/api/routers/share_repo.py +68 -0
  92. core/api/routers/status_updates.py +96 -0
  93. core/api/routers/tags.py +45 -0
  94. core/api/routers/tasks.py +526 -0
  95. core/api/routers/teams.py +425 -0
  96. core/api/routers/terminal.py +105 -0
  97. core/api/routers/users.py +331 -0
  98. core/api/routers/webhooks.py +330 -0
  99. core/api/runtime_settings.py +84 -0
  100. core/api/security.py +652 -0
  101. core/api/services/__init__.py +0 -0
  102. core/api/services/audit.py +58 -0
  103. core/api/services/auto_approval.py +82 -0
  104. core/api/services/brain/__init__.py +52 -0
  105. core/api/services/brain/baseline.py +230 -0
  106. core/api/services/brain/capabilities.py +75 -0
  107. core/api/services/brain/cascade_rollup.py +388 -0
  108. core/api/services/brain/compound_bridge.py +215 -0
  109. core/api/services/brain/cycle.py +1242 -0
  110. core/api/services/brain/cycle_snapshot.py +371 -0
  111. core/api/services/brain/digest_collector.py +147 -0
  112. core/api/services/brain/direction.py +421 -0
  113. core/api/services/brain/drift.py +356 -0
  114. core/api/services/brain/drift_router.py +409 -0
  115. core/api/services/brain/edge_metrics.py +79 -0
  116. core/api/services/brain/events_reader.py +222 -0
  117. core/api/services/brain/findings.py +1379 -0
  118. core/api/services/brain/findings_reader.py +1006 -0
  119. core/api/services/brain/jobs.py +733 -0
  120. core/api/services/brain/journal.py +206 -0
  121. core/api/services/brain/knowledge_forms.py +92 -0
  122. core/api/services/brain/llm/__init__.py +37 -0
  123. core/api/services/brain/llm/_runner.py +62 -0
  124. core/api/services/brain/llm/base.py +70 -0
  125. core/api/services/brain/llm/cache.py +99 -0
  126. core/api/services/brain/llm/constants.py +46 -0
  127. core/api/services/brain/llm/direction_alignment.py +289 -0
  128. core/api/services/brain/llm/factory.py +132 -0
  129. core/api/services/brain/llm/finding_reasoning.py +98 -0
  130. core/api/services/brain/llm/finding_summary.py +92 -0
  131. core/api/services/brain/llm/grounding.py +46 -0
  132. core/api/services/brain/llm/journal_polish.py +96 -0
  133. core/api/services/brain/llm/local_gateway.py +426 -0
  134. core/api/services/brain/llm/parsers.py +71 -0
  135. core/api/services/brain/llm/router_glue.py +422 -0
  136. core/api/services/brain/memory_ops.py +1677 -0
  137. core/api/services/brain/models.py +140 -0
  138. core/api/services/brain/owner_hint.py +211 -0
  139. core/api/services/brain/recap.py +307 -0
  140. core/api/services/brain/rules/__init__.py +65 -0
  141. core/api/services/brain/rules/_signals.py +205 -0
  142. core/api/services/brain/rules/dr1_activity_without_status.py +108 -0
  143. core/api/services/brain/rules/dr2_decision_without_adr.py +110 -0
  144. core/api/services/brain/rules/dr3_stale_open_loop.py +127 -0
  145. core/api/services/brain/rules/dr4_docs_governance_drift.py +79 -0
  146. core/api/services/brain/rules/dr5_playbook_changed.py +99 -0
  147. core/api/services/brain/rules/dr6_external_update_unpropagated.py +100 -0
  148. core/api/services/brain/rules/dr7_claimed_decision_gap.py +123 -0
  149. core/api/services/brain/rules/dr8_direction_misalignment.py +230 -0
  150. core/api/services/brain/runs_reader.py +485 -0
  151. core/api/services/brain/scope.py +79 -0
  152. core/api/services/brain/sources/__init__.py +46 -0
  153. core/api/services/brain/sources/base.py +86 -0
  154. core/api/services/brain/sources/git_kg.py +393 -0
  155. core/api/services/brain/sources/handoffs.py +157 -0
  156. core/api/services/brain/sources/ingestor.py +130 -0
  157. core/api/services/brain/sources/learnings.py +121 -0
  158. core/api/services/brain/sources/pir_tasks.py +245 -0
  159. core/api/services/brain/watermarks.py +147 -0
  160. core/api/services/brain/ws_emitter.py +170 -0
  161. core/api/services/cc_tasks_reader.py +76 -0
  162. core/api/services/ci_service.py +263 -0
  163. core/api/services/claude_metrics.py +796 -0
  164. core/api/services/codex_metrics.py +364 -0
  165. core/api/services/conversation_reader.py +102 -0
  166. core/api/services/cost_service.py +243 -0
  167. core/api/services/crypto.py +147 -0
  168. core/api/services/docs_governance/__init__.py +1 -0
  169. core/api/services/docs_governance/confidence.py +230 -0
  170. core/api/services/docs_governance/config.py +87 -0
  171. core/api/services/docs_governance/enrichment.py +65 -0
  172. core/api/services/docs_governance/frontmatter_validator.py +83 -0
  173. core/api/services/docs_governance/hard_gates.py +221 -0
  174. core/api/services/docs_governance/triage_orchestrator.py +98 -0
  175. core/api/services/embedding_internal.py +395 -0
  176. core/api/services/embedding_service.py +832 -0
  177. core/api/services/event_dispatcher.py +167 -0
  178. core/api/services/events.py +70 -0
  179. core/api/services/git_ops.py +621 -0
  180. core/api/services/graph_cosmo_service.py +440 -0
  181. core/api/services/graph_ranker.py +306 -0
  182. core/api/services/graph_service.py +1589 -0
  183. core/api/services/inbox.py +800 -0
  184. core/api/services/inbox_digest.py +221 -0
  185. core/api/services/inbox_digest_deep_research.py +80 -0
  186. core/api/services/inbox_digest_jobs.py +595 -0
  187. core/api/services/inbox_gmail_sync.py +167 -0
  188. core/api/services/inbox_llm_classifier.py +906 -0
  189. core/api/services/inbox_source_identity.py +116 -0
  190. core/api/services/inbox_sources.py +456 -0
  191. core/api/services/inbox_taxonomy.py +195 -0
  192. core/api/services/inbox_tldr.py +1079 -0
  193. core/api/services/inbox_triage.py +899 -0
  194. core/api/services/ingest/__init__.py +13 -0
  195. core/api/services/ingest/api_key_auth.py +136 -0
  196. core/api/services/ingest/auto_approve.py +120 -0
  197. core/api/services/ingest/classifier.py +138 -0
  198. core/api/services/ingest/confidence.py +173 -0
  199. core/api/services/ingest/dispatch.py +88 -0
  200. core/api/services/ingest/embedding_router.py +272 -0
  201. core/api/services/ingest/events.py +33 -0
  202. core/api/services/ingest/ignore_patterns.py +79 -0
  203. core/api/services/ingest/image_probe.py +218 -0
  204. core/api/services/ingest/ingress.py +263 -0
  205. core/api/services/ingest/insert_saga.py +793 -0
  206. core/api/services/ingest/llm/__init__.py +13 -0
  207. core/api/services/ingest/llm/anthropic_haiku.py +23 -0
  208. core/api/services/ingest/llm/base.py +59 -0
  209. core/api/services/ingest/llm/byok_provider.py +130 -0
  210. core/api/services/ingest/llm/classification_context.py +301 -0
  211. core/api/services/ingest/llm/config_store.py +246 -0
  212. core/api/services/ingest/llm/factory.py +24 -0
  213. core/api/services/ingest/llm/kg_enricher.py +306 -0
  214. core/api/services/ingest/llm/local_gateway.py +821 -0
  215. core/api/services/ingest/llm/local_vllm.py +23 -0
  216. core/api/services/ingest/llm/openai_nano.py +349 -0
  217. core/api/services/ingest/lock_advisory.py +57 -0
  218. core/api/services/ingest/parser_router.py +1756 -0
  219. core/api/services/ingest/parsers/__init__.py +1 -0
  220. core/api/services/ingest/parsers/docling_parser.py +142 -0
  221. core/api/services/ingest/parsers/docparse_gateway.py +178 -0
  222. core/api/services/ingest/parsers/docx_parser.py +127 -0
  223. core/api/services/ingest/parsers/folder_unpacker.py +85 -0
  224. core/api/services/ingest/parsers/gateway_aux.py +147 -0
  225. core/api/services/ingest/parsers/image_parser.py +251 -0
  226. core/api/services/ingest/parsers/internal_markdown.py +89 -0
  227. core/api/services/ingest/parsers/ocr_gateway.py +117 -0
  228. core/api/services/ingest/parsers/ocr_pdf_parser.py +112 -0
  229. core/api/services/ingest/parsers/pdf_types.py +13 -0
  230. core/api/services/ingest/parsers/transcript_parser.py +445 -0
  231. core/api/services/ingest/parsers/vision_gateway.py +186 -0
  232. core/api/services/ingest/parsers/xlsx_parser.py +91 -0
  233. core/api/services/ingest/parsers/zip_unpacker.py +126 -0
  234. core/api/services/ingest/preflight.py +393 -0
  235. core/api/services/ingest/retry_voyage.py +88 -0
  236. core/api/services/ingest/routing_policy.py +307 -0
  237. core/api/services/ingest/serializers/__init__.py +1 -0
  238. core/api/services/ingest/serializers/xlsx_to_markdown.py +80 -0
  239. core/api/services/ingest/skip_log.py +74 -0
  240. core/api/services/ingest/watcher.py +637 -0
  241. core/api/services/kg/__init__.py +0 -0
  242. core/api/services/kg/audit.py +49 -0
  243. core/api/services/kg/hybrid_search.py +691 -0
  244. core/api/services/kg/lens.py +339 -0
  245. core/api/services/kg/pr_impact.py +770 -0
  246. core/api/services/kg/queries.py +152 -0
  247. core/api/services/kg/ranking.py +89 -0
  248. core/api/services/kg/rrf.py +143 -0
  249. core/api/services/kg_watcher_control.py +161 -0
  250. core/api/services/local_llm/__init__.py +19 -0
  251. core/api/services/local_llm/async_client.py +385 -0
  252. core/api/services/local_llm/client.py +173 -0
  253. core/api/services/local_llm/url_validator.py +44 -0
  254. core/api/services/metrics_collector.py +646 -0
  255. core/api/services/metrics_providers.py +65 -0
  256. core/api/services/model_registry.py +266 -0
  257. core/api/services/model_router.py +137 -0
  258. core/api/services/n8n_client.py +77 -0
  259. core/api/services/newsletter_llm_gateway.py +66 -0
  260. core/api/services/notification_service.py +134 -0
  261. core/api/services/openai_responses.py +55 -0
  262. core/api/services/opencode_metrics.py +375 -0
  263. core/api/services/opencode_sessions.py +173 -0
  264. core/api/services/pii_redactor.py +138 -0
  265. core/api/services/pr_impact_pipeline/__init__.py +21 -0
  266. core/api/services/pr_impact_pipeline/differ.py +421 -0
  267. core/api/services/pr_impact_pipeline/dispatcher.py +415 -0
  268. core/api/services/pr_impact_pipeline/gc.py +93 -0
  269. core/api/services/pr_impact_pipeline/languages.py +192 -0
  270. core/api/services/pr_impact_pipeline/parser.py +178 -0
  271. core/api/services/pr_impact_pipeline/writer.py +394 -0
  272. core/api/services/pr_service.py +1393 -0
  273. core/api/services/project_paths.py +70 -0
  274. core/api/services/project_status_updates.py +265 -0
  275. core/api/services/providers.py +276 -0
  276. core/api/services/push_service.py +170 -0
  277. core/api/services/reminder_service.py +89 -0
  278. core/api/services/runas.py +41 -0
  279. core/api/services/salience_service.py +69 -0
  280. core/api/services/security_collector.py +281 -0
  281. core/api/services/session_catalog.py +385 -0
  282. core/api/services/session_metrics_service.py +301 -0
  283. core/api/services/session_ops.py +272 -0
  284. core/api/services/session_state.py +173 -0
  285. core/api/services/share_links.py +222 -0
  286. core/api/services/task_transitions.py +146 -0
  287. core/api/services/terminal_metrics.py +462 -0
  288. core/api/services/terminal_metrics_dump.py +203 -0
  289. core/api/services/tmux.py +1205 -0
  290. core/api/services/webhook_service.py +422 -0
  291. core/api/services/workspace_sync.py +164 -0
  292. core/api/templates/__init__.py +1 -0
  293. core/api/templates/markdown_share.py +164 -0
  294. core/api/terminal.py +1031 -0
  295. core/api/tests/__init__.py +0 -0
  296. core/api/tests/test_agent_facing_auth_dependencies.py +132 -0
  297. core/api/tests/test_audit_permissions.py +133 -0
  298. core/api/tests/test_backfill_session_conversations.py +90 -0
  299. core/api/tests/test_backfill_working_seconds_msg.py +129 -0
  300. core/api/tests/test_claude_metrics.py +326 -0
  301. core/api/tests/test_codex_metrics.py +189 -0
  302. core/api/tests/test_finder_paths.py +74 -0
  303. core/api/tests/test_git_ops_merge.py +155 -0
  304. core/api/tests/test_learnings_check_search.py +81 -0
  305. core/api/tests/test_metrics_providers.py +133 -0
  306. core/api/tests/test_migration_087.py +164 -0
  307. core/api/tests/test_migration_088.py +94 -0
  308. core/api/tests/test_migration_089.py +116 -0
  309. core/api/tests/test_openai_responses.py +24 -0
  310. core/api/tests/test_opencode_metrics.py +740 -0
  311. core/api/tests/test_opencode_sessions.py +321 -0
  312. core/api/tests/test_pr_workflow_e2e.py +457 -0
  313. core/api/tests/test_projects_handoffs.py +31 -0
  314. core/api/tests/test_providers.py +138 -0
  315. core/api/tests/test_safety_bridge.py +347 -0
  316. core/api/tests/test_session_catalog.py +142 -0
  317. core/api/tests/test_session_conversations.py +512 -0
  318. core/api/tests/test_session_metrics_service.py +270 -0
  319. core/api/tests/test_session_resume_paths.py +548 -0
  320. core/api/tests/test_session_theme_mode_migration.py +56 -0
  321. core/api/tests/test_sessions_rbac.py +131 -0
  322. core/api/tests/test_share_edit.py +398 -0
  323. core/api/tests/test_share_repo.py +200 -0
  324. core/api/tests/test_terminal_session_manager.py +98 -0
  325. core/api/tests/test_terminal_upload.py +34 -0
  326. core/api/tests/test_tmux.py +272 -0
  327. core/api/tests/test_workspace_sync.py +186 -0
  328. core/api/tests/test_ws_ticket_in_memory.py +73 -0
  329. core/api/use_cases/__init__.py +11 -0
  330. core/api/use_cases/_context.py +89 -0
  331. core/api/use_cases/_errors.py +62 -0
  332. core/api/use_cases/_roles.py +16 -0
  333. core/api/use_cases/audit.py +171 -0
  334. core/api/use_cases/brain.py +1232 -0
  335. core/api/use_cases/costs.py +249 -0
  336. core/api/use_cases/graph.py +1153 -0
  337. core/api/use_cases/handoffs.py +506 -0
  338. core/api/use_cases/ingest_triage.py +1229 -0
  339. core/api/use_cases/learnings.py +538 -0
  340. core/api/use_cases/projects.py +705 -0
  341. core/api/use_cases/pull_requests.py +415 -0
  342. core/api/use_cases/search.py +926 -0
  343. core/api/use_cases/tasks.py +1495 -0
  344. core/api/visibility.py +141 -0
  345. core/cli/__init__.py +5 -0
  346. core/cli/_index_source.py +632 -0
  347. core/cli/_runtime_ctx.py +160 -0
  348. core/cli/_transmute.py +241 -0
  349. core/cli/marvis_doctor.py +704 -0
  350. core/cli/marvis_feedback.py +396 -0
  351. core/cli/marvis_governance.py +315 -0
  352. core/cli/marvis_hooks.py +515 -0
  353. core/cli/marvis_init.py +757 -0
  354. core/cli/marvis_mcp.py +401 -0
  355. core/cli/marvis_runtime.py +855 -0
  356. core/cli/marvis_telemetry.py +228 -0
  357. core/scripts/_drift_check.py +716 -0
  358. core/scripts/_frontmatter.py +66 -0
  359. core/scripts/_graph_writer.py +189 -0
  360. core/scripts/ast_parser.py +1553 -0
  361. core/scripts/install_hooks/__init__.py +1 -0
  362. core/scripts/install_hooks/_config.sh +109 -0
  363. core/scripts/install_hooks/block-dangerous-bash.sh +23 -0
  364. core/scripts/install_hooks/block-db-direct-write.sh +23 -0
  365. core/scripts/install_hooks/block-push-no-task.sh +23 -0
  366. core/scripts/install_hooks/block-staging-to-prod.sh +23 -0
  367. core/scripts/install_hooks/block-subtree-push.sh +23 -0
  368. core/scripts/install_hooks/config.json +53 -0
  369. core/scripts/install_hooks/enforce-no-merge-main.sh +23 -0
  370. core/scripts/install_hooks/enforce-worktree.sh +23 -0
  371. core/scripts/install_hooks/quality-gate.sh +170 -0
  372. core/scripts/install_hooks/safety_bridge.py +968 -0
  373. core/scripts/install_hooks/secret-scan.sh +23 -0
  374. core/scripts/migrate_spike_node_ids.py +122 -0
  375. core/scripts/populate_artifacts.py +2198 -0
  376. core/scripts/populate_cross_project.py +2457 -0
  377. core/scripts/populate_inbox_nodes.py +357 -0
  378. core/scripts/populate_pr_impact.py +267 -0
  379. core/scripts/populate_project_nodes.py +603 -0
  380. core/scripts/populate_touch_counter.py +337 -0
  381. core/scripts/reparse_failed.py +57 -0
  382. core/scripts/safety_bridge.py +968 -0
  383. core/telemetry/__init__.py +9 -0
  384. core/telemetry/client.py +405 -0
  385. core/telemetry/schema.py +122 -0
  386. core/wizard/__init__.py +65 -0
  387. core/wizard/byok_vault.py +147 -0
  388. core/wizard/defaults.py +58 -0
  389. core/wizard/state.py +117 -0
  390. core/wizard/steps.py +70 -0
  391. core/wizard/validation.py +136 -0
  392. marvisx_cli-0.1.0.dist-info/METADATA +201 -0
  393. marvisx_cli-0.1.0.dist-info/RECORD +587 -0
  394. marvisx_cli-0.1.0.dist-info/WHEEL +5 -0
  395. marvisx_cli-0.1.0.dist-info/entry_points.txt +3 -0
  396. marvisx_cli-0.1.0.dist-info/licenses/LICENSE +98 -0
  397. marvisx_cli-0.1.0.dist-info/top_level.txt +3 -0
  398. migrations/001_initial.sql +33 -0
  399. migrations/002_tasks.sql +30 -0
  400. migrations/003_session_management.sql +7 -0
  401. migrations/004_projects_comments.sql +65 -0
  402. migrations/005_session_intelligence.sql +15 -0
  403. migrations/006_settings.sql +12 -0
  404. migrations/007_task_scoring.sql +12 -0
  405. migrations/008_cost_tracking.sql +31 -0
  406. migrations/009_session_card_metrics.sql +3 -0
  407. migrations/010_monitoring.sql +55 -0
  408. migrations/012_agent_api.sql +21 -0
  409. migrations/013_session_complete.sql +8 -0
  410. migrations/015_pull_requests.sql +43 -0
  411. migrations/015_pull_requests_down.sql +5 -0
  412. migrations/016_users_raci.sql +116 -0
  413. migrations/017_task_cost_entries.sql +87 -0
  414. migrations/018_agents.sql +73 -0
  415. migrations/018_agents_down.sql +13 -0
  416. migrations/019_review_feedback.sql +18 -0
  417. migrations/020_pr_commit_sha.sql +4 -0
  418. migrations/021_webhook_events.sql +18 -0
  419. migrations/022_devx_agent_managed.sql +11 -0
  420. migrations/022_devx_agent_managed_down.sql +6 -0
  421. migrations/023_devx_p1_gate.sql +7 -0
  422. migrations/023_devx_p1_gate_down.sql +3 -0
  423. migrations/024_chat_messages.sql +16 -0
  424. migrations/024_pr_conversation_id.sql +8 -0
  425. migrations/024_task_indexes.sql +21 -0
  426. migrations/024_task_indexes_down.sql +7 -0
  427. migrations/025_audit_log.sql +17 -0
  428. migrations/026_agent_tokens.sql +20 -0
  429. migrations/027_teams_auth_phase_b.sql +35 -0
  430. migrations/028_learnings.sql +23 -0
  431. migrations/029_team_roles.sql +14 -0
  432. migrations/030_finder_pins.sql +10 -0
  433. migrations/031_pr_deploy_status.sql +9 -0
  434. migrations/032_task_reminders.sql +7 -0
  435. migrations/033_events_retry_count.sql +6 -0
  436. migrations/033_session_owner.sql +9 -0
  437. migrations/034_notifications.sql +38 -0
  438. migrations/035_shared_links.sql +15 -0
  439. migrations/036_session_index_upgrade.sql +29 -0
  440. migrations/037_pr_approval.sql +15 -0
  441. migrations/038_pr_submitted_by.sql +6 -0
  442. migrations/039_push_subscriptions.sql +17 -0
  443. migrations/040_semantic_search.sql +16 -0
  444. migrations/041_workspaces.sql +63 -0
  445. migrations/042_oidc_providers.sql +24 -0
  446. migrations/043_ci_checks.sql +31 -0
  447. migrations/044_agent_metrics.sql +30 -0
  448. migrations/045_documents_doc_type.sql +5 -0
  449. migrations/046_salience.sql +13 -0
  450. migrations/047_seed_missing_agents.sql +6 -0
  451. migrations/048_fix_agent_paths_roles.sql +5 -0
  452. migrations/049_agent_role_and_learnings_schema.sql +3 -0
  453. migrations/050_session_provider.sql +2 -0
  454. migrations/051_session_launch_profile.sql +4 -0
  455. migrations/052_session_theme_mode.sql +2 -0
  456. migrations/052_task_kind.sql +4 -0
  457. migrations/053_inbox_items.sql +31 -0
  458. migrations/054_inbox_triage_contract.sql +30 -0
  459. migrations/055_inbox_topic_treatment.sql +12 -0
  460. migrations/056_inbox_treatment_read_save.sql +57 -0
  461. migrations/057_session_theme_mode_backfill.sql +4 -0
  462. migrations/058_inbox_item_status_lifecycle.sql +13 -0
  463. migrations/059_inbox_tldr_and_source_scores.sql +18 -0
  464. migrations/060_newsletter.sql +16 -0
  465. migrations/061_inbox_redesign.sql +69 -0
  466. migrations/062_fix_inbox_sources_backfill.sql +37 -0
  467. migrations/063_task_completion_mode.sql +23 -0
  468. migrations/064_judge_mode_setting.sql +4 -0
  469. migrations/065_knowledge_graph_spike.sql +40 -0
  470. migrations/066_digest_ranking_inputs.sql +10 -0
  471. migrations/066_kg_artifact_nodes.sql +129 -0
  472. migrations/067_inbox_digest_selections.sql +28 -0
  473. migrations/067_kg_temporal.sql +53 -0
  474. migrations/068_inbox_digest_app_settings.sql +9 -0
  475. migrations/068_kg_touch_counter.sql +52 -0
  476. migrations/069_kg_doc_types.sql +117 -0
  477. migrations/070_digest_ranking_inputs_recovery.sql +3 -0
  478. migrations/071_inbox_digest_selections_recovery.sql +3 -0
  479. migrations/072_inbox_digest_app_settings_recovery.sql +3 -0
  480. migrations/073_kg_cross_project.sql +216 -0
  481. migrations/073_kg_cross_project_down.sql +77 -0
  482. migrations/074_kg_infra_types.sql +208 -0
  483. migrations/074_kg_infra_types_down.sql +80 -0
  484. migrations/075_kg_file_state_recovery.sql +35 -0
  485. migrations/075_kg_file_state_recovery_down.sql +5 -0
  486. migrations/076_kg_watcher_state.sql +33 -0
  487. migrations/076_kg_watcher_state_down.sql +3 -0
  488. migrations/077_kg_doc_types_extend.sql +226 -0
  489. migrations/077_kg_doc_types_extend_down.sql +80 -0
  490. migrations/078_kg_fts5.sql +102 -0
  491. migrations/078_kg_fts5_down.sql +14 -0
  492. migrations/079_kg_missing_indexes.sql +31 -0
  493. migrations/079_kg_missing_indexes_down.sql +10 -0
  494. migrations/080_kg_fts5_extended.sql +232 -0
  495. migrations/080_kg_fts5_extended_down.sql +25 -0
  496. migrations/081_kg_lens_indexes.sql +9 -0
  497. migrations/081_kg_lens_indexes_down.sql +3 -0
  498. migrations/082_kg_pins.sql +26 -0
  499. migrations/082_kg_pins_down.sql +14 -0
  500. migrations/083_kg_graph_nodes_degree.sql +20 -0
  501. migrations/083_kg_graph_nodes_degree_down.sql +15 -0
  502. migrations/084_drop_legacy_scheduler_tables.sql +58 -0
  503. migrations/084_drop_legacy_scheduler_tables_down.sql +112 -0
  504. migrations/085_kg_edge_resolves_to.sql +142 -0
  505. migrations/085_kg_edge_resolves_to_down.sql +66 -0
  506. migrations/086_project_status_updates_feed.sql +20 -0
  507. migrations/086_project_status_updates_feed_down.sql +36 -0
  508. migrations/087_session_metrics_dual.sql +50 -0
  509. migrations/087_session_metrics_dual_down.sql +21 -0
  510. migrations/088_rename_context_pct_legacy.sql +23 -0
  511. migrations/088_rename_context_pct_legacy_down.sql +8 -0
  512. migrations/089_session_metrics_equivalent_cost.sql +26 -0
  513. migrations/089_session_metrics_equivalent_cost_down.sql +11 -0
  514. migrations/090_kg_inbox_node_type.sql +26 -0
  515. migrations/090_kg_inbox_node_type_down.sql +20 -0
  516. migrations/091_kg_inbox_node_type_check.sql +265 -0
  517. migrations/091_kg_inbox_node_type_check_down.sql +129 -0
  518. migrations/092_sessions_activity_state_ts.sql +29 -0
  519. migrations/092_sessions_activity_state_ts_down.sql +14 -0
  520. migrations/093_sessions_activity_state_column.sql +29 -0
  521. migrations/093_sessions_activity_state_column_down.sql +10 -0
  522. migrations/094_ingest_pending.sql +55 -0
  523. migrations/094_ingest_pending_down.sql +15 -0
  524. migrations/095_kg_intent_first.sql +77 -0
  525. migrations/095_kg_intent_first_down.sql +25 -0
  526. migrations/096_kg_xlsx_artifact_prefix.sql +17 -0
  527. migrations/096_kg_xlsx_artifact_prefix_down.sql +11 -0
  528. migrations/097_ingest_change_history.sql +37 -0
  529. migrations/097_ingest_change_history_down.sql +13 -0
  530. migrations/098_kg_node_type_business.sql +254 -0
  531. migrations/098_kg_node_type_business_down.sql +195 -0
  532. migrations/099_kg_edges_restore_weight.sql +58 -0
  533. migrations/099_kg_edges_restore_weight_down.sql +12 -0
  534. migrations/100_kg_enriched_at.sql +25 -0
  535. migrations/100_kg_enriched_at_down.sql +12 -0
  536. migrations/101_local_llm_shadow_comparisons.sql +66 -0
  537. migrations/101_local_llm_shadow_comparisons_down.sql +15 -0
  538. migrations/102_promote_llm_costs.sql +69 -0
  539. migrations/102_promote_llm_costs_down.sql +19 -0
  540. migrations/103_ingest_skipped_log.sql +46 -0
  541. migrations/103_ingest_skipped_log_down.sql +15 -0
  542. migrations/120_docs_governance.sql +50 -0
  543. migrations/120_docs_governance_down.sql +11 -0
  544. migrations/121_notification_event_fk_cleanup.sql +21 -0
  545. migrations/121_notification_event_fk_cleanup_down.sql +10 -0
  546. migrations/122_docs_drift_history.sql +34 -0
  547. migrations/122_docs_drift_history_down.sql +15 -0
  548. migrations/123_ingest_parser_waiting_status.sql +69 -0
  549. migrations/123_ingest_parser_waiting_status_down.sql +69 -0
  550. migrations/124_heypocket_recordings.sql +63 -0
  551. migrations/124_heypocket_recordings_down.sql +13 -0
  552. migrations/125_kg_node_type_record.sql +219 -0
  553. migrations/125_kg_node_type_record_down.sql +205 -0
  554. migrations/126_ingest_terminal_upload_source_kind.sql +69 -0
  555. migrations/126_ingest_terminal_upload_source_kind_down.sql +69 -0
  556. migrations/127_brain_v1_substrate.sql +200 -0
  557. migrations/127_brain_v1_substrate_down.sql +32 -0
  558. migrations/128_brain_drift_signals.sql +157 -0
  559. migrations/128_brain_drift_signals_down.sql +23 -0
  560. migrations/129_brain_memory_operations.sql +232 -0
  561. migrations/129_brain_memory_operations_down.sql +27 -0
  562. migrations/130_brain_findings.sql +258 -0
  563. migrations/130_brain_findings_down.sql +29 -0
  564. migrations/132_kg_pr_modifies.sql +242 -0
  565. migrations/132_kg_pr_modifies_down.sql +99 -0
  566. migrations/133_brain_v1_2_direction_schema.sql +476 -0
  567. migrations/133_brain_v1_2_direction_schema_down.sql +273 -0
  568. migrations/134_brain_journal_narrative_polished.sql +8 -0
  569. migrations/134_brain_journal_narrative_polished_down.sql +6 -0
  570. migrations/135_kg_edges_provider.sql +21 -0
  571. migrations/136_documents_fts.sql +56 -0
  572. migrations/137_promote_llm_costs.sql +59 -0
  573. migrations/137_promote_llm_costs_down.sql +19 -0
  574. migrations/138_ingest_api_keys.sql +39 -0
  575. migrations/138_ingest_api_keys_down.sql +8 -0
  576. migrations/139_ingest_pending_ingress.sql +91 -0
  577. migrations/139_ingest_pending_ingress_down.sql +73 -0
  578. migrations/140_ingest_idempotency_quota.sql +45 -0
  579. migrations/140_ingest_idempotency_quota_down.sql +9 -0
  580. migrations/141_ingest_pending_metadata.sql +16 -0
  581. migrations/141_ingest_pending_metadata_down.sql +7 -0
  582. migrations/142_llm_function_config.sql +36 -0
  583. migrations/142_llm_function_config_down.sql +8 -0
  584. migrations/143_kg_code_embeddings.sql +25 -0
  585. migrations/143_kg_code_embeddings_down.sql +5 -0
  586. migrations/__init__.py +4 -0
  587. projects/_template/project.yaml +46 -0
core/api/config.py ADDED
@@ -0,0 +1,533 @@
1
+ # v1.18.0 - 2026-05-16 - KG PR-Impact PRE phase: function cap + branch stale + replay buffer + pr_impact_enabled
2
+ from __future__ import annotations
3
+
4
+ import os
5
+ from pathlib import Path
6
+ from typing import Literal
7
+
8
+ from pydantic import AliasChoices, Field, SecretStr
9
+ from pydantic_settings import BaseSettings, SettingsConfigDict
10
+
11
+
12
+ class Settings(BaseSettings):
13
+ model_config = SettingsConfigDict(
14
+ env_file=".env",
15
+ env_file_encoding="utf-8",
16
+ extra="ignore", # ignora env vars non dichiarate
17
+ populate_by_name=True,
18
+ )
19
+
20
+ pir_env: str = "development"
21
+ # Free-form tenant identifier. Default "core" (OSS). Deploy bundles set
22
+ # DEPLOY_MODE to their own tenant slug via .env; no tenant names are
23
+ # hard-coded in core.
24
+ deploy_mode: str = Field(default="core", alias="DEPLOY_MODE")
25
+ pir_instance: str = Field(default="prod", alias="PIR_INSTANCE")
26
+ marvisx_phase: Literal["A", "B", "C", "D"] = Field(
27
+ default="A", alias="MARVISX_PHASE"
28
+ )
29
+ pir_canary_banner: bool = Field(default=False, alias="PIR_CANARY_BANNER")
30
+ pir_voyage_disabled: bool = Field(default=False, alias="PIR_VOYAGE_DISABLED")
31
+ pir_jwt_secret: str = "dev-secret-change-in-production"
32
+ pir_admin_password_hash: str = ""
33
+ db_path: str = Field(
34
+ default="console.db",
35
+ validation_alias=AliasChoices("PIR_DB_PATH", "DB_PATH"),
36
+ )
37
+ cookie_domain: str | None = Field(default=None, alias="COOKIE_DOMAIN")
38
+ cors_origins_prod: list[str] = Field(
39
+ default_factory=list,
40
+ alias="CORS_ORIGINS_PROD",
41
+ )
42
+ cors_origins_dev: list[str] = ["http://localhost:3000", "http://localhost:3001"]
43
+ console_base_url: str | None = Field(default=None, alias="CONSOLE_BASE_URL")
44
+ jwt_expiry_hours: int = 24
45
+ ws_ticket_ttl_seconds: int = 30
46
+ tasks_api_token: str = ""
47
+ sqlite_busy_timeout_ms: int = 30000 # 30s — backfill/reindex can hold lock >15s
48
+ db_pool_size: int = 8 # bounded connection pool size
49
+ auto_hibernate_enabled: bool = True
50
+ tasks_rate_limit_per_min: int = 60
51
+ kg_deep_rate_limit_per_min: int = (
52
+ 30 # max deep KG bundle requests per user per minute
53
+ )
54
+ inbox_sources_json: str = "{}"
55
+ inbox_max_title_chars: int = 500
56
+ inbox_max_content_chars: int = 20000
57
+ inbox_max_metadata_bytes: int = 20000
58
+ inbox_digest_scheduler_interval_seconds: int = 3600
59
+ brain_scheduler_interval_seconds: int = 3600
60
+ brain_run_off_peak_only: bool = Field(
61
+ default=False, alias="BRAIN_RUN_OFF_PEAK_ONLY"
62
+ )
63
+
64
+ # GitHub Webhook
65
+ github_webhook_secret: str = ""
66
+
67
+ # KG PR-Impact View (planned 2026-05-16, sub-01/02/03 sequential implementation)
68
+ # Source-of-truth single owner per setting: vedi docs/plans/2026-05-16-feat-kg-pr-impact-view-plan.md §9.10
69
+ # Magic numbers cross-sub extracted per Emilio decision (D-MAGIC-NUMBERS-SETTINGS)
70
+ pr_impact_enabled: Literal["off", "shadow", "on"] = Field(
71
+ default="shadow", alias="PR_IMPACT_ENABLED"
72
+ )
73
+ function_cap_default: int = Field(
74
+ default=800, alias="FUNCTION_CAP_DEFAULT"
75
+ ) # PR-impact response: top-N functions per priority ranking (sub-02 §2.1)
76
+ function_cap_max_deep_link: int = Field(
77
+ default=50, alias="FUNCTION_CAP_MAX_DEEP_LINK"
78
+ ) # sub-04 deep-link ?op= max highlighted nodes
79
+ kg_branch_stale_days: int = Field(
80
+ default=30, alias="KG_BRANCH_STALE_DAYS"
81
+ ) # branch is_stale threshold (sub-02 §2.2)
82
+ pr_events_replay_maxlen: int = Field(
83
+ default=20, alias="PR_EVENTS_REPLAY_MAXLEN"
84
+ ) # in-memory WS ring buffer per project_slug (sub-02 §3.2)
85
+ telegram_bot_token: str | None = Field(default=None, alias="TELEGRAM_BOT_TOKEN")
86
+ telegram_owner_chat_id: str | None = Field(
87
+ default=None, alias="TELEGRAM_OWNER_CHAT_ID"
88
+ )
89
+
90
+ # Plan 3 tenant scaffolding. Defaults stay off for marvis-personal/core.
91
+ multi_tenant_enabled: bool = Field(default=False, alias="MULTI_TENANT_ENABLED")
92
+ per_user_api_key_enabled: bool = Field(
93
+ default=False, alias="PER_USER_API_KEY_ENABLED"
94
+ )
95
+ byok_fernet_secret: str | None = Field(
96
+ default=None, alias="BYOK_FERNET_SECRET"
97
+ )
98
+ fernet_salt_version: str = Field(default="v1", alias="FERNET_SALT_VERSION")
99
+ uid_isolation_enabled: bool = Field(default=False, alias="UID_ISOLATION_ENABLED")
100
+ uid_pool_size: int = Field(default=0, alias="UID_POOL_SIZE")
101
+ uid_pool_prefix: str = Field(default="tenant", alias="UID_POOL_PREFIX")
102
+ uid_pool_base: int = Field(default=10100, alias="UID_POOL_BASE")
103
+ # Git run-as user for deployments where the API service account differs from
104
+ # the user that owns the git repositories. When set and != the current
105
+ # process user, git runs via `sudo -u <user>` and chown targets `<user>:<user>`.
106
+ # Empty default (single-user / self-hosted): git runs directly and chown is
107
+ # skipped — the API process already owns the repos. See services/runas.py.
108
+ git_runas_user: str = Field(default="", alias="GIT_RUNAS_USER")
109
+ force_password_change_on_first_login: bool = Field(
110
+ default=False, alias="FORCE_PASSWORD_CHANGE_ON_FIRST_LOGIN"
111
+ )
112
+ # Extra env-var names a tenant exposes into its user tmux panes, on top of
113
+ # the generic core defaults. JSON array form, e.g.
114
+ # TENANT_ENV_WHITELIST=["DATABASE_URL","DEPLOY_MODE","TENANT_SLUG"]
115
+ # The tenant overlay (deploy/<tenant>/, tenants/<tenant>/config.env) ships
116
+ # this. No tenant-specific whitelist is hardcoded in core.
117
+ tenant_env_whitelist: list[str] = Field(
118
+ default_factory=list, alias="TENANT_ENV_WHITELIST"
119
+ )
120
+
121
+ # Agents whose `hybrid` self-improvement proposals always require human
122
+ # review (auto-approval veto). JSON array form, e.g.
123
+ # SELF_IMPROVEMENT_AGENTS=["agent-a","agent-b"]
124
+ # Default empty: OSS core ships no internal agent names hardcoded. The deploy
125
+ # bundle (.env) sets the names so the safety veto stays armed on our prod /
126
+ # tenant installs. Empty list = no veto (auto-approval proceeds per the other
127
+ # rules), which is safe for a fresh OSS install that has no self-improvement
128
+ # agent. See services/auto_approval.py::ApprovalPolicy.evaluate.
129
+ self_improvement_agents: list[str] = Field(
130
+ default_factory=list, alias="SELF_IMPROVEMENT_AGENTS"
131
+ )
132
+
133
+ # Static agent identities accepted for X-Agent-Name attribution on top of the
134
+ # DB `users` table (the authoritative source via get_valid_agent_names) and
135
+ # the generic system identities. JSON array form, e.g.
136
+ # STATIC_AGENT_IDENTITIES=["agent-a","agent-b"]
137
+ # Default empty: OSS core hardcodes no tenant agent names. Agents still
138
+ # authenticate via their DB row; this list only covers the legacy shared
139
+ # TASKS_API_TOKEN attribution path + the scope-enforcement gate in rbac.py,
140
+ # which do not query the DB. The deploy bundle (.env) sets the names.
141
+ # See security.py::_VALID_AGENT_NAMES and rbac.py::require_scope.
142
+ static_agent_identities: list[str] = Field(
143
+ default_factory=list, alias="STATIC_AGENT_IDENTITIES"
144
+ )
145
+
146
+ # Monitoring
147
+ monitoring_metrics_interval: int = (
148
+ 60 # was 10 — caused DB lock every ~30min via metrics_collector.save_to_db
149
+ )
150
+ monitoring_docker_interval: int = (
151
+ 60 # Docker stats interval (separate from system metrics)
152
+ )
153
+ monitoring_security_interval: int = 60
154
+ monitoring_retention_raw_hours: int = 24
155
+ monitoring_retention_candles_days: int = 30
156
+ monitoring_retention_events_days: int = 30
157
+ monitoring_rate_limit_per_min: int = 30
158
+
159
+ # Finder
160
+ finder_root: str = "/data"
161
+ workspace_root: str | None = Field(
162
+ default=None, alias="WORKSPACE_ROOT"
163
+ )
164
+ repo_share_root: str | None = Field(
165
+ default=None, alias="REPO_SHARE_ROOT"
166
+ )
167
+ runtime_home: str | None = Field(default=None, alias="RUNTIME_HOME")
168
+ agents_base: str | None = Field(
169
+ default=None, alias="AGENTS_BASE"
170
+ )
171
+ constitution_path: str | None = Field(
172
+ default=None,
173
+ alias="CONSTITUTION_PATH",
174
+ )
175
+ finder_max_upload_bytes: int = 50_000_000 # 50MB
176
+ finder_max_edit_bytes: int = 1_000_000 # 1MB
177
+ finder_max_view_bytes: int = 10_000_000 # 10MB
178
+ finder_hidden_patterns: list[str] = [
179
+ ".ssh",
180
+ ".gnupg",
181
+ ".env",
182
+ "*.key",
183
+ "*.pem",
184
+ "node_modules",
185
+ ".npm",
186
+ "__pycache__",
187
+ ".cache",
188
+ "proc",
189
+ "sys",
190
+ "dev",
191
+ "run",
192
+ ]
193
+ finder_symlink_whitelist: list[str] = Field(
194
+ default_factory=list,
195
+ alias="FINDER_SYMLINK_WHITELIST",
196
+ )
197
+
198
+ # n8n Integration
199
+ n8n_webhook_url: str = ""
200
+ n8n_api_url: str = ""
201
+ n8n_api_key: str = ""
202
+ n8n_webhook_secret: str = ""
203
+ n8n_dispatch_interval: int = 30
204
+ n8n_max_retry_count: int = 5
205
+
206
+ # Web Push (VAPID)
207
+ vapid_private_key: str = ""
208
+ vapid_public_key: str = ""
209
+
210
+ # SSO / WorkOS AuthKit
211
+ workos_api_key: str = ""
212
+ workos_client_id: str = ""
213
+ workos_cookie_password: str = "" # 32+ chars for state cookie encryption
214
+ sso_enabled: bool = False # feature flag for gradual rollout
215
+ expose_openapi: bool = (
216
+ True # expose /docs and /openapi.json (auth-protected in prod)
217
+ )
218
+ openai_api_key: str = ""
219
+ anthropic_api_key: str = "" # used by inbox_tldr and other Anthropic SDK callers
220
+
221
+ # MAC-Phase 0.5: LiteLLM gateway endpoint. Example forms:
222
+ # http://<tailnet-host>:4000/v1
223
+ # https://llm.<your-domain>/v1
224
+ # The deploy bundle (e.g. deploy/marvis-personal/.env) provides the actual value.
225
+ llm_gateway_base_url: str = Field(default="", alias="LLM_GATEWAY_BASE_URL")
226
+ llm_gateway_enforce_public_base_url: bool = Field(
227
+ default=False, alias="LLM_GATEWAY_ENFORCE_PUBLIC_BASE_URL"
228
+ )
229
+ llm_gateway_api_key: SecretStr | None = Field(
230
+ default=None, alias="LLM_GATEWAY_API_KEY"
231
+ )
232
+ ingest_llm_gateway_api_key: SecretStr | None = Field(
233
+ default=None, alias="INGEST_LLM_GATEWAY_API_KEY"
234
+ )
235
+ ingest_llm_gateway_agent_name: str = Field(
236
+ default="marvisx-ingester", alias="INGEST_LLM_GATEWAY_AGENT_NAME"
237
+ )
238
+ newsletter_llm_gateway_api_key: SecretStr | None = Field(
239
+ default=None, alias="NEWSLETTER_LLM_GATEWAY_API_KEY"
240
+ )
241
+ newsletter_llm_gateway_agent_name: str = Field(
242
+ default="newsletter-digest", alias="NEWSLETTER_LLM_GATEWAY_AGENT_NAME"
243
+ )
244
+ # Inbox TLDR migration flags. Both default false → behaviour unchanged
245
+ # (Sonnet only). Setting shadow_mode=true logs cloud + local in parallel
246
+ # without affecting the user response. Setting use_local=true serves the
247
+ # local response and falls back to cloud only on gateway failure.
248
+ inbox_tldr_use_local: bool = Field(default=False, alias="INBOX_TLDR_USE_LOCAL")
249
+ inbox_tldr_shadow_mode: bool = Field(default=False, alias="INBOX_TLDR_SHADOW_MODE")
250
+ inbox_tldr_local_model: Literal["tier-think", "tier-fast"] = Field(
251
+ default="tier-think", alias="INBOX_TLDR_LOCAL_MODEL"
252
+ )
253
+ inbox_deep_research_local_model: Literal[
254
+ "tier-think", "tier-fast", "tier-write"
255
+ ] = Field(
256
+ default="tier-fast", alias="INBOX_DEEP_RESEARCH_LOCAL_MODEL"
257
+ )
258
+ inbox_deep_research_llm_gateway_api_key: SecretStr | None = Field(
259
+ default=None, alias="INBOX_DEEP_RESEARCH_LLM_GATEWAY_API_KEY"
260
+ )
261
+ inbox_deep_research_repair_model: Literal[
262
+ "tier-think", "tier-fast", "tier-write"
263
+ ] = Field(
264
+ default="tier-fast", alias="INBOX_DEEP_RESEARCH_REPAIR_MODEL"
265
+ )
266
+ inbox_deep_research_local_max_tokens: int = Field(
267
+ default=2000, alias="INBOX_DEEP_RESEARCH_LOCAL_MAX_TOKENS"
268
+ )
269
+ inbox_deep_research_local_timeout_seconds: float = Field(
270
+ default=300.0, alias="INBOX_DEEP_RESEARCH_LOCAL_TIMEOUT_SECONDS"
271
+ )
272
+ ingest_parser_max_concurrency: int = Field(
273
+ default=1, alias="INGEST_PARSER_MAX_CONCURRENCY"
274
+ )
275
+ ingest_local_parser_max_concurrency: int = Field(
276
+ default=1, alias="INGEST_LOCAL_PARSER_MAX_CONCURRENCY"
277
+ )
278
+ ingest_ocr_max_concurrency: int = Field(
279
+ default=3, alias="INGEST_OCR_MAX_CONCURRENCY"
280
+ )
281
+ ingest_docparse_max_concurrency: int = Field(
282
+ default=1, alias="INGEST_DOCPARSE_MAX_CONCURRENCY"
283
+ )
284
+ ingest_transcribe_max_concurrency: int = Field(
285
+ default=1, alias="INGEST_TRANSCRIBE_MAX_CONCURRENCY"
286
+ )
287
+ ingest_vision_max_concurrency: int = Field(
288
+ default=1, alias="INGEST_VISION_MAX_CONCURRENCY"
289
+ )
290
+ # Optional AUX proxy URL for non-chat gateway endpoints (/v1/ocr,
291
+ # /v1/audio/transcriptions). If unset, consumers derive it from
292
+ # LLM_GATEWAY_BASE_URL when the private Mac tailnet URL uses :4000.
293
+ llm_gateway_aux_base_url: str = Field(default="", alias="LLM_GATEWAY_AUX_BASE_URL")
294
+ ingest_transcribe_timeout_seconds: float = Field(
295
+ default=900.0, alias="INGEST_TRANSCRIBE_TIMEOUT_SECONDS"
296
+ )
297
+ ingest_transcribe_ffmpeg_timeout_seconds: float = Field(
298
+ default=900.0, alias="INGEST_TRANSCRIBE_FFMPEG_TIMEOUT_SECONDS"
299
+ )
300
+ ingest_transcribe_max_bytes: int = Field(
301
+ default=500 * 1024 * 1024, alias="INGEST_TRANSCRIBE_MAX_BYTES"
302
+ )
303
+ ingest_transcribe_language: str = Field(
304
+ default="", alias="INGEST_TRANSCRIBE_LANGUAGE"
305
+ )
306
+ ingest_ocr_timeout_seconds: float = Field(
307
+ default=300.0, alias="INGEST_OCR_TIMEOUT_SECONDS"
308
+ )
309
+ ingest_ocr_max_bytes: int = Field(
310
+ default=500 * 1024 * 1024, alias="INGEST_OCR_MAX_BYTES"
311
+ )
312
+ ingest_docparse_enabled: bool = Field(
313
+ default=False, alias="INGEST_DOCPARSE_ENABLED"
314
+ )
315
+ ingest_docparse_pdfs_enabled: bool = Field(
316
+ default=True, alias="INGEST_DOCPARSE_PDFS_ENABLED"
317
+ )
318
+ ingest_docparse_images_enabled: bool = Field(
319
+ default=True, alias="INGEST_DOCPARSE_IMAGES_ENABLED"
320
+ )
321
+ ingest_docparse_mode: Literal["fast", "standard", "precise"] = Field(
322
+ default="standard", alias="INGEST_DOCPARSE_MODE"
323
+ )
324
+ ingest_docparse_mode_override: Literal["", "fast", "standard", "precise"] = Field(
325
+ default="", alias="INGEST_DOCPARSE_MODE_OVERRIDE"
326
+ )
327
+ ingest_docparse_timeout_seconds: float = Field(
328
+ default=900.0, alias="INGEST_DOCPARSE_TIMEOUT_SECONDS"
329
+ )
330
+ ingest_docparse_max_bytes: int = Field(
331
+ default=500 * 1024 * 1024, alias="INGEST_DOCPARSE_MAX_BYTES"
332
+ )
333
+ ingest_vision_images_enabled: bool = Field(
334
+ default=False, alias="INGEST_VISION_IMAGES_ENABLED"
335
+ )
336
+ ingest_vision_timeout_seconds: float = Field(
337
+ default=120.0, alias="INGEST_VISION_TIMEOUT_SECONDS"
338
+ )
339
+ ingest_vision_max_bytes: int = Field(
340
+ default=20 * 1024 * 1024, alias="INGEST_VISION_MAX_BYTES"
341
+ )
342
+ ingest_vision_max_tokens: int = Field(
343
+ default=900, alias="INGEST_VISION_MAX_TOKENS"
344
+ )
345
+ ingest_docx_max_bytes: int = Field(
346
+ default=25 * 1024 * 1024, alias="INGEST_DOCX_MAX_BYTES"
347
+ )
348
+ ingest_llm_classifier_model: Literal["tier-fast"] = Field(
349
+ default="tier-fast", alias="INGEST_LLM_CLASSIFIER_MODEL"
350
+ )
351
+ ingest_llm_provider: Literal["local", "gateway", "mac", "tier-fast"] = Field(
352
+ default="local", alias="INGEST_LLM_PROVIDER"
353
+ )
354
+ ingest_llm_classifier_timeout_seconds: float = Field(
355
+ default=30.0, alias="INGEST_LLM_CLASSIFIER_TIMEOUT_SECONDS"
356
+ )
357
+ ingest_llm_classifier_max_concurrency: int = Field(
358
+ default=4, alias="INGEST_LLM_CLASSIFIER_MAX_CONCURRENCY"
359
+ )
360
+ ingest_llm_classifier_max_attempts: int = Field(
361
+ default=3, alias="INGEST_LLM_CLASSIFIER_MAX_ATTEMPTS"
362
+ )
363
+ ingest_llm_classifier_retry_after_cap_seconds: float = Field(
364
+ default=30.0, alias="INGEST_LLM_CLASSIFIER_RETRY_AFTER_CAP_SECONDS"
365
+ )
366
+ ingest_llm_gateway_priority: Literal[
367
+ "interactive", "batch", "background"
368
+ ] = Field(default="batch", alias="INGEST_LLM_GATEWAY_PRIORITY")
369
+ ingest_llm_gateway_initial_poll_delay_seconds: int = Field(
370
+ default=1, alias="INGEST_LLM_GATEWAY_INITIAL_POLL_DELAY_SECONDS"
371
+ )
372
+
373
+ # Phase 7.0: KG lens default for HTTP surface (MCP default is separate: MARVIS_MCP_DEEP_DEFAULT)
374
+ kg_http_deep_default: bool = (
375
+ False # pydantic-settings maps KG_HTTP_DEEP_DEFAULT env var
376
+ )
377
+
378
+ # WIP limit: max tasks in_progress per project (doc/none tasks count as 0.5)
379
+ wip_max_in_progress: int = 3
380
+
381
+ # Voyage AI embeddings
382
+ voyage_api_key: str = ""
383
+
384
+ # sqlite-vec
385
+ vec0_path: str = "/data/pir/lib/vec0"
386
+
387
+ # LifeOS
388
+ lifeos_data_dir: str = ""
389
+ lifeos_git_repo_dir: str = ""
390
+ lifeos_git_sync_interval: int = 30
391
+
392
+ # Brain v1.2 Wave 3.1 — LLM polish layer (master switch + sub-switches).
393
+ # Tier-write Gemma 3 12B QAT via Mac Gateway tenant `marvisx-brain`.
394
+ # Master defaults True so narrative_polished is populated out-of-the-box;
395
+ # sub-switches stay True (journal/finding_summary/finding_reasoning).
396
+ # Disable master via BRAIN_LLM_POLISH_ENABLED=false to fall back to deterministic.
397
+ brain_llm_polish_enabled: bool = Field(
398
+ default=True, alias="BRAIN_LLM_POLISH_ENABLED"
399
+ )
400
+ brain_llm_journal_polish_enabled: bool = Field(
401
+ default=True, alias="BRAIN_LLM_JOURNAL_POLISH_ENABLED"
402
+ )
403
+ brain_llm_finding_summary_enabled: bool = Field(
404
+ default=True, alias="BRAIN_LLM_FINDING_SUMMARY_ENABLED"
405
+ )
406
+ brain_llm_finding_reasoning_enabled: bool = Field(
407
+ default=True, alias="BRAIN_LLM_FINDING_REASONING_ENABLED"
408
+ )
409
+ brain_llm_gateway_base_url: str = Field(
410
+ default="",
411
+ alias="BRAIN_LLM_GATEWAY_BASE_URL",
412
+ )
413
+ brain_llm_gateway_api_key: SecretStr | None = Field(
414
+ default=None, alias="BRAIN_LLM_GATEWAY_API_KEY"
415
+ )
416
+ brain_llm_tenant: str = Field(
417
+ default="marvisx-brain", alias="BRAIN_LLM_TENANT"
418
+ )
419
+ brain_llm_model: str = Field(
420
+ default="tier-write", alias="BRAIN_LLM_MODEL"
421
+ )
422
+ brain_llm_polish_timeout_seconds: int = Field(
423
+ default=30, alias="BRAIN_LLM_POLISH_TIMEOUT_SECONDS"
424
+ )
425
+ brain_llm_polish_cache_ttl_seconds: int = Field(
426
+ default=3600, alias="BRAIN_LLM_POLISH_CACHE_TTL_SECONDS"
427
+ )
428
+ # Wave 3.1 polish UX (Emilio 2026-05-19): default DISABLED. Quando True,
429
+ # il polish path forza il LLM a citare ≥1 evidence_ref dal whitelist e
430
+ # rifiuta narrative "not grounded" — ha bloccato ~50% delle entries
431
+ # (~72% scope=company) senza prevenire hallucination reale (il body_json
432
+ # passato al LLM è già ground truth deterministico). Lasciato come escape
433
+ # hatch operatore via .env BRAIN_LLM_GROUNDING_STRICT=true.
434
+ brain_llm_grounding_strict: bool = Field(
435
+ default=False, alias="BRAIN_LLM_GROUNDING_STRICT"
436
+ )
437
+ brain_llm_retry_max_attempts: int = Field(
438
+ default=3, alias="BRAIN_LLM_RETRY_MAX_ATTEMPTS"
439
+ )
440
+ brain_llm_retry_backoff_seconds: float = Field(
441
+ default=1.0, alias="BRAIN_LLM_RETRY_BACKOFF_SECONDS"
442
+ )
443
+ brain_llm_semaphore_size: int = Field(
444
+ default=8, alias="BRAIN_LLM_SEMAPHORE_SIZE"
445
+ )
446
+
447
+ @property
448
+ def cors_origins(self) -> list[str]:
449
+ if self.pir_env == "production":
450
+ return self.cors_origins_prod
451
+ return self.cors_origins_dev
452
+
453
+ @property
454
+ def is_production(self) -> bool:
455
+ return self.pir_env == "production"
456
+
457
+ @property
458
+ def is_marvis_personal(self) -> bool:
459
+ return self.deploy_mode == "marvis-personal"
460
+
461
+ @property
462
+ def phase_index(self) -> int:
463
+ return ["A", "B", "C", "D"].index(self.marvisx_phase)
464
+
465
+ @property
466
+ def effective_console_base_url(self) -> str:
467
+ return self.console_base_url or "http://localhost:3000"
468
+
469
+ @property
470
+ def effective_workspace_root(self) -> str:
471
+ return self.workspace_root or os.getcwd()
472
+
473
+ @property
474
+ def effective_repo_share_root(self) -> str:
475
+ return self.repo_share_root or self.effective_workspace_root
476
+
477
+ @property
478
+ def effective_runtime_home(self) -> str:
479
+ return self.runtime_home or os.path.expanduser("~")
480
+
481
+ @property
482
+ def effective_agents_base(self) -> str:
483
+ return self.agents_base or f"{self.effective_runtime_home}/marvisx/data/agents"
484
+
485
+ @property
486
+ def effective_constitution_path(self) -> str:
487
+ return (
488
+ self.constitution_path
489
+ or f"{self.effective_workspace_root}/.claude/rules/constitution.md"
490
+ )
491
+
492
+ @property
493
+ def effective_git_runas_user(self) -> str:
494
+ """Run-as user for git/chown, or '' to act as the current process.
495
+
496
+ Returns '' when GIT_RUNAS_USER is unset or already matches the current
497
+ OS user (no privilege hop needed)."""
498
+ import pwd
499
+
500
+ user = (self.git_runas_user or "").strip()
501
+ if not user:
502
+ return ""
503
+ try:
504
+ if pwd.getpwuid(os.getuid()).pw_name == user:
505
+ return ""
506
+ except (KeyError, OSError):
507
+ pass
508
+ return user
509
+
510
+
511
+ settings = Settings()
512
+
513
+
514
+ def _resolve_repo_parents() -> list[Path]:
515
+ """Read ALLOWED_REPO_PARENTS from env (comma-separated absolute paths).
516
+
517
+ Falls back to a derivation from settings.effective_workspace_root +
518
+ /data/projects/ when ALLOWED_REPO_PARENTS is unset, so generic deploys
519
+ work out of the box. Personal/enterprise deploys override via .env.
520
+ """
521
+ raw = os.environ.get("ALLOWED_REPO_PARENTS", "").strip()
522
+ if raw:
523
+ return [Path(p.strip()).resolve() for p in raw.split(",") if p.strip()]
524
+ workspace = Path(settings.effective_workspace_root).resolve()
525
+ return [
526
+ (workspace / "projects").resolve(),
527
+ workspace,
528
+ Path("/data/projects/").resolve(),
529
+ ]
530
+
531
+
532
+ # Centralized allowlist for repo_path validation (Decision S14, was duplicated in git_ops + projects)
533
+ ALLOWED_REPO_PARENTS: list[Path] = _resolve_repo_parents()