signalwire-sdk 3.2.0.dev94__tar.gz → 3.2.0.dev96__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 (248) hide show
  1. {signalwire_sdk-3.2.0.dev94/signalwire/signalwire_sdk.egg-info → signalwire_sdk-3.2.0.dev96}/PKG-INFO +1 -1
  2. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/pyproject.toml +1 -1
  3. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/__init__.py +7 -5
  4. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/agent_server.py +10 -2
  5. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/core/logging_config.py +24 -3
  6. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/core/mixins/web_mixin.py +6 -0
  7. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/core/swml_service.py +6 -0
  8. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/relay/call.py +20 -7
  9. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/relay/client.py +148 -10
  10. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/relay/constants.py +8 -0
  11. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/rest/_base.py +80 -15
  12. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/rest/_pagination.py +11 -1
  13. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/rest/namespaces/calling_resources_generated.py +348 -55
  14. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/rest/namespaces/chat_resources_generated.py +9 -1
  15. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/rest/namespaces/datasphere_resources_generated.py +55 -9
  16. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/rest/namespaces/fabric_resources_generated.py +296 -62
  17. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/rest/namespaces/fax_resources_generated.py +2 -0
  18. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/rest/namespaces/logs_resources_generated.py +8 -2
  19. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/rest/namespaces/message_resources_generated.py +2 -0
  20. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/rest/namespaces/messages_resources_generated.py +16 -2
  21. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/rest/namespaces/project_resources_generated.py +23 -4
  22. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/rest/namespaces/projects_resources_generated.py +22 -4
  23. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/rest/namespaces/pubsub_resources_generated.py +9 -1
  24. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/rest/namespaces/relay_rest_resources_generated.py +362 -85
  25. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/rest/namespaces/video_resources_generated.py +154 -36
  26. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/rest/namespaces/voice_resources_generated.py +10 -2
  27. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96/signalwire/signalwire_sdk.egg-info}/PKG-INFO +1 -1
  28. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/LICENSE +0 -0
  29. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/README.md +0 -0
  30. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/man/sw-agent-init.1 +0 -0
  31. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/man/sw-search.1 +0 -0
  32. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/man/swaig-test.1 +0 -0
  33. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/setup.cfg +0 -0
  34. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/setup.py +0 -0
  35. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/agents/bedrock.py +0 -0
  36. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/cli/__init__.py +0 -0
  37. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/cli/build_search.py +0 -0
  38. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/cli/config.py +0 -0
  39. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/cli/core/__init__.py +0 -0
  40. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/cli/core/agent_loader.py +0 -0
  41. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/cli/core/argparse_helpers.py +0 -0
  42. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/cli/core/dynamic_config.py +0 -0
  43. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/cli/core/service_loader.py +0 -0
  44. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/cli/dokku.py +0 -0
  45. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/cli/execution/__init__.py +0 -0
  46. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/cli/execution/datamap_exec.py +0 -0
  47. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/cli/execution/webhook_exec.py +0 -0
  48. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/cli/init_project.py +0 -0
  49. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/cli/output/__init__.py +0 -0
  50. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/cli/output/output_formatter.py +0 -0
  51. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/cli/output/swml_dump.py +0 -0
  52. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/cli/simulation/__init__.py +0 -0
  53. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/cli/simulation/data_generation.py +0 -0
  54. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/cli/simulation/data_overrides.py +0 -0
  55. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/cli/simulation/mock_env.py +0 -0
  56. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/cli/swaig_test_wrapper.py +0 -0
  57. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/cli/test_swaig.py +0 -0
  58. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/cli/types.py +0 -0
  59. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/core/__init__.py +0 -0
  60. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/core/_agent_host.py +0 -0
  61. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/core/agent/__init__.py +0 -0
  62. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/core/agent/config/__init__.py +0 -0
  63. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/core/agent/deployment/__init__.py +0 -0
  64. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/core/agent/deployment/handlers/__init__.py +0 -0
  65. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/core/agent/prompt/__init__.py +0 -0
  66. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/core/agent/prompt/manager.py +0 -0
  67. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/core/agent/routing/__init__.py +0 -0
  68. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/core/agent/security/__init__.py +0 -0
  69. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/core/agent/swml/__init__.py +0 -0
  70. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/core/agent/tools/__init__.py +0 -0
  71. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/core/agent/tools/decorator.py +0 -0
  72. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/core/agent/tools/registry.py +0 -0
  73. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/core/agent/tools/type_inference.py +0 -0
  74. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/core/agent_base.py +0 -0
  75. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/core/auth_handler.py +0 -0
  76. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/core/config_loader.py +0 -0
  77. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/core/contexts.py +0 -0
  78. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/core/data_map.py +0 -0
  79. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/core/function_result.py +0 -0
  80. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/core/mixins/__init__.py +0 -0
  81. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/core/mixins/_mixin_host.py +0 -0
  82. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/core/mixins/ai_config_mixin.py +0 -0
  83. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/core/mixins/auth_mixin.py +0 -0
  84. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/core/mixins/mcp_server_mixin.py +0 -0
  85. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/core/mixins/prompt_mixin.py +0 -0
  86. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/core/mixins/serverless_mixin.py +0 -0
  87. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/core/mixins/skill_mixin.py +0 -0
  88. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/core/mixins/state_mixin.py +0 -0
  89. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/core/mixins/tool_mixin.py +0 -0
  90. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/core/pom_builder.py +0 -0
  91. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/core/post_prompt_generated.py +0 -0
  92. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/core/security/__init__.py +0 -0
  93. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/core/security/security_utils.py +0 -0
  94. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/core/security/session_manager.py +0 -0
  95. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/core/security/webhook_middleware.py +0 -0
  96. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/core/security/webhook_validator.py +0 -0
  97. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/core/security_config.py +0 -0
  98. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/core/skill_base.py +0 -0
  99. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/core/skill_manager.py +0 -0
  100. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/core/swaig_actions_generated.py +0 -0
  101. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/core/swaig_function.py +0 -0
  102. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/core/swaig_request_generated.py +0 -0
  103. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/core/swml_builder.py +0 -0
  104. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/core/swml_handler.py +0 -0
  105. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/core/swml_renderer.py +0 -0
  106. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/core/swml_verbs_generated.py +0 -0
  107. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/core/web.py +0 -0
  108. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/livewire/__init__.py +0 -0
  109. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/livewire/plugins.py +0 -0
  110. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/mcp_gateway/__init__.py +0 -0
  111. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/mcp_gateway/gateway_service.py +0 -0
  112. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/mcp_gateway/mcp_manager.py +0 -0
  113. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/mcp_gateway/session_manager.py +0 -0
  114. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/pom/__init__.py +0 -0
  115. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/pom/pom.py +0 -0
  116. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/pom/pom_tool.py +0 -0
  117. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/prefabs/__init__.py +0 -0
  118. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/prefabs/concierge.py +0 -0
  119. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/prefabs/faq_bot.py +0 -0
  120. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/prefabs/info_gatherer.py +0 -0
  121. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/prefabs/receptionist.py +0 -0
  122. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/prefabs/survey.py +0 -0
  123. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/py.typed +0 -0
  124. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/relay/__init__.py +0 -0
  125. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/relay/event.py +0 -0
  126. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/relay/message.py +0 -0
  127. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/relay/protocol_types_generated.py +0 -0
  128. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/rest/__init__.py +0 -0
  129. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/rest/_request_options.py +0 -0
  130. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/rest/call_handler.py +0 -0
  131. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/rest/client.py +0 -0
  132. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/rest/namespaces/__init__.py +0 -0
  133. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/rest/namespaces/_client_tree_generated.py +0 -0
  134. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/rest/namespaces/addresses.py +0 -0
  135. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/rest/namespaces/calling.py +0 -0
  136. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/rest/namespaces/calling_types_generated.py +0 -0
  137. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/rest/namespaces/chat.py +0 -0
  138. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/rest/namespaces/chat_types_generated.py +0 -0
  139. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/rest/namespaces/datasphere.py +0 -0
  140. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/rest/namespaces/datasphere_types_generated.py +0 -0
  141. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/rest/namespaces/fabric.py +0 -0
  142. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/rest/namespaces/fabric_types_generated.py +0 -0
  143. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/rest/namespaces/fax_types_generated.py +0 -0
  144. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/rest/namespaces/imported_numbers.py +0 -0
  145. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/rest/namespaces/logs.py +0 -0
  146. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/rest/namespaces/logs_types_generated.py +0 -0
  147. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/rest/namespaces/lookup.py +0 -0
  148. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/rest/namespaces/message_types_generated.py +0 -0
  149. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/rest/namespaces/messages_types_generated.py +0 -0
  150. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/rest/namespaces/mfa.py +0 -0
  151. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/rest/namespaces/number_groups.py +0 -0
  152. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/rest/namespaces/phone_numbers.py +0 -0
  153. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/rest/namespaces/project.py +0 -0
  154. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/rest/namespaces/project_types_generated.py +0 -0
  155. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/rest/namespaces/projects_types_generated.py +0 -0
  156. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/rest/namespaces/pubsub.py +0 -0
  157. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/rest/namespaces/pubsub_types_generated.py +0 -0
  158. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/rest/namespaces/queues.py +0 -0
  159. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/rest/namespaces/recordings.py +0 -0
  160. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/rest/namespaces/registry.py +0 -0
  161. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/rest/namespaces/relay_rest_types_generated.py +0 -0
  162. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/rest/namespaces/short_codes.py +0 -0
  163. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/rest/namespaces/sip_profile.py +0 -0
  164. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/rest/namespaces/swml_webhooks_types_generated.py +0 -0
  165. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/rest/namespaces/verified_callers.py +0 -0
  166. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/rest/namespaces/video.py +0 -0
  167. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/rest/namespaces/video_types_generated.py +0 -0
  168. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/rest/namespaces/voice_types_generated.py +0 -0
  169. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/schema.json +0 -0
  170. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/search/__init__.py +0 -0
  171. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/search/document_processor.py +0 -0
  172. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/search/index_builder.py +0 -0
  173. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/search/migration.py +0 -0
  174. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/search/models.py +0 -0
  175. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/search/pgvector_backend.py +0 -0
  176. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/search/query_processor.py +0 -0
  177. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/search/search_engine.py +0 -0
  178. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/search/search_service.py +0 -0
  179. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/skills/README.md +0 -0
  180. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/skills/__init__.py +0 -0
  181. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/skills/api_ninjas_trivia/README.md +0 -0
  182. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/skills/api_ninjas_trivia/__init__.py +0 -0
  183. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/skills/api_ninjas_trivia/skill.py +0 -0
  184. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/skills/claude_skills/README.md +0 -0
  185. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/skills/claude_skills/__init__.py +0 -0
  186. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/skills/claude_skills/skill.py +0 -0
  187. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/skills/datasphere/README.md +0 -0
  188. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/skills/datasphere/__init__.py +0 -0
  189. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/skills/datasphere/skill.py +0 -0
  190. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/skills/datasphere_serverless/README.md +0 -0
  191. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/skills/datasphere_serverless/__init__.py +0 -0
  192. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/skills/datasphere_serverless/skill.py +0 -0
  193. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/skills/datetime/README.md +0 -0
  194. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/skills/datetime/__init__.py +0 -0
  195. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/skills/datetime/skill.py +0 -0
  196. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/skills/google_maps/__init__.py +0 -0
  197. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/skills/google_maps/skill.py +0 -0
  198. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/skills/info_gatherer/README.md +0 -0
  199. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/skills/info_gatherer/__init__.py +0 -0
  200. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/skills/info_gatherer/skill.py +0 -0
  201. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/skills/joke/README.md +0 -0
  202. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/skills/joke/__init__.py +0 -0
  203. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/skills/joke/skill.py +0 -0
  204. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/skills/math/README.md +0 -0
  205. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/skills/math/__init__.py +0 -0
  206. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/skills/math/skill.py +0 -0
  207. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/skills/mcp_gateway/README.md +0 -0
  208. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/skills/mcp_gateway/__init__.py +0 -0
  209. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/skills/mcp_gateway/skill.py +0 -0
  210. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/skills/native_vector_search/README.md +0 -0
  211. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/skills/native_vector_search/__init__.py +0 -0
  212. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/skills/native_vector_search/skill.py +0 -0
  213. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/skills/play_background_file/README.md +0 -0
  214. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/skills/play_background_file/__init__.py +0 -0
  215. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/skills/play_background_file/skill.py +0 -0
  216. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/skills/registry.py +0 -0
  217. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/skills/spider/README.md +0 -0
  218. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/skills/spider/__init__.py +0 -0
  219. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/skills/spider/skill.py +0 -0
  220. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/skills/swml_transfer/README.md +0 -0
  221. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/skills/swml_transfer/__init__.py +0 -0
  222. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/skills/swml_transfer/skill.py +0 -0
  223. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/skills/weather_api/README.md +0 -0
  224. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/skills/weather_api/__init__.py +0 -0
  225. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/skills/weather_api/skill.py +0 -0
  226. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/skills/web_search/README.md +0 -0
  227. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/skills/web_search/__init__.py +0 -0
  228. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/skills/web_search/skill.py +0 -0
  229. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/skills/web_search/skill_improved.py +0 -0
  230. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/skills/web_search/skill_original.py +0 -0
  231. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/skills/wikipedia_search/README.md +0 -0
  232. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/skills/wikipedia_search/__init__.py +0 -0
  233. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/skills/wikipedia_search/skill.py +0 -0
  234. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/utils/__init__.py +0 -0
  235. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/utils/pom_utils.py +0 -0
  236. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/utils/schema_utils.py +0 -0
  237. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/utils/token_generators.py +0 -0
  238. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/utils/url_validator.py +0 -0
  239. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/utils/validators.py +0 -0
  240. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/web/__init__.py +0 -0
  241. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire/web/web_service.py +0 -0
  242. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire_sdk.egg-info/SOURCES.txt +0 -0
  243. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire_sdk.egg-info/dependency_links.txt +0 -0
  244. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire_sdk.egg-info/entry_points.txt +0 -0
  245. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire_sdk.egg-info/requires.txt +0 -0
  246. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/signalwire/signalwire_sdk.egg-info/top_level.txt +0 -0
  247. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/tests/test_examples.py +0 -0
  248. {signalwire_sdk-3.2.0.dev94 → signalwire_sdk-3.2.0.dev96}/tests/test_mcp_integration.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: signalwire-sdk
3
- Version: 3.2.0.dev94
3
+ Version: 3.2.0.dev96
4
4
  Summary: SignalWire SDK
5
5
  Author-email: SignalWire Team <info@signalwire.com>
6
6
  License: MIT
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "signalwire-sdk"
7
- version = "3.2.0.dev94"
7
+ version = "3.2.0.dev96"
8
8
  description = "SignalWire SDK"
9
9
  authors = [
10
10
  {name = "SignalWire Team", email = "info@signalwire.com"}
@@ -14,8 +14,12 @@ A package for building AI agents using SignalWire's AI and SWML capabilities.
14
14
 
15
15
  from typing import TYPE_CHECKING, Any
16
16
 
17
- # Configure logging before any other imports to ensure early initialization
18
- from .core.logging_config import configure_logging
17
+ # Library-safe logging: importing signalwire installs ONLY a NullHandler on the
18
+ # `signalwire` namespace logger (done at logging_config module load). It does NOT
19
+ # configure global logging — that would hijack the host application's logging (its
20
+ # structlog config, its root/generic-named loggers). The app opts in to SDK log
21
+ # OUTPUT by calling configure_logging() explicitly; the server/CLI entry points do.
22
+ from .core.logging_config import configure_logging # noqa: F401 (re-exported)
19
23
 
20
24
  if TYPE_CHECKING:
21
25
  from signalwire.rest.client import RestClient as _RestClient
@@ -48,9 +52,7 @@ if TYPE_CHECKING:
48
52
  from signalwire.utils.schema_utils import SchemaValidationError
49
53
  from signalwire.web import WebService
50
54
 
51
- configure_logging()
52
-
53
- __version__ = "3.2.0.dev94"
55
+ __version__ = "3.2.0.dev96"
54
56
 
55
57
  # §6.2-python (owner decision: NO packaging split — one package, all deps required;
56
58
  # IMPORT-TIME behavior only): the agent/web symbols are lazy-imported via module
@@ -68,7 +68,7 @@ class AgentServer:
68
68
  self.app = FastAPI(
69
69
  title="SignalWire AI Agents",
70
70
  description="Hosted SignalWire AI Agents",
71
- version="3.2.0.dev94",
71
+ version="3.2.0.dev96",
72
72
  redirect_slashes=False,
73
73
  docs_url=None,
74
74
  redoc_url=None,
@@ -372,7 +372,15 @@ class AgentServer:
372
372
 
373
373
  # Imported lazily here so tests can patch
374
374
  # signalwire.core.logging_config.get_execution_mode at call time.
375
- from signalwire.core.logging_config import get_execution_mode
375
+ from signalwire.core.logging_config import (
376
+ configure_logging,
377
+ get_execution_mode,
378
+ )
379
+
380
+ # Opt-in to SDK logging OUTPUT at the run entry point (import is
381
+ # library-safe / silent; running is where the app wants logs). This also
382
+ # applies the CGI-off / auto detection that configure_logging performs.
383
+ configure_logging()
376
384
 
377
385
  # Detect execution mode
378
386
  mode = get_execution_mode()
@@ -43,6 +43,25 @@ def strip_control_chars(
43
43
  _logging_configured = False
44
44
 
45
45
 
46
+ def _install_library_null_handler() -> None:
47
+ """Attach a NullHandler to the ``signalwire`` namespace logger (the standard
48
+ library pattern for a well-behaved library, per the stdlib logging HOWTO).
49
+
50
+ This is the ONLY logging side effect of importing the SDK. It makes the SDK's
51
+ own loggers silent-by-default (no "No handlers could be found" warning) WITHOUT
52
+ touching the root logger, the host application's structlog config, or any
53
+ host-owned logger — so ``import signalwire`` never hijacks the app's logging.
54
+ The app opts in to SDK log OUTPUT by calling ``configure_logging()`` explicitly
55
+ (the server/CLI entry points do this)."""
56
+ sw_logger = logging.getLogger("signalwire")
57
+ if not any(isinstance(h, logging.NullHandler) for h in sw_logger.handlers):
58
+ sw_logger.addHandler(logging.NullHandler())
59
+
60
+
61
+ # Import-time side effect: ONLY the library NullHandler. NOT global configuration.
62
+ _install_library_null_handler()
63
+
64
+
46
65
  def get_execution_mode() -> str:
47
66
  """
48
67
  Determine the execution mode based on environment variables
@@ -307,7 +326,9 @@ def get_logger(name: str) -> Any:
307
326
  Returns:
308
327
  A structlog BoundLogger that supports .bind(), .info(), .debug(), etc.
309
328
  """
310
- # Ensure logging is configured
311
- configure_logging()
312
-
329
+ # Library-safe: do NOT auto-configure global logging here. Every SDK module
330
+ # calls get_logger() at import, so auto-configuring would hijack the host
331
+ # app's logging the moment any SDK submodule is imported. The SDK's own
332
+ # logger carries a NullHandler (installed at module load) so it's silent by
333
+ # default; the app opts in to SDK output via configure_logging().
313
334
  return structlog.get_logger(name)
@@ -204,6 +204,12 @@ class WebMixin(_HostTyped): # type: ignore[misc] # _HostTyped is object at run
204
204
  """
205
205
  import uvicorn
206
206
 
207
+ # Opt-in to SDK logging OUTPUT at the serve entry point (importing the SDK
208
+ # is library-safe / silent; running a server is where the app wants logs).
209
+ from signalwire.core.logging_config import configure_logging
210
+
211
+ configure_logging()
212
+
207
213
  if self._app is None:
208
214
  # Create a FastAPI app with explicit redirect_slashes=False
209
215
  app = FastAPI(
@@ -1200,6 +1200,12 @@ class SWMLService(ToolMixin):
1200
1200
  """
1201
1201
  import uvicorn
1202
1202
 
1203
+ # Opt-in to SDK logging OUTPUT at the serve entry point (import is
1204
+ # library-safe / silent; serving is where the app wants logs).
1205
+ from signalwire.core.logging_config import configure_logging
1206
+
1207
+ configure_logging()
1208
+
1203
1209
  # Store SSL configuration (override environment if explicitly provided)
1204
1210
  if ssl_enabled is not None:
1205
1211
  self.ssl_enabled = ssl_enabled
@@ -112,9 +112,15 @@ class Action:
112
112
  )
113
113
 
114
114
  async def wait(self, timeout: float | None = None) -> RelayEvent:
115
- """Wait for the action to complete. Returns the terminal event."""
115
+ """Wait for the action to complete. Returns the terminal event.
116
+
117
+ Raises ``asyncio.TimeoutError`` if ``timeout`` is exceeded. A timed-out
118
+ wait does NOT poison the action: ``self._done`` is shielded so the
119
+ ``wait_for`` cancellation stops the wait, not the underlying completion
120
+ future — the still-pending terminal event resolves the action normally
121
+ and a subsequent ``wait()`` returns it (mirrors ``Message.wait``)."""
116
122
  if timeout is not None:
117
- return await asyncio.wait_for(self._done, timeout=timeout)
123
+ return await asyncio.wait_for(asyncio.shield(self._done), timeout=timeout)
118
124
  return await self._done
119
125
 
120
126
  @property
@@ -377,9 +383,14 @@ class Call:
377
383
  The outer JSON-RPC method is ``"calling.<method>"`` (e.g.
378
384
  ``"calling.answer"``) with ``node_id`` and ``call_id`` in params.
379
385
 
380
- All server errors are logged gracefully and return an empty dict
381
- rather than raising exceptions. This is an SDK stack traces
382
- from server-side errors should never bubble up to the developer.
386
+ A2 contract (the documented behavior only "call gone" is swallowed):
387
+ a **404** or **410** server error means the call no longer exists, so the
388
+ verb is a no-op and returns ``{}`` (the caller's action can't proceed but
389
+ that isn't an error worth raising). **Every other non-2xx server error
390
+ RAISES** — the previous code swallowed ALL errors carrying a ``code``,
391
+ which hid real failures (auth, bad params, server faults) the developer
392
+ must see. The docstring/contract always promised 404/410-only; the code
393
+ now matches it.
383
394
  """
384
395
  rpc_method = f"calling.{method}"
385
396
  params: dict[str, Any] = {
@@ -392,11 +403,13 @@ class Call:
392
403
  return await self._client.execute(rpc_method, params)
393
404
  except Exception as exc:
394
405
  code = getattr(exc, "code", None)
395
- if code is not None:
406
+ if code in (404, 410):
407
+ # Call gone — swallow per the documented contract, return no-op.
396
408
  logger.warning(
397
- f"Call {self.call_id} error during {method} (code={code}): {exc}"
409
+ f"Call {self.call_id} gone during {method} (code={code}): {exc}"
398
410
  )
399
411
  return {}
412
+ # Any other server error (or a non-coded exception) propagates.
400
413
  raise
401
414
 
402
415
  # ------------------------------------------------------------------
@@ -24,6 +24,7 @@ import asyncio
24
24
  import json
25
25
  import os
26
26
  import re
27
+ import ssl as ssl_module
27
28
  import uuid
28
29
  from typing import Any, TYPE_CHECKING
29
30
  from collections.abc import Callable, Coroutine
@@ -60,6 +61,7 @@ from .constants import (
60
61
  METHOD_SIGNALWIRE_RECEIVE,
61
62
  METHOD_SIGNALWIRE_UNRECEIVE,
62
63
  RECONNECT_BACKOFF_FACTOR,
64
+ RECONNECT_MAX_AUTH_ATTEMPTS,
63
65
  RECONNECT_MAX_DELAY,
64
66
  RECONNECT_MIN_DELAY,
65
67
  )
@@ -100,6 +102,48 @@ except ValueError:
100
102
  _active_clients: set[int] = set()
101
103
 
102
104
 
105
+ # Credential-bearing JSON keys whose VALUES must never appear in debug logs
106
+ # (SECRET-SCRUB, A6/enterprise): the raw RELAY frames carry the connect
107
+ # authentication (project/token/jwt_token) and the server's encrypted
108
+ # ``authorization_state`` re-auth blob. Logging a raw frame verbatim leaks these.
109
+ _SCRUB_KEYS = ("token", "project", "jwt_token", "authorization_state")
110
+ _SCRUB_RE = re.compile(
111
+ r'("(?:' + "|".join(_SCRUB_KEYS) + r')"\s*:\s*)"(?:\\.|[^"\\])*"'
112
+ )
113
+
114
+
115
+ def _scrub_frame(raw: object) -> str:
116
+ """Return a log-safe repr of a raw RELAY frame with credential VALUES masked.
117
+
118
+ Masks the string values of ``token``/``project``/``jwt_token``/
119
+ ``authorization_state`` keys wherever they appear in the (JSON) frame, so a
120
+ ``SIGNALWIRE_LOG_LEVEL=debug`` session never emits live credentials or the
121
+ re-auth blob. Non-string / structural content is preserved so the frame stays
122
+ diagnostic."""
123
+ text = (
124
+ raw.decode("utf-8", "replace")
125
+ if isinstance(raw, (bytes, bytearray))
126
+ else str(raw)
127
+ )
128
+ return _SCRUB_RE.sub(r'\1"***"', text)
129
+
130
+
131
+ def _build_relay_ssl_context() -> ssl_module.SSLContext | None:
132
+ """Return a TLS context trusting SIGNALWIRE_RELAY_CA_FILE, or None.
133
+
134
+ A5 fleet CA-var contract: when SIGNALWIRE_RELAY_CA_FILE names a CA bundle,
135
+ build a default client TLS context that loads it as the trust root for the
136
+ RELAY WebSocket connection. Unset → None (websockets uses the system trust
137
+ store). Kept module-level so the connect path stays readable and the behavior
138
+ is unit-testable without a live socket."""
139
+ ca_file = os.environ.get("SIGNALWIRE_RELAY_CA_FILE")
140
+ if not ca_file:
141
+ return None
142
+ ctx = ssl_module.create_default_context()
143
+ ctx.load_verify_locations(cafile=ca_file)
144
+ return ctx
145
+
146
+
103
147
  class RelayClient:
104
148
  """Manages a WebSocket connection to SignalWire RELAY.
105
149
 
@@ -134,12 +178,24 @@ class RelayClient:
134
178
  # JWT auth — project/token not required (project_id is inside the token)
135
179
  if not self.project:
136
180
  self.project = ""
137
- elif not self.project or not self.token:
138
- raise ValueError(
139
- "project and token are required (or provide jwt_token). "
140
- "Pass them directly or set SIGNALWIRE_PROJECT_ID / "
141
- "SIGNALWIRE_API_TOKEN / SIGNALWIRE_JWT_TOKEN env vars."
142
- )
181
+ else:
182
+ # A6 credential contract: fail fast pre-connect with a PER-VARIABLE
183
+ # actionable error name exactly which credential is missing and the
184
+ # env var that supplies it, so the caller fixes the right one (a
185
+ # combined "project and token" message misleads when only one is
186
+ # absent). JWT is the alternative for both.
187
+ if not self.project:
188
+ raise ValueError(
189
+ "project is required. Pass project=... or set the "
190
+ "SIGNALWIRE_PROJECT_ID env var (or use jwt_token / "
191
+ "SIGNALWIRE_JWT_TOKEN for JWT auth)."
192
+ )
193
+ if not self.token:
194
+ raise ValueError(
195
+ "token is required. Pass token=... or set the "
196
+ "SIGNALWIRE_API_TOKEN env var (or use jwt_token / "
197
+ "SIGNALWIRE_JWT_TOKEN for JWT auth)."
198
+ )
143
199
 
144
200
  # Validate host to prevent SSRF / header injection
145
201
  if any(c in self.host for c in ("@", "/", "?", "\r", "\n", " ")):
@@ -205,6 +261,12 @@ class RelayClient:
205
261
  self._execute_queue: list[
206
262
  tuple[dict[str, Any], asyncio.Future[dict[str, Any]]]
207
263
  ] = []
264
+ # A6 bounded-reconnect state: whether authentication has ever succeeded
265
+ # (a drop after this is a TRANSIENT loss → reconnect indefinitely), and
266
+ # the count of consecutive auth rejections before any success (a
267
+ # PERMANENT credential failure → bounded, then raise).
268
+ self._authenticated_ever: bool = False
269
+ self._auth_reject_attempts: int = 0
208
270
 
209
271
  def __del__(self) -> None:
210
272
  _active_clients.discard(id(self))
@@ -228,6 +290,30 @@ class RelayClient:
228
290
  task.add_done_callback(self._bg_tasks.discard)
229
291
  return task
230
292
 
293
+ def _scrub_log(self, text: str) -> str:
294
+ """Log-safe repr with live credential VALUES masked wherever they appear.
295
+
296
+ Composes over :func:`_scrub_frame` (the key-shape mask, ``"token":"..."``)
297
+ and then masks any *verbatim* occurrence of THIS connection's live
298
+ credential values (project / token / jwt_token / authorization_state).
299
+ The key-shape pass alone misses a credential value the server reflects
300
+ into a NON-credential field (e.g. an auth-response ``identity`` derived
301
+ from the project, or the raw ``identity=`` diagnostic) — so a
302
+ ``SIGNALWIRE_LOG_LEVEL=debug`` session must never emit the raw value even
303
+ there. Defense-in-depth: value-masking is stronger than, and additive to,
304
+ the key-shape scrub.
305
+ """
306
+ out = _scrub_frame(text)
307
+ for value in (
308
+ self.project,
309
+ self.token,
310
+ self.jwt_token,
311
+ self._authorization_state,
312
+ ):
313
+ if value:
314
+ out = out.replace(value, "***")
315
+ return out
316
+
231
317
  @property
232
318
  def relay_protocol(self) -> str:
233
319
  """Server-assigned protocol string from the connect response."""
@@ -268,8 +354,20 @@ class RelayClient:
268
354
  uri = f"wss://{self.host}"
269
355
  logger.info(f"Connecting to {uri}")
270
356
 
357
+ # A5 (fleet CA-var contract, hard-cut no aliases): a custom CA bundle for
358
+ # the RELAY WebSocket transport is supplied via SIGNALWIRE_RELAY_CA_FILE.
359
+ # When set, build a TLS context that trusts it; unset → websockets' default
360
+ # (system trust store). This is the RELAY half of the fleet-standard pair
361
+ # (SIGNALWIRE_REST_CA_FILE is the REST half).
362
+ ssl_context = _build_relay_ssl_context()
363
+
271
364
  self._ws = await websockets.connect(
272
- uri, ping_interval=None, max_size=10 * 1024 * 1024
365
+ uri,
366
+ ping_interval=None,
367
+ max_size=10 * 1024 * 1024,
368
+ # ssl_context is None when SIGNALWIRE_RELAY_CA_FILE is unset, which
369
+ # tells websockets to use its default (system trust store) for wss://.
370
+ ssl=ssl_context,
273
371
  )
274
372
  self._connected = True
275
373
  self._reconnect_delay = RECONNECT_MIN_DELAY
@@ -318,6 +416,12 @@ class RelayClient:
318
416
 
319
417
  result = await self._send_request(METHOD_SIGNALWIRE_CONNECT, params)
320
418
 
419
+ # Authentication succeeded: from here on a connection drop is a
420
+ # transient loss (reconnect indefinitely), not a credential failure.
421
+ # Reset the permanent-auth-failure counter (A6 bounded reconnect).
422
+ self._authenticated_ever = True
423
+ self._auth_reject_attempts = 0
424
+
321
425
  # Capture server-assigned protocol and identity
322
426
  self._relay_protocol = result.get("protocol", self._relay_protocol)
323
427
  self._identity = result.get("identity", self._identity)
@@ -327,7 +431,10 @@ class RelayClient:
327
431
  # session. Internal-only — not part of the public surface.
328
432
  self._session_id = result.get("sessionid", self._session_id)
329
433
  logger.debug(
330
- f"Auth response: protocol={self._relay_protocol} identity={self._identity}"
434
+ self._scrub_log(
435
+ f"Auth response: protocol={self._relay_protocol} "
436
+ f"identity={self._identity}"
437
+ )
331
438
  )
332
439
 
333
440
  async def disconnect(self) -> None:
@@ -587,6 +694,30 @@ class RelayClient:
587
694
  await self._recv_task
588
695
  except asyncio.CancelledError:
589
696
  break
697
+ except RelayError as exc:
698
+ # A6 bounded reconnect: a RelayError out of connect() before we
699
+ # ever authenticated is a PERMANENT credential rejection (the
700
+ # server refused the connect creds), NOT a transient blip.
701
+ # Bound the retries and then RAISE the server's message — never
702
+ # loop forever on bad creds, never silent exit-0. A transport
703
+ # error (ConnectionRefused/OSError, server not up yet) is caught
704
+ # by the broad handler below and keeps reconnecting.
705
+ if not self._authenticated_ever:
706
+ self._auth_reject_attempts += 1
707
+ if self._auth_reject_attempts >= RECONNECT_MAX_AUTH_ATTEMPTS:
708
+ logger.error(
709
+ "RELAY authentication rejected "
710
+ f"{self._auth_reject_attempts} time(s); giving up: {exc}"
711
+ )
712
+ await self.disconnect()
713
+ raise
714
+ logger.warning(
715
+ "RELAY authentication rejected "
716
+ f"({self._auth_reject_attempts}/{RECONNECT_MAX_AUTH_ATTEMPTS}); "
717
+ f"retrying: {exc}"
718
+ )
719
+ else:
720
+ logger.exception("Connection error")
590
721
  except Exception:
591
722
  logger.exception("Connection error")
592
723
 
@@ -721,10 +852,17 @@ class RelayClient:
721
852
  try:
722
853
  msg = json.loads(raw)
723
854
  except json.JSONDecodeError:
724
- logger.warning(f"Invalid JSON received: {raw!r}")
855
+ logger.warning(
856
+ f"Invalid JSON received: {self._scrub_log(str(raw))!r}"
857
+ )
725
858
  continue
726
859
 
727
- logger.debug(f"<< {raw!r}")
860
+ # SECRET-SCRUB: mask credential/authorization_state values so a
861
+ # debug-level log never leaks live creds or the re-auth blob —
862
+ # including a value the server reflects into a non-credential
863
+ # field (e.g. identity derived from the project). _scrub_log
864
+ # composes the key-shape mask with verbatim value-masking.
865
+ logger.debug(f"<< {self._scrub_log(str(raw))}")
728
866
  await self._handle_message(msg)
729
867
  except websockets.exceptions.ConnectionClosed:
730
868
  logger.info("WebSocket connection closed")
@@ -115,6 +115,14 @@ ROOM_STATE_LEAVE = "leave"
115
115
  RECONNECT_MIN_DELAY = 1.0
116
116
  RECONNECT_MAX_DELAY = 30.0
117
117
  RECONNECT_BACKOFF_FACTOR = 2.0
118
+ # A6 credential-failure contract (bounded reconnect): a transient connection
119
+ # loss reconnects indefinitely (a server blip must self-heal), but a PERMANENT
120
+ # authentication rejection (the server refuses the credentials — a 401-class
121
+ # error frame to signalwire.connect, before we have ever authenticated) must
122
+ # NOT loop forever. After this many consecutive auth rejections with no
123
+ # intervening successful auth, _run_forever re-raises the server's message and
124
+ # stops — never an infinite reconnect on bad creds, never a silent exit-0.
125
+ RECONNECT_MAX_AUTH_ATTEMPTS = 3
118
126
 
119
127
  # Default host
120
128
  DEFAULT_RELAY_HOST = "relay.signalwire.com"
@@ -9,6 +9,7 @@ See LICENSE file in the project root for full license information.
9
9
  HTTP client infrastructure and base resource classes for the REST client.
10
10
  """
11
11
 
12
+ import os
12
13
  import time
13
14
  from importlib.metadata import PackageNotFoundError, version
14
15
  from typing import Any, Generic, TypeVar, cast
@@ -153,6 +154,15 @@ class HttpClient:
153
154
  self._request_options = request_options
154
155
  self._session = requests.Session()
155
156
  self._session.auth = (project, token)
157
+ # A5 (fleet CA-var contract, hard-cut no aliases): a custom CA bundle for
158
+ # the REST transport is supplied via SIGNALWIRE_REST_CA_FILE. When set, it
159
+ # becomes the session's verify bundle (requests uses it for TLS
160
+ # verification of the platform cert). Unset → requests' default trust
161
+ # store. This is the REST half of the fleet-standard pair
162
+ # (SIGNALWIRE_RELAY_CA_FILE is the RELAY half).
163
+ _rest_ca_file = os.environ.get("SIGNALWIRE_REST_CA_FILE")
164
+ if _rest_ca_file:
165
+ self._session.verify = _rest_ca_file
156
166
  self._session.headers.update(
157
167
  {
158
168
  "Content-Type": "application/json",
@@ -332,10 +342,21 @@ class ReadResource(BaseResource, Generic[TList, TItem]):
332
342
  defined once here.
333
343
  """
334
344
 
335
- def list(self, **params: Any) -> TList:
336
- return cast(TList, self._http.get(self._base_path, params=params or None))
345
+ def list(
346
+ self, *, request_options: RequestOptions | None = None, **params: Any
347
+ ) -> TList:
348
+ return cast(
349
+ TList,
350
+ self._http.get(
351
+ self._base_path,
352
+ params=params or None,
353
+ request_options=request_options,
354
+ ),
355
+ )
337
356
 
338
- def paginate(self, **params: Any) -> PaginatedIterator:
357
+ def paginate(
358
+ self, *, request_options: RequestOptions | None = None, **params: Any
359
+ ) -> PaginatedIterator:
339
360
  """Iterate every item across all pages of this resource's list endpoint.
340
361
 
341
362
  ``list()`` returns a single raw page (the server's first response). For
@@ -347,14 +368,24 @@ class ReadResource(BaseResource, Generic[TList, TItem]):
347
368
 
348
369
  Wires the resource layer to the tested ``PaginatedIterator`` (which walks
349
370
  ``resp["data"]`` and follows ``resp["links"]["next"]``), so callers no
350
- longer hand-construct the path + token loop.
371
+ longer hand-construct the path + token loop. ``request_options`` (a per-call
372
+ timeout / retry / header override) is applied to EVERY page fetch.
351
373
  """
352
374
  return PaginatedIterator(
353
- self._http, self._base_path, params=params or None, data_key="data"
375
+ self._http,
376
+ self._base_path,
377
+ params=params or None,
378
+ data_key="data",
379
+ request_options=request_options,
354
380
  )
355
381
 
356
- def get(self, resource_id: str) -> TItem:
357
- return cast(TItem, self._http.get(self._path(resource_id)))
382
+ def get(
383
+ self, resource_id: str, *, request_options: RequestOptions | None = None
384
+ ) -> TItem:
385
+ return cast(
386
+ TItem,
387
+ self._http.get(self._path(resource_id), request_options=request_options),
388
+ )
358
389
 
359
390
 
360
391
  class CrudResource(ReadResource[TList, TItem], Generic[TList, TItem, TCreate, TUpdate]):
@@ -369,7 +400,9 @@ class CrudResource(ReadResource[TList, TItem], Generic[TList, TItem, TCreate, TU
369
400
 
370
401
  _update_method = "PATCH"
371
402
 
372
- def create(self, **kwargs: Any) -> TItem:
403
+ def create(
404
+ self, *, request_options: RequestOptions | None = None, **kwargs: Any
405
+ ) -> TItem:
373
406
  # Honest fallback: the body accepts arbitrary wire fields and at runtime
374
407
  # is a plain dict. Concrete resources override this with a generated
375
408
  # CLOSED typed signature (explicit spec fields + an ``extras`` door); the
@@ -377,26 +410,58 @@ class CrudResource(ReadResource[TList, TItem], Generic[TList, TItem, TCreate, TU
377
410
  # TCreate shape to the signature oracle, NOT this base method body. (A
378
411
  # bare ``**kwargs: TCreate`` here is wrong — it would type each kwarg
379
412
  # VALUE as a whole TCreate — and is what the generated overrides replace.)
380
- return cast(TItem, self._http.post(self._base_path, body=kwargs))
413
+ return cast(
414
+ TItem,
415
+ self._http.post(
416
+ self._base_path, body=kwargs, request_options=request_options
417
+ ),
418
+ )
381
419
 
382
- def update(self, resource_id: str, /, **kwargs: Any) -> TItem:
420
+ def update(
421
+ self,
422
+ resource_id: str,
423
+ /,
424
+ *,
425
+ request_options: RequestOptions | None = None,
426
+ **kwargs: Any,
427
+ ) -> TItem:
383
428
  # resource_id is positional-only so a subclass may rename it without an LSP
384
429
  # override conflict. Same contract as ``create``: honest ``**kwargs: Any``
385
430
  # fallback; the concrete generated override carries the closed typed shape, the
386
431
  # binding carries TUpdate for the oracle.
387
432
  method = getattr(self._http, self._update_method.lower())
388
- return cast(TItem, method(self._path(resource_id), body=kwargs))
433
+ return cast(
434
+ TItem,
435
+ method(
436
+ self._path(resource_id),
437
+ body=kwargs,
438
+ request_options=request_options,
439
+ ),
440
+ )
389
441
 
390
- def delete(self, resource_id: str) -> TItem:
391
- return cast(TItem, self._http.delete(self._path(resource_id)))
442
+ def delete(
443
+ self, resource_id: str, *, request_options: RequestOptions | None = None
444
+ ) -> TItem:
445
+ return cast(
446
+ TItem,
447
+ self._http.delete(self._path(resource_id), request_options=request_options),
448
+ )
392
449
 
393
450
 
394
451
  class CrudWithAddresses(CrudResource[TList, TItem, TCreate, TUpdate]):
395
452
  """CRUD resource that also supports listing addresses."""
396
453
 
397
- def list_addresses(self, resource_id: str, **params: Any) -> Any:
454
+ def list_addresses(
455
+ self,
456
+ resource_id: str,
457
+ *,
458
+ request_options: RequestOptions | None = None,
459
+ **params: Any,
460
+ ) -> Any:
398
461
  return self._http.get(
399
- self._path(resource_id, "addresses"), params=params or None
462
+ self._path(resource_id, "addresses"),
463
+ params=params or None,
464
+ request_options=request_options,
400
465
  )
401
466
 
402
467
 
@@ -11,6 +11,8 @@ Pagination support for list endpoints that return paged results.
11
11
 
12
12
  from typing import TYPE_CHECKING, Any
13
13
 
14
+ from signalwire.rest._request_options import RequestOptions
15
+
14
16
  if TYPE_CHECKING:
15
17
  # Type-only import: _base imports this module (ReadResource.paginate), so a
16
18
  # runtime `from ._base import HttpClient` would be a circular import. HttpClient
@@ -32,11 +34,15 @@ class PaginatedIterator:
32
34
  path: str,
33
35
  params: dict[str, Any] | None = None,
34
36
  data_key: str = "data",
37
+ request_options: RequestOptions | None = None,
35
38
  ) -> None:
36
39
  self._http = http
37
40
  self._path = path
38
41
  self._params: dict[str, Any] = dict(params or {})
39
42
  self._data_key = data_key
43
+ # Per-call request options (timeout / retry / headers) applied to every
44
+ # page fetch this iterator performs.
45
+ self._request_options = request_options
40
46
  self._current_page: Any = None
41
47
  self._items: list[Any] = []
42
48
  self._index = 0
@@ -61,7 +67,11 @@ class PaginatedIterator:
61
67
  return item
62
68
 
63
69
  def _fetch_next(self) -> None:
64
- resp = self._http.get(self._path, params=self._params or None)
70
+ resp = self._http.get(
71
+ self._path,
72
+ params=self._params or None,
73
+ request_options=self._request_options,
74
+ )
65
75
  data = resp.get(self._data_key, [])
66
76
  self._items.extend(data)
67
77