MetaList 0.18.0__tar.gz → 0.20.0__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 (569) hide show
  1. {metalist-0.18.0/metalist.egg-info → metalist-0.20.0}/PKG-INFO +21 -1
  2. {metalist-0.18.0 → metalist-0.20.0}/README.md +20 -0
  3. {metalist-0.18.0 → metalist-0.20.0}/app/api/note_requests.py +43 -3
  4. {metalist-0.18.0 → metalist-0.20.0}/app/api/routes/ai.py +275 -21
  5. {metalist-0.18.0 → metalist-0.20.0}/app/api/routes/notes.py +157 -18
  6. {metalist-0.18.0 → metalist-0.20.0}/app/exception_boundaries.py +1 -0
  7. {metalist-0.18.0 → metalist-0.20.0}/app/presentation/render/note_renderer.py +1 -1
  8. {metalist-0.18.0 → metalist-0.20.0}/app/services/agent/cloud_privacy.py +52 -19
  9. metalist-0.20.0/app/services/agent/export_title_suggestions.py +187 -0
  10. {metalist-0.18.0 → metalist-0.20.0}/app/services/agent/failure_explanations.py +1 -0
  11. metalist-0.20.0/app/services/agent/note_tag_suggestions.py +318 -0
  12. {metalist-0.18.0 → metalist-0.20.0}/app/services/agent/scope.py +4 -2
  13. {metalist-0.18.0 → metalist-0.20.0}/app/services/agent/skills/help-ai.md +32 -0
  14. {metalist-0.18.0 → metalist-0.20.0}/app/services/agent/skills/help-data.md +9 -2
  15. {metalist-0.18.0 → metalist-0.20.0}/app/services/agent/skills/help-formatting.md +22 -6
  16. {metalist-0.18.0 → metalist-0.20.0}/app/services/agent/skills/help-menus.md +2 -1
  17. {metalist-0.18.0 → metalist-0.20.0}/app/services/agent/skills/help-notes.md +4 -3
  18. {metalist-0.18.0 → metalist-0.20.0}/app/services/agent/skills/help-privacy.md +6 -4
  19. {metalist-0.18.0 → metalist-0.20.0}/app/services/agent/skills/help-releases.md +20 -0
  20. {metalist-0.18.0 → metalist-0.20.0}/app/services/agent/skills/help-search.md +2 -1
  21. {metalist-0.18.0 → metalist-0.20.0}/app/services/agent/skills/help-tags.md +14 -9
  22. {metalist-0.18.0 → metalist-0.20.0}/app/services/agent/structured_inference.py +2 -0
  23. {metalist-0.18.0 → metalist-0.20.0}/app/services/agent/tagging_run.py +43 -25
  24. {metalist-0.18.0 → metalist-0.20.0}/app/services/ai_chat_rendering.py +2 -2
  25. metalist-0.20.0/app/services/ai_setup.py +84 -0
  26. {metalist-0.18.0 → metalist-0.20.0}/app/services/code_rendering.py +28 -2
  27. {metalist-0.18.0 → metalist-0.20.0}/app/services/content_formatting.py +40 -20
  28. {metalist-0.18.0 → metalist-0.20.0}/app/services/diagnostics.py +7 -1
  29. {metalist-0.18.0 → metalist-0.20.0}/app/services/embedded_references.py +51 -49
  30. metalist-0.20.0/app/services/html_export.py +652 -0
  31. {metalist-0.18.0 → metalist-0.20.0}/app/services/markdown_rendering.py +5 -0
  32. {metalist-0.18.0 → metalist-0.20.0}/app/services/note_fullscreen.py +1 -1
  33. {metalist-0.18.0 → metalist-0.20.0}/app/services/note_store.py +21 -0
  34. {metalist-0.18.0 → metalist-0.20.0}/app/services/search_history.py +25 -0
  35. {metalist-0.18.0 → metalist-0.20.0}/app/services/search_index.py +61 -17
  36. {metalist-0.18.0 → metalist-0.20.0}/app/services/snapshot.py +23 -20
  37. metalist-0.20.0/app/services/tag_details.py +224 -0
  38. {metalist-0.18.0 → metalist-0.20.0}/app/services/tag_suggestion_reasons.py +130 -66
  39. {metalist-0.18.0 → metalist-0.20.0}/app/services/tag_suggestions.py +19 -6
  40. {metalist-0.18.0 → metalist-0.20.0}/app/services/tag_term_matching.py +57 -0
  41. {metalist-0.18.0 → metalist-0.20.0}/app/services/undo_state.py +88 -1
  42. {metalist-0.18.0 → metalist-0.20.0}/app/services/windows_process_control.py +6 -2
  43. {metalist-0.18.0 → metalist-0.20.0}/app/startup_sanity_config.py +2 -0
  44. {metalist-0.18.0 → metalist-0.20.0}/app/static/config/agent-menu-actions.json +6 -0
  45. {metalist-0.18.0 → metalist-0.20.0}/app/static/config/command_palette_tags.json +4 -0
  46. {metalist-0.18.0 → metalist-0.20.0}/app/static/css/main.css +322 -33
  47. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/ai-chat/ai-chat-api.js +157 -0
  48. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/api-client.js +172 -23
  49. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/command-palette/command-palette-controller.js +12 -65
  50. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/command-palette/endpoint-registry.js +7 -0
  51. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/config.js +10 -1
  52. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/context-menu/context-menu-registry.js +80 -19
  53. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/context-menu/context-menu-service.js +24 -0
  54. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/context-menu/input-caret-service.js +15 -3
  55. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/error-handler.js +3 -1
  56. metalist-0.20.0/app/static/js/modules/modals/export-options-modal.js +314 -0
  57. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/actions/content-actions.js +3 -1
  58. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/actions/note-actions.js +2 -1
  59. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/actions/ui-actions.js +15 -1
  60. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/events/context-menu-events.js +460 -299
  61. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/events/keyboard-events.js +168 -10
  62. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/events/mouse-events.js +92 -6
  63. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/command-gate-service.js +12 -0
  64. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/differential-view-service.js +10 -0
  65. metalist-0.20.0/app/static/js/modules/mode-manager/services/export-service.js +64 -0
  66. metalist-0.20.0/app/static/js/modules/mode-manager/services/export-title-service.js +59 -0
  67. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/paste-format-detection-service.js +127 -1
  68. metalist-0.20.0/app/static/js/modules/mode-manager/services/paste-format-guess-service.js +317 -0
  69. metalist-0.20.0/app/static/js/modules/mode-manager/services/paste-format-offer-service.js +185 -0
  70. metalist-0.20.0/app/static/js/modules/mode-manager/services/print-service.js +82 -0
  71. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/tag-bar-service.js +34 -1
  72. metalist-0.20.0/app/static/js/modules/mode-manager/services/tag-proposal-service.js +359 -0
  73. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/viewport-hold-service.js +72 -0
  74. {metalist-0.18.0 → metalist-0.20.0}/app/usecases/prefetch_link_titles.py +1 -1
  75. metalist-0.20.0/app/usecases/tag_proposals.py +390 -0
  76. {metalist-0.18.0 → metalist-0.20.0}/app/version.py +1 -1
  77. {metalist-0.18.0 → metalist-0.20.0}/main.py +24 -1
  78. {metalist-0.18.0 → metalist-0.20.0/metalist.egg-info}/PKG-INFO +21 -1
  79. {metalist-0.18.0 → metalist-0.20.0}/metalist.egg-info/SOURCES.txt +10 -0
  80. metalist-0.18.0/app/services/html_export.py +0 -327
  81. metalist-0.18.0/app/static/js/modules/mode-manager/services/tag-proposal-service.js +0 -141
  82. metalist-0.18.0/app/usecases/tag_proposals.py +0 -236
  83. {metalist-0.18.0 → metalist-0.20.0}/MANIFEST.in +0 -0
  84. {metalist-0.18.0 → metalist-0.20.0}/app/__init__.py +0 -0
  85. {metalist-0.18.0 → metalist-0.20.0}/app/api/__init__.py +0 -0
  86. {metalist-0.18.0 → metalist-0.20.0}/app/api/deps.py +0 -0
  87. {metalist-0.18.0 → metalist-0.20.0}/app/api/dev.py +0 -0
  88. {metalist-0.18.0 → metalist-0.20.0}/app/api/middleware/auth.py +0 -0
  89. {metalist-0.18.0 → metalist-0.20.0}/app/api/request_auth.py +0 -0
  90. {metalist-0.18.0 → metalist-0.20.0}/app/api/routes/app_updates.py +0 -0
  91. {metalist-0.18.0 → metalist-0.20.0}/app/api/routes/auth.py +0 -0
  92. {metalist-0.18.0 → metalist-0.20.0}/app/api/routes/backups.py +0 -0
  93. {metalist-0.18.0 → metalist-0.20.0}/app/api/routes/files.py +0 -0
  94. {metalist-0.18.0 → metalist-0.20.0}/app/api/routes/ontology.py +0 -0
  95. {metalist-0.18.0 → metalist-0.20.0}/app/api/routes/reminders.py +0 -0
  96. {metalist-0.18.0 → metalist-0.20.0}/app/api/routes/remote_images.py +0 -0
  97. {metalist-0.18.0 → metalist-0.20.0}/app/api/routes/test.py +0 -0
  98. {metalist-0.18.0 → metalist-0.20.0}/app/api/transactions.py +0 -0
  99. {metalist-0.18.0 → metalist-0.20.0}/app/api/upload_limits.py +0 -0
  100. {metalist-0.18.0 → metalist-0.20.0}/app/config.py +0 -0
  101. {metalist-0.18.0 → metalist-0.20.0}/app/data_directory.py +0 -0
  102. {metalist-0.18.0 → metalist-0.20.0}/app/db/__init__.py +0 -0
  103. {metalist-0.18.0 → metalist-0.20.0}/app/db/backup_settings_sql.py +0 -0
  104. {metalist-0.18.0 → metalist-0.20.0}/app/db/editable_files_migration.py +0 -0
  105. {metalist-0.18.0 → metalist-0.20.0}/app/db/engine.py +0 -0
  106. {metalist-0.18.0 → metalist-0.20.0}/app/db/file_schema.py +0 -0
  107. {metalist-0.18.0 → metalist-0.20.0}/app/db/file_session.py +0 -0
  108. {metalist-0.18.0 → metalist-0.20.0}/app/db/files_sql.py +0 -0
  109. {metalist-0.18.0 → metalist-0.20.0}/app/db/link_titles_sql.py +0 -0
  110. {metalist-0.18.0 → metalist-0.20.0}/app/db/live_recovery.py +0 -0
  111. {metalist-0.18.0 → metalist-0.20.0}/app/db/migrations.py +0 -0
  112. {metalist-0.18.0 → metalist-0.20.0}/app/db/notes_sql.py +0 -0
  113. {metalist-0.18.0 → metalist-0.20.0}/app/db/ontology_rules_sql.py +0 -0
  114. {metalist-0.18.0 → metalist-0.20.0}/app/db/reminders_sql.py +0 -0
  115. {metalist-0.18.0 → metalist-0.20.0}/app/db/retire_sounds.py +0 -0
  116. {metalist-0.18.0 → metalist-0.20.0}/app/db/schema.py +0 -0
  117. {metalist-0.18.0 → metalist-0.20.0}/app/db/search_history_sql.py +0 -0
  118. {metalist-0.18.0 → metalist-0.20.0}/app/db/session.py +0 -0
  119. {metalist-0.18.0 → metalist-0.20.0}/app/db/settings_sql.py +0 -0
  120. {metalist-0.18.0 → metalist-0.20.0}/app/db/tab_state_sql.py +0 -0
  121. {metalist-0.18.0 → metalist-0.20.0}/app/db/version.py +0 -0
  122. {metalist-0.18.0 → metalist-0.20.0}/app/encryption_audit.py +0 -0
  123. {metalist-0.18.0 → metalist-0.20.0}/app/https_proxy.py +0 -0
  124. {metalist-0.18.0 → metalist-0.20.0}/app/main.py +0 -0
  125. {metalist-0.18.0 → metalist-0.20.0}/app/models/commands.py +0 -0
  126. {metalist-0.18.0 → metalist-0.20.0}/app/models/database.py +0 -0
  127. {metalist-0.18.0 → metalist-0.20.0}/app/models/entities.py +0 -0
  128. {metalist-0.18.0 → metalist-0.20.0}/app/models/enums.py +0 -0
  129. {metalist-0.18.0 → metalist-0.20.0}/app/models/linked_list.py +0 -0
  130. {metalist-0.18.0 → metalist-0.20.0}/app/models/list_operations.py +0 -0
  131. {metalist-0.18.0 → metalist-0.20.0}/app/models/list_traversal.py +0 -0
  132. {metalist-0.18.0 → metalist-0.20.0}/app/models/note_crud.py +0 -0
  133. {metalist-0.18.0 → metalist-0.20.0}/app/models/utils.py +0 -0
  134. {metalist-0.18.0 → metalist-0.20.0}/app/network_settings.py +0 -0
  135. {metalist-0.18.0 → metalist-0.20.0}/app/presentation/__init__.py +0 -0
  136. {metalist-0.18.0 → metalist-0.20.0}/app/presentation/login_page.py +0 -0
  137. {metalist-0.18.0 → metalist-0.20.0}/app/presentation/render/__init__.py +0 -0
  138. {metalist-0.18.0 → metalist-0.20.0}/app/presentation/templates.py +0 -0
  139. {metalist-0.18.0 → metalist-0.20.0}/app/security/authenticated_logging.py +0 -0
  140. {metalist-0.18.0 → metalist-0.20.0}/app/security/encryption.py +0 -0
  141. {metalist-0.18.0 → metalist-0.20.0}/app/security/http_error_diagnostics.py +0 -0
  142. {metalist-0.18.0 → metalist-0.20.0}/app/security/http_headers.py +0 -0
  143. {metalist-0.18.0 → metalist-0.20.0}/app/security/note_html.py +0 -0
  144. {metalist-0.18.0 → metalist-0.20.0}/app/security/request_boundary.py +0 -0
  145. {metalist-0.18.0 → metalist-0.20.0}/app/security/request_hosts.py +0 -0
  146. {metalist-0.18.0 → metalist-0.20.0}/app/security/sensitive_cache.py +0 -0
  147. {metalist-0.18.0 → metalist-0.20.0}/app/security/sensitive_logging.py +0 -0
  148. {metalist-0.18.0 → metalist-0.20.0}/app/security/shell_execution.py +0 -0
  149. {metalist-0.18.0 → metalist-0.20.0}/app/security/validation_errors.py +0 -0
  150. {metalist-0.18.0 → metalist-0.20.0}/app/server_runtime.py +0 -0
  151. {metalist-0.18.0 → metalist-0.20.0}/app/services/__init__.py +0 -0
  152. {metalist-0.18.0 → metalist-0.20.0}/app/services/agent/__init__.py +0 -0
  153. {metalist-0.18.0 → metalist-0.20.0}/app/services/agent/action_log.py +0 -0
  154. {metalist-0.18.0 → metalist-0.20.0}/app/services/agent/agent_loop.py +0 -0
  155. {metalist-0.18.0 → metalist-0.20.0}/app/services/agent/agent_tools.py +0 -0
  156. {metalist-0.18.0 → metalist-0.20.0}/app/services/agent/citation_tokens.py +0 -0
  157. {metalist-0.18.0 → metalist-0.20.0}/app/services/agent/context.py +0 -0
  158. {metalist-0.18.0 → metalist-0.20.0}/app/services/agent/evidence_serialization.py +0 -0
  159. {metalist-0.18.0 → metalist-0.20.0}/app/services/agent/execution_errors.py +0 -0
  160. {metalist-0.18.0 → metalist-0.20.0}/app/services/agent/help_catalog.py +0 -0
  161. {metalist-0.18.0 → metalist-0.20.0}/app/services/agent/history.py +0 -0
  162. {metalist-0.18.0 → metalist-0.20.0}/app/services/agent/inference.py +0 -0
  163. {metalist-0.18.0 → metalist-0.20.0}/app/services/agent/investigation.py +0 -0
  164. {metalist-0.18.0 → metalist-0.20.0}/app/services/agent/judging.py +0 -0
  165. {metalist-0.18.0 → metalist-0.20.0}/app/services/agent/menu_actions.py +0 -0
  166. {metalist-0.18.0 → metalist-0.20.0}/app/services/agent/model_policy.py +0 -0
  167. {metalist-0.18.0 → metalist-0.20.0}/app/services/agent/note_aliases.py +0 -0
  168. {metalist-0.18.0 → metalist-0.20.0}/app/services/agent/openai_cost_tracking.py +0 -0
  169. {metalist-0.18.0 → metalist-0.20.0}/app/services/agent/openai_inference.py +0 -0
  170. {metalist-0.18.0 → metalist-0.20.0}/app/services/agent/prompt_settings.py +0 -0
  171. {metalist-0.18.0 → metalist-0.20.0}/app/services/agent/prompts/__init__.py +0 -0
  172. {metalist-0.18.0 → metalist-0.20.0}/app/services/agent/prompts/agent.md +0 -0
  173. {metalist-0.18.0 → metalist-0.20.0}/app/services/agent/prompts/final-response.md +0 -0
  174. {metalist-0.18.0 → metalist-0.20.0}/app/services/agent/prompts/system.md +0 -0
  175. {metalist-0.18.0 → metalist-0.20.0}/app/services/agent/release_notes.py +0 -0
  176. {metalist-0.18.0 → metalist-0.20.0}/app/services/agent/retrieval_settings.py +0 -0
  177. {metalist-0.18.0 → metalist-0.20.0}/app/services/agent/runtime.py +0 -0
  178. {metalist-0.18.0 → metalist-0.20.0}/app/services/agent/skill_settings.py +0 -0
  179. {metalist-0.18.0 → metalist-0.20.0}/app/services/agent/skills/__init__.py +0 -0
  180. {metalist-0.18.0 → metalist-0.20.0}/app/services/agent/skills/help-dictation.md +0 -0
  181. {metalist-0.18.0 → metalist-0.20.0}/app/services/agent/skills/help-references.md +0 -0
  182. {metalist-0.18.0 → metalist-0.20.0}/app/services/agent/skills/help-reminders.md +0 -0
  183. {metalist-0.18.0 → metalist-0.20.0}/app/services/agent/skills/staged-summary.md +0 -0
  184. {metalist-0.18.0 → metalist-0.20.0}/app/services/agent/skills/tag-proposals.md +0 -0
  185. {metalist-0.18.0 → metalist-0.20.0}/app/services/agent/skills/web-browsing.md +0 -0
  186. {metalist-0.18.0 → metalist-0.20.0}/app/services/agent/staged_summary.py +0 -0
  187. {metalist-0.18.0 → metalist-0.20.0}/app/services/agent/strict_structured_outputs.py +0 -0
  188. {metalist-0.18.0 → metalist-0.20.0}/app/services/agent/summary_batch_store.py +0 -0
  189. {metalist-0.18.0 → metalist-0.20.0}/app/services/agent/tagging.py +0 -0
  190. {metalist-0.18.0 → metalist-0.20.0}/app/services/agent/token_estimation.py +0 -0
  191. {metalist-0.18.0 → metalist-0.20.0}/app/services/agent/tool_calling.py +0 -0
  192. {metalist-0.18.0 → metalist-0.20.0}/app/services/agent/trace.py +0 -0
  193. {metalist-0.18.0 → metalist-0.20.0}/app/services/agent/web_actions.py +0 -0
  194. {metalist-0.18.0 → metalist-0.20.0}/app/services/agent/web_capabilities.py +0 -0
  195. {metalist-0.18.0 → metalist-0.20.0}/app/services/agent/web_evidence.py +0 -0
  196. {metalist-0.18.0 → metalist-0.20.0}/app/services/agent/web_fetch.py +0 -0
  197. {metalist-0.18.0 → metalist-0.20.0}/app/services/agent/web_settings.py +0 -0
  198. {metalist-0.18.0 → metalist-0.20.0}/app/services/ai_chat.py +0 -0
  199. {metalist-0.18.0 → metalist-0.20.0}/app/services/ai_chat_stream.py +0 -0
  200. {metalist-0.18.0 → metalist-0.20.0}/app/services/app_update_worker.py +0 -0
  201. {metalist-0.18.0 → metalist-0.20.0}/app/services/app_updates.py +0 -0
  202. {metalist-0.18.0 → metalist-0.20.0}/app/services/auth.py +0 -0
  203. {metalist-0.18.0 → metalist-0.20.0}/app/services/auth_cache_state.py +0 -0
  204. {metalist-0.18.0 → metalist-0.20.0}/app/services/auth_service.py +0 -0
  205. {metalist-0.18.0 → metalist-0.20.0}/app/services/backlink_index.py +0 -0
  206. {metalist-0.18.0 → metalist-0.20.0}/app/services/backlinks.py +0 -0
  207. {metalist-0.18.0 → metalist-0.20.0}/app/services/backup_service.py +0 -0
  208. {metalist-0.18.0 → metalist-0.20.0}/app/services/backup_settings_service.py +0 -0
  209. {metalist-0.18.0 → metalist-0.20.0}/app/services/bulk_operation.py +0 -0
  210. {metalist-0.18.0 → metalist-0.20.0}/app/services/chess_rendering.py +0 -0
  211. {metalist-0.18.0 → metalist-0.20.0}/app/services/client_state_service.py +0 -0
  212. {metalist-0.18.0 → metalist-0.20.0}/app/services/content_cache.py +0 -0
  213. {metalist-0.18.0 → metalist-0.20.0}/app/services/dictation_cleanup.py +0 -0
  214. {metalist-0.18.0 → metalist-0.20.0}/app/services/diff_rendering.py +0 -0
  215. {metalist-0.18.0 → metalist-0.20.0}/app/services/encryption.py +0 -0
  216. {metalist-0.18.0 → metalist-0.20.0}/app/services/exception_capture.py +0 -0
  217. {metalist-0.18.0 → metalist-0.20.0}/app/services/file_edit_sessions.py +0 -0
  218. {metalist-0.18.0 → metalist-0.20.0}/app/services/file_registry.py +0 -0
  219. {metalist-0.18.0 → metalist-0.20.0}/app/services/file_storage.py +0 -0
  220. {metalist-0.18.0 → metalist-0.20.0}/app/services/file_text_preview.py +0 -0
  221. {metalist-0.18.0 → metalist-0.20.0}/app/services/footnote_layout.py +0 -0
  222. {metalist-0.18.0 → metalist-0.20.0}/app/services/footnote_rendering.py +0 -0
  223. {metalist-0.18.0 → metalist-0.20.0}/app/services/hierarchy.py +0 -0
  224. {metalist-0.18.0 → metalist-0.20.0}/app/services/html_unformatting.py +0 -0
  225. {metalist-0.18.0 → metalist-0.20.0}/app/services/hydration_state.py +0 -0
  226. {metalist-0.18.0 → metalist-0.20.0}/app/services/image_size_formatting.py +0 -0
  227. {metalist-0.18.0 → metalist-0.20.0}/app/services/inline_image_dimensions.py +0 -0
  228. {metalist-0.18.0 → metalist-0.20.0}/app/services/inline_image_occurrences.py +0 -0
  229. {metalist-0.18.0 → metalist-0.20.0}/app/services/input_errors.py +0 -0
  230. {metalist-0.18.0 → metalist-0.20.0}/app/services/integrity.py +0 -0
  231. {metalist-0.18.0 → metalist-0.20.0}/app/services/latex_rendering.py +0 -0
  232. {metalist-0.18.0 → metalist-0.20.0}/app/services/link_title_retry.py +0 -0
  233. {metalist-0.18.0 → metalist-0.20.0}/app/services/link_titles.py +0 -0
  234. {metalist-0.18.0 → metalist-0.20.0}/app/services/login_rate_limit.py +0 -0
  235. {metalist-0.18.0 → metalist-0.20.0}/app/services/maintenance_mode.py +0 -0
  236. {metalist-0.18.0 → metalist-0.20.0}/app/services/mermaid_note_rendering.py +0 -0
  237. {metalist-0.18.0 → metalist-0.20.0}/app/services/namespace_deletion_jobs.py +0 -0
  238. {metalist-0.18.0 → metalist-0.20.0}/app/services/namespace_deletion_worker.py +0 -0
  239. {metalist-0.18.0 → metalist-0.20.0}/app/services/namespace_rename_jobs.py +0 -0
  240. {metalist-0.18.0 → metalist-0.20.0}/app/services/namespace_rename_worker.py +0 -0
  241. {metalist-0.18.0 → metalist-0.20.0}/app/services/namespace_runtime_guard.py +0 -0
  242. {metalist-0.18.0 → metalist-0.20.0}/app/services/namespace_switcher.py +0 -0
  243. {metalist-0.18.0 → metalist-0.20.0}/app/services/note_image_tags.py +0 -0
  244. {metalist-0.18.0 → metalist-0.20.0}/app/services/note_ordering.py +0 -0
  245. {metalist-0.18.0 → metalist-0.20.0}/app/services/ontology_rules_store.py +0 -0
  246. {metalist-0.18.0 → metalist-0.20.0}/app/services/openai_credentials.py +0 -0
  247. {metalist-0.18.0 → metalist-0.20.0}/app/services/password_note_fields.py +0 -0
  248. {metalist-0.18.0 → metalist-0.20.0}/app/services/public_http.py +0 -0
  249. {metalist-0.18.0 → metalist-0.20.0}/app/services/reference_presentation.py +0 -0
  250. {metalist-0.18.0 → metalist-0.20.0}/app/services/reminders.py +0 -0
  251. {metalist-0.18.0 → metalist-0.20.0}/app/services/remote_image_proxy.py +0 -0
  252. {metalist-0.18.0 → metalist-0.20.0}/app/services/request_recovery.py +0 -0
  253. {metalist-0.18.0 → metalist-0.20.0}/app/services/resource_limits.py +0 -0
  254. {metalist-0.18.0 → metalist-0.20.0}/app/services/root_sorting.py +0 -0
  255. {metalist-0.18.0 → metalist-0.20.0}/app/services/runtime_generation.py +0 -0
  256. {metalist-0.18.0 → metalist-0.20.0}/app/services/runtime_hardening.py +0 -0
  257. {metalist-0.18.0 → metalist-0.20.0}/app/services/runtime_lock.py +0 -0
  258. {metalist-0.18.0 → metalist-0.20.0}/app/services/search_history_storage.py +0 -0
  259. {metalist-0.18.0 → metalist-0.20.0}/app/services/search_query.py +0 -0
  260. {metalist-0.18.0 → metalist-0.20.0}/app/services/search_text.py +0 -0
  261. {metalist-0.18.0 → metalist-0.20.0}/app/services/selected_text_tag.py +0 -0
  262. {metalist-0.18.0 → metalist-0.20.0}/app/services/self_update.py +0 -0
  263. {metalist-0.18.0 → metalist-0.20.0}/app/services/session_timeout_service.py +0 -0
  264. {metalist-0.18.0 → metalist-0.20.0}/app/services/shell_session_service.py +0 -0
  265. {metalist-0.18.0 → metalist-0.20.0}/app/services/store.py +0 -0
  266. {metalist-0.18.0 → metalist-0.20.0}/app/services/structured_note_renderers.py +0 -0
  267. {metalist-0.18.0 → metalist-0.20.0}/app/services/sync.py +0 -0
  268. {metalist-0.18.0 → metalist-0.20.0}/app/services/sync_state.py +0 -0
  269. {metalist-0.18.0 → metalist-0.20.0}/app/services/tab_state.py +0 -0
  270. {metalist-0.18.0 → metalist-0.20.0}/app/services/tag_case.py +0 -0
  271. {metalist-0.18.0 → metalist-0.20.0}/app/services/tag_ontology.py +0 -0
  272. {metalist-0.18.0 → metalist-0.20.0}/app/services/tag_rename.py +0 -0
  273. {metalist-0.18.0 → metalist-0.20.0}/app/services/test_reset.py +0 -0
  274. {metalist-0.18.0 → metalist-0.20.0}/app/services/tokens.py +0 -0
  275. {metalist-0.18.0 → metalist-0.20.0}/app/services/update_backups.py +0 -0
  276. {metalist-0.18.0 → metalist-0.20.0}/app/services/update_installer.py +0 -0
  277. {metalist-0.18.0 → metalist-0.20.0}/app/services/update_preflight.py +0 -0
  278. {metalist-0.18.0 → metalist-0.20.0}/app/services/view_cache.py +0 -0
  279. {metalist-0.18.0 → metalist-0.20.0}/app/services/view_diff.py +0 -0
  280. {metalist-0.18.0 → metalist-0.20.0}/app/services/view_state.py +0 -0
  281. {metalist-0.18.0 → metalist-0.20.0}/app/startup_console.py +0 -0
  282. {metalist-0.18.0 → metalist-0.20.0}/app/startup_environment.py +0 -0
  283. {metalist-0.18.0 → metalist-0.20.0}/app/startup_js_sanity.py +0 -0
  284. {metalist-0.18.0 → metalist-0.20.0}/app/startup_sanity.py +0 -0
  285. {metalist-0.18.0 → metalist-0.20.0}/app/static/css/floating-notes.css +0 -0
  286. {metalist-0.18.0 → metalist-0.20.0}/app/static/css/forms.css +0 -0
  287. {metalist-0.18.0 → metalist-0.20.0}/app/static/img/favicon.ico +0 -0
  288. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/locked.js +0 -0
  289. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/main.js +0 -0
  290. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/activity-tracker.js +0 -0
  291. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/ai-chat/agent-menu-actions.js +0 -0
  292. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/ai-chat/agent-retrieval-settings.js +0 -0
  293. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/ai-chat/agent-web-settings.js +0 -0
  294. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/ai-chat/ai-agent-debug-view.js +0 -0
  295. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/ai-chat/ai-chat-message-placement.js +0 -0
  296. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/ai-chat/ai-chat-panel-controller.js +0 -0
  297. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/ai-chat/ai-chat-panel-service.js +0 -0
  298. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/ai-chat/ai-thinking-level-service.js +0 -0
  299. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/ai-chat/bulk-elapsed-clock.js +0 -0
  300. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/ai-chat/bulk-proposal-ui.js +0 -0
  301. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/ai-chat/bulk-scope-question.js +0 -0
  302. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/ai-chat/cloud-privacy-policy.js +0 -0
  303. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/ai-chat/confirmation-card.js +0 -0
  304. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/ai-chat/confirmation-question.js +0 -0
  305. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/ai-chat/proposal-menu.js +0 -0
  306. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/ai-chat/summary-batch-preview.js +0 -0
  307. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/api-error-diagnostics.js +0 -0
  308. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/api-failure-classification-service.js +0 -0
  309. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/app-update-service.js +0 -0
  310. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/application-state.js +0 -0
  311. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/async-result.js +0 -0
  312. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/auth.js +0 -0
  313. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/client-state-api.js +0 -0
  314. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/client-state-migration.js +0 -0
  315. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/command-palette/activation-auth-policy.js +0 -0
  316. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/command-palette/backup-command-availability.js +0 -0
  317. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/command-palette/click-activation-service.js +0 -0
  318. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/command-palette/note-layout-preferences.js +0 -0
  319. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/command-palette/preferences-store.js +0 -0
  320. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/command-palette/tag-config-loader.js +0 -0
  321. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/command-palette/usage-store.js +0 -0
  322. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/connectivity-monitor.js +0 -0
  323. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/context-menu/context-menu-target-service.js +0 -0
  324. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/disconnect-editing-state-service.js +0 -0
  325. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/dom-utils-fixed.js +0 -0
  326. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/dom-utils.js +0 -0
  327. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/editor-commands.js +0 -0
  328. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/editor-selection.js +0 -0
  329. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/editor-toolbar.js +0 -0
  330. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/elapsed-time.js +0 -0
  331. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/error-overlay.js +0 -0
  332. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/excalidraw/excalidraw-actions.js +0 -0
  333. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/excalidraw/excalidraw-autosave.js +0 -0
  334. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/excalidraw/excalidraw-editor-service.js +0 -0
  335. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/excalidraw/excalidraw-editor-state.js +0 -0
  336. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/excalidraw/excalidraw-loader.js +0 -0
  337. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/excalidraw/excalidraw-preview-service.js +0 -0
  338. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/excalidraw/excalidraw-scene.js +0 -0
  339. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/expected-errors.js +0 -0
  340. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/location-flags.js +0 -0
  341. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/login-namespace-picker.js +0 -0
  342. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/maintenance.js +0 -0
  343. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/modals/ai-agent-settings-modal.js +0 -0
  344. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/modals/alphabetize-root-notes-modal.js +0 -0
  345. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/modals/backup-restore-modal.js +0 -0
  346. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/modals/backup-result-modal.js +0 -0
  347. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/modals/backup-retention-modal.js +0 -0
  348. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/modals/backup-settings-modal.js +0 -0
  349. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/modals/base-modal.js +0 -0
  350. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/modals/confirmation-modal.js +0 -0
  351. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/modals/delete-namespace-modal.js +0 -0
  352. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/modals/delete-namespace-validation.js +0 -0
  353. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/modals/dictation-settings-modal.js +0 -0
  354. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/modals/help-modal.js +0 -0
  355. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/modals/link-title-retry-modal.js +0 -0
  356. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/modals/namespace-loading-page.js +0 -0
  357. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/modals/namespace-modals.js +0 -0
  358. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/modals/namespace-ports-profile.js +0 -0
  359. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/modals/namespace-rename-validation.js +0 -0
  360. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/modals/note-layout-appearance-modal.js +0 -0
  361. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/modals/ontology-modal.js +0 -0
  362. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/modals/password-modal.js +0 -0
  363. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/modals/prioritize-modal.js +0 -0
  364. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/modals/random-password-modal.js +0 -0
  365. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/modals/reminder-modal.js +0 -0
  366. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/modals/reset-updated-at-modal.js +0 -0
  367. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/modals/search-suggestion-statistics-modal.js +0 -0
  368. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/modals/session-timeout-modal.js +0 -0
  369. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/modals/tag-suggestion-settings-modal.js +0 -0
  370. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/modals/version-info-modal.js +0 -0
  371. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/modals/visual-cues-modal.js +0 -0
  372. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/actions/history-actions.js +0 -0
  373. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/actions/search-actions.js +0 -0
  374. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/actions/selection-actions.js +0 -0
  375. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/events/focus-events.js +0 -0
  376. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/events/inactivity-events.js +0 -0
  377. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/events/input-events.js +0 -0
  378. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/events/search-events.js +0 -0
  379. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/mode-context.js +0 -0
  380. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/mode-logger.js +0 -0
  381. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/mode-manager-controller.js +0 -0
  382. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/add-style-service.js +0 -0
  383. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/animated-scroll-service.js +0 -0
  384. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/backlinks-navigation-service.js +0 -0
  385. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/backlinks-panel-service.js +0 -0
  386. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/clipboard-shortcut-policy-service.js +0 -0
  387. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/collapse-affordance-service.js +0 -0
  388. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/collapse-editing-policy-service.js +0 -0
  389. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/context-menu-target-service.js +0 -0
  390. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/deselect-selection-state-service.js +0 -0
  391. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/dictation-note-paste.js +0 -0
  392. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/dictation-paste-padding.js +0 -0
  393. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/dictation-paste-service.js +0 -0
  394. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/dropped-file-preview-service.js +0 -0
  395. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/edit-session-collapse-policy-service.js +0 -0
  396. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/edit-session-collapse-service.js +0 -0
  397. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/embedded-image-service.js +0 -0
  398. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/file-drop-policy-service.js +0 -0
  399. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/file-image-preview-service.js +0 -0
  400. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/file-reference-service.js +0 -0
  401. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/filtered-refresh-selection-service.js +0 -0
  402. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/floating-note-dom.js +0 -0
  403. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/floating-note-layout.js +0 -0
  404. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/floating-note-service.js +0 -0
  405. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/footnote-navigation-service.js +0 -0
  406. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/history-selection-policy-service.js +0 -0
  407. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/history-shortcut-policy-service.js +0 -0
  408. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/html-paste-sanitizer-service.js +0 -0
  409. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/image-context-menu-action-service.js +0 -0
  410. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/image-file-insert-choice-modal-service.js +0 -0
  411. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/infinite-scroll-service.js +0 -0
  412. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/link-context-menu-action-service.js +0 -0
  413. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/link-title-prefetch-service.js +0 -0
  414. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/markdown-render-service.js +0 -0
  415. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/mermaid-render-service.js +0 -0
  416. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/note-click-target-service.js +0 -0
  417. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/note-clipboard-write-service.js +0 -0
  418. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/note-collapse-animation-service.js +0 -0
  419. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/note-context-menu-action-service.js +0 -0
  420. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/note-delete-shortcut-policy-service.js +0 -0
  421. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/note-drag-indicator-service.js +0 -0
  422. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/note-drag-service.js +0 -0
  423. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/note-fullscreen-service.js +0 -0
  424. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/note-reposition-animation-service.js +0 -0
  425. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/note-split-service.js +0 -0
  426. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/note-timestamp-hover-service.js +0 -0
  427. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/password-clipboard-service.js +0 -0
  428. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/polling-service.js +0 -0
  429. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/reference-source-navigation-service.js +0 -0
  430. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/remote-image-proxy-service.js +0 -0
  431. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/remove-formatting-service.js +0 -0
  432. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/root-date-separator-service.js +0 -0
  433. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/root-note-creation-service.js +0 -0
  434. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/root-sort-indicator-service.js +0 -0
  435. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/root-sort-service.js +0 -0
  436. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/scroll-anchor-service.js +0 -0
  437. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/scroll-restoration-service.js +0 -0
  438. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/scroll-to-top-service.js +0 -0
  439. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/search-contexts-overlay-service.js +0 -0
  440. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/search-debounce-service.js +0 -0
  441. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/search-input-service.js +0 -0
  442. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/search-interaction-service.js +0 -0
  443. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/search-redaction-reveal-service.js +0 -0
  444. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/search-suggestion-statistics-service.js +0 -0
  445. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/search-suggestion-window-labels-service.js +0 -0
  446. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/search-suggestion-windows-service.js +0 -0
  447. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/search-suggestions-service.js +0 -0
  448. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/search-syntax-service.js +0 -0
  449. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/selected-formatting-removal-service.js +0 -0
  450. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/selected-text-tag-service.js +0 -0
  451. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/shell-output-service.js +0 -0
  452. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/tab-dom-cache-service.js +0 -0
  453. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/tab-drag-service.js +0 -0
  454. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/tab-duplication-service.js +0 -0
  455. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/tab-state-service.js +0 -0
  456. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/tag-input-selection-service.js +0 -0
  457. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/tag-suggestions-service.js +0 -0
  458. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/tag-syntax-service.js +0 -0
  459. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/todo-toggle-editing-service.js +0 -0
  460. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/untagged-view-indicator-service.js +0 -0
  461. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/mode-manager/services/view-mode-search-shortcut-service.js +0 -0
  462. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/note-html-sanitizer.js +0 -0
  463. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/password-generator.js +0 -0
  464. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/password-operation-response.js +0 -0
  465. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/password-policy.js +0 -0
  466. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/password-strength.js +0 -0
  467. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/reminder-store.js +0 -0
  468. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/reminder-surface-service.js +0 -0
  469. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/selection-markers.js +0 -0
  470. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/session-auth.js +0 -0
  471. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/tag-token.js +0 -0
  472. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/modules/uuid.js +0 -0
  473. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/namespace-deleted.js +0 -0
  474. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/namespace-renamed.js +0 -0
  475. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/vendor/dompurify-3.4.16.LICENSE.txt +0 -0
  476. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/vendor/dompurify-3.4.16.min.js +0 -0
  477. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/vendor/excalidraw-0.18.1-assets/excalidraw.css +0 -0
  478. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/vendor/excalidraw-0.18.1-assets/fonts/Assistant/Assistant-Bold.woff2 +0 -0
  479. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/vendor/excalidraw-0.18.1-assets/fonts/Assistant/Assistant-Medium.woff2 +0 -0
  480. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/vendor/excalidraw-0.18.1-assets/fonts/Assistant/Assistant-Regular.woff2 +0 -0
  481. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/vendor/excalidraw-0.18.1-assets/fonts/Assistant/Assistant-SemiBold.woff2 +0 -0
  482. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/vendor/excalidraw-0.18.1-assets/fonts/Cascadia/CascadiaCode-Regular.woff2 +0 -0
  483. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/vendor/excalidraw-0.18.1-assets/fonts/ComicShanns/ComicShanns-Regular-279a7b317d12eb88de06167bd672b4b4.woff2 +0 -0
  484. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/vendor/excalidraw-0.18.1-assets/fonts/ComicShanns/ComicShanns-Regular-6e066e8de2ac57ea9283adb9c24d7f0c.woff2 +0 -0
  485. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/vendor/excalidraw-0.18.1-assets/fonts/ComicShanns/ComicShanns-Regular-dc6a8806fa96795d7b3be5026f989a17.woff2 +0 -0
  486. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/vendor/excalidraw-0.18.1-assets/fonts/ComicShanns/ComicShanns-Regular-fcb0fc02dcbee4c9846b3e2508668039.woff2 +0 -0
  487. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/vendor/excalidraw-0.18.1-assets/fonts/Excalifont/Excalifont-Regular-349fac6ca4700ffec595a7150a0d1e1d.woff2 +0 -0
  488. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/vendor/excalidraw-0.18.1-assets/fonts/Excalifont/Excalifont-Regular-3f2c5db56cc93c5a6873b1361d730c16.woff2 +0 -0
  489. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/vendor/excalidraw-0.18.1-assets/fonts/Excalifont/Excalifont-Regular-41b173a47b57366892116a575a43e2b6.woff2 +0 -0
  490. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/vendor/excalidraw-0.18.1-assets/fonts/Excalifont/Excalifont-Regular-623ccf21b21ef6b3a0d87738f77eb071.woff2 +0 -0
  491. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/vendor/excalidraw-0.18.1-assets/fonts/Excalifont/Excalifont-Regular-a88b72a24fb54c9f94e3b5fdaa7481c9.woff2 +0 -0
  492. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/vendor/excalidraw-0.18.1-assets/fonts/Excalifont/Excalifont-Regular-b9dcf9d2e50a1eaf42fc664b50a3fd0d.woff2 +0 -0
  493. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/vendor/excalidraw-0.18.1-assets/fonts/Excalifont/Excalifont-Regular-be310b9bcd4f1a43f571c46df7809174.woff2 +0 -0
  494. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/vendor/excalidraw-0.18.1-assets/fonts/Lilita/Lilita-Regular-i7dPIFZ9Zz-WBtRtedDbYE98RXi4EwSsbg.woff2 +0 -0
  495. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/vendor/excalidraw-0.18.1-assets/fonts/Lilita/Lilita-Regular-i7dPIFZ9Zz-WBtRtedDbYEF8RXi4EwQ.woff2 +0 -0
  496. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/vendor/excalidraw-0.18.1-assets/fonts/Nunito/Nunito-Regular-XRXI3I6Li01BKofiOc5wtlZ2di8HDIkhdTA3j6zbXWjgevT5.woff2 +0 -0
  497. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/vendor/excalidraw-0.18.1-assets/fonts/Nunito/Nunito-Regular-XRXI3I6Li01BKofiOc5wtlZ2di8HDIkhdTQ3j6zbXWjgeg.woff2 +0 -0
  498. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/vendor/excalidraw-0.18.1-assets/fonts/Nunito/Nunito-Regular-XRXI3I6Li01BKofiOc5wtlZ2di8HDIkhdTk3j6zbXWjgevT5.woff2 +0 -0
  499. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/vendor/excalidraw-0.18.1-assets/fonts/Nunito/Nunito-Regular-XRXI3I6Li01BKofiOc5wtlZ2di8HDIkhdTo3j6zbXWjgevT5.woff2 +0 -0
  500. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/vendor/excalidraw-0.18.1-assets/fonts/Nunito/Nunito-Regular-XRXI3I6Li01BKofiOc5wtlZ2di8HDIkhdTs3j6zbXWjgevT5.woff2 +0 -0
  501. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/vendor/excalidraw-0.18.1-assets/fonts/Virgil/Virgil-Regular.woff2 +0 -0
  502. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/vendor/excalidraw-0.18.1.LICENSE.txt +0 -0
  503. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/vendor/excalidraw-0.18.1.min.js +0 -0
  504. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/vendor/markdown-it-14.3.2.LICENSE.txt +0 -0
  505. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/vendor/markdown-it-14.3.2.min.js +0 -0
  506. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/vendor/mermaid-11.16.1.LICENSE.txt +0 -0
  507. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/vendor/mermaid-11.16.1.min.js +0 -0
  508. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/vendor/zxcvbn-4.4.2.LICENSE.txt +0 -0
  509. {metalist-0.18.0 → metalist-0.20.0}/app/static/js/vendor/zxcvbn-4.4.2.js +0 -0
  510. {metalist-0.18.0 → metalist-0.20.0}/app/static/note-html-policy.json +0 -0
  511. {metalist-0.18.0 → metalist-0.20.0}/app/templates/_logo_mark.html +0 -0
  512. {metalist-0.18.0 → metalist-0.20.0}/app/templates/base.html +0 -0
  513. {metalist-0.18.0 → metalist-0.20.0}/app/templates/index.html +0 -0
  514. {metalist-0.18.0 → metalist-0.20.0}/app/templates/locked.html +0 -0
  515. {metalist-0.18.0 → metalist-0.20.0}/app/templates/maintenance.html +0 -0
  516. {metalist-0.18.0 → metalist-0.20.0}/app/templates/namespace_deleted.html +0 -0
  517. {metalist-0.18.0 → metalist-0.20.0}/app/templates/namespace_renamed.html +0 -0
  518. {metalist-0.18.0 → metalist-0.20.0}/app/templates/notes_list.html +0 -0
  519. {metalist-0.18.0 → metalist-0.20.0}/app/upload_limits.py +0 -0
  520. {metalist-0.18.0 → metalist-0.20.0}/app/usecases/__init__.py +0 -0
  521. {metalist-0.18.0 → metalist-0.20.0}/app/usecases/add_selected_text_tag.py +0 -0
  522. {metalist-0.18.0 → metalist-0.20.0}/app/usecases/alphabetize_root_notes.py +0 -0
  523. {metalist-0.18.0 → metalist-0.20.0}/app/usecases/base.py +0 -0
  524. {metalist-0.18.0 → metalist-0.20.0}/app/usecases/bulk_tag_proposals.py +0 -0
  525. {metalist-0.18.0 → metalist-0.20.0}/app/usecases/collapse.py +0 -0
  526. {metalist-0.18.0 → metalist-0.20.0}/app/usecases/copy_note.py +0 -0
  527. {metalist-0.18.0 → metalist-0.20.0}/app/usecases/create_child.py +0 -0
  528. {metalist-0.18.0 → metalist-0.20.0}/app/usecases/create_file_preview_child.py +0 -0
  529. {metalist-0.18.0 → metalist-0.20.0}/app/usecases/create_note.py +0 -0
  530. {metalist-0.18.0 → metalist-0.20.0}/app/usecases/create_sibling.py +0 -0
  531. {metalist-0.18.0 → metalist-0.20.0}/app/usecases/delete_subtree.py +0 -0
  532. {metalist-0.18.0 → metalist-0.20.0}/app/usecases/delete_tag.py +0 -0
  533. {metalist-0.18.0 → metalist-0.20.0}/app/usecases/expand.py +0 -0
  534. {metalist-0.18.0 → metalist-0.20.0}/app/usecases/file_edit_session.py +0 -0
  535. {metalist-0.18.0 → metalist-0.20.0}/app/usecases/indent.py +0 -0
  536. {metalist-0.18.0 → metalist-0.20.0}/app/usecases/lock.py +0 -0
  537. {metalist-0.18.0 → metalist-0.20.0}/app/usecases/move.py +0 -0
  538. {metalist-0.18.0 → metalist-0.20.0}/app/usecases/move_to_top.py +0 -0
  539. {metalist-0.18.0 → metalist-0.20.0}/app/usecases/outdent.py +0 -0
  540. {metalist-0.18.0 → metalist-0.20.0}/app/usecases/paste_child.py +0 -0
  541. {metalist-0.18.0 → metalist-0.20.0}/app/usecases/paste_sibling.py +0 -0
  542. {metalist-0.18.0 → metalist-0.20.0}/app/usecases/prioritize.py +0 -0
  543. {metalist-0.18.0 → metalist-0.20.0}/app/usecases/redo.py +0 -0
  544. {metalist-0.18.0 → metalist-0.20.0}/app/usecases/rename_tag.py +0 -0
  545. {metalist-0.18.0 → metalist-0.20.0}/app/usecases/reset_updated_at.py +0 -0
  546. {metalist-0.18.0 → metalist-0.20.0}/app/usecases/resize_image.py +0 -0
  547. {metalist-0.18.0 → metalist-0.20.0}/app/usecases/run_shell.py +0 -0
  548. {metalist-0.18.0 → metalist-0.20.0}/app/usecases/search_comment_autofill.py +0 -0
  549. {metalist-0.18.0 → metalist-0.20.0}/app/usecases/search_context_tags.py +0 -0
  550. {metalist-0.18.0 → metalist-0.20.0}/app/usecases/set_collapse_bulk.py +0 -0
  551. {metalist-0.18.0 → metalist-0.20.0}/app/usecases/set_collapse_in_context.py +0 -0
  552. {metalist-0.18.0 → metalist-0.20.0}/app/usecases/split_note.py +0 -0
  553. {metalist-0.18.0 → metalist-0.20.0}/app/usecases/toggle_reference_mode.py +0 -0
  554. {metalist-0.18.0 → metalist-0.20.0}/app/usecases/toggle_todo_done.py +0 -0
  555. {metalist-0.18.0 → metalist-0.20.0}/app/usecases/undo.py +0 -0
  556. {metalist-0.18.0 → metalist-0.20.0}/app/usecases/unformat_content.py +0 -0
  557. {metalist-0.18.0 → metalist-0.20.0}/app/usecases/update_content.py +0 -0
  558. {metalist-0.18.0 → metalist-0.20.0}/app/usecases/view.py +0 -0
  559. {metalist-0.18.0 → metalist-0.20.0}/app/utils/__init__.py +0 -0
  560. {metalist-0.18.0 → metalist-0.20.0}/app/utils/encryption.py +0 -0
  561. {metalist-0.18.0 → metalist-0.20.0}/app/utils/text_utils.py +0 -0
  562. {metalist-0.18.0 → metalist-0.20.0}/convert-from-legacy.py +0 -0
  563. {metalist-0.18.0 → metalist-0.20.0}/metalist.egg-info/dependency_links.txt +0 -0
  564. {metalist-0.18.0 → metalist-0.20.0}/metalist.egg-info/entry_points.txt +0 -0
  565. {metalist-0.18.0 → metalist-0.20.0}/metalist.egg-info/requires.txt +0 -0
  566. {metalist-0.18.0 → metalist-0.20.0}/metalist.egg-info/top_level.txt +0 -0
  567. {metalist-0.18.0 → metalist-0.20.0}/pyproject.toml +0 -0
  568. {metalist-0.18.0 → metalist-0.20.0}/scripts/generate-lan-cert.sh +0 -0
  569. {metalist-0.18.0 → metalist-0.20.0}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: metalist
3
- Version: 0.18.0
3
+ Version: 0.20.0
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
@@ -51,6 +51,26 @@ A minimalist single-user note-taking app focused on server-side rendering (SSR),
51
51
  - Multi-tab search contexts with server-persisted scroll/search state (survives browser restarts)
52
52
  - Manual namespace backups/restores to a user-selected backup folder with retention controls
53
53
 
54
+ ## Changes in 0.20.0
55
+
56
+ - **Suggest Tags with AI** for the note being edited: right-click the note, or its tag bar past the tags, and choose 🤖 Suggest Tags with AI (for a note with children: This Note, or This Note and Its Children). The proposals appear under the tag bar for `+`/`−`, tags not yet in use marked **new** and grouped on the right; adding them can be undone. It shows only when an AI is set up and may see the note.
57
+ - The robot count (🤖 N) next to a note: click it to open every collapsed note on the way to its proposals and edit the highest one that has some; right-click it to Accept All or Reject All, one undo step.
58
+ - **Print**: the note menu's Print ▸ Note / View, the background menu's Print View, or the palette's Print view, through the browser's print dialog (Save as PDF included). Always light, fully expanded, without arrows, using the page's full width with its own margins, so the browser prints no header or footer.
59
+ - Export as HTML and Print open one dialog: a title with a smart default (the note's first line, or MetaList with the view's search), which heads the page unless the first note already says it and names an exported file; 🤖 Suggest with AI for the title, asked only when you press it, which never sees private notes, usernames, passwords or the search; and, when some would show, an Include Unredacted Passwords / Usernames checkbox (unchecked: each shows `••••••••`, whatever its length).
60
+ - Exported HTML files show the view as it is shown (its search, sort order, Untagged view; notes a search leaves out are absent), start fully expanded with MetaList's collapse arrows working in the file, and copy usernames, included passwords and `@copyable` values on click. Collapsing no longer shifts the page sideways.
61
+ - `@username` notes are private like `@password`: never sent to the AI. Username, password and email rows show just their icon, named on hover, and the value.
62
+ - Text never runs out of its note: a word too long for the line (a token, a hash, a long URL) breaks at the note's edge, and code, `@json`, `@diff`, CSV and Markdown code blocks wrap. Images shrink to fit their note or embedded note, and `@size` scales them only up to that width.
63
+ - The light theme's header bar is black again, with white icons and count, and the AI chat header now lines up with the search bar.
64
+
65
+ ## Changes in 0.19.0
66
+
67
+ - Paste formats: pasting a fenced code block that names its language tags the note `@markdown`, and `@markdown` now colours that code like `@code=<language>` (with its label). Code copied from VS Code is tagged `@code=<its language>`, and cells copied from Excel, Google Sheets or Numbers become CSV text tagged `@csv`.
68
+ - Text that only looks like a format is offered, not applied: after pasting CSV-like, Markdown-like or code-like text, a small card says "Apply as CSV" / "Apply as Markdown" / "Apply as Python" (up to three close languages for code) with "Skip". The card moves nothing and never takes the keyboard; typing on, Escape or a click elsewhere closes it. It also appears when the paste makes a new note.
69
+ - Right-click a tag already in the tag bar or the search box for **About this tag**: whether the note names it, the notes it shares with your other tags, whether a parent or a rule already gives it, its synonyms and implications, how many notes have it ("Only on this note" hints at a typo); in the search box, what it finds and what leaving it out removes. Edit Tag Relationships now comes first in the tag menu, at a fixed place under the pointer.
70
+ - Tag suggestions: a web address in a note counts only by its site's name, so a YouTube link no longer suggests `HTTPS` or `V-neuron`; one letter alone no longer names a tag with longer words. A suggestion without evidence now says only what is true (what you typed matches, which part of its name the note has) instead of "Nothing in this note points to it".
71
+ - Fixed: accepting a Grammarly suggestion while editing could undo itself and end editing, when Grammarly drew its card in an element MetaList did not recognise.
72
+ - Removed the "Make pseudo-suggestions" test item from the note menu.
73
+
54
74
  ## Changes in 0.18.0
55
75
 
56
76
  - Right-click a tag suggestion, in the tag bar or the search bar, to see why it is suggested. The menu starts with **Why suggested**: the words of the note that name the tag (or a synonym), the tags it goes with and on how many notes, notes sharing this note's tags, whether it already applies through a parent or a rule, or that nothing in the note points to it; then how often the tag is used. Search suggestions also say how often you worked with a tag lately (when that put it first), what your search so far finds, or what leaving it out removes. The reasons are MetaList's own ranking, not the AI's, and appear instantly.
@@ -12,6 +12,26 @@ 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.20.0
16
+
17
+ - **Suggest Tags with AI** for the note being edited: right-click the note, or its tag bar past the tags, and choose 🤖 Suggest Tags with AI (for a note with children: This Note, or This Note and Its Children). The proposals appear under the tag bar for `+`/`−`, tags not yet in use marked **new** and grouped on the right; adding them can be undone. It shows only when an AI is set up and may see the note.
18
+ - The robot count (🤖 N) next to a note: click it to open every collapsed note on the way to its proposals and edit the highest one that has some; right-click it to Accept All or Reject All, one undo step.
19
+ - **Print**: the note menu's Print ▸ Note / View, the background menu's Print View, or the palette's Print view, through the browser's print dialog (Save as PDF included). Always light, fully expanded, without arrows, using the page's full width with its own margins, so the browser prints no header or footer.
20
+ - Export as HTML and Print open one dialog: a title with a smart default (the note's first line, or MetaList with the view's search), which heads the page unless the first note already says it and names an exported file; 🤖 Suggest with AI for the title, asked only when you press it, which never sees private notes, usernames, passwords or the search; and, when some would show, an Include Unredacted Passwords / Usernames checkbox (unchecked: each shows `••••••••`, whatever its length).
21
+ - Exported HTML files show the view as it is shown (its search, sort order, Untagged view; notes a search leaves out are absent), start fully expanded with MetaList's collapse arrows working in the file, and copy usernames, included passwords and `@copyable` values on click. Collapsing no longer shifts the page sideways.
22
+ - `@username` notes are private like `@password`: never sent to the AI. Username, password and email rows show just their icon, named on hover, and the value.
23
+ - Text never runs out of its note: a word too long for the line (a token, a hash, a long URL) breaks at the note's edge, and code, `@json`, `@diff`, CSV and Markdown code blocks wrap. Images shrink to fit their note or embedded note, and `@size` scales them only up to that width.
24
+ - The light theme's header bar is black again, with white icons and count, and the AI chat header now lines up with the search bar.
25
+
26
+ ## Changes in 0.19.0
27
+
28
+ - Paste formats: pasting a fenced code block that names its language tags the note `@markdown`, and `@markdown` now colours that code like `@code=<language>` (with its label). Code copied from VS Code is tagged `@code=<its language>`, and cells copied from Excel, Google Sheets or Numbers become CSV text tagged `@csv`.
29
+ - Text that only looks like a format is offered, not applied: after pasting CSV-like, Markdown-like or code-like text, a small card says "Apply as CSV" / "Apply as Markdown" / "Apply as Python" (up to three close languages for code) with "Skip". The card moves nothing and never takes the keyboard; typing on, Escape or a click elsewhere closes it. It also appears when the paste makes a new note.
30
+ - Right-click a tag already in the tag bar or the search box for **About this tag**: whether the note names it, the notes it shares with your other tags, whether a parent or a rule already gives it, its synonyms and implications, how many notes have it ("Only on this note" hints at a typo); in the search box, what it finds and what leaving it out removes. Edit Tag Relationships now comes first in the tag menu, at a fixed place under the pointer.
31
+ - Tag suggestions: a web address in a note counts only by its site's name, so a YouTube link no longer suggests `HTTPS` or `V-neuron`; one letter alone no longer names a tag with longer words. A suggestion without evidence now says only what is true (what you typed matches, which part of its name the note has) instead of "Nothing in this note points to it".
32
+ - Fixed: accepting a Grammarly suggestion while editing could undo itself and end editing, when Grammarly drew its card in an element MetaList did not recognise.
33
+ - Removed the "Make pseudo-suggestions" test item from the note menu.
34
+
15
35
  ## Changes in 0.18.0
16
36
 
17
37
  - Right-click a tag suggestion, in the tag bar or the search bar, to see why it is suggested. The menu starts with **Why suggested**: the words of the note that name the tag (or a synonym), the tags it goes with and on how many notes, notes sharing this note's tags, whether it already applies through a parent or a rule, or that nothing in the note points to it; then how often the tag is used. Search suggestions also say how often you worked with a tag lately (when that put it first), what your search so far finds, or what leaving it out removes. The reasons are MetaList's own ranking, not the AI's, and appear instantly.
@@ -127,6 +127,22 @@ class TagSuggestionsRequest(TypedDict):
127
127
  prefix: Text
128
128
  content_html: Content
129
129
 
130
+ @with_config(ConfigDict(strict=True))
131
+ class TagDetailsRequest(TypedDict):
132
+ note_id: Identifier
133
+ tag: Identifier
134
+ tags_text: Text
135
+ content_html: Content
136
+
137
+ @with_config(ConfigDict(strict=True))
138
+ class SearchTagDetailsRequest(TypedDict):
139
+ query: Text
140
+ tag: Identifier
141
+ start: Nonnegative
142
+ end: Nonnegative
143
+ excluded: bool
144
+ windowDays: WindowDays
145
+
130
146
  @with_config(ConfigDict(strict=True))
131
147
  class CreateNoteTopRequest(TypedDict):
132
148
  first_visible_note_id: Text | None
@@ -181,14 +197,38 @@ class AddSelectedTextTagRequest(TypedDict):
181
197
  viewport: Viewport
182
198
 
183
199
  @with_config(ConfigDict(strict=True))
184
- class MakePseudoTagProposalsRequest(TypedDict):
200
+ class AcceptTagProposalRequest(TypedDict):
201
+ proposal: Identifier
185
202
  clientId: Identifier
186
203
  undoContext: UndoContext
187
204
  viewport: Viewport
188
205
 
189
206
  @with_config(ConfigDict(strict=True))
190
- class AcceptTagProposalRequest(TypedDict):
191
- proposal: Identifier
207
+ class AddTagProposalsRequest(TypedDict):
208
+ # note id -> the AI's tags for it: the edited note and maybe notes below it.
209
+ proposals: Annotated[dict[Identifier, Annotated[list[Identifier], Field(min_length=1, max_length=12)]],
210
+ Field(min_length=1, max_length=20_000)]
211
+ clientId: Identifier
212
+ undoContext: UndoContext
213
+ viewport: Viewport
214
+
215
+ # The view a robot count (🤖 N) was shown in: its tab, search ('' for none) and kind.
216
+ @with_config(ConfigDict(strict=True))
217
+ class OpenToTagProposalsRequest(TypedDict):
218
+ search: Text
219
+ tabId: Identifier
220
+ isUntaggedView: bool
221
+ clientId: Identifier
222
+ undoContext: UndoContext
223
+ viewport: Viewport
224
+
225
+ @with_config(ConfigDict(strict=True))
226
+ class ResolveRobotProposalsRequest(TypedDict):
227
+ action: Literal["accept", "reject"]
228
+ includesHiddenChildren: bool
229
+ search: Text
230
+ tabId: Identifier
231
+ isUntaggedView: bool
192
232
  clientId: Identifier
193
233
  undoContext: UndoContext
194
234
  viewport: Viewport
@@ -18,6 +18,10 @@ from app.api.transactions import transactional_route
18
18
  from app.models.utils import note_data_to_html
19
19
  from app.models.utils import render_note_data_read_only
20
20
  from app.services.ai_chat_stream import ChatTurnStream
21
+ from app.services.ai_setup import AiConnection
22
+ from app.services.ai_setup import AiNotSetUp
23
+ from app.services.ai_setup import AiProvider
24
+ from app.services.ai_setup import connect_to_ai
21
25
  from app.services.ai_chat import ai_chat_store
22
26
  from app.services.ai_chat_rendering import find_note_citation_ids
23
27
  from app.services.ai_chat_rendering import find_web_citation_ids
@@ -27,9 +31,7 @@ from app.services.agent.cloud_privacy import cloud_privacy_evaluator
27
31
  from app.services.agent.cloud_privacy import resolve_cloud_privacy_boundary
28
32
  from app.services.agent.inference import InferenceAdapter
29
33
  from app.services.agent.model_policy import SingleModelPolicy
30
- from app.services.agent.openai_inference import OPENAI_API_BASE_URL
31
34
  from app.services.agent.openai_inference import OPENAI_MODELS
32
- from app.services.agent.openai_inference import OpenAIInferenceAdapter
33
35
  from app.services.agent.openai_inference import validate_openai_model
34
36
  from app.services.agent.openai_cost_tracking import OpenAICostSnapshot
35
37
  from app.services.agent.openai_cost_tracking import openai_cost_tracker
@@ -64,6 +66,19 @@ from app.usecases.bulk_tag_proposals import apply_bulk_proposals, prepare_propos
64
66
  from app.services.bulk_operation import bulk_operation_guard
65
67
  from app.services.tab_state import tab_state_store
66
68
  from app.services.tokens import token_service
69
+ from app.services.agent.inference import InferenceProviderError
70
+ from app.services.agent.note_tag_suggestions import build_note_tag_request
71
+ from app.services.agent.export_title_suggestions import MAX_PREVIOUS_TITLES
72
+ from app.services.agent.export_title_suggestions import ExportTitleRequest
73
+ from app.services.agent.export_title_suggestions import build_export_title_request
74
+ from app.services.agent.export_title_suggestions import suggest_export_title
75
+ from app.services.agent.note_tag_suggestions import NoteTagRequest
76
+ from app.services.agent.note_tag_suggestions import suggest_note_tags
77
+ from app.services.snapshot import resolve_view_scope_membership
78
+ from app.services.html_export import resolve_export_scope
79
+ from app.services.ai_setup import describe_ai_setup
80
+ from app.services.input_errors import InputRejected
81
+ from app.utils.text_utils import strip_html
67
82
 
68
83
 
69
84
  router = APIRouter(prefix="/ai", tags=["ai"])
@@ -71,6 +86,15 @@ router = APIRouter(prefix="/ai", tags=["ai"])
71
86
  agent_context_builder = AgentContextBuilder()
72
87
 
73
88
 
89
+ def _require_ai_connection(*, provider: AiProvider, token: str, session_key: str) -> AiConnection:
90
+ # lint: allow-PY001 rationale="the user removed or never added the AI's key: an expected 409, not a defect"
91
+ try:
92
+ return connect_to_ai(provider=provider, token=token, session_key=session_key)
93
+ # lint: allow-PY001 rationale="the user removed or never added the AI's key: an expected 409, not a defect"
94
+ except AiNotSetUp as exc:
95
+ raise HTTPException(status_code=409, detail="The AI is not set up: add its API key in AI settings") from exc
96
+
97
+
74
98
  def _agent_runtime(*, inference: InferenceAdapter) -> AgentRuntime:
75
99
  return AgentRuntime(
76
100
  context_builder=agent_context_builder,
@@ -163,6 +187,76 @@ class AiChatRequest(BaseModel):
163
187
  return self
164
188
 
165
189
 
190
+ class NoteTagSuggestionAvailabilityRequest(BaseModel):
191
+ model_config = ConfigDict(extra="forbid")
192
+
193
+ provider: AiProvider
194
+ # The note as it is being edited, unsaved changes included.
195
+ tags: str = Field(..., max_length=1024 * 1024)
196
+ content_html: str = Field(..., max_length=16 * 1024 * 1024)
197
+
198
+
199
+ class NoteTagSuggestionAvailabilityResponse(BaseModel):
200
+ is_available: bool
201
+
202
+
203
+ class NoteTagSuggestionRequest(BaseModel):
204
+ model_config = ConfigDict(extra="forbid")
205
+
206
+ provider: AiProvider
207
+ model: str
208
+ thinking_level: Literal["off", "low", "medium", "high"]
209
+ # The note alone, or it and the notes below it the view shows.
210
+ include_children: bool
211
+ # The view the note is in ('' for no search).
212
+ search: str = Field(..., max_length=1024 * 1024)
213
+ tab_id: str = Field(..., min_length=1, max_length=256)
214
+ is_untagged_view: bool
215
+
216
+ @model_validator(mode="after")
217
+ def validate_model_for_provider(self) -> Self:
218
+ assert self.provider == "openai"
219
+ validate_openai_model(self.model)
220
+ return self
221
+
222
+
223
+ class ExportTitleScope(BaseModel):
224
+ model_config = ConfigDict(extra="forbid")
225
+
226
+ provider: AiProvider
227
+ # The view exported or printed, or the one its note is in ('' for no search).
228
+ search: str = Field(..., max_length=1024 * 1024)
229
+ tab_id: str = Field(..., min_length=1, max_length=256)
230
+ is_untagged_view: bool
231
+ # A note with its children ("note"), or the view as it is shown ("view", note_id '').
232
+ exported: Literal["note", "view"]
233
+ note_id: str = Field(..., max_length=256)
234
+
235
+ @model_validator(mode="after")
236
+ def validate_exported_note(self) -> Self:
237
+ if (self.exported == "note") != (self.note_id != ""):
238
+ raise ValueError("A note's export names the note; the view's export names none")
239
+ return self
240
+
241
+
242
+ class ExportTitleAvailabilityResponse(BaseModel):
243
+ # "unavailable": no AI is set up (no button); then as ExportTitleAvailability.
244
+ availability: Literal["unavailable", "nothing", "private", "partial", "ready"]
245
+
246
+
247
+ class ExportTitleSuggestionRequest(ExportTitleScope):
248
+ model: str
249
+ thinking_level: Literal["off", "low", "medium", "high"]
250
+ # Titles the AI gave in this dialog: it gives another.
251
+ previous_titles: list[str] = Field(..., max_length=MAX_PREVIOUS_TITLES)
252
+
253
+ @model_validator(mode="after")
254
+ def validate_model_for_provider(self) -> Self:
255
+ assert self.provider == "openai"
256
+ validate_openai_model(self.model)
257
+ return self
258
+
259
+
166
260
  class AiChatActivity(BaseModel):
167
261
  sequence: int = Field(..., ge=1)
168
262
  action: str
@@ -330,6 +424,178 @@ def delete_openai_credential(
330
424
  )
331
425
 
332
426
 
427
+ @router.post(
428
+ "/notes/{note_id}/tag-suggestions/availability",
429
+ response_model=NoteTagSuggestionAvailabilityResponse,
430
+ )
431
+ @transactional_route
432
+ def note_tag_suggestion_availability(
433
+ note_id: str,
434
+ payload: NoteTagSuggestionAvailabilityRequest,
435
+ response: Response,
436
+ token: Annotated[str, Depends(require_request_auth_token)],
437
+ ) -> NoteTagSuggestionAvailabilityResponse:
438
+ """Whether "Suggest Tags with AI" applies: an AI is set up and may see the note as edited."""
439
+ response.headers["Cache-Control"] = "no-store"
440
+ if not note_store.has_note(note_id):
441
+ raise HTTPException(status_code=404, detail=f"Note not found: {note_id}")
442
+ session_key = token_service.get_session_key(token)
443
+ if not describe_ai_setup(token=token, session_key=session_key).is_ready_for(payload.provider):
444
+ return NoteTagSuggestionAvailabilityResponse(is_available=False)
445
+ boundary = resolve_cloud_privacy_boundary(
446
+ preferences=load_client_preferences(token=token),
447
+ provider=payload.provider,
448
+ )
449
+ draft_effective_tags = note_store.draft_effective_tag_terms(
450
+ note_id=note_id, tags=payload.tags, plaintext=strip_html(payload.content_html),
451
+ )
452
+ reason = cloud_privacy_evaluator.draft_exclusion_reason(
453
+ note_id=note_id, draft_effective_tags=draft_effective_tags,
454
+ draft_content_html=payload.content_html, boundary=boundary,
455
+ )
456
+ return NoteTagSuggestionAvailabilityResponse(is_available=reason == "")
457
+
458
+
459
+ @router.post("/notes/{note_id}/tag-suggestions")
460
+ @transactional_route
461
+ def suggest_tags_for_note_with_ai(
462
+ note_id: str,
463
+ payload: NoteTagSuggestionRequest,
464
+ token: Annotated[str, Depends(require_request_auth_token)],
465
+ ) -> StreamingResponse:
466
+ """Ask the AI for the saved note's tags (and its children's): streams {"type": "progress"}
467
+ lines, then one {"type": "proposals"} or {"type": "failed"} line.
468
+
469
+ Nothing is written: the client adds what it gets back as proposals
470
+ (POST /notes/{id}/tag-proposals/add), one undo step.
471
+ """
472
+ if not note_store.has_note(note_id):
473
+ raise HTTPException(status_code=404, detail=f"Note not found: {note_id}")
474
+ session_key = token_service.get_session_key(token)
475
+ connection = _require_ai_connection(provider=payload.provider, token=token, session_key=session_key)
476
+ preferences = load_client_preferences(token=token)
477
+ boundary = resolve_cloud_privacy_boundary(preferences=preferences, provider=payload.provider)
478
+ if cloud_privacy_evaluator.exclusion_reason(note_id=note_id, boundary=boundary) != "":
479
+ raise InputRejected(
480
+ "Your AI privacy settings hide this note from the AI, so it can't suggest tags for it"
481
+ )
482
+ scope = resolve_view_scope_membership(
483
+ search=payload.search,
484
+ sort_mode=tab_state_store.get_sort_mode(tab_id=payload.tab_id),
485
+ is_untagged_view=payload.is_untagged_view,
486
+ )
487
+ if note_id not in scope.allowed_note_ids:
488
+ raise HTTPException(status_code=409, detail="The note is not in this view any more")
489
+ request = build_note_tag_request(
490
+ note_id=note_id, include_children=payload.include_children, boundary=boundary,
491
+ tag_skill=DEFAULT_AGENT_SKILLS.for_action("tag_proposals"), scope_note_ids=scope.allowed_note_ids,
492
+ batch_tokens=resolve_tagging_batch_tokens(preferences, payload.provider),
493
+ )
494
+ events = _note_tag_suggestion_events(
495
+ request=request, connection=connection, payload=payload,
496
+ web_settings=resolve_agent_web_settings(preferences=preferences), session_key=session_key,
497
+ )
498
+ return StreamingResponse(
499
+ events,
500
+ media_type="application/x-ndjson",
501
+ headers={"X-Accel-Buffering": "no", "Cache-Control": "no-store", "Content-Encoding": "identity"},
502
+ )
503
+
504
+
505
+ async def _note_tag_suggestion_events(
506
+ *, request: NoteTagRequest, connection: AiConnection, payload: NoteTagSuggestionRequest,
507
+ web_settings: AgentWebSettings, session_key: str,
508
+ ) -> AsyncIterator[str]:
509
+ events = suggest_note_tags(
510
+ request=request, inference=connection.inference, base_url=connection.base_url,
511
+ model=payload.model, thinking_level=payload.thinking_level,
512
+ web_settings=web_settings, session_key=session_key,
513
+ )
514
+ # lint: allow-PY001 rationale="the AI provider failed or kept answering unusably: the client shows why"
515
+ try:
516
+ async for event in events:
517
+ yield json.dumps(event) + "\n"
518
+ # lint: allow-PY001 rationale="the AI provider failed or kept answering unusably: the client shows why"
519
+ except InferenceProviderError as exc:
520
+ yield json.dumps({"type": "failed", "message": str(exc)}) + "\n"
521
+ finally:
522
+ await events.aclose()
523
+
524
+
525
+ def _export_title_request(scope: ExportTitleScope, token: str) -> ExportTitleRequest:
526
+ """What the AI may read of the export or print: the notes the AI privacy settings let it see."""
527
+ note_id = None
528
+ if scope.exported == "note":
529
+ note_id = scope.note_id
530
+ if not note_store.has_note(note_id):
531
+ raise HTTPException(status_code=404, detail=f"Note not found: {note_id}")
532
+ search = None
533
+ if scope.search != "":
534
+ search = scope.search
535
+ root_ids, allowed_note_ids = resolve_export_scope(
536
+ search=search,
537
+ sort_mode=tab_state_store.get_sort_mode(tab_id=scope.tab_id),
538
+ is_untagged_view=scope.is_untagged_view, root_note_id=note_id,
539
+ )
540
+ boundary = resolve_cloud_privacy_boundary(preferences=load_client_preferences(token=token), provider=scope.provider)
541
+ return build_export_title_request(root_ids=root_ids, allowed_note_ids=allowed_note_ids, boundary=boundary)
542
+
543
+
544
+ @router.post("/export-title/availability", response_model=ExportTitleAvailabilityResponse)
545
+ @transactional_route
546
+ def export_title_availability(
547
+ payload: ExportTitleScope,
548
+ response: Response,
549
+ token: Annotated[str, Depends(require_request_auth_token)],
550
+ ) -> ExportTitleAvailabilityResponse:
551
+ """Whether the Export and Print dialog's "Suggest with AI" can ask, and what it says about private notes."""
552
+ response.headers["Cache-Control"] = "no-store"
553
+ session_key = token_service.get_session_key(token)
554
+ if not describe_ai_setup(token=token, session_key=session_key).is_ready_for(payload.provider):
555
+ return ExportTitleAvailabilityResponse(availability="unavailable")
556
+ return ExportTitleAvailabilityResponse(availability=_export_title_request(payload, token).availability)
557
+
558
+
559
+ @router.post("/export-title")
560
+ @transactional_route
561
+ def suggest_export_title_with_ai(
562
+ payload: ExportTitleSuggestionRequest,
563
+ token: Annotated[str, Depends(require_request_auth_token)],
564
+ ) -> StreamingResponse:
565
+ """Ask the AI for an export's or print's title: one {"type": "title"} or {"type": "failed"} line.
566
+ Streamed, so Stop (the client going away) ends the request."""
567
+ session_key = token_service.get_session_key(token)
568
+ connection = _require_ai_connection(provider=payload.provider, token=token, session_key=session_key)
569
+ request = _export_title_request(payload, token)
570
+ if not request.notes:
571
+ raise InputRejected("These notes are private, so the AI can't suggest a title")
572
+ web_mode = resolve_agent_web_settings(preferences=load_client_preferences(token=token)).mode
573
+ return StreamingResponse(
574
+ _export_title_events(
575
+ request=request, payload=payload, connection=connection, web_mode=web_mode, session_key=session_key,
576
+ ),
577
+ media_type="application/x-ndjson",
578
+ headers={"X-Accel-Buffering": "no", "Cache-Control": "no-store", "Content-Encoding": "identity"},
579
+ )
580
+
581
+
582
+ async def _export_title_events(
583
+ *, request: ExportTitleRequest, payload: ExportTitleSuggestionRequest, connection: AiConnection,
584
+ web_mode: str, session_key: str,
585
+ ) -> AsyncIterator[str]:
586
+ # lint: allow-PY001 rationale="the AI provider failed or answered without a title: the dialog shows why"
587
+ try:
588
+ title = await suggest_export_title(
589
+ request=request, previous_titles=tuple(payload.previous_titles), inference=connection.inference,
590
+ base_url=connection.base_url, model=payload.model, thinking_level=payload.thinking_level,
591
+ web_mode=web_mode, session_key=session_key,
592
+ )
593
+ yield json.dumps({"type": "title", "title": title}) + "\n"
594
+ # lint: allow-PY001 rationale="the AI provider failed or answered without a title: the dialog shows why"
595
+ except InferenceProviderError as exc:
596
+ yield json.dumps({"type": "failed", "message": str(exc)}) + "\n"
597
+
598
+
333
599
  def _openai_cost_response(snapshot: OpenAICostSnapshot) -> OpenAICostResponse:
334
600
  return OpenAICostResponse(
335
601
  estimated_cost_usd=float(snapshot.estimated_cost_usd),
@@ -576,19 +842,7 @@ def stream_ai_chat(
576
842
  token: Annotated[str, Depends(require_request_auth_token)],
577
843
  ) -> StreamingResponse:
578
844
  session_key = token_service.get_session_key(token)
579
- credential_status = openai_credential_store.status(
580
- token=token,
581
- session_key=session_key,
582
- )
583
- if not credential_status.configured:
584
- raise HTTPException(
585
- status_code=409,
586
- detail="OpenAI API key is not configured",
587
- )
588
- openai_api_key = openai_credential_store.resolve(
589
- token=token,
590
- session_key=session_key,
591
- )
845
+ connection = _require_ai_connection(provider=payload.provider, token=token, session_key=session_key)
592
846
  preferences = load_client_preferences(token=token)
593
847
  prompts = DEFAULT_AGENT_PROMPTS
594
848
  skills = DEFAULT_AGENT_SKILLS
@@ -655,7 +909,7 @@ def stream_ai_chat(
655
909
  turn_stream = ChatTurnStream(store=ai_chat_store, notes=note_store,
656
910
  web_evidence_store=web_evidence_store, session_key=session_key,
657
911
  turn_id=turn_id, initial_input_tokens=initial_approx_input_tokens)
658
- events = _stream_runtime_events(payload=payload, openai_api_key=openai_api_key,
912
+ events = _stream_runtime_events(payload=payload, connection=connection,
659
913
  session_key=session_key, provider_messages=provider_messages, prompts=prompts, skills=skills,
660
914
  retrieval_settings=retrieval_settings, frozen_scope=frozen_scope, tagging_run=tagging_run,
661
915
  web_settings=web_settings, earlier_actions=earlier_actions,
@@ -673,16 +927,16 @@ def stream_ai_chat(
673
927
 
674
928
 
675
929
  async def _stream_runtime_events(
676
- *, payload: AiChatRequest, openai_api_key: str, session_key: str,
930
+ *, payload: AiChatRequest, connection: AiConnection, session_key: str,
677
931
  provider_messages: list[dict[str, str]], prompts: AgentPromptSet, skills: AgentSkillSet,
678
932
  retrieval_settings: AgentRetrievalSettings, frozen_scope: ScopedSearchSnapshot,
679
933
  tagging_run: TaggingRun, initial_input_tokens: int, web_settings: AgentWebSettings,
680
934
  earlier_actions: list[dict[str, object]],
681
935
  ) -> AsyncIterator[dict[str, object]]:
682
- action, start_label = 'provider_runtime', 'Connecting to OpenAI API'
683
- ready_label = 'OpenAI API ready · 1,050,000-token context'
684
- base_url = OPENAI_API_BASE_URL
685
- runtime = _agent_runtime(inference=OpenAIInferenceAdapter(api_key=openai_api_key, cost_tracker=openai_cost_tracker))
936
+ action, start_label = 'provider_runtime', f'Connecting to {connection.label} API'
937
+ ready_label = connection.ready_label
938
+ base_url = connection.base_url
939
+ runtime = _agent_runtime(inference=connection.inference)
686
940
  yield dict(type='action_status', action=action, status='started', label=start_label,
687
941
  approx_input_tokens=initial_input_tokens, output_tokens_received=0, duration_ms=0.0)
688
942
  yield dict(type='action_status', action=action, status='completed', label=ready_label,