iatoolkit 2.54.1__tar.gz → 2.56.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 (256) hide show
  1. {iatoolkit-2.54.1/src/iatoolkit.egg-info → iatoolkit-2.56.0}/PKG-INFO +1 -1
  2. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/__init__.py +1 -1
  3. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/common/interfaces/signup_policy_resolver.py +17 -0
  4. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/common/routes.py +9 -0
  5. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/config/system_tools_pack.yaml +6 -0
  6. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/locales/en.yaml +25 -0
  7. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/locales/es.yaml +25 -0
  8. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/services/auth_service.py +78 -0
  9. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/services/configuration_service.py +8 -2
  10. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/services/profile_service.py +131 -10
  11. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/services/system_tools.py +5 -2
  12. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/templates/_login_widget.html +4 -0
  13. iatoolkit-2.56.0/src/iatoolkit/templates/login_global.html +76 -0
  14. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/templates/signup.html +3 -1
  15. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/views/home_view.py +7 -0
  16. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/views/login_view.py +131 -0
  17. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/views/signup_view.py +48 -13
  18. {iatoolkit-2.54.1 → iatoolkit-2.56.0/src/iatoolkit.egg-info}/PKG-INFO +1 -1
  19. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit.egg-info/SOURCES.txt +1 -0
  20. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/LICENSE +0 -0
  21. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/LICENSE_COMMUNITY.md +0 -0
  22. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/pyproject.toml +0 -0
  23. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/readme.md +0 -0
  24. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/setup.cfg +0 -0
  25. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/base_company.py +0 -0
  26. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/cli_commands.py +0 -0
  27. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/common/__init__.py +0 -0
  28. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/common/bridge_validation.py +0 -0
  29. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/common/exceptions.py +0 -0
  30. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/common/interfaces/__init__.py +0 -0
  31. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/common/interfaces/asset_storage.py +0 -0
  32. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/common/interfaces/database_provider.py +0 -0
  33. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/common/interfaces/memory_compilation_trigger.py +0 -0
  34. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/common/interfaces/memory_lint_trigger.py +0 -0
  35. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/common/interfaces/secret_provider.py +0 -0
  36. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/common/interfaces/web_search_provider.py +0 -0
  37. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/common/model_registry.py +0 -0
  38. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/common/secret_resolver.py +0 -0
  39. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/common/session_manager.py +0 -0
  40. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/common/text_splitter.py +0 -0
  41. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/common/url_safety.py +0 -0
  42. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/common/util.py +0 -0
  43. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/company_registry.py +0 -0
  44. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/config/__init__.py +0 -0
  45. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/config/llm_capabilities.yaml +0 -0
  46. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/config/system_prompts/channel_business_identity.prompt +0 -0
  47. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/config/system_prompts/channel_conversation_continuity.prompt +0 -0
  48. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/config/system_prompts/channel_response_style.prompt +0 -0
  49. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/config/system_prompts/chat_state_rules.prompt +0 -0
  50. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/config/system_prompts/chat_user_profile.prompt +0 -0
  51. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/config/system_prompts/core_identity.prompt +0 -0
  52. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/config/system_prompts/email_output.prompt +0 -0
  53. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/config/system_prompts/file_download_output.prompt +0 -0
  54. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/config/system_prompts/format_styles.prompt +0 -0
  55. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/config/system_prompts/html_structures.prompt +0 -0
  56. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/config/system_prompts/links_documents.prompt +0 -0
  57. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/config/system_prompts/memory_usage.prompt +0 -0
  58. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/config/system_prompts/multimodal_basics.prompt +0 -0
  59. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/config/system_prompts/output_basics.prompt +0 -0
  60. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/config/system_prompts/presentation_formatting.prompt +0 -0
  61. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/config/system_prompts/query_main.prompt +0 -0
  62. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/config/system_prompts/sql_aggregation_scope.prompt +0 -0
  63. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/config/system_prompts/sql_casting.prompt +0 -0
  64. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/config/system_prompts/sql_core.prompt +0 -0
  65. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/config/system_prompts/sql_jsonb.prompt +0 -0
  66. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/config/system_prompts/sql_mysql_casting.prompt +0 -0
  67. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/config/system_prompts/sql_mysql_json.prompt +0 -0
  68. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/config/system_prompts/sql_redshift_basics.prompt +0 -0
  69. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/config/system_prompts/sql_redshift_types.prompt +0 -0
  70. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/config/system_prompts/sql_rules.prompt +0 -0
  71. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/config/system_prompts/tool_html_passthrough.prompt +0 -0
  72. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/config/system_prompts_pack.yaml +0 -0
  73. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/core.py +0 -0
  74. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/infra/__init__.py +0 -0
  75. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/infra/brevo_mail_app.py +0 -0
  76. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/infra/call_service.py +0 -0
  77. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/infra/connectors/__init__.py +0 -0
  78. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/infra/connectors/file_connector.py +0 -0
  79. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/infra/connectors/file_connector_factory.py +0 -0
  80. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/infra/connectors/google_cloud_storage_connector.py +0 -0
  81. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/infra/connectors/google_drive_connector.py +0 -0
  82. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/infra/connectors/local_file_connector.py +0 -0
  83. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/infra/connectors/s3_connector.py +0 -0
  84. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/infra/google_auth_client.py +0 -0
  85. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/infra/google_chat_app.py +0 -0
  86. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/infra/inference_embeddings_client.py +0 -0
  87. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/infra/inference_handler.py +0 -0
  88. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/infra/jina_embeddings_client.py +0 -0
  89. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/infra/llm_gateway_resolver.py +0 -0
  90. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/infra/llm_providers/__init__.py +0 -0
  91. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/infra/llm_providers/anthropic_adapter.py +0 -0
  92. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/infra/llm_providers/deepseek_adapter.py +0 -0
  93. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/infra/llm_providers/gemini_adapter.py +0 -0
  94. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/infra/llm_providers/openai_adapter.py +0 -0
  95. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/infra/llm_providers/openai_compatible_chat_adapter.py +0 -0
  96. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/infra/llm_providers/openrouter_adapter.py +0 -0
  97. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/infra/llm_proxy.py +0 -0
  98. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/infra/llm_response.py +0 -0
  99. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/infra/redis_session_manager.py +0 -0
  100. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/repositories/__init__.py +0 -0
  101. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/repositories/api_key_repo.py +0 -0
  102. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/repositories/bridge_repo.py +0 -0
  103. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/repositories/database_manager.py +0 -0
  104. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/repositories/document_repo.py +0 -0
  105. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/repositories/env_secret_provider.py +0 -0
  106. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/repositories/filesystem_asset_repository.py +0 -0
  107. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/repositories/knowledge_wiki_repo.py +0 -0
  108. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/repositories/llm_query_repo.py +0 -0
  109. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/repositories/mcp_token_repo.py +0 -0
  110. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/repositories/memory_repo.py +0 -0
  111. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/repositories/models.py +0 -0
  112. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/repositories/profile_repo.py +0 -0
  113. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/repositories/prompt_resource_repo.py +0 -0
  114. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/repositories/sql_dataset_repo.py +0 -0
  115. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/repositories/sql_source_repo.py +0 -0
  116. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/repositories/vs_repo.py +0 -0
  117. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/runtime_logging.py +0 -0
  118. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/services/__init__.py +0 -0
  119. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/services/api_key_service.py +0 -0
  120. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/services/attachment_policy_service.py +0 -0
  121. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/services/branding_service.py +0 -0
  122. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/services/company_context_service.py +0 -0
  123. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/services/context_builder_service.py +0 -0
  124. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/services/dispatcher_service.py +0 -0
  125. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/services/embedding_service.py +0 -0
  126. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/services/excel_service.py +0 -0
  127. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/services/file_processor_service.py +0 -0
  128. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/services/history_manager_service.py +0 -0
  129. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/services/http_tool_service.py +0 -0
  130. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/services/i18n_service.py +0 -0
  131. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/services/inference_service.py +0 -0
  132. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/services/jwt_service.py +0 -0
  133. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/services/knowledge_base_service.py +0 -0
  134. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/services/language_service.py +0 -0
  135. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/services/license_service.py +0 -0
  136. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/services/llm_client_service.py +0 -0
  137. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/services/mail_service.py +0 -0
  138. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/services/markdown_wiki_service.py +0 -0
  139. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/services/mcp_token_service.py +0 -0
  140. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/services/memory_compiler_service.py +0 -0
  141. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/services/memory_lint_service.py +0 -0
  142. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/services/memory_lookup_policy_service.py +0 -0
  143. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/services/memory_service.py +0 -0
  144. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/services/memory_wiki_service.py +0 -0
  145. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/services/noop_memory_compilation_trigger.py +0 -0
  146. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/services/noop_memory_lint_trigger.py +0 -0
  147. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/services/operator_identity_service.py +0 -0
  148. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/services/parsers/__init__.py +0 -0
  149. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/services/parsers/contracts.py +0 -0
  150. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/services/parsers/image_normalizer.py +0 -0
  151. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/services/parsers/parsing_service.py +0 -0
  152. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/services/parsers/pdf_ocr_detection.py +0 -0
  153. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/services/parsers/provider_factory.py +0 -0
  154. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/services/parsers/provider_resolver.py +0 -0
  155. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/services/parsers/providers/__init__.py +0 -0
  156. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/services/parsers/providers/basic_provider.py +0 -0
  157. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/services/parsers/providers/docling_provider.py +0 -0
  158. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/services/parsers/validator.py +0 -0
  159. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/services/pdf_service.py +0 -0
  160. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/services/prompt_resource_service.py +0 -0
  161. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/services/prompt_service.py +0 -0
  162. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/services/query_service.py +0 -0
  163. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/services/signup_policy_resolver.py +0 -0
  164. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/services/sql_dataset_service.py +0 -0
  165. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/services/sql_service.py +0 -0
  166. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/services/sql_source_service.py +0 -0
  167. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/services/storage_service.py +0 -0
  168. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/services/structured_output_service.py +0 -0
  169. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/services/system_prompt_catalog.py +0 -0
  170. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/services/telemetry_service.py +0 -0
  171. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/services/tenant_wiki_service.py +0 -0
  172. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/services/tool_output_contract.py +0 -0
  173. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/services/tool_service.py +0 -0
  174. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/services/user_feedback_service.py +0 -0
  175. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/services/user_session_context_service.py +0 -0
  176. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/services/visual_kb_service.py +0 -0
  177. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/services/visual_tool_service.py +0 -0
  178. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/services/warmup_service.py +0 -0
  179. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/services/web_search/__init__.py +0 -0
  180. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/services/web_search/provider_factory.py +0 -0
  181. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/services/web_search/providers/__init__.py +0 -0
  182. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/services/web_search/providers/brave_provider.py +0 -0
  183. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/services/web_search_service.py +0 -0
  184. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/static/images/fernando.jpeg +0 -0
  185. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/static/images/iatoolkit_core.png +0 -0
  186. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/static/images/iatoolkit_logo.png +0 -0
  187. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/static/js/chat_feedback_button.js +0 -0
  188. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/static/js/chat_filepond.js +0 -0
  189. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/static/js/chat_header_navigation.js +0 -0
  190. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/static/js/chat_help_content.js +0 -0
  191. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/static/js/chat_history_button.js +0 -0
  192. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/static/js/chat_logout_button.js +0 -0
  193. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/static/js/chat_main.js +0 -0
  194. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/static/js/chat_memory_button.js +0 -0
  195. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/static/js/chat_model_selector.js +0 -0
  196. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/static/js/chat_onboarding_button.js +0 -0
  197. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/static/js/chat_prompt_manager.js +0 -0
  198. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/static/js/chat_reload_button.js +0 -0
  199. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/static/styles/account.css +0 -0
  200. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/static/styles/chat_iatoolkit.css +0 -0
  201. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/static/styles/chat_modal.css +0 -0
  202. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/static/styles/chat_public.css +0 -0
  203. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/static/styles/documents.css +0 -0
  204. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/static/styles/landing_page.css +0 -0
  205. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/static/styles/llm_output.css +0 -0
  206. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/static/styles/onboarding.css +0 -0
  207. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/templates/_chat_header.html +0 -0
  208. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/templates/_company_header.html +0 -0
  209. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/templates/account.html +0 -0
  210. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/templates/base.html +0 -0
  211. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/templates/change_password.html +0 -0
  212. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/templates/chat.html +0 -0
  213. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/templates/chat_memory_modal.html +0 -0
  214. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/templates/chat_modals.html +0 -0
  215. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/templates/error.html +0 -0
  216. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/templates/forgot_password.html +0 -0
  217. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/templates/home_hosted_default.html +0 -0
  218. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/templates/memory/wiki_schema.md +0 -0
  219. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/templates/onboarding_shell.html +0 -0
  220. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/templates/pdf/base.html +0 -0
  221. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/templates/pdf/letter.html +0 -0
  222. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/templates/pdf/report.html +0 -0
  223. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/templates/pdf/simple.html +0 -0
  224. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/views/__init__.py +0 -0
  225. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/views/account_view.py +0 -0
  226. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/views/api_key_api_view.py +0 -0
  227. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/views/base_login_view.py +0 -0
  228. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/views/categories_api_view.py +0 -0
  229. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/views/change_password_view.py +0 -0
  230. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/views/chat_context_preview_api_view.py +0 -0
  231. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/views/chat_view.py +0 -0
  232. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/views/configuration_api_view.py +0 -0
  233. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/views/connectors_api_view.py +0 -0
  234. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/views/embedding_api_view.py +0 -0
  235. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/views/forgot_password_view.py +0 -0
  236. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/views/help_content_api_view.py +0 -0
  237. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/views/history_api_view.py +0 -0
  238. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/views/init_context_api_view.py +0 -0
  239. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/views/llmquery_api_view.py +0 -0
  240. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/views/load_document_api_view.py +0 -0
  241. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/views/logout_api_view.py +0 -0
  242. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/views/memory_api_view.py +0 -0
  243. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/views/profile_api_view.py +0 -0
  244. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/views/prompt_api_view.py +0 -0
  245. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/views/prompt_context_preview_api_view.py +0 -0
  246. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/views/prompt_resource_api_view.py +0 -0
  247. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/views/rag_api_view.py +0 -0
  248. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/views/root_redirect_view.py +0 -0
  249. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/views/static_page_view.py +0 -0
  250. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/views/tool_api_view.py +0 -0
  251. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/views/user_feedback_api_view.py +0 -0
  252. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/views/users_api_view.py +0 -0
  253. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit/views/verify_user_view.py +0 -0
  254. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit.egg-info/dependency_links.txt +0 -0
  255. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit.egg-info/requires.txt +0 -0
  256. {iatoolkit-2.54.1 → iatoolkit-2.56.0}/src/iatoolkit.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: iatoolkit
3
- Version: 2.54.1
3
+ Version: 2.56.0
4
4
  Summary: IAToolkit
5
5
  Author: Fernando Libedinsky
6
6
  License-Expression: MIT
@@ -3,7 +3,7 @@
3
3
  #
4
4
  # IAToolkit is open source software.
5
5
 
6
- __version__ = "2.54.1"
6
+ __version__ = "2.56.0"
7
7
 
8
8
  # Expose main classes and functions at the top level of the package
9
9
 
@@ -31,3 +31,20 @@ class SignupPolicyResolver(ABC):
31
31
  invite_token: str | None = None,
32
32
  ) -> SignupPolicyDecision:
33
33
  """Evaluates whether the email can sign up for the target company."""
34
+
35
+ def requires_invitation(self, company_short_name: str) -> bool:
36
+ return False
37
+
38
+ def invitation_email(self, company_short_name: str, invite_token: str | None) -> str | None:
39
+ return None
40
+
41
+ def claim_signup(
42
+ self,
43
+ company_short_name: str,
44
+ email: str,
45
+ invite_token: str | None = None,
46
+ *,
47
+ verified_email: bool = False,
48
+ ) -> bool:
49
+ """Claim an invitation in the same transaction that creates membership."""
50
+ return True
@@ -33,6 +33,8 @@ def register_views(app):
33
33
  LoginView,
34
34
  GoogleLoginStartView,
35
35
  GoogleLoginCallbackView,
36
+ GlobalLoginView,
37
+ GlobalGoogleLoginStartView,
36
38
  FinalizeContextView,
37
39
  )
38
40
  from iatoolkit.views.configuration_api_view import ConfigurationApiView, ValidateConfigurationApiView
@@ -74,6 +76,13 @@ def register_views(app):
74
76
  view_func=GoogleLoginCallbackView.as_view('login_google_callback')
75
77
  )
76
78
 
79
+ # company-less login: identifies the person, the caller picks the company
80
+ app.add_url_rule('/auth/login', view_func=GlobalLoginView.as_view('global_login'))
81
+ app.add_url_rule(
82
+ '/auth/login/google',
83
+ view_func=GlobalGoogleLoginStartView.as_view('global_login_google_start')
84
+ )
85
+
77
86
  # Chat Route (Direct Access)
78
87
  app.add_url_rule('/<company_short_name>/chat',
79
88
  view_func=ChatView.as_view('chat'))
@@ -129,8 +129,11 @@ tools:
129
129
  - function_name: iat_send_email
130
130
  description: iatoolkit mail system. envia mails cuando un usuario lo solicita.
131
131
  # Sends a real email: simulated, never sent, when an evaluation replays an agent.
132
+ # Not destructive (it changes nothing that existed) and not open-world for MCP
133
+ # annotations: it goes through the tenant's own mail service.
132
134
  execution_config:
133
135
  side_effects: true
136
+ destructive: false
134
137
  routing_profile:
135
138
  tags:
136
139
  - email
@@ -559,6 +562,9 @@ tools:
559
562
  - metadata_filter
560
563
  - function_name: iat_web_search
561
564
  description: Realiza una búsqueda web externa para obtener información pública actualizada.
565
+ # Reaches the public internet: openWorldHint in MCP annotations.
566
+ execution_config:
567
+ open_world: true
562
568
  routing_profile:
563
569
  tags:
564
570
  - web
@@ -274,6 +274,10 @@ ui:
274
274
  pending_review: "Pending review"
275
275
  revoked: "Revoked"
276
276
 
277
+ global_login:
278
+ title: "Sign in to IAToolkit"
279
+ intro: "Sign in with the account you use in IAToolkit. You will choose the company next."
280
+
277
281
  login_widget:
278
282
  title: "Sign In"
279
283
  welcome_message: "Enter your credentials or register to access this platform."
@@ -287,6 +291,7 @@ ui:
287
291
  forgot_password_link: "Forgot your password?"
288
292
  no_account_prompt: "Don't have an account?"
289
293
  signup_link: "Sign Up"
294
+ invitation_prompt: "Need access? Ask your administrator for an invitation."
290
295
 
291
296
  signup:
292
297
  title: "Create an Account"
@@ -607,6 +612,25 @@ ui:
607
612
  status_closed: "Closed"
608
613
  admin:
609
614
  workspace: "Workspace"
615
+ team_invitations: "Invitations"
616
+ team_invite_user: "Invite user"
617
+ team_invite_none: "No invitations yet."
618
+ team_invite_loading: "Loading invitations..."
619
+ team_invite_email: "Email"
620
+ team_invite_pending: "Pending"
621
+ team_invite_accepted: "Accepted"
622
+ team_invite_expired: "Expired"
623
+ team_invite_revoked_status: "Revoked"
624
+ team_invite_status: "Status"
625
+ team_invite_invited_by: "Invited by"
626
+ team_invite_expires: "Expires"
627
+ team_invite_revoke: "Revoke"
628
+ team_invite_revoked: "Invitation revoked."
629
+ team_invite_load_error: "Could not load invitations."
630
+ team_invite_revoke_error: "Could not revoke invitation."
631
+ team_policy_note: "Choose domain access or require an invitation for each new member."
632
+ team_policy_static_note: "Signup policies are enforced when the company runs in hosted mode."
633
+ team_policy_invite_only: "Invitation only"
610
634
  configuration: "Configuration"
611
635
  error: "Error"
612
636
  company_config: "Company Configuration (company.yaml)"
@@ -2582,6 +2606,7 @@ errors:
2582
2606
  user_already_registered: "The user with email '{email}' already exists in this company."
2583
2607
  password_mismatch: "The passwords do not match. Please try again."
2584
2608
  signup_not_allowed: "Signup is not allowed for this company. Please contact an administrator."
2609
+ invitation_required: "A valid invitation is required to join this company. Ask your administrator for a new link."
2585
2610
  change_password:
2586
2611
  token_expired: "The password change link has expired. Please request a new one."
2587
2612
  password_mismatch: "The passwords do not match. Please try again."
@@ -288,6 +288,10 @@ ui:
288
288
  revoked: "Revocado"
289
289
 
290
290
 
291
+ global_login:
292
+ title: "Iniciar sesión en IAToolkit"
293
+ intro: "Ingresa con la cuenta que usas en IAToolkit. Después podrás elegir la empresa."
294
+
291
295
  login_widget:
292
296
  title: "Iniciar Sesión"
293
297
  welcome_message: "Ingresa tus credenciales o registrate para acceder a la plataforma."
@@ -301,6 +305,7 @@ ui:
301
305
  forgot_password_link: "¿Olvidaste tu contraseña?"
302
306
  no_account_prompt: "¿No tienes una cuenta?"
303
307
  signup_link: "Regístrate"
308
+ invitation_prompt: "¿Necesitas acceso? Solicita una invitación al administrador."
304
309
 
305
310
  signup:
306
311
  title: "Crear una Cuenta"
@@ -621,6 +626,25 @@ ui:
621
626
  status_closed: "Cerrada"
622
627
  admin:
623
628
  workspace: "Recursos"
629
+ team_invitations: "Invitaciones"
630
+ team_invite_user: "Invitar usuario"
631
+ team_invite_none: "Todavía no hay invitaciones."
632
+ team_invite_loading: "Cargando invitaciones..."
633
+ team_invite_email: "Correo"
634
+ team_invite_pending: "Pendiente"
635
+ team_invite_accepted: "Aceptada"
636
+ team_invite_expired: "Vencida"
637
+ team_invite_revoked_status: "Revocada"
638
+ team_invite_status: "Estado"
639
+ team_invite_invited_by: "Invitado por"
640
+ team_invite_expires: "Vence"
641
+ team_invite_revoke: "Revocar"
642
+ team_invite_revoked: "Invitación revocada."
643
+ team_invite_load_error: "No se pudieron cargar las invitaciones."
644
+ team_invite_revoke_error: "No se pudo revocar la invitación."
645
+ team_policy_note: "Permite el acceso por dominio o exige una invitación para cada nuevo miembro."
646
+ team_policy_static_note: "Las políticas de registro se aplican cuando la empresa funciona en modo alojado."
647
+ team_policy_invite_only: "Solo con invitación"
624
648
  configuration: "Configuración"
625
649
  error: "Error"
626
650
  company_config: "Configuración Empresa (company.yaml)"
@@ -2601,6 +2625,7 @@ errors:
2601
2625
  user_already_registered: "El usuario con email '{email}' ya existe en esta empresa."
2602
2626
  password_mismatch: "Las contraseñas no coinciden. Por favor, inténtalo de nuevo."
2603
2627
  signup_not_allowed: "El registro no está habilitado para esta empresa. Contacta a un administrador."
2628
+ invitation_required: "Necesitas una invitación vigente para unirte a esta empresa. Solicita un nuevo enlace al administrador."
2604
2629
  change_password:
2605
2630
  token_expired: "El enlace de cambio de contraseña ha expirado. Por favor, solicita uno nuevo."
2606
2631
  password_mismatch: "Las contraseñas no coinciden. Por favor, inténtalo nuevamente."
@@ -22,6 +22,8 @@ class AuthService:
22
22
  Centralized service for handling authentication for all incoming requests.
23
23
  It determines the user's identity based on either a Flask session cookie or an API Key.
24
24
  """
25
+ # access-log scope for logins that are not tied to a company
26
+ GLOBAL_ACCESS_SCOPE = '*'
25
27
 
26
28
  @inject
27
29
  def __init__(self, profile_service: ProfileService,
@@ -186,6 +188,82 @@ class AuthService:
186
188
 
187
189
  return auth_response
188
190
 
191
+ def login_global_local_user(self, email: str, password: str) -> dict:
192
+ # company-less login: authenticates the person, the company is chosen later
193
+ auth_response = self.profile_service.authenticate_local_credentials(email=email, password=password)
194
+ if not auth_response.get('success'):
195
+ self.log_access(
196
+ company_short_name=self.GLOBAL_ACCESS_SCOPE,
197
+ user_identifier=email,
198
+ auth_type='local',
199
+ outcome='failure',
200
+ reason_code='INVALID_CREDENTIALS',
201
+ )
202
+ return auth_response
203
+
204
+ self.profile_service.set_global_identity(auth_response['user_identifier'], 'local')
205
+ self.log_access(
206
+ company_short_name=self.GLOBAL_ACCESS_SCOPE,
207
+ auth_type='local',
208
+ outcome='success',
209
+ user_identifier=auth_response['user_identifier'],
210
+ )
211
+ return auth_response
212
+
213
+ def login_global_google_user(self, code: str, state: str, nonce: str, redirect_uri: str) -> dict:
214
+ try:
215
+ google_identity = self.google_auth_client.exchange_code_for_identity(
216
+ code=code,
217
+ state=state,
218
+ nonce=nonce,
219
+ redirect_uri=redirect_uri,
220
+ )
221
+ except GoogleAuthError as exc:
222
+ self.log_access(
223
+ company_short_name=self.GLOBAL_ACCESS_SCOPE,
224
+ auth_type='google',
225
+ outcome='failure',
226
+ reason_code=exc.reason_code,
227
+ )
228
+ return {
229
+ 'success': False,
230
+ 'reason_code': exc.reason_code,
231
+ 'message': self.i18n_service.t(exc.message_key),
232
+ }
233
+ except Exception:
234
+ logging.exception("Unexpected Google auth client failure in company-less login.")
235
+ self.log_access(
236
+ company_short_name=self.GLOBAL_ACCESS_SCOPE,
237
+ auth_type='google',
238
+ outcome='failure',
239
+ reason_code='GOOGLE_AUTH_UNEXPECTED_ERROR',
240
+ )
241
+ return {
242
+ 'success': False,
243
+ 'reason_code': 'GOOGLE_AUTH_UNEXPECTED_ERROR',
244
+ 'message': self.i18n_service.t('errors.auth.google_login_failed'),
245
+ }
246
+
247
+ auth_response = self.profile_service.identify_google_user(google_identity)
248
+ if not auth_response.get('success'):
249
+ self.log_access(
250
+ company_short_name=self.GLOBAL_ACCESS_SCOPE,
251
+ auth_type='google',
252
+ outcome='failure',
253
+ reason_code=auth_response.get('reason_code', 'GOOGLE_LOGIN_FAILED'),
254
+ user_identifier=google_identity.email,
255
+ )
256
+ return auth_response
257
+
258
+ self.profile_service.set_global_identity(auth_response['user_identifier'], 'google')
259
+ self.log_access(
260
+ company_short_name=self.GLOBAL_ACCESS_SCOPE,
261
+ auth_type='google',
262
+ outcome='success',
263
+ user_identifier=auth_response['user_identifier'],
264
+ )
265
+ return auth_response
266
+
189
267
  def verify(self, anonymous: bool = False, company_short_name: str = None) -> dict:
190
268
  """
191
269
  Verifies the current request and identifies the user.
@@ -794,8 +794,14 @@ class ConfigurationService:
794
794
  if execution_config is not None:
795
795
  if not isinstance(execution_config, dict):
796
796
  add_error(f"tools[{i}].execution_config", "Must be a dictionary.")
797
- elif "side_effects" in execution_config and not isinstance(execution_config["side_effects"], bool):
798
- add_error(f"tools[{i}].execution_config.side_effects", "Must be true or false.")
797
+ else:
798
+ # side_effects drives evaluations; destructive / open_world / title
799
+ # feed the MCP tool annotations.
800
+ for flag in ("side_effects", "destructive", "open_world"):
801
+ if flag in execution_config and not isinstance(execution_config[flag], bool):
802
+ add_error(f"tools[{i}].execution_config.{flag}", "Must be true or false.")
803
+ if "title" in execution_config and not isinstance(execution_config["title"], str):
804
+ add_error(f"tools[{i}].execution_config.title", "Must be a string.")
799
805
 
800
806
  # 6. Prompts
801
807
  prompt_list, categories_config = self._get_prompt_config(config)
@@ -21,6 +21,7 @@ import random
21
21
  import re
22
22
  import string
23
23
  import logging
24
+ import time
24
25
  from html import escape
25
26
  from datetime import datetime
26
27
  from typing import List, Dict
@@ -30,6 +31,8 @@ from iatoolkit.services.signup_policy_resolver import AllowAllSignupPolicyResolv
30
31
 
31
32
 
32
33
  class ProfileService:
34
+ GLOBAL_IDENTITY_SESSION_KEY = 'global_identity'
35
+
33
36
  @inject
34
37
  def __init__(self,
35
38
  i18n_service: I18nService,
@@ -65,6 +68,16 @@ class ProfileService:
65
68
  except Exception as rollback_error:
66
69
  logging.warning(f"ProfileService rollback failed: {rollback_error}")
67
70
 
71
+ def signup_invitation_context(
72
+ self, company_short_name: str, invite_token: str | None,
73
+ ) -> tuple[bool, str | None]:
74
+ required = self.signup_policy_resolver.requires_invitation(company_short_name)
75
+ email = (
76
+ self.signup_policy_resolver.invitation_email(company_short_name, invite_token)
77
+ if invite_token else None
78
+ )
79
+ return required, email
80
+
68
81
  @staticmethod
69
82
  def _normalize_name_value(value: str | None, fallback: str) -> str:
70
83
  text = str(value or "").strip()
@@ -159,22 +172,110 @@ class ProfileService:
159
172
  if not str(user.last_name or "").strip():
160
173
  user.last_name = last_name
161
174
 
162
- def login(self, company_short_name: str, email: str, password: str) -> dict:
175
+ def _check_local_credentials(self, email: str, password: str) -> tuple[User | None, dict | None]:
176
+ # check if user exists
177
+ user = self.profile_repo.get_user_by_email(email)
178
+ if not user:
179
+ return None, {'success': False, 'message': self.i18n_service.t('errors.auth.user_not_found')}
180
+
181
+ if self._is_google_auth_user(user):
182
+ return None, {
183
+ 'success': False,
184
+ 'message': self.i18n_service.t('errors.auth.google_account_requires_google_login')
185
+ }
186
+
187
+ # check the encrypted password
188
+ if not user.password or not check_password_hash(user.password, password):
189
+ return None, {'success': False, 'message': self.i18n_service.t('errors.auth.invalid_password')}
190
+
191
+ return user, None
192
+
193
+ def authenticate_local_credentials(self, email: str, password: str) -> dict:
194
+ """
195
+ Checks email and password without tying the user to a company and
196
+ without opening a company session. Used by the company-less login,
197
+ where the company is chosen afterwards among the user's memberships.
198
+ """
163
199
  try:
164
- # check if user exists
200
+ user, error = self._check_local_credentials(email, password)
201
+ if error:
202
+ return error
203
+
204
+ if not user.verified:
205
+ return {'success': False,
206
+ "message": self.i18n_service.t('errors.services.account_not_verified')}
207
+
208
+ return {'success': True, 'user_identifier': user.email}
209
+ except Exception as e:
210
+ self._safe_rollback()
211
+ logging.error(f"Error in authenticate_local_credentials: {e}")
212
+ return {'success': False, "message": str(e)}
213
+
214
+ def identify_google_user(self, google_identity: GoogleIdentity) -> dict:
215
+ """
216
+ Resolves an existing user from a Google identity without creating the
217
+ user, linking the Google account or adding a company membership: the
218
+ company-less login only lets in people who already belong somewhere.
219
+ """
220
+ email = str(google_identity.email or "").strip().lower()
221
+ if not email or not google_identity.email_verified:
222
+ return {
223
+ "success": False,
224
+ "message": self.i18n_service.t('errors.auth.google_email_not_verified'),
225
+ "reason_code": "GOOGLE_EMAIL_NOT_VERIFIED",
226
+ }
227
+
228
+ user = self.profile_repo.get_user_by_google_sub(google_identity.subject)
229
+ if user:
230
+ if str(user.email or "").strip().lower() != email:
231
+ return {
232
+ "success": False,
233
+ "message": self.i18n_service.t('errors.auth.google_account_email_changed'),
234
+ "reason_code": "GOOGLE_EMAIL_CHANGED",
235
+ }
236
+ else:
165
237
  user = self.profile_repo.get_user_by_email(email)
166
238
  if not user:
167
- return {'success': False, 'message': self.i18n_service.t('errors.auth.user_not_found')}
168
-
169
- if self._is_google_auth_user(user):
170
239
  return {
171
- 'success': False,
172
- 'message': self.i18n_service.t('errors.auth.google_account_requires_google_login')
240
+ "success": False,
241
+ "message": self.i18n_service.t('errors.auth.user_not_found'),
242
+ "reason_code": "USER_NOT_FOUND",
243
+ }
244
+ if self._is_google_auth_user(user) and user.google_sub and user.google_sub != google_identity.subject:
245
+ return {
246
+ "success": False,
247
+ "message": self.i18n_service.t('errors.auth.google_account_conflict'),
248
+ "reason_code": "GOOGLE_ACCOUNT_CONFLICT",
173
249
  }
174
250
 
175
- # check the encrypted password
176
- if not user.password or not check_password_hash(user.password, password):
177
- return {'success': False, 'message': self.i18n_service.t('errors.auth.invalid_password')}
251
+ return {"success": True, "user_identifier": user.email}
252
+
253
+ def set_global_identity(self, user_identifier: str, auth_method: str):
254
+ """
255
+ Records who authenticated through the company-less login. It is not a
256
+ company session: no profile is built and no company page trusts it.
257
+ """
258
+ SessionManager.set_permanent(True)
259
+ SessionManager.set(self.GLOBAL_IDENTITY_SESSION_KEY, {
260
+ 'user_identifier': user_identifier,
261
+ 'auth_method': auth_method,
262
+ 'authenticated_at': int(time.time()),
263
+ })
264
+
265
+ def get_global_identity(self) -> dict | None:
266
+ identity = SessionManager.get(self.GLOBAL_IDENTITY_SESSION_KEY)
267
+ if not isinstance(identity, dict) or not identity.get('user_identifier'):
268
+ return None
269
+ return identity
270
+
271
+ def clear_global_identity(self):
272
+ SessionManager.remove(self.GLOBAL_IDENTITY_SESSION_KEY)
273
+
274
+ def login(self, company_short_name: str, email: str, password: str) -> dict:
275
+ try:
276
+ user, error = self._check_local_credentials(email, password)
277
+ if error:
278
+ return error
178
279
 
179
280
  company = self.profile_repo.get_company_by_short_name(company_short_name)
180
281
  if not company:
@@ -488,6 +589,7 @@ class ProfileService:
488
589
  )
489
590
  policy_metadata = dict(policy_decision.metadata or {})
490
591
  if not policy_decision.allowed:
592
+ self._safe_rollback()
491
593
  logging.info(
492
594
  "Google login blocked by signup policy. company=%s email=%s policy=%s metadata=%s",
493
595
  company_short_name,
@@ -506,6 +608,15 @@ class ProfileService:
506
608
  "message": message,
507
609
  "reason_code": "SIGNUP_NOT_ALLOWED",
508
610
  }
611
+ if not self.signup_policy_resolver.claim_signup(
612
+ company_short_name, email, verified_email=True,
613
+ ):
614
+ self._safe_rollback()
615
+ return {
616
+ "success": False,
617
+ "message": self.i18n_service.t('errors.signup.signup_not_allowed'),
618
+ "reason_code": "SIGNUP_NOT_ALLOWED",
619
+ }
509
620
  user.companies.append(company)
510
621
  provisioning_outcome = "company_associated" if user.id is not None else provisioning_outcome
511
622
  persisted = True
@@ -664,6 +775,11 @@ class ProfileService:
664
775
  return {"error": self.i18n_service.t('errors.signup.user_already_registered', email=email)}
665
776
  else:
666
777
  # add new company to existing user
778
+ if not self.signup_policy_resolver.claim_signup(
779
+ company_short_name, email, invite_token,
780
+ ):
781
+ self._safe_rollback()
782
+ return {"error": self.i18n_service.t('errors.signup.signup_not_allowed')}
667
783
  existing_user.companies.append(company)
668
784
  self.profile_repo.save_user(existing_user)
669
785
  self._promote_bootstrap_owner_if_needed(company, existing_user)
@@ -699,6 +815,11 @@ class ProfileService:
699
815
  )
700
816
 
701
817
  # associate new company to user
818
+ if not self.signup_policy_resolver.claim_signup(
819
+ company_short_name, email, invite_token,
820
+ ):
821
+ self._safe_rollback()
822
+ return {"error": self.i18n_service.t('errors.signup.signup_not_allowed')}
702
823
  new_user.companies.append(company)
703
824
 
704
825
  # and create in the database
@@ -60,8 +60,11 @@ def _validate_system_tool_entry(entry: dict, index: int) -> dict:
60
60
  if execution_config is not None:
61
61
  if not isinstance(execution_config, dict):
62
62
  raise ValueError(f"tools[{index}].execution_config must be an object")
63
- if "side_effects" in execution_config and not isinstance(execution_config["side_effects"], bool):
64
- raise ValueError(f"tools[{index}].execution_config.side_effects must be a boolean")
63
+ for flag in ("side_effects", "destructive", "open_world"):
64
+ if flag in execution_config and not isinstance(execution_config[flag], bool):
65
+ raise ValueError(f"tools[{index}].execution_config.{flag} must be a boolean")
66
+ if "title" in execution_config and not isinstance(execution_config["title"], str):
67
+ raise ValueError(f"tools[{index}].execution_config.title must be a string")
65
68
 
66
69
  normalized = {
67
70
  "function_name": function_name,
@@ -54,10 +54,14 @@
54
54
 
55
55
  <!-- 3. Nueva Sección de Registro más Atractiva -->
56
56
  <div class="mt-4 pt-3 text-center" style="border-top: 1px solid #e0e0e0;">
57
+ {% if signup_invitation_required|default(false) %}
58
+ <span class="text-muted small">{{ t('ui.login_widget.invitation_prompt') }}</span>
59
+ {% else %}
57
60
  <span class="text-muted small">{{ t('ui.login_widget.no_account_prompt') }}</span>
58
61
  <a href="{{ url_for('signup', company_short_name=company_short_name, lang=current_lang) }}" id="signup-link"
59
62
  class="fw-bold ms-1 text-decoration-none" style="color: var(--brand-primary-color);">
60
63
  {{ t('ui.login_widget.signup_link') }}</a>
64
+ {% endif %}
61
65
  </div>
62
66
 
63
67
  <!-- 4. Enlace de Recuperación de Contraseña (más sutil) -->
@@ -0,0 +1,76 @@
1
+ {% extends "base.html" %}
2
+
3
+ {% block title %}{{ t('ui.global_login.title') }}{% endblock %}
4
+
5
+ {% block styles %}
6
+ <style>
7
+ /* No company yet, so no company branding: the page wears IAToolkit's own. */
8
+ :root {
9
+ --brand-primary-color: #4A55A2;
10
+ --brand-secondary-color: #3A448A;
11
+ --brand-text-on-primary: #ffffff;
12
+ }
13
+ body { background: linear-gradient(180deg, #ffffff 0%, #f1f3fb 100%); min-height: 100vh; }
14
+ .iat-brand { display: inline-flex; align-items: center; gap: 10px; font-weight: 700; font-size: 1.35rem; color: #1d2541; text-decoration: none; }
15
+ .iat-brand-mark { display: inline-grid; place-items: center; width: 32px; height: 32px; border-radius: 8px; background: #4A55A2; color: #fff; }
16
+ /* Self-contained: this page does not load the company-home stylesheet. */
17
+ .global-login .branded-form-container { background: #fff; border-radius: 12px; padding: 32px; border-top: 4px solid #4A55A2; box-shadow: 0 8px 28px rgba(29, 37, 65, .10); }
18
+ .global-login .btn-branded-primary { background: #4A55A2; border: 1px solid #4A55A2; color: #fff; }
19
+ .global-login .btn-branded-primary:hover { background: #3A448A; border-color: #3A448A; color: #fff; }
20
+ .global-login .form-control:focus { border-color: #4A55A2; box-shadow: 0 0 0 .2rem rgba(74, 85, 162, .18); }
21
+ </style>
22
+ {% endblock %}
23
+
24
+ {% block content %}
25
+ <div class="container my-5 global-login">
26
+ <div class="text-center mb-4">
27
+ <span class="iat-brand">
28
+ <span class="iat-brand-mark" aria-hidden="true"><svg width="16" height="16" viewBox="0 0 16 16" fill="currentColor"><path d="M6 1l1.2 3.6L11 6 7.2 7.4 6 11 4.8 7.4 1 6l3.8-1.4z"/><path d="M12 9l.7 1.8 1.8.7-1.8.7L12 14l-.7-1.8-1.8-.7 1.8-.7z"/></svg></span>
29
+ <span>IAToolkit</span>
30
+ </span>
31
+ </div>
32
+ <div class="row justify-content-center">
33
+ <div class="col-md-6 col-lg-5">
34
+ <div class="branded-form-container">
35
+ <div class="text-center mb-4">
36
+ <h1 class="h4 fw-bold">{{ t('ui.global_login.title') }}</h1>
37
+ <p class="text-muted widget-intro-text">{{ t('ui.global_login.intro') }}</p>
38
+ </div>
39
+
40
+ {% if google_login_enabled|default(false) %}
41
+ <a href="{{ url_for('global_login_google_start', lang=lang, next=login_next) }}"
42
+ class="btn btn-outline-secondary w-100 fw-semibold py-2 d-flex align-items-center justify-content-center gap-2">
43
+ <i class="bi bi-google"></i>
44
+ {{ t('ui.login_widget.google_button') }}
45
+ </a>
46
+
47
+ <div class="d-flex align-items-center gap-3 my-4">
48
+ <div class="flex-grow-1" style="height: 1px; background: #e0e0e0;"></div>
49
+ <span class="text-muted text-uppercase" style="font-size: 0.75rem; letter-spacing: 0.08em;">
50
+ {{ t('ui.login_widget.or_continue_with') }}
51
+ </span>
52
+ <div class="flex-grow-1" style="height: 1px; background: #e0e0e0;"></div>
53
+ </div>
54
+ {% endif %}
55
+
56
+ <form id="global-login-form" action="{{ url_for('global_login', lang=lang) }}" method="post">
57
+ {% if login_next %}<input type="hidden" name="next" value="{{ login_next }}">{% endif %}
58
+ <input type="hidden" name="lang" value="{{ lang }}">
59
+ <div class="mb-3">
60
+ <label for="email" class="form-label d-block">{{ t('ui.signup.email_label') }}</label>
61
+ <input type="email" id="email" name="email" class="form-control"
62
+ required value="{{ form_data.email if form_data is defined else '' }}">
63
+ </div>
64
+ <div class="mb-3">
65
+ <label for="password" class="form-label d-block">{{ t('ui.signup.password_label') }}</label>
66
+ <input type="password" id="password" name="password" class="form-control" required>
67
+ </div>
68
+ <button type="submit" class="btn btn-branded-primary w-100 fw-bold py-2">
69
+ {{ t('ui.login_widget.login_button') }}
70
+ </button>
71
+ </form>
72
+ </div>
73
+ </div>
74
+ </div>
75
+ </div>
76
+ {% endblock %}
@@ -39,11 +39,13 @@
39
39
  {% endif %}
40
40
 
41
41
  <form action="{{ url_for('signup', company_short_name=company_short_name) }}" method="post">
42
+ {% if invite_token %}<input type="hidden" name="invite_token" value="{{ invite_token }}">{% endif %}
42
43
  <div class="mb-3">
43
44
  <label for="email" class="form-label text-secondary">{{ t('ui.signup.email_label') }}</label>
44
45
  <input type="email" autocomplete="off" id="email" name="email"
45
46
  class="form-control" required
46
- value="{{ form_data.email if form_data else '' }}">
47
+ value="{{ invited_email or (form_data.email if form_data else '') }}"
48
+ {% if invited_email %}readonly{% endif %}>
47
49
  </div>
48
50
 
49
51
  <div class="row">
@@ -36,6 +36,11 @@ class HomeView(MethodView):
36
36
  self._trigger_warmup(company_short_name, "home_pre_login")
37
37
 
38
38
  branding_data = self.branding_service.get_company_branding(company_short_name)
39
+ invitation_context = self.profile_service.signup_invitation_context(company_short_name, None)
40
+ login_context = (
41
+ {"signup_invitation_required": True}
42
+ if isinstance(invitation_context, tuple) and invitation_context[0] is True else {}
43
+ )
39
44
 
40
45
  template_name = self.util.get_template_by_language("home")
41
46
  home_template = self.util.get_company_template(company_short_name, template_name)
@@ -52,6 +57,7 @@ class HomeView(MethodView):
52
57
  company_short_name=company_short_name,
53
58
  company=company,
54
59
  branding=branding_data,
60
+ **login_context,
55
61
  )
56
62
 
57
63
  message = self.i18n_service.t(
@@ -70,6 +76,7 @@ class HomeView(MethodView):
70
76
  home_template,
71
77
  company_short_name=company_short_name,
72
78
  branding=branding_data,
79
+ **login_context,
73
80
  )
74
81
  except Exception as e:
75
82
  message = self.i18n_service.t('errors.templates.processing_error', error=str(e))