MetaList 0.7.0__tar.gz → 0.7.2__tar.gz

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 (473) hide show
  1. {metalist-0.7.0 → metalist-0.7.2}/MANIFEST.in +2 -0
  2. {metalist-0.7.0/metalist.egg-info → metalist-0.7.2}/PKG-INFO +21 -1
  3. {metalist-0.7.0 → metalist-0.7.2}/README.md +20 -0
  4. {metalist-0.7.0 → metalist-0.7.2}/app/api/routes/ai.py +53 -104
  5. {metalist-0.7.0 → metalist-0.7.2}/app/exception_boundaries.py +2 -12
  6. metalist-0.7.2/app/network_settings.py +61 -0
  7. {metalist-0.7.0 → metalist-0.7.2}/app/security/request_boundary.py +1 -74
  8. metalist-0.7.2/app/security/request_hosts.py +83 -0
  9. {metalist-0.7.0 → metalist-0.7.2}/app/services/agent/actions.py +14 -77
  10. {metalist-0.7.0 → metalist-0.7.2}/app/services/agent/cloud_privacy.py +33 -9
  11. {metalist-0.7.0 → metalist-0.7.2}/app/services/agent/context.py +67 -22
  12. metalist-0.7.2/app/services/agent/help_catalog.py +43 -0
  13. metalist-0.7.2/app/services/agent/history.py +106 -0
  14. {metalist-0.7.0 → metalist-0.7.2}/app/services/agent/investigation.py +5 -3
  15. metalist-0.7.2/app/services/agent/judging.py +15 -0
  16. metalist-0.7.2/app/services/agent/menu_actions.py +63 -0
  17. {metalist-0.7.0 → metalist-0.7.2}/app/services/agent/openai_inference.py +16 -6
  18. {metalist-0.7.0 → metalist-0.7.2}/app/services/agent/prompts/final-response.md +14 -0
  19. metalist-0.7.2/app/services/agent/prompts/help-response.md +25 -0
  20. {metalist-0.7.0 → metalist-0.7.2}/app/services/agent/prompts/system.md +24 -7
  21. {metalist-0.7.0 → metalist-0.7.2}/app/services/agent/retrieval_settings.py +7 -31
  22. {metalist-0.7.0 → metalist-0.7.2}/app/services/agent/runtime.py +128 -137
  23. {metalist-0.7.0 → metalist-0.7.2}/app/services/agent/scope.py +109 -0
  24. {metalist-0.7.0 → metalist-0.7.2}/app/services/agent/skill_settings.py +8 -1
  25. metalist-0.7.2/app/services/agent/skills/help-ai.md +79 -0
  26. metalist-0.7.2/app/services/agent/skills/help-data.md +40 -0
  27. metalist-0.7.2/app/services/agent/skills/help-formatting.md +176 -0
  28. metalist-0.7.2/app/services/agent/skills/help-menus.md +116 -0
  29. metalist-0.7.2/app/services/agent/skills/help-notes.md +200 -0
  30. metalist-0.7.2/app/services/agent/skills/help-privacy.md +135 -0
  31. metalist-0.7.2/app/services/agent/skills/help-references.md +106 -0
  32. metalist-0.7.2/app/services/agent/skills/help-reminders.md +152 -0
  33. metalist-0.7.2/app/services/agent/skills/help-search.md +214 -0
  34. metalist-0.7.2/app/services/agent/skills/help-tags.md +354 -0
  35. metalist-0.7.0/app/services/agent/ollama_inference.py → metalist-0.7.2/app/services/agent/structured_inference.py +22 -151
  36. {metalist-0.7.0 → metalist-0.7.2}/app/services/agent/trace.py +82 -7
  37. {metalist-0.7.0 → metalist-0.7.2}/app/services/ai_chat_stream.py +2 -3
  38. {metalist-0.7.0 → metalist-0.7.2}/app/services/client_state_service.py +10 -17
  39. {metalist-0.7.0 → metalist-0.7.2}/app/services/content_formatting.py +51 -2
  40. {metalist-0.7.0 → metalist-0.7.2}/app/services/search_index.py +52 -11
  41. {metalist-0.7.0 → metalist-0.7.2}/app/services/snapshot.py +3 -1
  42. {metalist-0.7.0 → metalist-0.7.2}/app/startup_sanity_config.py +0 -5
  43. metalist-0.7.2/app/static/config/agent-menu-actions.json +326 -0
  44. {metalist-0.7.0 → metalist-0.7.2}/app/static/config/command_palette_tags.json +3 -3
  45. {metalist-0.7.0 → metalist-0.7.2}/app/static/css/main.css +29 -22
  46. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/main.js +1 -0
  47. metalist-0.7.2/app/static/js/modules/ai-chat/agent-menu-actions.js +43 -0
  48. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/ai-chat/agent-retrieval-settings.js +2 -22
  49. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/ai-chat/ai-agent-debug-view.js +16 -0
  50. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/ai-chat/ai-chat-api.js +30 -101
  51. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/ai-chat/ai-chat-panel-controller.js +37 -9
  52. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/ai-chat/ai-chat-panel-service.js +8 -12
  53. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/ai-chat/ai-thinking-level-service.js +4 -15
  54. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/auth.js +8 -20
  55. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/command-palette/command-palette-controller.js +51 -54
  56. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/config.js +2 -1
  57. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/modals/ai-agent-settings-modal.js +48 -236
  58. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/modals/backup-restore-modal.js +2 -0
  59. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/modals/base-modal.js +8 -3
  60. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/modals/delete-namespace-modal.js +3 -0
  61. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/modals/namespace-modals.js +12 -0
  62. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/modals/password-modal.js +6 -3
  63. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/modals/session-timeout-modal.js +3 -0
  64. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/modals/version-info-modal.js +5 -1
  65. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/mode-manager/actions/content-actions.js +33 -10
  66. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/mode-manager/events/context-menu-events.js +6 -0
  67. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/mode-manager/events/keyboard-events.js +1 -1
  68. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/mode-manager/events/mouse-events.js +20 -0
  69. metalist-0.7.2/app/static/js/modules/session-auth.js +43 -0
  70. {metalist-0.7.0 → metalist-0.7.2}/app/templates/index.html +3 -2
  71. {metalist-0.7.0 → metalist-0.7.2}/app/version.py +1 -1
  72. {metalist-0.7.0 → metalist-0.7.2}/main.py +9 -0
  73. {metalist-0.7.0 → metalist-0.7.2/metalist.egg-info}/PKG-INFO +21 -1
  74. {metalist-0.7.0 → metalist-0.7.2}/metalist.egg-info/SOURCES.txt +20 -3
  75. metalist-0.7.0/app/services/managed_ollama_runtime.py +0 -499
  76. metalist-0.7.0/app/services/ollama_provider.py +0 -519
  77. metalist-0.7.0/app/static/js/modules/session-auth.js +0 -22
  78. {metalist-0.7.0 → metalist-0.7.2}/app/__init__.py +0 -0
  79. {metalist-0.7.0 → metalist-0.7.2}/app/api/__init__.py +0 -0
  80. {metalist-0.7.0 → metalist-0.7.2}/app/api/deps.py +0 -0
  81. {metalist-0.7.0 → metalist-0.7.2}/app/api/dev.py +0 -0
  82. {metalist-0.7.0 → metalist-0.7.2}/app/api/middleware/auth.py +0 -0
  83. {metalist-0.7.0 → metalist-0.7.2}/app/api/note_requests.py +0 -0
  84. {metalist-0.7.0 → metalist-0.7.2}/app/api/request_auth.py +0 -0
  85. {metalist-0.7.0 → metalist-0.7.2}/app/api/routes/app_updates.py +0 -0
  86. {metalist-0.7.0 → metalist-0.7.2}/app/api/routes/auth.py +0 -0
  87. {metalist-0.7.0 → metalist-0.7.2}/app/api/routes/backups.py +0 -0
  88. {metalist-0.7.0 → metalist-0.7.2}/app/api/routes/files.py +0 -0
  89. {metalist-0.7.0 → metalist-0.7.2}/app/api/routes/notes.py +0 -0
  90. {metalist-0.7.0 → metalist-0.7.2}/app/api/routes/ontology.py +0 -0
  91. {metalist-0.7.0 → metalist-0.7.2}/app/api/routes/reminders.py +0 -0
  92. {metalist-0.7.0 → metalist-0.7.2}/app/api/routes/remote_images.py +0 -0
  93. {metalist-0.7.0 → metalist-0.7.2}/app/api/routes/test.py +0 -0
  94. {metalist-0.7.0 → metalist-0.7.2}/app/api/transactions.py +0 -0
  95. {metalist-0.7.0 → metalist-0.7.2}/app/api/upload_limits.py +0 -0
  96. {metalist-0.7.0 → metalist-0.7.2}/app/config.py +0 -0
  97. {metalist-0.7.0 → metalist-0.7.2}/app/data_directory.py +0 -0
  98. {metalist-0.7.0 → metalist-0.7.2}/app/db/__init__.py +0 -0
  99. {metalist-0.7.0 → metalist-0.7.2}/app/db/backup_settings_sql.py +0 -0
  100. {metalist-0.7.0 → metalist-0.7.2}/app/db/engine.py +0 -0
  101. {metalist-0.7.0 → metalist-0.7.2}/app/db/file_schema.py +0 -0
  102. {metalist-0.7.0 → metalist-0.7.2}/app/db/file_session.py +0 -0
  103. {metalist-0.7.0 → metalist-0.7.2}/app/db/files_sql.py +0 -0
  104. {metalist-0.7.0 → metalist-0.7.2}/app/db/link_titles_sql.py +0 -0
  105. {metalist-0.7.0 → metalist-0.7.2}/app/db/live_recovery.py +0 -0
  106. {metalist-0.7.0 → metalist-0.7.2}/app/db/migrations.py +0 -0
  107. {metalist-0.7.0 → metalist-0.7.2}/app/db/notes_sql.py +0 -0
  108. {metalist-0.7.0 → metalist-0.7.2}/app/db/ontology_rules_sql.py +0 -0
  109. {metalist-0.7.0 → metalist-0.7.2}/app/db/reminders_sql.py +0 -0
  110. {metalist-0.7.0 → metalist-0.7.2}/app/db/retire_sounds.py +0 -0
  111. {metalist-0.7.0 → metalist-0.7.2}/app/db/schema.py +0 -0
  112. {metalist-0.7.0 → metalist-0.7.2}/app/db/search_history_sql.py +0 -0
  113. {metalist-0.7.0 → metalist-0.7.2}/app/db/session.py +0 -0
  114. {metalist-0.7.0 → metalist-0.7.2}/app/db/settings_sql.py +0 -0
  115. {metalist-0.7.0 → metalist-0.7.2}/app/db/tab_state_sql.py +0 -0
  116. {metalist-0.7.0 → metalist-0.7.2}/app/db/version.py +0 -0
  117. {metalist-0.7.0 → metalist-0.7.2}/app/encryption_audit.py +0 -0
  118. {metalist-0.7.0 → metalist-0.7.2}/app/https_proxy.py +0 -0
  119. {metalist-0.7.0 → metalist-0.7.2}/app/main.py +0 -0
  120. {metalist-0.7.0 → metalist-0.7.2}/app/models/commands.py +0 -0
  121. {metalist-0.7.0 → metalist-0.7.2}/app/models/database.py +0 -0
  122. {metalist-0.7.0 → metalist-0.7.2}/app/models/entities.py +0 -0
  123. {metalist-0.7.0 → metalist-0.7.2}/app/models/enums.py +0 -0
  124. {metalist-0.7.0 → metalist-0.7.2}/app/models/linked_list.py +0 -0
  125. {metalist-0.7.0 → metalist-0.7.2}/app/models/list_operations.py +0 -0
  126. {metalist-0.7.0 → metalist-0.7.2}/app/models/list_traversal.py +0 -0
  127. {metalist-0.7.0 → metalist-0.7.2}/app/models/note_crud.py +0 -0
  128. {metalist-0.7.0 → metalist-0.7.2}/app/models/utils.py +0 -0
  129. {metalist-0.7.0 → metalist-0.7.2}/app/presentation/__init__.py +0 -0
  130. {metalist-0.7.0 → metalist-0.7.2}/app/presentation/render/__init__.py +0 -0
  131. {metalist-0.7.0 → metalist-0.7.2}/app/presentation/render/note_renderer.py +0 -0
  132. {metalist-0.7.0 → metalist-0.7.2}/app/presentation/templates.py +0 -0
  133. {metalist-0.7.0 → metalist-0.7.2}/app/security/authenticated_logging.py +0 -0
  134. {metalist-0.7.0 → metalist-0.7.2}/app/security/encryption.py +0 -0
  135. {metalist-0.7.0 → metalist-0.7.2}/app/security/http_headers.py +0 -0
  136. {metalist-0.7.0 → metalist-0.7.2}/app/security/note_html.py +0 -0
  137. {metalist-0.7.0 → metalist-0.7.2}/app/security/sensitive_cache.py +0 -0
  138. {metalist-0.7.0 → metalist-0.7.2}/app/security/sensitive_logging.py +0 -0
  139. {metalist-0.7.0 → metalist-0.7.2}/app/security/shell_execution.py +0 -0
  140. {metalist-0.7.0 → metalist-0.7.2}/app/security/validation_errors.py +0 -0
  141. {metalist-0.7.0 → metalist-0.7.2}/app/server_runtime.py +0 -0
  142. {metalist-0.7.0 → metalist-0.7.2}/app/services/__init__.py +0 -0
  143. {metalist-0.7.0 → metalist-0.7.2}/app/services/agent/__init__.py +0 -0
  144. {metalist-0.7.0 → metalist-0.7.2}/app/services/agent/evidence_serialization.py +0 -0
  145. {metalist-0.7.0 → metalist-0.7.2}/app/services/agent/inference.py +0 -0
  146. {metalist-0.7.0 → metalist-0.7.2}/app/services/agent/model_policy.py +0 -0
  147. {metalist-0.7.0 → metalist-0.7.2}/app/services/agent/openai_cost_tracking.py +0 -0
  148. {metalist-0.7.0 → metalist-0.7.2}/app/services/agent/permissions.py +0 -0
  149. {metalist-0.7.0 → metalist-0.7.2}/app/services/agent/prompt_settings.py +0 -0
  150. {metalist-0.7.0 → metalist-0.7.2}/app/services/agent/prompts/__init__.py +0 -0
  151. {metalist-0.7.0 → metalist-0.7.2}/app/services/agent/prompts/tool-result.md +0 -0
  152. {metalist-0.7.0 → metalist-0.7.2}/app/services/agent/skills/__init__.py +0 -0
  153. {metalist-0.7.0 → metalist-0.7.2}/app/services/agent/skills/scoped-investigation.md +0 -0
  154. {metalist-0.7.0 → metalist-0.7.2}/app/services/agent/tagging.py +0 -0
  155. {metalist-0.7.0 → metalist-0.7.2}/app/services/agent/tagging_run.py +0 -0
  156. {metalist-0.7.0 → metalist-0.7.2}/app/services/agent/token_estimation.py +0 -0
  157. {metalist-0.7.0 → metalist-0.7.2}/app/services/agent/tools.py +0 -0
  158. {metalist-0.7.0 → metalist-0.7.2}/app/services/ai_chat.py +0 -0
  159. {metalist-0.7.0 → metalist-0.7.2}/app/services/ai_chat_rendering.py +0 -0
  160. {metalist-0.7.0 → metalist-0.7.2}/app/services/app_update_worker.py +0 -0
  161. {metalist-0.7.0 → metalist-0.7.2}/app/services/app_updates.py +0 -0
  162. {metalist-0.7.0 → metalist-0.7.2}/app/services/auth.py +0 -0
  163. {metalist-0.7.0 → metalist-0.7.2}/app/services/auth_cache_state.py +0 -0
  164. {metalist-0.7.0 → metalist-0.7.2}/app/services/auth_service.py +0 -0
  165. {metalist-0.7.0 → metalist-0.7.2}/app/services/backlink_index.py +0 -0
  166. {metalist-0.7.0 → metalist-0.7.2}/app/services/backlinks.py +0 -0
  167. {metalist-0.7.0 → metalist-0.7.2}/app/services/backup_service.py +0 -0
  168. {metalist-0.7.0 → metalist-0.7.2}/app/services/backup_settings_service.py +0 -0
  169. {metalist-0.7.0 → metalist-0.7.2}/app/services/bulk_operation.py +0 -0
  170. {metalist-0.7.0 → metalist-0.7.2}/app/services/content_cache.py +0 -0
  171. {metalist-0.7.0 → metalist-0.7.2}/app/services/diagnostics.py +0 -0
  172. {metalist-0.7.0 → metalist-0.7.2}/app/services/embedded_references.py +0 -0
  173. {metalist-0.7.0 → metalist-0.7.2}/app/services/encryption.py +0 -0
  174. {metalist-0.7.0 → metalist-0.7.2}/app/services/exception_capture.py +0 -0
  175. {metalist-0.7.0 → metalist-0.7.2}/app/services/file_registry.py +0 -0
  176. {metalist-0.7.0 → metalist-0.7.2}/app/services/file_storage.py +0 -0
  177. {metalist-0.7.0 → metalist-0.7.2}/app/services/footnote_layout.py +0 -0
  178. {metalist-0.7.0 → metalist-0.7.2}/app/services/footnote_rendering.py +0 -0
  179. {metalist-0.7.0 → metalist-0.7.2}/app/services/hierarchy.py +0 -0
  180. {metalist-0.7.0 → metalist-0.7.2}/app/services/html_export.py +0 -0
  181. {metalist-0.7.0 → metalist-0.7.2}/app/services/html_unformatting.py +0 -0
  182. {metalist-0.7.0 → metalist-0.7.2}/app/services/hydration_state.py +0 -0
  183. {metalist-0.7.0 → metalist-0.7.2}/app/services/image_size_formatting.py +0 -0
  184. {metalist-0.7.0 → metalist-0.7.2}/app/services/inline_image_occurrences.py +0 -0
  185. {metalist-0.7.0 → metalist-0.7.2}/app/services/input_errors.py +0 -0
  186. {metalist-0.7.0 → metalist-0.7.2}/app/services/integrity.py +0 -0
  187. {metalist-0.7.0 → metalist-0.7.2}/app/services/latex_rendering.py +0 -0
  188. {metalist-0.7.0 → metalist-0.7.2}/app/services/link_titles.py +0 -0
  189. {metalist-0.7.0 → metalist-0.7.2}/app/services/login_rate_limit.py +0 -0
  190. {metalist-0.7.0 → metalist-0.7.2}/app/services/maintenance_mode.py +0 -0
  191. {metalist-0.7.0 → metalist-0.7.2}/app/services/markdown_rendering.py +0 -0
  192. {metalist-0.7.0 → metalist-0.7.2}/app/services/namespace_deletion_jobs.py +0 -0
  193. {metalist-0.7.0 → metalist-0.7.2}/app/services/namespace_deletion_worker.py +0 -0
  194. {metalist-0.7.0 → metalist-0.7.2}/app/services/namespace_rename_jobs.py +0 -0
  195. {metalist-0.7.0 → metalist-0.7.2}/app/services/namespace_rename_worker.py +0 -0
  196. {metalist-0.7.0 → metalist-0.7.2}/app/services/namespace_runtime_guard.py +0 -0
  197. {metalist-0.7.0 → metalist-0.7.2}/app/services/namespace_switcher.py +0 -0
  198. {metalist-0.7.0 → metalist-0.7.2}/app/services/note_fullscreen.py +0 -0
  199. {metalist-0.7.0 → metalist-0.7.2}/app/services/note_image_tags.py +0 -0
  200. {metalist-0.7.0 → metalist-0.7.2}/app/services/note_ordering.py +0 -0
  201. {metalist-0.7.0 → metalist-0.7.2}/app/services/note_store.py +0 -0
  202. {metalist-0.7.0 → metalist-0.7.2}/app/services/ontology_rules_store.py +0 -0
  203. {metalist-0.7.0 → metalist-0.7.2}/app/services/openai_credentials.py +0 -0
  204. {metalist-0.7.0 → metalist-0.7.2}/app/services/password_note_fields.py +0 -0
  205. {metalist-0.7.0 → metalist-0.7.2}/app/services/reference_presentation.py +0 -0
  206. {metalist-0.7.0 → metalist-0.7.2}/app/services/reminders.py +0 -0
  207. {metalist-0.7.0 → metalist-0.7.2}/app/services/remote_image_proxy.py +0 -0
  208. {metalist-0.7.0 → metalist-0.7.2}/app/services/request_recovery.py +0 -0
  209. {metalist-0.7.0 → metalist-0.7.2}/app/services/resource_limits.py +0 -0
  210. {metalist-0.7.0 → metalist-0.7.2}/app/services/root_sorting.py +0 -0
  211. {metalist-0.7.0 → metalist-0.7.2}/app/services/runtime_generation.py +0 -0
  212. {metalist-0.7.0 → metalist-0.7.2}/app/services/runtime_hardening.py +0 -0
  213. {metalist-0.7.0 → metalist-0.7.2}/app/services/runtime_lock.py +0 -0
  214. {metalist-0.7.0 → metalist-0.7.2}/app/services/search_history.py +0 -0
  215. {metalist-0.7.0 → metalist-0.7.2}/app/services/search_history_storage.py +0 -0
  216. {metalist-0.7.0 → metalist-0.7.2}/app/services/search_query.py +0 -0
  217. {metalist-0.7.0 → metalist-0.7.2}/app/services/search_text.py +0 -0
  218. {metalist-0.7.0 → metalist-0.7.2}/app/services/selected_text_tag.py +0 -0
  219. {metalist-0.7.0 → metalist-0.7.2}/app/services/self_update.py +0 -0
  220. {metalist-0.7.0 → metalist-0.7.2}/app/services/session_timeout_service.py +0 -0
  221. {metalist-0.7.0 → metalist-0.7.2}/app/services/shell_session_service.py +0 -0
  222. {metalist-0.7.0 → metalist-0.7.2}/app/services/store.py +0 -0
  223. {metalist-0.7.0 → metalist-0.7.2}/app/services/structured_note_renderers.py +0 -0
  224. {metalist-0.7.0 → metalist-0.7.2}/app/services/sync.py +0 -0
  225. {metalist-0.7.0 → metalist-0.7.2}/app/services/sync_state.py +0 -0
  226. {metalist-0.7.0 → metalist-0.7.2}/app/services/tab_state.py +0 -0
  227. {metalist-0.7.0 → metalist-0.7.2}/app/services/tag_case.py +0 -0
  228. {metalist-0.7.0 → metalist-0.7.2}/app/services/tag_ontology.py +0 -0
  229. {metalist-0.7.0 → metalist-0.7.2}/app/services/tag_rename.py +0 -0
  230. {metalist-0.7.0 → metalist-0.7.2}/app/services/tag_suggestions.py +0 -0
  231. {metalist-0.7.0 → metalist-0.7.2}/app/services/tag_term_matching.py +0 -0
  232. {metalist-0.7.0 → metalist-0.7.2}/app/services/test_reset.py +0 -0
  233. {metalist-0.7.0 → metalist-0.7.2}/app/services/tokens.py +0 -0
  234. {metalist-0.7.0 → metalist-0.7.2}/app/services/undo_state.py +0 -0
  235. {metalist-0.7.0 → metalist-0.7.2}/app/services/update_backups.py +0 -0
  236. {metalist-0.7.0 → metalist-0.7.2}/app/services/update_preflight.py +0 -0
  237. {metalist-0.7.0 → metalist-0.7.2}/app/services/view_cache.py +0 -0
  238. {metalist-0.7.0 → metalist-0.7.2}/app/services/view_diff.py +0 -0
  239. {metalist-0.7.0 → metalist-0.7.2}/app/services/view_state.py +0 -0
  240. {metalist-0.7.0 → metalist-0.7.2}/app/services/windows_process_control.py +0 -0
  241. {metalist-0.7.0 → metalist-0.7.2}/app/startup_environment.py +0 -0
  242. {metalist-0.7.0 → metalist-0.7.2}/app/startup_js_sanity.py +0 -0
  243. {metalist-0.7.0 → metalist-0.7.2}/app/startup_sanity.py +0 -0
  244. {metalist-0.7.0 → metalist-0.7.2}/app/static/css/floating-notes.css +0 -0
  245. {metalist-0.7.0 → metalist-0.7.2}/app/static/css/forms.css +0 -0
  246. {metalist-0.7.0 → metalist-0.7.2}/app/static/img/favicon.ico +0 -0
  247. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/locked.js +0 -0
  248. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/activity-tracker.js +0 -0
  249. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/ai-chat/agent-prompt-service.js +0 -0
  250. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/ai-chat/bulk-elapsed-clock.js +0 -0
  251. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/ai-chat/bulk-proposal-ui.js +0 -0
  252. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/ai-chat/cloud-privacy-policy.js +0 -0
  253. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/ai-chat/proposal-menu.js +0 -0
  254. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/api-client.js +0 -0
  255. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/api-failure-classification-service.js +0 -0
  256. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/app-update-service.js +0 -0
  257. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/application-state.js +0 -0
  258. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/async-result.js +0 -0
  259. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/client-state-api.js +0 -0
  260. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/client-state-migration.js +0 -0
  261. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/command-palette/activation-auth-policy.js +0 -0
  262. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/command-palette/backup-command-availability.js +0 -0
  263. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/command-palette/click-activation-service.js +0 -0
  264. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/command-palette/endpoint-registry.js +0 -0
  265. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/command-palette/note-layout-preferences.js +0 -0
  266. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/command-palette/preferences-store.js +0 -0
  267. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/command-palette/tag-config-loader.js +0 -0
  268. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/command-palette/usage-store.js +0 -0
  269. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/connectivity-monitor.js +0 -0
  270. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/context-menu/context-menu-registry.js +0 -0
  271. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/context-menu/context-menu-service.js +0 -0
  272. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/context-menu/context-menu-target-service.js +0 -0
  273. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/context-menu/input-caret-service.js +0 -0
  274. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/disconnect-editing-state-service.js +0 -0
  275. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/dom-utils-fixed.js +0 -0
  276. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/dom-utils.js +0 -0
  277. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/editor-commands.js +0 -0
  278. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/editor-selection.js +0 -0
  279. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/editor-toolbar.js +0 -0
  280. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/elapsed-time.js +0 -0
  281. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/error-handler.js +0 -0
  282. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/error-overlay.js +0 -0
  283. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/expected-errors.js +0 -0
  284. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/location-flags.js +0 -0
  285. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/login-namespace-picker.js +0 -0
  286. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/maintenance.js +0 -0
  287. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/modals/agent-prompt-editor-modal.js +0 -0
  288. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/modals/alphabetize-root-notes-modal.js +0 -0
  289. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/modals/backup-result-modal.js +0 -0
  290. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/modals/backup-retention-modal.js +0 -0
  291. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/modals/backup-settings-modal.js +0 -0
  292. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/modals/confirmation-modal.js +0 -0
  293. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/modals/delete-namespace-validation.js +0 -0
  294. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/modals/help-modal.js +0 -0
  295. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/modals/namespace-loading-page.js +0 -0
  296. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/modals/namespace-ports-profile.js +0 -0
  297. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/modals/namespace-rename-validation.js +0 -0
  298. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/modals/note-layout-appearance-modal.js +0 -0
  299. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/modals/ontology-modal.js +0 -0
  300. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/modals/prioritize-modal.js +0 -0
  301. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/modals/random-password-modal.js +0 -0
  302. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/modals/reminder-modal.js +0 -0
  303. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/modals/reset-updated-at-modal.js +0 -0
  304. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/modals/search-suggestion-statistics-modal.js +0 -0
  305. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/mode-manager/actions/history-actions.js +0 -0
  306. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/mode-manager/actions/note-actions.js +0 -0
  307. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/mode-manager/actions/search-actions.js +0 -0
  308. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/mode-manager/actions/selection-actions.js +0 -0
  309. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/mode-manager/actions/ui-actions.js +0 -0
  310. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/mode-manager/events/focus-events.js +0 -0
  311. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/mode-manager/events/inactivity-events.js +0 -0
  312. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/mode-manager/events/input-events.js +0 -0
  313. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/mode-manager/events/search-events.js +0 -0
  314. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/mode-manager/mode-context.js +0 -0
  315. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/mode-manager/mode-logger.js +0 -0
  316. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/mode-manager/mode-manager-controller.js +0 -0
  317. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/mode-manager/services/add-style-service.js +0 -0
  318. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/mode-manager/services/animated-scroll-service.js +0 -0
  319. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/mode-manager/services/backlinks-navigation-service.js +0 -0
  320. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/mode-manager/services/backlinks-panel-service.js +0 -0
  321. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/mode-manager/services/clipboard-shortcut-policy-service.js +0 -0
  322. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/mode-manager/services/collapse-affordance-service.js +0 -0
  323. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/mode-manager/services/collapse-editing-policy-service.js +0 -0
  324. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/mode-manager/services/command-gate-service.js +0 -0
  325. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/mode-manager/services/context-menu-target-service.js +0 -0
  326. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/mode-manager/services/deselect-selection-state-service.js +0 -0
  327. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/mode-manager/services/differential-view-service.js +0 -0
  328. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/mode-manager/services/edit-session-collapse-policy-service.js +0 -0
  329. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/mode-manager/services/edit-session-collapse-service.js +0 -0
  330. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/mode-manager/services/embedded-image-service.js +0 -0
  331. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/mode-manager/services/file-drop-policy-service.js +0 -0
  332. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/mode-manager/services/file-image-preview-service.js +0 -0
  333. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/mode-manager/services/file-reference-service.js +0 -0
  334. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/mode-manager/services/filtered-refresh-selection-service.js +0 -0
  335. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/mode-manager/services/floating-note-dom.js +0 -0
  336. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/mode-manager/services/floating-note-layout.js +0 -0
  337. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/mode-manager/services/floating-note-service.js +0 -0
  338. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/mode-manager/services/footnote-navigation-service.js +0 -0
  339. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/mode-manager/services/history-selection-policy-service.js +0 -0
  340. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/mode-manager/services/history-shortcut-policy-service.js +0 -0
  341. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/mode-manager/services/html-paste-sanitizer-service.js +0 -0
  342. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/mode-manager/services/image-context-menu-action-service.js +0 -0
  343. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/mode-manager/services/image-file-insert-choice-modal-service.js +0 -0
  344. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/mode-manager/services/infinite-scroll-service.js +0 -0
  345. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/mode-manager/services/link-context-menu-action-service.js +0 -0
  346. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/mode-manager/services/link-title-prefetch-service.js +0 -0
  347. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/mode-manager/services/markdown-render-service.js +0 -0
  348. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/mode-manager/services/mermaid-render-service.js +0 -0
  349. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/mode-manager/services/note-click-target-service.js +0 -0
  350. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/mode-manager/services/note-clipboard-write-service.js +0 -0
  351. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/mode-manager/services/note-collapse-animation-service.js +0 -0
  352. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/mode-manager/services/note-context-menu-action-service.js +0 -0
  353. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/mode-manager/services/note-delete-shortcut-policy-service.js +0 -0
  354. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/mode-manager/services/note-drag-service.js +0 -0
  355. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/mode-manager/services/note-fullscreen-service.js +0 -0
  356. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/mode-manager/services/note-reposition-animation-service.js +0 -0
  357. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/mode-manager/services/note-split-service.js +0 -0
  358. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/mode-manager/services/note-timestamp-hover-service.js +0 -0
  359. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/mode-manager/services/password-clipboard-service.js +0 -0
  360. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/mode-manager/services/polling-service.js +0 -0
  361. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/mode-manager/services/reference-source-navigation-service.js +0 -0
  362. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/mode-manager/services/remote-image-proxy-service.js +0 -0
  363. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/mode-manager/services/remove-formatting-service.js +0 -0
  364. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/mode-manager/services/root-date-separator-service.js +0 -0
  365. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/mode-manager/services/root-sort-indicator-service.js +0 -0
  366. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/mode-manager/services/root-sort-service.js +0 -0
  367. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/mode-manager/services/scroll-anchor-service.js +0 -0
  368. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/mode-manager/services/scroll-restoration-service.js +0 -0
  369. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/mode-manager/services/scroll-to-top-service.js +0 -0
  370. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/mode-manager/services/search-contexts-overlay-service.js +0 -0
  371. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/mode-manager/services/search-debounce-service.js +0 -0
  372. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/mode-manager/services/search-input-service.js +0 -0
  373. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/mode-manager/services/search-interaction-service.js +0 -0
  374. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/mode-manager/services/search-redaction-reveal-service.js +0 -0
  375. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/mode-manager/services/search-suggestion-statistics-service.js +0 -0
  376. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/mode-manager/services/search-suggestion-window-labels-service.js +0 -0
  377. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/mode-manager/services/search-suggestion-windows-service.js +0 -0
  378. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/mode-manager/services/search-suggestions-service.js +0 -0
  379. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/mode-manager/services/search-syntax-service.js +0 -0
  380. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/mode-manager/services/selected-formatting-removal-service.js +0 -0
  381. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/mode-manager/services/selected-text-tag-service.js +0 -0
  382. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/mode-manager/services/shell-output-service.js +0 -0
  383. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/mode-manager/services/tab-dom-cache-service.js +0 -0
  384. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/mode-manager/services/tab-drag-service.js +0 -0
  385. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/mode-manager/services/tab-duplication-service.js +0 -0
  386. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/mode-manager/services/tab-state-service.js +0 -0
  387. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/mode-manager/services/tag-bar-service.js +0 -0
  388. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/mode-manager/services/tag-input-selection-service.js +0 -0
  389. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/mode-manager/services/tag-proposal-service.js +0 -0
  390. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/mode-manager/services/tag-suggestions-service.js +0 -0
  391. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/mode-manager/services/tag-syntax-service.js +0 -0
  392. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/mode-manager/services/todo-toggle-editing-service.js +0 -0
  393. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/mode-manager/services/untagged-view-indicator-service.js +0 -0
  394. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/mode-manager/services/view-mode-search-shortcut-service.js +0 -0
  395. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/note-html-sanitizer.js +0 -0
  396. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/password-generator.js +0 -0
  397. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/password-operation-response.js +0 -0
  398. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/password-policy.js +0 -0
  399. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/password-strength.js +0 -0
  400. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/reminder-store.js +0 -0
  401. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/reminder-surface-service.js +0 -0
  402. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/selection-markers.js +0 -0
  403. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/tag-token.js +0 -0
  404. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/modules/uuid.js +0 -0
  405. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/namespace-deleted.js +0 -0
  406. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/namespace-renamed.js +0 -0
  407. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/vendor/dompurify-3.4.13.LICENSE.txt +0 -0
  408. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/vendor/dompurify-3.4.13.min.js +0 -0
  409. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/vendor/markdown-it-14.3.2.LICENSE.txt +0 -0
  410. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/vendor/markdown-it-14.3.2.min.js +0 -0
  411. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/vendor/mermaid-11.16.1.LICENSE.txt +0 -0
  412. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/vendor/mermaid-11.16.1.min.js +0 -0
  413. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/vendor/zxcvbn-4.4.2.LICENSE.txt +0 -0
  414. {metalist-0.7.0 → metalist-0.7.2}/app/static/js/vendor/zxcvbn-4.4.2.js +0 -0
  415. {metalist-0.7.0 → metalist-0.7.2}/app/static/note-html-policy.json +0 -0
  416. {metalist-0.7.0 → metalist-0.7.2}/app/static/video/login-startup.mp4 +0 -0
  417. {metalist-0.7.0 → metalist-0.7.2}/app/templates/base.html +0 -0
  418. {metalist-0.7.0 → metalist-0.7.2}/app/templates/locked.html +0 -0
  419. {metalist-0.7.0 → metalist-0.7.2}/app/templates/maintenance.html +0 -0
  420. {metalist-0.7.0 → metalist-0.7.2}/app/templates/namespace_deleted.html +0 -0
  421. {metalist-0.7.0 → metalist-0.7.2}/app/templates/namespace_renamed.html +0 -0
  422. {metalist-0.7.0 → metalist-0.7.2}/app/templates/notes_list.html +0 -0
  423. {metalist-0.7.0 → metalist-0.7.2}/app/upload_limits.py +0 -0
  424. {metalist-0.7.0 → metalist-0.7.2}/app/usecases/__init__.py +0 -0
  425. {metalist-0.7.0 → metalist-0.7.2}/app/usecases/add_selected_text_tag.py +0 -0
  426. {metalist-0.7.0 → metalist-0.7.2}/app/usecases/alphabetize_root_notes.py +0 -0
  427. {metalist-0.7.0 → metalist-0.7.2}/app/usecases/base.py +0 -0
  428. {metalist-0.7.0 → metalist-0.7.2}/app/usecases/bulk_tag_proposals.py +0 -0
  429. {metalist-0.7.0 → metalist-0.7.2}/app/usecases/collapse.py +0 -0
  430. {metalist-0.7.0 → metalist-0.7.2}/app/usecases/copy_note.py +0 -0
  431. {metalist-0.7.0 → metalist-0.7.2}/app/usecases/create_child.py +0 -0
  432. {metalist-0.7.0 → metalist-0.7.2}/app/usecases/create_note.py +0 -0
  433. {metalist-0.7.0 → metalist-0.7.2}/app/usecases/create_sibling.py +0 -0
  434. {metalist-0.7.0 → metalist-0.7.2}/app/usecases/delete_subtree.py +0 -0
  435. {metalist-0.7.0 → metalist-0.7.2}/app/usecases/delete_tag.py +0 -0
  436. {metalist-0.7.0 → metalist-0.7.2}/app/usecases/expand.py +0 -0
  437. {metalist-0.7.0 → metalist-0.7.2}/app/usecases/indent.py +0 -0
  438. {metalist-0.7.0 → metalist-0.7.2}/app/usecases/lock.py +0 -0
  439. {metalist-0.7.0 → metalist-0.7.2}/app/usecases/move.py +0 -0
  440. {metalist-0.7.0 → metalist-0.7.2}/app/usecases/move_to_top.py +0 -0
  441. {metalist-0.7.0 → metalist-0.7.2}/app/usecases/outdent.py +0 -0
  442. {metalist-0.7.0 → metalist-0.7.2}/app/usecases/paste_child.py +0 -0
  443. {metalist-0.7.0 → metalist-0.7.2}/app/usecases/paste_sibling.py +0 -0
  444. {metalist-0.7.0 → metalist-0.7.2}/app/usecases/prefetch_link_titles.py +0 -0
  445. {metalist-0.7.0 → metalist-0.7.2}/app/usecases/prioritize.py +0 -0
  446. {metalist-0.7.0 → metalist-0.7.2}/app/usecases/redo.py +0 -0
  447. {metalist-0.7.0 → metalist-0.7.2}/app/usecases/rename_tag.py +0 -0
  448. {metalist-0.7.0 → metalist-0.7.2}/app/usecases/reset_updated_at.py +0 -0
  449. {metalist-0.7.0 → metalist-0.7.2}/app/usecases/resize_image.py +0 -0
  450. {metalist-0.7.0 → metalist-0.7.2}/app/usecases/run_shell.py +0 -0
  451. {metalist-0.7.0 → metalist-0.7.2}/app/usecases/search_comment_autofill.py +0 -0
  452. {metalist-0.7.0 → metalist-0.7.2}/app/usecases/search_context_tags.py +0 -0
  453. {metalist-0.7.0 → metalist-0.7.2}/app/usecases/set_collapse_bulk.py +0 -0
  454. {metalist-0.7.0 → metalist-0.7.2}/app/usecases/set_collapse_in_context.py +0 -0
  455. {metalist-0.7.0 → metalist-0.7.2}/app/usecases/split_note.py +0 -0
  456. {metalist-0.7.0 → metalist-0.7.2}/app/usecases/tag_proposals.py +0 -0
  457. {metalist-0.7.0 → metalist-0.7.2}/app/usecases/toggle_reference_mode.py +0 -0
  458. {metalist-0.7.0 → metalist-0.7.2}/app/usecases/toggle_todo_done.py +0 -0
  459. {metalist-0.7.0 → metalist-0.7.2}/app/usecases/undo.py +0 -0
  460. {metalist-0.7.0 → metalist-0.7.2}/app/usecases/unformat_content.py +0 -0
  461. {metalist-0.7.0 → metalist-0.7.2}/app/usecases/update_content.py +0 -0
  462. {metalist-0.7.0 → metalist-0.7.2}/app/usecases/view.py +0 -0
  463. {metalist-0.7.0 → metalist-0.7.2}/app/utils/__init__.py +0 -0
  464. {metalist-0.7.0 → metalist-0.7.2}/app/utils/encryption.py +0 -0
  465. {metalist-0.7.0 → metalist-0.7.2}/app/utils/text_utils.py +0 -0
  466. {metalist-0.7.0 → metalist-0.7.2}/convert-from-legacy.py +0 -0
  467. {metalist-0.7.0 → metalist-0.7.2}/metalist.egg-info/dependency_links.txt +0 -0
  468. {metalist-0.7.0 → metalist-0.7.2}/metalist.egg-info/entry_points.txt +0 -0
  469. {metalist-0.7.0 → metalist-0.7.2}/metalist.egg-info/requires.txt +0 -0
  470. {metalist-0.7.0 → metalist-0.7.2}/metalist.egg-info/top_level.txt +0 -0
  471. {metalist-0.7.0 → metalist-0.7.2}/pyproject.toml +0 -0
  472. {metalist-0.7.0 → metalist-0.7.2}/scripts/generate-lan-cert.sh +0 -0
  473. {metalist-0.7.0 → metalist-0.7.2}/setup.cfg +0 -0
@@ -1,4 +1,6 @@
1
1
  recursive-include app/static *
2
2
  recursive-include app/templates *.html
3
+ prune evals
4
+ prune tests
3
5
  prune app/__pycache__
4
6
  global-exclude .DS_Store
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: metalist
3
- Version: 0.7.0
3
+ Version: 0.7.2
4
4
  Summary: A minimalist single-user note-taking app with SSR, fast in-memory tree operations, and efficient sync.
5
5
  Project-URL: Homepage, https://github.com/evolvingstuff/metalist
6
6
  Project-URL: Repository, https://github.com/evolvingstuff/metalist
@@ -48,6 +48,14 @@ A minimalist single-user note-taking app focused on server-side rendering (SSR),
48
48
  - Multi-tab search contexts with server-persisted scroll/search state (survives browser restarts)
49
49
  - Manual namespace backups/restores to a user-selected backup folder with retention controls
50
50
 
51
+ ## Changes in 0.7.2
52
+
53
+ - Login, hydration, and workspace requests share one initialized browser-tab identity. Losing the `sessionStorage` copy while opening the workspace no longer causes the reported missing-tab-ID crash. The external trigger for that storage loss remains unconfirmed.
54
+ - AI chat preserves the edited note and includes its permitted tree, tags, and cached URL titles. Search redaction and cloud privacy restrictions apply to every node; blocked selections expose only their availability reason.
55
+ - Added on-demand AI help and menu actions, privacy previews, and Copy Response throughout assistant message bubbles. OpenAI is the supported inference provider.
56
+ - LLM regressions use current production prompts, default to five trials with cache-aware parallel scheduling, and can run only cases affected by prompt or skill changes.
57
+ - Improved Grammarly edit preservation and search suggestions that match the complete active clause.
58
+
51
59
  ## Changes in 0.6.3
52
60
 
53
61
  - Restored HTTPS connection reuse and aligned the pending accept queue with the existing worker capacity, so a browser’s six-connection startup burst is admitted.
@@ -213,6 +221,18 @@ METALIST_HOST=192.168.1.20 metalist
213
221
  ```
214
222
  On a fresh machine, that first launch also creates the default TLS cert pair automatically. Then open either `http://192.168.1.20:8000` or `https://192.168.1.20:8443` from the other machine.
215
223
 
224
+ Starting with 0.7.1, an explicit `METALIST_HOST` or `METALIST_ALLOWED_HOSTS`
225
+ configuration is saved in `~/MetaList/network-settings.json` (under
226
+ `METALIST_DATA_DIRECTORY` when overridden). Subsequent source launches, plain
227
+ `metalist` launches, and updates reuse it. Environment values override and update
228
+ the corresponding saved settings. This file stores only the bind address and
229
+ allowed hosts; namespace ports remain in their launch profiles, and shell access
230
+ still requires `--enable-shell` on each launch. Invalid settings stop startup
231
+ with an error. Fresh installations continue to bind to localhost by default.
232
+ To switch back to local-only access, run `METALIST_HOST=127.0.0.1 metalist` once.
233
+ Older installed versions do not read this file; keep supplying their LAN settings
234
+ until upgrading to 0.7.1 or later.
235
+
216
236
  To keep `@shell` available on the host laptop while allowing another laptop to
217
237
  use normal MetaList features, bind both interfaces and explicitly allow the LAN
218
238
  address:
@@ -12,6 +12,14 @@ A minimalist single-user note-taking app focused on server-side rendering (SSR),
12
12
  - Multi-tab search contexts with server-persisted scroll/search state (survives browser restarts)
13
13
  - Manual namespace backups/restores to a user-selected backup folder with retention controls
14
14
 
15
+ ## Changes in 0.7.2
16
+
17
+ - Login, hydration, and workspace requests share one initialized browser-tab identity. Losing the `sessionStorage` copy while opening the workspace no longer causes the reported missing-tab-ID crash. The external trigger for that storage loss remains unconfirmed.
18
+ - AI chat preserves the edited note and includes its permitted tree, tags, and cached URL titles. Search redaction and cloud privacy restrictions apply to every node; blocked selections expose only their availability reason.
19
+ - Added on-demand AI help and menu actions, privacy previews, and Copy Response throughout assistant message bubbles. OpenAI is the supported inference provider.
20
+ - LLM regressions use current production prompts, default to five trials with cache-aware parallel scheduling, and can run only cases affected by prompt or skill changes.
21
+ - Improved Grammarly edit preservation and search suggestions that match the complete active clause.
22
+
15
23
  ## Changes in 0.6.3
16
24
 
17
25
  - Restored HTTPS connection reuse and aligned the pending accept queue with the existing worker capacity, so a browser’s six-connection startup burst is admitted.
@@ -177,6 +185,18 @@ METALIST_HOST=192.168.1.20 metalist
177
185
  ```
178
186
  On a fresh machine, that first launch also creates the default TLS cert pair automatically. Then open either `http://192.168.1.20:8000` or `https://192.168.1.20:8443` from the other machine.
179
187
 
188
+ Starting with 0.7.1, an explicit `METALIST_HOST` or `METALIST_ALLOWED_HOSTS`
189
+ configuration is saved in `~/MetaList/network-settings.json` (under
190
+ `METALIST_DATA_DIRECTORY` when overridden). Subsequent source launches, plain
191
+ `metalist` launches, and updates reuse it. Environment values override and update
192
+ the corresponding saved settings. This file stores only the bind address and
193
+ allowed hosts; namespace ports remain in their launch profiles, and shell access
194
+ still requires `--enable-shell` on each launch. Invalid settings stop startup
195
+ with an error. Fresh installations continue to bind to localhost by default.
196
+ To switch back to local-only access, run `METALIST_HOST=127.0.0.1 metalist` once.
197
+ Older installed versions do not read this file; keep supplying their LAN settings
198
+ until upgrading to 0.7.1 or later.
199
+
180
200
  To keep `@shell` available on the host laptop while allowing another laptop to
181
201
  use normal MetaList features, bind both interfaces and explicitly allow the LAN
182
202
  address:
@@ -2,7 +2,6 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
- import asyncio
6
5
  import json
7
6
  import math
8
7
  from collections.abc import AsyncIterator
@@ -27,7 +26,6 @@ from app.services.agent.cloud_privacy import cloud_privacy_evaluator
27
26
  from app.services.agent.cloud_privacy import resolve_cloud_privacy_boundary
28
27
  from app.services.agent.inference import InferenceAdapter
29
28
  from app.services.agent.model_policy import SingleModelPolicy
30
- from app.services.agent.ollama_inference import OllamaInferenceAdapter
31
29
  from app.services.agent.openai_inference import OPENAI_API_BASE_URL
32
30
  from app.services.agent.openai_inference import OPENAI_MODELS
33
31
  from app.services.agent.openai_inference import OpenAIInferenceAdapter
@@ -50,15 +48,10 @@ from app.services.agent.skill_settings import resolve_agent_skill_set
50
48
  from app.services.agent.token_estimation import estimate_input_tokens
51
49
  from app.services.agent.tools import read_only_agent_tools
52
50
  from app.services.agent.trace import agent_trace_store
51
+ from app.services.agent.menu_actions import MenuResult, menu_action_store
53
52
  from app.services.client_state_service import load_client_preferences
54
53
  from app.services.markdown_rendering import render_markdown_to_html
55
- from app.services.managed_ollama_runtime import ManagedOllamaRuntimeError
56
- from app.services.managed_ollama_runtime import managed_ollama_runtime
57
54
  from app.services.note_store import store as note_store
58
- from app.services.ollama_provider import OllamaProviderError
59
- from app.services.ollama_provider import ollama_provider
60
- from app.services.ollama_provider import resolve_ollama_think_value
61
- from app.services.ollama_provider import validate_ollama_model
62
55
  from app.services.openai_credentials import OpenAICredentialInputError
63
56
  from app.services.openai_credentials import openai_credential_store
64
57
  from app.services.openai_credentials import validate_openai_api_key
@@ -89,17 +82,10 @@ def _agent_runtime(*, inference: InferenceAdapter) -> AgentRuntime:
89
82
  )
90
83
 
91
84
 
92
- agent_runtime = _agent_runtime(
93
- inference=OllamaInferenceAdapter(provider=ollama_provider)
94
- )
95
-
96
-
97
-
98
-
99
85
  class AiModelsRequest(BaseModel):
100
86
  model_config = ConfigDict(extra="forbid")
101
87
 
102
- provider: Literal["ollama", "openai"]
88
+ provider: Literal["openai"]
103
89
 
104
90
 
105
91
  class AiModelsResponse(BaseModel):
@@ -128,7 +114,7 @@ class OpenAICostResponse(BaseModel):
128
114
  class CloudPrivacyPreviewRequest(BaseModel):
129
115
  model_config = ConfigDict(extra="forbid")
130
116
 
131
- provider: Literal["ollama", "openai"]
117
+ provider: Literal["openai"]
132
118
  note_ids: list[str] = Field(..., max_length=20_000)
133
119
 
134
120
  @field_validator("note_ids")
@@ -163,27 +149,16 @@ class AiPromptDefaultsResponse(BaseModel):
163
149
  skills: list[AiSkillDefaultResponse]
164
150
 
165
151
 
166
- class AiModelPullRequest(BaseModel):
167
- model_config = ConfigDict(extra="forbid")
168
-
169
- provider: Literal["ollama"]
170
- model: str
171
-
172
- @field_validator("model")
173
- @classmethod
174
- def validate_model(cls, value: str) -> str:
175
- return validate_ollama_model(value)
176
-
177
-
178
152
  class AiChatRequest(BaseModel):
179
153
  model_config = ConfigDict(extra="forbid")
180
154
 
181
- provider: Literal["ollama", "openai"]
155
+ provider: Literal["openai"]
182
156
  model: str
183
157
  thinking_level: Literal["off", "low", "medium", "high"]
184
158
  show_diagnostics: bool
185
159
  message: str = Field(..., max_length=32_000)
186
160
  scope: AgentScopeDescriptor
161
+ selected_note_id: str = Field(..., max_length=128, pattern=r"^(?:\S+)?$")
187
162
 
188
163
  @field_validator("model")
189
164
  @classmethod
@@ -202,14 +177,7 @@ class AiChatRequest(BaseModel):
202
177
 
203
178
  @model_validator(mode="after")
204
179
  def validate_thinking_level_for_model(self) -> Self:
205
- if self.provider == "ollama":
206
- validate_ollama_model(self.model)
207
- resolve_ollama_think_value(
208
- model=self.model,
209
- thinking_level=self.thinking_level,
210
- )
211
- else:
212
- validate_openai_model(self.model)
180
+ validate_openai_model(self.model)
213
181
  return self
214
182
 
215
183
 
@@ -239,7 +207,7 @@ class AiChatMessage(BaseModel):
239
207
  rendered_thinking: str
240
208
  status: Literal["complete", "streaming", "error"]
241
209
  error: str
242
- provider: Literal["ollama", "openai"]
210
+ provider: Literal["openai"]
243
211
  model: str
244
212
  activities: list[AiChatActivity]
245
213
 
@@ -308,15 +276,8 @@ async def list_ai_models(
308
276
  payload: AiModelsRequest,
309
277
  token: Annotated[str, Depends(require_request_auth_token)],
310
278
  ) -> AiModelsResponse:
311
- del token
312
- if payload.provider == "openai":
313
- return AiModelsResponse(models=list(OPENAI_MODELS))
314
- try:
315
- runtime_info = await asyncio.to_thread(managed_ollama_runtime.ensure_running)
316
- models = await ollama_provider.list_models(base_url=runtime_info.base_url)
317
- except (ManagedOllamaRuntimeError, OllamaProviderError) as exc:
318
- raise HTTPException(status_code=502, detail=str(exc)) from exc
319
- return AiModelsResponse(models=models)
279
+ del token, payload
280
+ return AiModelsResponse(models=list(OPENAI_MODELS))
320
281
 
321
282
 
322
283
  @router.get(
@@ -476,38 +437,6 @@ def preview_cloud_privacy(
476
437
  )
477
438
 
478
439
 
479
- @router.post("/models/pull")
480
- @transactional_route
481
- def pull_ai_model(
482
- payload: AiModelPullRequest,
483
- token: Annotated[str, Depends(require_request_auth_token)],
484
- ) -> StreamingResponse:
485
- del token
486
-
487
- async def stream_events() -> AsyncIterator[str]:
488
- try:
489
- runtime_info = await asyncio.to_thread(managed_ollama_runtime.ensure_running)
490
- async for event in ollama_provider.stream_pull(
491
- base_url=runtime_info.base_url,
492
- model=payload.model,
493
- ):
494
- yield f"{json.dumps(event, separators=(',', ':'))}\n"
495
- # lint: allow-PY001 rationale="pull errors must be delivered after response headers are sent"
496
- except (ManagedOllamaRuntimeError, OllamaProviderError) as exc:
497
- error_event = {"type": "error", "message": str(exc)}
498
- yield f"{json.dumps(error_event, separators=(',', ':'))}\n"
499
-
500
- return StreamingResponse(
501
- stream_events(),
502
- media_type="application/x-ndjson",
503
- headers={
504
- "X-Accel-Buffering": "no",
505
- "Cache-Control": "no-store",
506
- "Content-Encoding": "identity",
507
- },
508
- )
509
-
510
-
511
440
  @router.get("/session", response_model=AiSessionResponse)
512
441
  def get_ai_session(
513
442
  response: Response,
@@ -589,6 +518,17 @@ def get_ai_debug_snapshot(
589
518
  )
590
519
 
591
520
 
521
+ @router.get("/history")
522
+ def get_ai_history(
523
+ response: Response,
524
+ token: Annotated[str, Depends(require_request_auth_token)],
525
+ ) -> list:
526
+ response.headers["Cache-Control"] = "no-store"
527
+ return agent_trace_store.export_history(
528
+ session_key=token_service.get_session_key(token)
529
+ )
530
+
531
+
592
532
  @router.put("/debug", response_model=AiDebugSnapshotResponse)
593
533
  @transactional_route
594
534
  def put_ai_debug_details(
@@ -666,21 +606,19 @@ def stream_ai_chat(
666
606
  token: Annotated[str, Depends(require_request_auth_token)],
667
607
  ) -> StreamingResponse:
668
608
  session_key = token_service.get_session_key(token)
669
- openai_api_key = ""
670
- if payload.provider == "openai":
671
- credential_status = openai_credential_store.status(
672
- token=token,
673
- session_key=session_key,
674
- )
675
- if not credential_status.configured:
676
- raise HTTPException(
677
- status_code=409,
678
- detail="OpenAI API key is not configured",
679
- )
680
- openai_api_key = openai_credential_store.resolve(
681
- token=token,
682
- session_key=session_key,
609
+ credential_status = openai_credential_store.status(
610
+ token=token,
611
+ session_key=session_key,
612
+ )
613
+ if not credential_status.configured:
614
+ raise HTTPException(
615
+ status_code=409,
616
+ detail="OpenAI API key is not configured",
683
617
  )
618
+ openai_api_key = openai_credential_store.resolve(
619
+ token=token,
620
+ session_key=session_key,
621
+ )
684
622
  preferences = load_client_preferences(token=token)
685
623
  prompts = resolve_agent_prompt_set(preferences=preferences)
686
624
  skills = resolve_agent_skill_set(preferences=preferences)
@@ -704,6 +642,11 @@ def stream_ai_chat(
704
642
  authoritative_sort_mode = tab_state_store.get_sort_mode(
705
643
  tab_id=payload.scope.scope_tab_id
706
644
  )
645
+ active_search_query = authoritative_search_query
646
+ active_sort_mode = authoritative_sort_mode
647
+ if payload.selected_note_id and payload.scope.active_tab_id != payload.scope.scope_tab_id:
648
+ active_search_query = tab_state_store.get_search_query(tab_id=authoritative_active_tab_id)
649
+ active_sort_mode = tab_state_store.get_sort_mode(tab_id=authoritative_active_tab_id)
707
650
  frozen_scope = scoped_search_snapshot_factory.freeze(
708
651
  descriptor=payload.scope,
709
652
  authoritative_search_query=authoritative_search_query,
@@ -711,6 +654,9 @@ def stream_ai_chat(
711
654
  run_id=str(uuid4()),
712
655
  session_key=session_key,
713
656
  privacy_boundary=privacy_boundary,
657
+ selected_note_id=payload.selected_note_id,
658
+ authoritative_active_search_query=active_search_query,
659
+ authoritative_active_sort_mode=active_sort_mode,
714
660
  )
715
661
  tagging_run = TaggingRun(token=token, snapshot=frozen_scope, preferences=preferences, sync_uuid=get_current_sync_uuid(),
716
662
  batch_tokens=resolve_tagging_batch_tokens(preferences, payload.provider))
@@ -758,18 +704,9 @@ async def _stream_runtime_events(
758
704
  action, start_label = 'provider_runtime', 'Connecting to OpenAI API'
759
705
  ready_label = 'OpenAI API ready · 1,050,000-token context'
760
706
  base_url = OPENAI_API_BASE_URL
761
- if payload.provider == 'ollama':
762
- action = 'ollama_runtime'
763
- start_label = 'Starting MetaList-managed Ollama · 32,768-token context'
764
- runtime = agent_runtime
765
- else:
766
- runtime = _agent_runtime(inference=OpenAIInferenceAdapter(api_key=openai_api_key, cost_tracker=openai_cost_tracker))
707
+ runtime = _agent_runtime(inference=OpenAIInferenceAdapter(api_key=openai_api_key, cost_tracker=openai_cost_tracker))
767
708
  yield dict(type='action_status', action=action, status='started', label=start_label,
768
709
  approx_input_tokens=initial_input_tokens, output_tokens_received=0, duration_ms=0.0)
769
- if payload.provider == 'ollama':
770
- runtime_info = await asyncio.to_thread(managed_ollama_runtime.ensure_running)
771
- base_url = runtime_info.base_url
772
- ready_label = f'MetaList-managed Ollama ready · {runtime_info.context_tokens:,}-token context'
773
710
  yield dict(type='action_status', action=action, status='completed', label=ready_label,
774
711
  approx_input_tokens=initial_input_tokens, output_tokens_received=0, duration_ms=0.0)
775
712
  events = runtime.stream_scoped(session_key=session_key, base_url=base_url,
@@ -783,6 +720,18 @@ async def _stream_runtime_events(
783
720
  await events.aclose()
784
721
 
785
722
 
723
+ @router.post("/menu-result")
724
+ @transactional_route
725
+ async def acknowledge_menu_result(payload: MenuResult, token: Annotated[str, Depends(require_request_auth_token)]):
726
+ # lint: allow-PY001 rationale="browser acknowledgments can be stale or belong to another session"
727
+ try:
728
+ menu_action_store.acknowledge(session_key=token_service.get_session_key(token), result=payload)
729
+ # lint: allow-PY001 rationale="reject invalid external menu acknowledgment with a conflict response"
730
+ except ValueError as exc:
731
+ raise HTTPException(status_code=409, detail=str(exc)) from exc
732
+ return {"acknowledged": True}
733
+
734
+
786
735
  class BulkAnswerRequest(BaseModel):
787
736
  model_config = ConfigDict(extra="forbid")
788
737
  question_id: str = Field(..., min_length=1)
@@ -78,9 +78,9 @@ CAPTURE_BOUNDARIES = {
78
78
  # Translate explicitly validated domain input/resource rejection.
79
79
  'app/main.py:namespace_renamed_page:job_record_capture': ('NamespaceInputRejected', 'FileNotFoundError'),
80
80
  # Parse external host, URL, IP or date input at the named boundary.
81
- 'app/security/request_boundary.py:_normalize_configured_hostname:normalize_capture': ('ValueError',),
81
+ 'app/security/request_hosts.py:_normalize_configured_hostname:normalize_capture': ('ValueError',),
82
82
  # Parse external host, URL, IP or date input at the named boundary.
83
- 'app/security/request_boundary.py:_normalize_hostname:ip_capture': ('ValueError',),
83
+ 'app/security/request_hosts.py:_normalize_hostname:ip_capture': ('ValueError',),
84
84
  # Parse external host, URL, IP or date input at the named boundary.
85
85
  'app/security/request_boundary.py:_parse_host_header:parse_capture': ('ValueError',),
86
86
  # Parse external host, URL, IP or date input at the named boundary.
@@ -112,16 +112,6 @@ CAPTURE_BOUNDARIES = {
112
112
  # Handle expected external I/O, provider, image or LaTeX parsing failure.
113
113
  'app/services/link_titles.py:fetch_link_title:target_capture': ('_LinkTitleTargetRejected',),
114
114
  # Handle expected external I/O, provider, image or LaTeX parsing failure.
115
- 'app/services/managed_ollama_runtime.py:_acquire_startup_lock:lock_capture': ('FileExistsError',),
116
- # Handle expected external I/O, provider, image or LaTeX parsing failure.
117
- 'app/services/managed_ollama_runtime.py:_is_process_running:process_capture': ('ProcessLookupError', 'PermissionError'),
118
- # Handle expected external I/O, provider, image or LaTeX parsing failure.
119
- 'app/services/managed_ollama_runtime.py:_probe_ollama_version:probe_capture': ('HTTPError', 'JSONDecodeError'),
120
- # Handle expected external I/O, provider, image or LaTeX parsing failure.
121
- 'app/services/managed_ollama_runtime.py:_remove_stale_lock:missing_capture': ('FileNotFoundError',),
122
- # Handle expected external I/O, provider, image or LaTeX parsing failure.
123
- 'app/services/managed_ollama_runtime.py:_remove_stale_lock:read_capture': ('FileNotFoundError',),
124
- # Handle expected external I/O, provider, image or LaTeX parsing failure.
125
115
  'app/services/namespace_deletion_worker.py:_is_process_running:kill_capture': ('ProcessLookupError', 'PermissionError'),
126
116
  # Handle expected external I/O, provider, image or LaTeX parsing failure.
127
117
  'app/services/namespace_deletion_worker.py:_send_signal_if_running:signal_capture': ('ProcessLookupError',),
@@ -0,0 +1,61 @@
1
+ """Machine network preferences shared by source and installed entrypoints.
2
+
3
+ Only an explicit bind address/allowed-host configuration opts into persistence.
4
+ Namespace ports, shell capability, credentials, and backups are not stored here.
5
+ """
6
+ from __future__ import annotations
7
+
8
+ from collections.abc import MutableMapping
9
+ import json
10
+ import os
11
+ from pathlib import Path
12
+ from uuid import uuid4
13
+
14
+ from app.data_directory import resolve_data_directory
15
+ from app.security.request_hosts import resolve_allowed_request_hosts
16
+
17
+
18
+ _NETWORK_ENVIRONMENT_KEYS = frozenset({'METALIST_HOST', 'METALIST_ALLOWED_HOSTS'})
19
+
20
+
21
+ def _validate_network_environment(environment: object) -> dict[str, str]:
22
+ if not isinstance(environment, dict) or not environment.keys() <= _NETWORK_ENVIRONMENT_KEYS:
23
+ raise RuntimeError('Network settings must contain only bind-address and allowed-host preferences')
24
+ if any(not isinstance(value, str) for value in environment.values()):
25
+ raise RuntimeError('Network settings values must be strings')
26
+ resolve_allowed_request_hosts(environ=environment)
27
+ return dict(environment)
28
+
29
+
30
+ def _read_network_settings(path: Path) -> dict[str, str]:
31
+ if not path.exists():
32
+ return {}
33
+ payload = json.loads(path.read_text(encoding='utf-8'))
34
+ if (not isinstance(payload, dict) or set(payload) != {'version', 'environment'}
35
+ or type(payload['version']) is not int or payload['version'] != 1):
36
+ raise RuntimeError(f'Invalid network settings file: {path}')
37
+ return _validate_network_environment(payload['environment'])
38
+
39
+
40
+ def _write_network_settings(path: Path, environment: dict[str, str]) -> None:
41
+ assert path.name == 'network-settings.json'
42
+ path.parent.mkdir(parents=True, exist_ok=True)
43
+ temporary = path.with_name(f'.network-settings-{uuid4().hex}.tmp')
44
+ try:
45
+ with temporary.open('x', encoding='utf-8') as stream:
46
+ json.dump({'version': 1, 'environment': environment}, stream, indent=2)
47
+ stream.write('\n')
48
+ os.replace(temporary, path)
49
+ finally:
50
+ temporary.unlink(missing_ok=True)
51
+
52
+
53
+ def apply_network_settings(*, environ: MutableMapping[str, str], persist: bool) -> None:
54
+ path = resolve_data_directory(environ=environ) / 'network-settings.json'
55
+ saved = _read_network_settings(path)
56
+ configured = {key: environ[key] for key in _NETWORK_ENVIRONMENT_KEYS if key in environ}
57
+ effective = _validate_network_environment(saved | configured)
58
+ if persist and effective != saved:
59
+ _write_network_settings(path, effective)
60
+ print(f'[startup] Saved network settings: {path}', flush=True)
61
+ environ.update(effective)
@@ -1,9 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
- from collections.abc import Mapping
4
3
  from dataclasses import dataclass
5
- import ipaddress
6
- import re
7
4
  from urllib.parse import urlsplit
8
5
 
9
6
  from fastapi import Request
@@ -12,14 +9,10 @@ from starlette.middleware.base import BaseHTTPMiddleware
12
9
 
13
10
  from app.api.request_auth import AUTH_COOKIE_NAME
14
11
  from app.services.exception_capture import CapturedExceptionContext
12
+ from app.security.request_hosts import _normalize_hostname, resolve_allowed_request_hosts
15
13
 
16
14
 
17
- _LOOPBACK_REQUEST_HOSTS = frozenset({"127.0.0.1", "::1", "localhost"})
18
- _WILDCARD_BIND_HOSTS = frozenset({"0.0.0.0", "::"})
19
15
  _SAFE_METHODS = frozenset({"GET", "HEAD", "OPTIONS"})
20
- _DNS_HOSTNAME_PATTERN = re.compile(
21
- r"^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?(?:\.[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?)*$"
22
- )
23
16
 
24
17
 
25
18
  @dataclass(frozen=True)
@@ -28,72 +21,6 @@ class RequestBoundaryRejection:
28
21
  detail: str
29
22
 
30
23
 
31
- def _normalize_hostname(*, hostname: str, context: str) -> str:
32
- normalized = hostname.strip().casefold().rstrip(".")
33
- if normalized == "":
34
- raise ValueError(f"{context} hostname must not be empty")
35
-
36
- ip_capture = CapturedExceptionContext(ValueError, boundary='app/security/request_boundary.py:_normalize_hostname:ip_capture')
37
- parsed_ip: ipaddress._BaseAddress | None = None
38
- with ip_capture:
39
- parsed_ip = ipaddress.ip_address(normalized)
40
- if ip_capture.captured_exception is None:
41
- if parsed_ip is None:
42
- raise RuntimeError("IP parser returned no address")
43
- return parsed_ip.compressed.casefold()
44
-
45
- if _DNS_HOSTNAME_PATTERN.fullmatch(normalized) is None:
46
- raise ValueError(f"{context} contains an invalid hostname: {hostname!r}")
47
- return normalized
48
-
49
-
50
- def _normalize_configured_hostname(*, raw_value: str) -> str:
51
- value = raw_value.strip()
52
- if value == "":
53
- raise RuntimeError("METALIST_ALLOWED_HOSTS contains an empty entry")
54
- if value == "*":
55
- raise RuntimeError("METALIST_ALLOWED_HOSTS must not contain '*'")
56
- if "://" in value or "/" in value or "@" in value:
57
- raise RuntimeError(
58
- "METALIST_ALLOWED_HOSTS entries must be hostnames without schemes, ports, or paths"
59
- )
60
-
61
- unbracketed_value = value
62
- if value.startswith("[") and value.endswith("]"):
63
- unbracketed_value = value[1:-1]
64
- normalize_capture = CapturedExceptionContext(ValueError, boundary='app/security/request_boundary.py:_normalize_configured_hostname:normalize_capture')
65
- normalized_hostname: str | None = None
66
- with normalize_capture:
67
- normalized_hostname = _normalize_hostname(
68
- hostname=unbracketed_value,
69
- context="METALIST_ALLOWED_HOSTS",
70
- )
71
- if normalize_capture.captured_exception is not None:
72
- exc = normalize_capture.captured_exception
73
- raise RuntimeError(str(exc)) from exc
74
- if normalized_hostname is None:
75
- raise RuntimeError("METALIST_ALLOWED_HOSTS parser returned no hostname")
76
- return normalized_hostname
77
-
78
-
79
- def resolve_allowed_request_hosts(*, environ: Mapping[str, str]) -> frozenset[str]:
80
- allowed_hosts = set(_LOOPBACK_REQUEST_HOSTS)
81
- bind_host = environ.get("METALIST_HOST", "127.0.0.1").strip()
82
- if bind_host == "":
83
- raise RuntimeError("METALIST_HOST must not be empty")
84
- normalized_bind_host = _normalize_configured_hostname(raw_value=bind_host)
85
- if normalized_bind_host not in _WILDCARD_BIND_HOSTS:
86
- allowed_hosts.add(normalized_bind_host)
87
-
88
- configured_hosts = environ.get("METALIST_ALLOWED_HOSTS")
89
- if configured_hosts is not None:
90
- if configured_hosts.strip() == "":
91
- raise RuntimeError("METALIST_ALLOWED_HOSTS must not be empty when set")
92
- for raw_host in configured_hosts.split(","):
93
- allowed_hosts.add(_normalize_configured_hostname(raw_value=raw_host))
94
- return frozenset(allowed_hosts)
95
-
96
-
97
24
  def _parse_host_header(host_header: str) -> tuple[str, int | None] | None:
98
25
  raw_host = host_header.strip()
99
26
  if raw_host == "":
@@ -0,0 +1,83 @@
1
+ """Host validation shared by early launch settings and HTTP request checks."""
2
+ from __future__ import annotations
3
+
4
+ from collections.abc import Mapping
5
+ import ipaddress
6
+ import re
7
+
8
+ from app.services.exception_capture import CapturedExceptionContext
9
+
10
+
11
+ _LOOPBACK_REQUEST_HOSTS = frozenset({"127.0.0.1", "::1", "localhost"})
12
+ _WILDCARD_BIND_HOSTS = frozenset({"0.0.0.0", "::"})
13
+ _DNS_HOSTNAME_PATTERN = re.compile(
14
+ r"^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?(?:\.[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?)*$"
15
+ )
16
+
17
+
18
+ def _normalize_hostname(*, hostname: str, context: str) -> str:
19
+ normalized = hostname.strip().casefold().rstrip(".")
20
+ if normalized == "":
21
+ raise ValueError(f"{context} hostname must not be empty")
22
+
23
+ ip_capture = CapturedExceptionContext(ValueError, boundary='app/security/request_hosts.py:_normalize_hostname:ip_capture')
24
+ parsed_ip: ipaddress._BaseAddress | None = None
25
+ with ip_capture:
26
+ parsed_ip = ipaddress.ip_address(normalized)
27
+ if ip_capture.captured_exception is None:
28
+ if parsed_ip is None:
29
+ raise RuntimeError("IP parser returned no address")
30
+ return parsed_ip.compressed.casefold()
31
+
32
+ if _DNS_HOSTNAME_PATTERN.fullmatch(normalized) is None:
33
+ raise ValueError(f"{context} contains an invalid hostname: {hostname!r}")
34
+ return normalized
35
+
36
+
37
+ def _normalize_configured_hostname(*, raw_value: str) -> str:
38
+ value = raw_value.strip()
39
+ if value == "":
40
+ raise RuntimeError("METALIST_ALLOWED_HOSTS contains an empty entry")
41
+ if value == "*":
42
+ raise RuntimeError("METALIST_ALLOWED_HOSTS must not contain '*'")
43
+ if "://" in value or "/" in value or "@" in value:
44
+ raise RuntimeError(
45
+ "METALIST_ALLOWED_HOSTS entries must be hostnames without schemes, ports, or paths"
46
+ )
47
+
48
+ unbracketed_value = value
49
+ if value.startswith("[") and value.endswith("]"):
50
+ unbracketed_value = value[1:-1]
51
+ normalize_capture = CapturedExceptionContext(ValueError, boundary='app/security/request_hosts.py:_normalize_configured_hostname:normalize_capture')
52
+ normalized_hostname: str | None = None
53
+ with normalize_capture:
54
+ normalized_hostname = _normalize_hostname(
55
+ hostname=unbracketed_value,
56
+ context="METALIST_ALLOWED_HOSTS",
57
+ )
58
+ if normalize_capture.captured_exception is not None:
59
+ exc = normalize_capture.captured_exception
60
+ raise RuntimeError(str(exc)) from exc
61
+ if normalized_hostname is None:
62
+ raise RuntimeError("METALIST_ALLOWED_HOSTS parser returned no hostname")
63
+ return normalized_hostname
64
+
65
+
66
+ def resolve_allowed_request_hosts(*, environ: Mapping[str, str]) -> frozenset[str]:
67
+ allowed_hosts = set(_LOOPBACK_REQUEST_HOSTS)
68
+ bind_host = environ.get("METALIST_HOST", "127.0.0.1").strip()
69
+ if bind_host == "":
70
+ raise RuntimeError("METALIST_HOST must not be empty")
71
+ normalized_bind_host = _normalize_configured_hostname(raw_value=bind_host)
72
+ if normalized_bind_host not in _WILDCARD_BIND_HOSTS:
73
+ allowed_hosts.add(normalized_bind_host)
74
+
75
+ configured_hosts = environ.get("METALIST_ALLOWED_HOSTS")
76
+ if configured_hosts is not None:
77
+ if configured_hosts.strip() == "":
78
+ raise RuntimeError("METALIST_ALLOWED_HOSTS must not be empty when set")
79
+ for raw_host in configured_hosts.split(","):
80
+ allowed_hosts.add(_normalize_configured_hostname(raw_value=raw_host))
81
+ return frozenset(allowed_hosts)
82
+
83
+