imperal-sdk 5.2.1__tar.gz → 5.3.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 (280) hide show
  1. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/CHANGELOG.md +31 -0
  2. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/PKG-INFO +1 -1
  3. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/api_surface.json +7 -0
  4. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/sdk_claims.json +11 -1
  5. imperal_sdk-5.3.0/src/imperal_sdk/__init__.py +188 -0
  6. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/src/imperal_sdk/billing/client.py +94 -2
  7. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/src/imperal_sdk/context.py +7 -0
  8. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/src/imperal_sdk/devtools/generate_sdk_claims.py +3 -0
  9. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/src/imperal_sdk/types/models.py +53 -0
  10. imperal_sdk-5.3.0/tests/contract/test_artifacts_freshness.py +101 -0
  11. imperal_sdk-5.3.0/tests/fixtures/contract/kernel-contract.sample.json +146 -0
  12. imperal_sdk-5.3.0/tests/test_billing.py +73 -0
  13. imperal_sdk-5.3.0/tests/test_import_light.py +103 -0
  14. imperal_sdk-5.2.1/src/imperal_sdk/__init__.py +0 -73
  15. imperal_sdk-5.2.1/tests/fixtures/contract/kernel-contract.sample.json +0 -28
  16. imperal_sdk-5.2.1/tests/test_billing.py +0 -33
  17. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/.github/workflows/identity-contract.yml +0 -0
  18. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/.github/workflows/publish.yml +0 -0
  19. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/.github/workflows/test.yml +0 -0
  20. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/.gitignore +0 -0
  21. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/LICENSE +0 -0
  22. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/README.md +0 -0
  23. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/docs/sdl-facets.md +0 -0
  24. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/pyproject.toml +0 -0
  25. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/sdl_roles.json +0 -0
  26. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/src/.codebase-index-cache.pkl +0 -0
  27. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/src/imperal_sdk/ai/__init__.py +0 -0
  28. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/src/imperal_sdk/ai/client.py +0 -0
  29. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/src/imperal_sdk/auth/__init__.py +0 -0
  30. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/src/imperal_sdk/auth/client.py +0 -0
  31. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/src/imperal_sdk/auth/middleware.py +0 -0
  32. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/src/imperal_sdk/billing/__init__.py +0 -0
  33. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/src/imperal_sdk/cache/__init__.py +0 -0
  34. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/src/imperal_sdk/cache/client.py +0 -0
  35. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/src/imperal_sdk/cache/protocol.py +0 -0
  36. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/src/imperal_sdk/chat/__init__.py +0 -0
  37. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/src/imperal_sdk/chat/action_result.py +0 -0
  38. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/src/imperal_sdk/chat/error_codes.py +0 -0
  39. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/src/imperal_sdk/chat/exceptions.py +0 -0
  40. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/src/imperal_sdk/chat/extension.py +0 -0
  41. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/src/imperal_sdk/chat/filters.py +0 -0
  42. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/src/imperal_sdk/chat/guards.py +0 -0
  43. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/src/imperal_sdk/chat/kernel_primitives.py +0 -0
  44. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/src/imperal_sdk/chat/narration.py +0 -0
  45. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/src/imperal_sdk/chat/narration_guard.py +0 -0
  46. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/src/imperal_sdk/chat/prompt.py +0 -0
  47. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/src/imperal_sdk/chat/refusal.py +0 -0
  48. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/src/imperal_sdk/cli/__init__.py +0 -0
  49. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/src/imperal_sdk/cli/main.py +0 -0
  50. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/src/imperal_sdk/config/__init__.py +0 -0
  51. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/src/imperal_sdk/config/client.py +0 -0
  52. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/src/imperal_sdk/devtools/__init__.py +0 -0
  53. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/src/imperal_sdk/devtools/contract_checks.py +0 -0
  54. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/src/imperal_sdk/devtools/generate_api_surface.py +0 -0
  55. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/src/imperal_sdk/devtools/validate_identity_contract.py +0 -0
  56. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/src/imperal_sdk/errors.py +0 -0
  57. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/src/imperal_sdk/extension.py +0 -0
  58. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/src/imperal_sdk/extensions/__init__.py +0 -0
  59. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/src/imperal_sdk/extensions/client.py +0 -0
  60. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/src/imperal_sdk/http/__init__.py +0 -0
  61. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/src/imperal_sdk/http/client.py +0 -0
  62. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/src/imperal_sdk/manifest.py +0 -0
  63. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/src/imperal_sdk/manifest_schema.py +0 -0
  64. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/src/imperal_sdk/notify/__init__.py +0 -0
  65. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/src/imperal_sdk/notify/client.py +0 -0
  66. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/src/imperal_sdk/prompts/__init__.py +0 -0
  67. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/src/imperal_sdk/prompts/icnli_integrity_rules.txt +0 -0
  68. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/src/imperal_sdk/prompts/kernel_formatting_rule.txt +0 -0
  69. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/src/imperal_sdk/prompts/kernel_proactivity_rule.txt +0 -0
  70. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/src/imperal_sdk/protocols.py +0 -0
  71. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/src/imperal_sdk/rpc/__init__.py +0 -0
  72. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/src/imperal_sdk/rpc/codec.py +0 -0
  73. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/src/imperal_sdk/rpc/contract.py +0 -0
  74. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/src/imperal_sdk/runtime/__init__.py +0 -0
  75. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/src/imperal_sdk/runtime/executor.py +0 -0
  76. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/src/imperal_sdk/runtime/llm_provider.py +0 -0
  77. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/src/imperal_sdk/runtime/message_adapter.py +0 -0
  78. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/src/imperal_sdk/schemas/action_result.schema.json +0 -0
  79. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/src/imperal_sdk/schemas/balance_info.schema.json +0 -0
  80. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/src/imperal_sdk/schemas/chat_result.schema.json +0 -0
  81. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/src/imperal_sdk/schemas/completion_result.schema.json +0 -0
  82. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/src/imperal_sdk/schemas/document.schema.json +0 -0
  83. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/src/imperal_sdk/schemas/event.schema.json +0 -0
  84. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/src/imperal_sdk/schemas/file_info.schema.json +0 -0
  85. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/src/imperal_sdk/schemas/function_call.schema.json +0 -0
  86. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/src/imperal_sdk/schemas/http_response.schema.json +0 -0
  87. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/src/imperal_sdk/schemas/imperal.schema.json +0 -0
  88. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/src/imperal_sdk/schemas/limits_result.schema.json +0 -0
  89. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/src/imperal_sdk/schemas/subscription_info.schema.json +0 -0
  90. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/src/imperal_sdk/sdl/__init__.py +0 -0
  91. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/src/imperal_sdk/sdl/_generate_roles_json.py +0 -0
  92. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/src/imperal_sdk/sdl/entity.py +0 -0
  93. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/src/imperal_sdk/sdl/facets/__init__.py +0 -0
  94. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/src/imperal_sdk/sdl/facets/catalog.py +0 -0
  95. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/src/imperal_sdk/sdl/facets/comm.py +0 -0
  96. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/src/imperal_sdk/sdl/facets/content.py +0 -0
  97. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/src/imperal_sdk/sdl/facets/device.py +0 -0
  98. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/src/imperal_sdk/sdl/facets/event.py +0 -0
  99. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/src/imperal_sdk/sdl/facets/geo.py +0 -0
  100. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/src/imperal_sdk/sdl/facets/identity.py +0 -0
  101. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/src/imperal_sdk/sdl/facets/media.py +0 -0
  102. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/src/imperal_sdk/sdl/facets/metric.py +0 -0
  103. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/src/imperal_sdk/sdl/facets/money.py +0 -0
  104. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/src/imperal_sdk/sdl/facets/net.py +0 -0
  105. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/src/imperal_sdk/sdl/facets/people.py +0 -0
  106. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/src/imperal_sdk/sdl/facets/quantity.py +0 -0
  107. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/src/imperal_sdk/sdl/facets/rating.py +0 -0
  108. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/src/imperal_sdk/sdl/facets/security.py +0 -0
  109. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/src/imperal_sdk/sdl/facets/task.py +0 -0
  110. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/src/imperal_sdk/sdl/facets/time.py +0 -0
  111. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/src/imperal_sdk/sdl/field.py +0 -0
  112. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/src/imperal_sdk/sdl/roles.py +0 -0
  113. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/src/imperal_sdk/secrets/__init__.py +0 -0
  114. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/src/imperal_sdk/secrets/client.py +0 -0
  115. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/src/imperal_sdk/secrets/exceptions.py +0 -0
  116. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/src/imperal_sdk/secrets/panel_handler.py +0 -0
  117. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/src/imperal_sdk/secrets/spec.py +0 -0
  118. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/src/imperal_sdk/security/__init__.py +0 -0
  119. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/src/imperal_sdk/security/call_token.py +0 -0
  120. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/src/imperal_sdk/skeleton/__init__.py +0 -0
  121. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/src/imperal_sdk/skeleton/client.py +0 -0
  122. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/src/imperal_sdk/storage/__init__.py +0 -0
  123. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/src/imperal_sdk/storage/client.py +0 -0
  124. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/src/imperal_sdk/store/__init__.py +0 -0
  125. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/src/imperal_sdk/store/client.py +0 -0
  126. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/src/imperal_sdk/store/exceptions.py +0 -0
  127. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/src/imperal_sdk/testing/__init__.py +0 -0
  128. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/src/imperal_sdk/testing/mock_context.py +0 -0
  129. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/src/imperal_sdk/testing/mock_secrets.py +0 -0
  130. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/src/imperal_sdk/types/__init__.py +0 -0
  131. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/src/imperal_sdk/types/action_result.py +0 -0
  132. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/src/imperal_sdk/types/chat_result.py +0 -0
  133. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/src/imperal_sdk/types/client_contracts.py +0 -0
  134. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/src/imperal_sdk/types/contracts.py +0 -0
  135. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/src/imperal_sdk/types/contributions.py +0 -0
  136. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/src/imperal_sdk/types/events.py +0 -0
  137. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/src/imperal_sdk/types/health.py +0 -0
  138. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/src/imperal_sdk/types/identity.py +0 -0
  139. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/src/imperal_sdk/types/pagination.py +0 -0
  140. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/src/imperal_sdk/types/store_contracts.py +0 -0
  141. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/src/imperal_sdk/ui/__init__.py +0 -0
  142. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/src/imperal_sdk/ui/actions.py +0 -0
  143. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/src/imperal_sdk/ui/base.py +0 -0
  144. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/src/imperal_sdk/ui/data.py +0 -0
  145. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/src/imperal_sdk/ui/display.py +0 -0
  146. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/src/imperal_sdk/ui/feedback.py +0 -0
  147. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/src/imperal_sdk/ui/graph.py +0 -0
  148. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/src/imperal_sdk/ui/input_components.py +0 -0
  149. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/src/imperal_sdk/ui/interactive.py +0 -0
  150. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/src/imperal_sdk/ui/layout.py +0 -0
  151. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/src/imperal_sdk/ui/theme.py +0 -0
  152. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/src/imperal_sdk/validator.py +0 -0
  153. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/src/imperal_sdk/validator_v1_6_0.py +0 -0
  154. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/tests/conftest.py +0 -0
  155. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/tests/contract/__init__.py +0 -0
  156. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/tests/contract/test_contract_checks_selftest.py +0 -0
  157. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/tests/contract/test_generate_sdk_claims.py +0 -0
  158. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/tests/contract/test_sample_contract_shape.py +0 -0
  159. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/tests/contract/test_sdk_matches_kernel_contract.py +0 -0
  160. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/tests/contracts/__init__.py +0 -0
  161. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/tests/contracts/test_store_contracts.py +0 -0
  162. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/tests/fixtures/openapi/auth-gateway.json +0 -0
  163. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/tests/fixtures/openapi/registry.json +0 -0
  164. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/tests/fixtures/openapi/sharelock-cases.json +0 -0
  165. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/tests/rpc/__init__.py +0 -0
  166. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/tests/rpc/test_codec.py +0 -0
  167. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/tests/rpc/test_contract.py +0 -0
  168. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/tests/runtime/__init__.py +0 -0
  169. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/tests/runtime/test_llm_provider_config_store.py +0 -0
  170. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/tests/runtime/test_llm_provider_ctx_injection.py +0 -0
  171. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/tests/store/__init__.py +0 -0
  172. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/tests/store/test_list_users_client.py +0 -0
  173. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/tests/store/test_query_all_client.py +0 -0
  174. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/tests/test_action_result_typed.py +0 -0
  175. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/tests/test_as_user.py +0 -0
  176. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/tests/test_auth.py +0 -0
  177. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/tests/test_cache_client.py +0 -0
  178. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/tests/test_cache_model.py +0 -0
  179. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/tests/test_call_token.py +0 -0
  180. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/tests/test_chat_extension_deprecation.py +0 -0
  181. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/tests/test_chat_extension_no_llm_router.py +0 -0
  182. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/tests/test_chat_filters.py +0 -0
  183. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/tests/test_chat_function_background_flag.py +0 -0
  184. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/tests/test_chat_guards.py +0 -0
  185. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/tests/test_chat_guards_bleed.py +0 -0
  186. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/tests/test_chat_prompt.py +0 -0
  187. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/tests/test_chat_result.py +0 -0
  188. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/tests/test_cli.py +0 -0
  189. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/tests/test_client_contracts.py +0 -0
  190. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/tests/test_config_client.py +0 -0
  191. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/tests/test_context.py +0 -0
  192. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/tests/test_context_background_task.py +0 -0
  193. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/tests/test_context_deliver_chat_message.py +0 -0
  194. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/tests/test_context_guards.py +0 -0
  195. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/tests/test_contracts.py +0 -0
  196. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/tests/test_contracts_live.py +0 -0
  197. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/tests/test_contributions.py +0 -0
  198. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/tests/test_data_model_kwarg.py +0 -0
  199. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/tests/test_document_contract.py +0 -0
  200. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/tests/test_emits_decorator.py +0 -0
  201. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/tests/test_error_codes.py +0 -0
  202. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/tests/test_errors.py +0 -0
  203. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/tests/test_event_schema_v2.py +0 -0
  204. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/tests/test_events_health.py +0 -0
  205. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/tests/test_extension.py +0 -0
  206. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/tests/test_extension_v2.py +0 -0
  207. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/tests/test_extensions_emit.py +0 -0
  208. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/tests/test_http_timeout_override.py +0 -0
  209. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/tests/test_id_shape_guard.py +0 -0
  210. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/tests/test_identity_contract.py +0 -0
  211. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/tests/test_imperal_schema_v2.py +0 -0
  212. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/tests/test_kernel_primitives.py +0 -0
  213. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/tests/test_manifest.py +0 -0
  214. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/tests/test_manifest_no_orchestrator_tool.py +0 -0
  215. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/tests/test_manifest_roundtrip_gate.py +0 -0
  216. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/tests/test_manifest_schema.py +0 -0
  217. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/tests/test_manifest_v2_events.py +0 -0
  218. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/tests/test_manifest_v2_other_sections.py +0 -0
  219. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/tests/test_manifest_v2_webhooks.py +0 -0
  220. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/tests/test_manifest_validator_v2.py +0 -0
  221. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/tests/test_mock_context.py +0 -0
  222. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/tests/test_models.py +0 -0
  223. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/tests/test_narration_emission.py +0 -0
  224. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/tests/test_narration_guard.py +0 -0
  225. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/tests/test_openai_max_completion_tokens.py +0 -0
  226. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/tests/test_pagination.py +0 -0
  227. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/tests/test_panel_rendering_contract.py +0 -0
  228. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/tests/test_panels.py +0 -0
  229. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/tests/test_phase_a_dead_removal.py +0 -0
  230. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/tests/test_phase_a_drift.py +0 -0
  231. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/tests/test_phase_a_text.py +0 -0
  232. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/tests/test_sdk_version_stamp.py +0 -0
  233. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/tests/test_sdl_entity.py +0 -0
  234. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/tests/test_sdl_entity_marker.py +0 -0
  235. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/tests/test_sdl_exports.py +0 -0
  236. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/tests/test_sdl_facet_catalog.py +0 -0
  237. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/tests/test_sdl_facet_collisions.py +0 -0
  238. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/tests/test_sdl_facet_comm.py +0 -0
  239. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/tests/test_sdl_facet_content.py +0 -0
  240. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/tests/test_sdl_facet_device.py +0 -0
  241. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/tests/test_sdl_facet_event.py +0 -0
  242. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/tests/test_sdl_facet_exports.py +0 -0
  243. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/tests/test_sdl_facet_field.py +0 -0
  244. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/tests/test_sdl_facet_geo.py +0 -0
  245. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/tests/test_sdl_facet_identity.py +0 -0
  246. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/tests/test_sdl_facet_media.py +0 -0
  247. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/tests/test_sdl_facet_metric.py +0 -0
  248. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/tests/test_sdl_facet_money.py +0 -0
  249. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/tests/test_sdl_facet_net.py +0 -0
  250. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/tests/test_sdl_facet_people.py +0 -0
  251. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/tests/test_sdl_facet_quantity.py +0 -0
  252. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/tests/test_sdl_facet_rating.py +0 -0
  253. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/tests/test_sdl_facet_security.py +0 -0
  254. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/tests/test_sdl_facet_task.py +0 -0
  255. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/tests/test_sdl_facet_time.py +0 -0
  256. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/tests/test_sdl_facets_catalog.py +0 -0
  257. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/tests/test_sdl_facets_doc.py +0 -0
  258. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/tests/test_sdl_facets_pkg.py +0 -0
  259. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/tests/test_sdl_field.py +0 -0
  260. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/tests/test_sdl_roles.py +0 -0
  261. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/tests/test_sdl_roles_json.py +0 -0
  262. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/tests/test_sdl_roles_of_facets.py +0 -0
  263. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/tests/test_skeleton_decorator.py +0 -0
  264. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/tests/test_spec_validation.py +0 -0
  265. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/tests/test_ui.py +0 -0
  266. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/tests/test_ui_fileupload_enhanced.py +0 -0
  267. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/tests/test_ui_html.py +0 -0
  268. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/tests/test_ui_image_enhanced.py +0 -0
  269. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/tests/test_ui_open.py +0 -0
  270. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/tests/test_ui_theme.py +0 -0
  271. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/tests/test_user.py +0 -0
  272. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/tests/test_v7_emit_refusal.py +0 -0
  273. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/tests/test_validator.py +0 -0
  274. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/tests/test_validator_drift.py +0 -0
  275. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/tests/test_validator_pep563.py +0 -0
  276. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/tests/test_validator_v1_6_0_rules.py +0 -0
  277. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/tests/test_validator_v25.py +0 -0
  278. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/tests/test_write_arg_bleed.py +0 -0
  279. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/tests/tools/__init__.py +0 -0
  280. {imperal_sdk-5.2.1 → imperal_sdk-5.3.0}/tests/tools/test_generate_api_surface.py +0 -0
@@ -2,6 +2,37 @@
2
2
 
3
3
  All notable changes to `imperal-sdk` are documented here.
4
4
 
5
+ ## 5.3.0 — 2026-06-16 — BillingClient write/payment methods
6
+
7
+ Additive — **nothing to migrate**.
8
+
9
+ ### Added
10
+ - `ctx.billing` write/payment methods: `list_payment_methods`, `list_payments`,
11
+ `create_setup_intent`, `set_default_payment_method`, `remove_payment_method`,
12
+ `change_plan`, `topup`. Reads degrade safely; writes surface errors so the
13
+ caller can render Stripe failures / drive the Payment Element.
14
+ - `BillingClient` now sends `X-Acting-User` on the service-token path so
15
+ `get_user_or_service` gateway endpoints resolve the acting user.
16
+
17
+ ## 5.2.2 — 2026-06-11 — Import-light package root
18
+
19
+ Performance / robustness release. **Zero API changes** — every public name,
20
+ submodule attribute, star-import and `dir()` entry resolves exactly as before
21
+ (verified by an eager-parity test over the whole surface).
22
+
23
+ ### Changed
24
+
25
+ - **The package root is now import-light (PEP 562 lazy surface).**
26
+ `import imperal_sdk` — and importing transport-free helpers such as
27
+ `imperal_sdk.chat.filters` or `imperal_sdk.chat.error_codes` — no longer
28
+ loads the HTTP client stack. Heavy dependencies load on first use of the
29
+ names that actually need them (`Context`, the service clients,
30
+ `get_llm_provider`, …). Benefits: faster cold imports, and helper modules
31
+ are now safe to import from restricted/sandboxed execution contexts that
32
+ forbid network-stack loading.
33
+
34
+ Nothing to migrate — rebuild against `imperal-sdk>=5.2.2` at your convenience.
35
+
5
36
  ## 5.2.1 — 2026-06-01 — ChatExtension ergonomics & honest deprecations
6
37
 
7
38
  Small, fully backward-compatible cleanup of `ChatExtension`. No API removals;
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: imperal-sdk
3
- Version: 5.2.1
3
+ Version: 5.3.0
4
4
  Summary: SDK for building Imperal Cloud extensions
5
5
  Author: Valentin Scerbacov, Imperal, Inc.
6
6
  License-Expression: AGPL-3.0-or-later
@@ -3,9 +3,16 @@
3
3
  "complete"
4
4
  ],
5
5
  "billing": [
6
+ "change_plan",
6
7
  "check_limits",
8
+ "create_setup_intent",
7
9
  "get_balance",
8
10
  "get_subscription",
11
+ "list_payment_methods",
12
+ "list_payments",
13
+ "remove_payment_method",
14
+ "set_default_payment_method",
15
+ "topup",
9
16
  "track_usage"
10
17
  ],
11
18
  "config": [
@@ -1,5 +1,5 @@
1
1
  {
2
- "_sdk_version": "5.1.0",
2
+ "_sdk_version": "5.3.0",
3
3
  "constants": {
4
4
  "max_call_depth": {
5
5
  "counts_root": true,
@@ -17,11 +17,21 @@
17
17
  "long_running": "advisory"
18
18
  },
19
19
  "http_payloads": {
20
+ "POST /v1/billing/change-plan": [
21
+ "plan_id",
22
+ "period"
23
+ ],
20
24
  "POST /v1/billing/internal/usage/track": [
21
25
  "meter",
22
26
  "quantity",
23
27
  "user_id",
24
28
  "tenant_id"
29
+ ],
30
+ "POST /v1/billing/payment-methods/setup": [],
31
+ "POST /v1/billing/topup": [
32
+ "tokens",
33
+ "price_cents",
34
+ "save_payment_method"
25
35
  ]
26
36
  }
27
37
  }
@@ -0,0 +1,188 @@
1
+ """Imperal Cloud SDK — build extensions for the Imperal platform."""
2
+ from typing import TYPE_CHECKING
3
+
4
+ __version__ = "5.3.0"
5
+
6
+ # 5.2.2 (2026-06-11): the package root resolves its public surface lazily
7
+ # (PEP 562). The eager imports pulled the HTTP transport (Context / client
8
+ # modules -> httpx) into EVERY consumer of ANY submodule — including
9
+ # Temporal workflow code that lazily imports transport-free helpers such as
10
+ # ``imperal_sdk.chat.filters`` (httpx subclasses urllib.request.Request,
11
+ # which the workflow sandbox restricts; kernel federal
12
+ # I-SANDBOX-SAFE-LAZY-IMPORTS, live incident 2026-06-10). The public
13
+ # surface is unchanged: every name resolves to the same object, star-import
14
+ # honors __all__, and resolved names are cached back into module globals.
15
+
16
+ # name -> defining module (resolved on first attribute access)
17
+ _LAZY_ATTRS = {
18
+ # Core
19
+ "Extension": "imperal_sdk.extension",
20
+ "ToolDef": "imperal_sdk.extension",
21
+ "SignalDef": "imperal_sdk.extension",
22
+ "ScheduleDef": "imperal_sdk.extension",
23
+ "LifecycleHook": "imperal_sdk.extension",
24
+ "HealthCheckDef": "imperal_sdk.extension",
25
+ "WebhookDef": "imperal_sdk.extension",
26
+ "EventHandlerDef": "imperal_sdk.extension",
27
+ "ExposedMethod": "imperal_sdk.extension",
28
+ "TrayDef": "imperal_sdk.extension",
29
+ "Context": "imperal_sdk.context",
30
+ "ImperalAuth": "imperal_sdk.auth",
31
+ "AuthError": "imperal_sdk.auth",
32
+ "User": "imperal_sdk.types.identity",
33
+ "UserContext": "imperal_sdk.types.identity",
34
+ "Tenant": "imperal_sdk.types.identity",
35
+ "TenantContext": "imperal_sdk.types.identity",
36
+ "generate_manifest": "imperal_sdk.manifest",
37
+ "save_manifest": "imperal_sdk.manifest",
38
+ "ChatExtension": "imperal_sdk.chat",
39
+ "ActionResult": "imperal_sdk.chat.action_result",
40
+ # LLM
41
+ "get_llm_provider": "imperal_sdk.runtime.llm_provider",
42
+ "LLMProvider": "imperal_sdk.runtime.llm_provider",
43
+ "LLMConfig": "imperal_sdk.runtime.llm_provider",
44
+ "LLMUsage": "imperal_sdk.runtime.llm_provider",
45
+ "MessageAdapter": "imperal_sdk.runtime.message_adapter",
46
+ # IPC
47
+ "ExtensionsClient": "imperal_sdk.extensions.client",
48
+ "CircularCallError": "imperal_sdk.extensions.client",
49
+ # Errors
50
+ "ImperalError": "imperal_sdk.errors",
51
+ "APIError": "imperal_sdk.errors",
52
+ "NotFoundError": "imperal_sdk.errors",
53
+ "RateLimitError": "imperal_sdk.errors",
54
+ "ValidationError": "imperal_sdk.errors",
55
+ "ExtensionError": "imperal_sdk.errors",
56
+ "QuotaExceededError": "imperal_sdk.errors",
57
+ "SkeletonAccessForbidden": "imperal_sdk.errors",
58
+ # Types
59
+ "Page": "imperal_sdk.types",
60
+ "ChatResult": "imperal_sdk.types",
61
+ "FunctionCall": "imperal_sdk.types",
62
+ "Document": "imperal_sdk.types",
63
+ "CompletionResult": "imperal_sdk.types",
64
+ "LimitsResult": "imperal_sdk.types",
65
+ "SubscriptionInfo": "imperal_sdk.types",
66
+ "BalanceInfo": "imperal_sdk.types",
67
+ "FileInfo": "imperal_sdk.types",
68
+ "HTTPResponse": "imperal_sdk.types",
69
+ "Event": "imperal_sdk.types",
70
+ "WebhookRequest": "imperal_sdk.types",
71
+ "WebhookResponse": "imperal_sdk.types",
72
+ "HealthStatus": "imperal_sdk.types",
73
+ # Protocol + Validator
74
+ "ExtensionProtocol": "imperal_sdk.protocols",
75
+ "validate_extension": "imperal_sdk.validator",
76
+ "ValidationReport": "imperal_sdk.validator",
77
+ "ValidationIssue": "imperal_sdk.validator",
78
+ "validate_source_tree": "imperal_sdk.validator_v1_6_0",
79
+ "validate_manifest_v1_6_0": "imperal_sdk.validator_v1_6_0",
80
+ # Secrets (importable from the root since 5.1.0; not in __all__ —
81
+ # preserved as-is)
82
+ "SecretSpec": "imperal_sdk.secrets",
83
+ "SecretClient": "imperal_sdk.secrets",
84
+ "SecretStatus": "imperal_sdk.secrets",
85
+ "SecretNotDeclaredError": "imperal_sdk.secrets",
86
+ "SecretWriteForbidden": "imperal_sdk.secrets",
87
+ "SecretVaultUnavailable": "imperal_sdk.secrets",
88
+ "SecretValueTooLarge": "imperal_sdk.secrets",
89
+ "SecretDeclarationConflict": "imperal_sdk.secrets",
90
+ }
91
+
92
+ # Any imperal_sdk submodule also resolves as a root attribute (the eager
93
+ # imports used to expose chat/context/errors/ui/sdl/... as side effects;
94
+ # the generic fallback in __getattr__ preserves that surface).
95
+
96
+ if TYPE_CHECKING: # pragma: no cover — IDE / type-checker surface only
97
+ from imperal_sdk.extension import (
98
+ Extension, ToolDef, SignalDef, ScheduleDef,
99
+ LifecycleHook, HealthCheckDef, WebhookDef, EventHandlerDef, ExposedMethod, TrayDef,
100
+ )
101
+ from imperal_sdk.context import Context
102
+ from imperal_sdk.auth import ImperalAuth, AuthError
103
+ from imperal_sdk.types.identity import User, UserContext, Tenant, TenantContext
104
+ from imperal_sdk.manifest import generate_manifest, save_manifest
105
+ from imperal_sdk.chat import ChatExtension
106
+ from imperal_sdk.chat.action_result import ActionResult
107
+ from imperal_sdk import ui
108
+ from imperal_sdk import sdl
109
+ from imperal_sdk.runtime.llm_provider import get_llm_provider, LLMProvider, LLMConfig, LLMUsage
110
+ from imperal_sdk.runtime.message_adapter import MessageAdapter
111
+ from imperal_sdk.extensions.client import ExtensionsClient, CircularCallError
112
+ from imperal_sdk.errors import (
113
+ ImperalError, APIError, NotFoundError, RateLimitError,
114
+ ValidationError, ExtensionError, QuotaExceededError,
115
+ SkeletonAccessForbidden,
116
+ )
117
+ from imperal_sdk.types import (
118
+ Page, ChatResult, FunctionCall,
119
+ Document, CompletionResult, LimitsResult, SubscriptionInfo,
120
+ BalanceInfo, FileInfo, HTTPResponse,
121
+ Event, WebhookRequest, WebhookResponse, HealthStatus,
122
+ )
123
+ from imperal_sdk.protocols import ExtensionProtocol
124
+ from imperal_sdk.validator import validate_extension, ValidationReport, ValidationIssue
125
+ from imperal_sdk.validator_v1_6_0 import (
126
+ validate_source_tree,
127
+ validate_manifest_v1_6_0,
128
+ )
129
+ from imperal_sdk.secrets import (
130
+ SecretSpec, SecretClient, SecretStatus,
131
+ SecretNotDeclaredError, SecretWriteForbidden, SecretVaultUnavailable,
132
+ SecretValueTooLarge, SecretDeclarationConflict,
133
+ )
134
+
135
+
136
+ def __getattr__(name: str):
137
+ import importlib
138
+
139
+ src = _LAZY_ATTRS.get(name)
140
+ if src is not None:
141
+ obj = getattr(importlib.import_module(src), name)
142
+ globals()[name] = obj
143
+ return obj
144
+ if name.startswith("_"):
145
+ raise AttributeError(f"module 'imperal_sdk' has no attribute {name!r}")
146
+ try:
147
+ mod = importlib.import_module(f"imperal_sdk.{name}")
148
+ except ImportError:
149
+ raise AttributeError(
150
+ f"module 'imperal_sdk' has no attribute {name!r}"
151
+ ) from None
152
+ globals()[name] = mod
153
+ return mod
154
+
155
+
156
+ def __dir__():
157
+ return sorted(set(globals()) | set(_LAZY_ATTRS) | {"ui", "sdl"})
158
+
159
+
160
+ __all__ = [
161
+ # Core
162
+ "Extension", "ToolDef", "SignalDef", "ScheduleDef",
163
+ "LifecycleHook", "HealthCheckDef", "WebhookDef", "EventHandlerDef", "ExposedMethod", "TrayDef",
164
+ "Context", "ImperalAuth", "AuthError",
165
+ "User", "UserContext", "Tenant", "TenantContext",
166
+ "ChatExtension", "ActionResult",
167
+ "generate_manifest", "save_manifest",
168
+ # IPC
169
+ "ExtensionsClient", "CircularCallError",
170
+ # LLM
171
+ "get_llm_provider", "LLMProvider", "LLMConfig", "LLMUsage", "MessageAdapter",
172
+ # Errors
173
+ "ImperalError", "APIError", "NotFoundError", "RateLimitError",
174
+ "ValidationError", "ExtensionError", "QuotaExceededError",
175
+ "SkeletonAccessForbidden",
176
+ # Types
177
+ "Page", "ChatResult", "FunctionCall",
178
+ "Document", "CompletionResult", "LimitsResult", "SubscriptionInfo",
179
+ "BalanceInfo", "FileInfo", "HTTPResponse",
180
+ "Event", "WebhookRequest", "WebhookResponse", "HealthStatus",
181
+ # Protocol + Validator
182
+ "ExtensionProtocol", "validate_extension", "ValidationReport", "ValidationIssue",
183
+ "validate_source_tree", "validate_manifest_v1_6_0",
184
+ # UI
185
+ "ui",
186
+ # SDL — Structured Data Layer
187
+ "sdl",
188
+ ]
@@ -6,7 +6,10 @@ from dataclasses import dataclass
6
6
  from typing import Any
7
7
  import httpx
8
8
 
9
- from imperal_sdk.types.models import BalanceInfo
9
+ from imperal_sdk.types.models import (
10
+ BalanceInfo, PaymentMethod, SetupIntentResult, ChangePlanResult,
11
+ TopupResult, PaymentRecord,
12
+ )
10
13
 
11
14
  log = logging.getLogger(__name__)
12
15
 
@@ -53,7 +56,10 @@ class BillingClient:
53
56
 
54
57
  def _headers(self) -> dict:
55
58
  if self._service_token:
56
- return {"X-Service-Token": self._service_token}
59
+ h = {"X-Service-Token": self._service_token}
60
+ if self._user_id:
61
+ h["X-Acting-User"] = self._user_id
62
+ return h
57
63
  return {"Authorization": f"Bearer {self._auth_token}"}
58
64
 
59
65
  def _uid(self, user: Any = None) -> str:
@@ -160,3 +166,89 @@ class BillingClient:
160
166
  except Exception as e:
161
167
  log.warning("Billing get_balance failed: %s", e)
162
168
  return BalanceInfo(balance=0, plan="unknown", cap=0)
169
+
170
+ # ─── Payment methods + plan changes + top-up + payment history ──────── #
171
+
172
+ async def list_payment_methods(self, user: Any = None) -> list[PaymentMethod]:
173
+ uid = self._uid(user)
174
+ try:
175
+ async with httpx.AsyncClient() as client:
176
+ url = (f"{self._gateway_url}/v1/billing/internal/payment-methods/{uid}"
177
+ if (self._service_token and uid)
178
+ else f"{self._gateway_url}/v1/billing/payment-methods")
179
+ resp = await client.get(url, headers=self._headers(), timeout=10)
180
+ resp.raise_for_status()
181
+ return [PaymentMethod(**m) for m in resp.json()]
182
+ except Exception as e:
183
+ log.warning("Billing list_payment_methods failed: %s", e)
184
+ return []
185
+
186
+ async def list_payments(self, user: Any = None, limit: int = 50, offset: int = 0) -> list[PaymentRecord]:
187
+ uid = self._uid(user)
188
+ try:
189
+ async with httpx.AsyncClient() as client:
190
+ if self._service_token and uid:
191
+ url = f"{self._gateway_url}/v1/billing/internal/payments/{uid}"
192
+ else:
193
+ url = f"{self._gateway_url}/v1/billing/payments"
194
+ resp = await client.get(url, headers=self._headers(),
195
+ params={"limit": limit, "offset": offset}, timeout=15)
196
+ resp.raise_for_status()
197
+ return [PaymentRecord(**p) for p in resp.json()]
198
+ except Exception as e:
199
+ log.warning("Billing list_payments failed: %s", e)
200
+ return []
201
+
202
+ async def create_setup_intent(self, user: Any = None) -> SetupIntentResult:
203
+ """Add-card SetupIntent. Surfaces errors (the ext needs the client secret)."""
204
+ async with httpx.AsyncClient() as client:
205
+ resp = await client.post(f"{self._gateway_url}/v1/billing/payment-methods/setup",
206
+ headers=self._headers(), timeout=10)
207
+ resp.raise_for_status()
208
+ d = resp.json()
209
+ return SetupIntentResult(client_secret=d.get("client_secret", ""),
210
+ publishable_key=d.get("publishable_key", ""))
211
+
212
+ async def set_default_payment_method(self, pm_id: str, user: Any = None) -> bool:
213
+ async with httpx.AsyncClient() as client:
214
+ resp = await client.put(f"{self._gateway_url}/v1/billing/payment-methods/{pm_id}/default",
215
+ headers=self._headers(), timeout=10)
216
+ resp.raise_for_status()
217
+ return True
218
+
219
+ async def remove_payment_method(self, pm_id: str, user: Any = None) -> bool:
220
+ async with httpx.AsyncClient() as client:
221
+ resp = await client.delete(f"{self._gateway_url}/v1/billing/payment-methods/{pm_id}",
222
+ headers=self._headers(), timeout=10)
223
+ resp.raise_for_status()
224
+ return True
225
+
226
+ async def change_plan(self, plan_id: str, period: str = "monthly", user: Any = None) -> ChangePlanResult:
227
+ """Upgrade (prorated, immediate) / downgrade (scheduled). Surfaces errors."""
228
+ async with httpx.AsyncClient() as client:
229
+ resp = await client.post(f"{self._gateway_url}/v1/billing/change-plan",
230
+ json={"plan_id": plan_id, "period": period},
231
+ headers=self._headers(), timeout=15)
232
+ resp.raise_for_status()
233
+ d = resp.json()
234
+ return ChangePlanResult(
235
+ action=d.get("action", ""), plan=d.get("plan", ""),
236
+ succeeded=bool(d.get("succeeded", False)),
237
+ requires_action=bool(d.get("requires_action", False)),
238
+ client_secret=d.get("client_secret", ""),
239
+ effective_at=d.get("effective_at", "") or "",
240
+ pending=bool(d.get("pending", False)))
241
+
242
+ async def topup(self, tokens: int, price_cents: int, save_payment_method: bool = True,
243
+ user: Any = None) -> TopupResult:
244
+ """Token top-up PaymentIntent. Surfaces errors."""
245
+ async with httpx.AsyncClient() as client:
246
+ resp = await client.post(f"{self._gateway_url}/v1/billing/topup",
247
+ json={"tokens": tokens, "price_cents": price_cents,
248
+ "save_payment_method": save_payment_method},
249
+ headers=self._headers(), timeout=15)
250
+ resp.raise_for_status()
251
+ d = resp.json()
252
+ return TopupResult(client_secret=d.get("client_secret", ""),
253
+ payment_intent_id=d.get("payment_intent_id", ""),
254
+ publishable_key=d.get("publishable_key", ""))
@@ -50,6 +50,13 @@ class BillingProtocol(Protocol):
50
50
  async def get_subscription(self) -> SubscriptionInfo: ...
51
51
  async def track_usage(self, meter: str, quantity: int = 1, user: Any = None) -> bool: ...
52
52
  async def get_balance(self) -> BalanceInfo: ...
53
+ async def list_payment_methods(self, user: Any = None) -> list: ...
54
+ async def list_payments(self, user: Any = None, limit: int = 50, offset: int = 0) -> list: ...
55
+ async def create_setup_intent(self, user: Any = None): ...
56
+ async def set_default_payment_method(self, pm_id: str, user: Any = None) -> bool: ...
57
+ async def remove_payment_method(self, pm_id: str, user: Any = None) -> bool: ...
58
+ async def change_plan(self, plan_id: str, period: str = "monthly", user: Any = None): ...
59
+ async def topup(self, tokens: int, price_cents: int, save_payment_method: bool = True, user: Any = None): ...
53
60
 
54
61
 
55
62
  @runtime_checkable
@@ -35,6 +35,9 @@ _DECORATOR_ROLES = {
35
35
  # stale 'amount' field must never reappear here.
36
36
  _HTTP_PAYLOADS: dict[str, list[str]] = {
37
37
  "POST /v1/billing/internal/usage/track": ["meter", "quantity", "user_id", "tenant_id"],
38
+ "POST /v1/billing/change-plan": ["plan_id", "period"],
39
+ "POST /v1/billing/topup": ["tokens", "price_cents", "save_payment_method"],
40
+ "POST /v1/billing/payment-methods/setup": [],
38
41
  }
39
42
 
40
43
 
@@ -123,3 +123,56 @@ class HTTPStatusError(Exception):
123
123
  self.status_code = status_code
124
124
  self.body = body or ""
125
125
  super().__init__(f"HTTP {status_code}: {str(body)[:200]}")
126
+
127
+
128
+ @dataclass
129
+ class PaymentMethod:
130
+ """Result item from ctx.billing.list_payment_methods()."""
131
+ id: str = ""
132
+ type: str = "card"
133
+ brand: str = ""
134
+ last4: str = ""
135
+ exp_month: int = 0
136
+ exp_year: int = 0
137
+ is_default: bool = False
138
+
139
+
140
+ @dataclass
141
+ class SetupIntentResult:
142
+ """Result from ctx.billing.create_setup_intent() — drives a Stripe add-card flow."""
143
+ client_secret: str = ""
144
+ publishable_key: str = ""
145
+
146
+
147
+ @dataclass
148
+ class ChangePlanResult:
149
+ """Result from ctx.billing.change_plan()."""
150
+ action: str = ""
151
+ plan: str = ""
152
+ succeeded: bool = False
153
+ requires_action: bool = False
154
+ client_secret: str = ""
155
+ effective_at: str = ""
156
+ pending: bool = False
157
+
158
+
159
+ @dataclass
160
+ class TopupResult:
161
+ """Result from ctx.billing.topup() — drives a Stripe Payment Element."""
162
+ client_secret: str = ""
163
+ payment_intent_id: str = ""
164
+ publishable_key: str = ""
165
+
166
+
167
+ @dataclass
168
+ class PaymentRecord:
169
+ """Result item from ctx.billing.list_payments()."""
170
+ payment_intent_id: str = ""
171
+ amount_cents: int = 0
172
+ currency: str = "usd"
173
+ tokens: int = 0
174
+ status: str = ""
175
+ type: str = ""
176
+ created_at: str | None = None
177
+ completed_at: str | None = None
178
+ receipt_url: str = ""
@@ -0,0 +1,101 @@
1
+ """Conformance A8/A9 — committed contract artifacts must match the live SDK.
2
+
3
+ A8 (audit 2026-06-04, re-validated 2026-06-12): ``sdk_claims.json`` sat at
4
+ 5.1.0 while the SDK shipped 5.2.0/5.2.1/5.2.2 — the kernel-side
5
+ ``contract_guard`` freshness layer keyed on the stale pin and the lag was
6
+ invisible to every local gate. A9: the docs_guard snapshots
7
+ (``api_surface.json`` / ``ctx_surface.json``) had the same hand-copied-rot
8
+ failure mode. These tests run under preflight Edge 1 (``tests/contract/``),
9
+ so a stale committed artifact turns the one sanctioned gate red locally.
10
+ """
11
+ from __future__ import annotations
12
+
13
+ import dataclasses
14
+ import json
15
+ from pathlib import Path
16
+
17
+ import pytest
18
+
19
+ from imperal_sdk.devtools.generate_api_surface import generate_surface
20
+ from imperal_sdk.devtools.generate_sdk_claims import generate_claims
21
+
22
+ REPO = Path(__file__).resolve().parents[2]
23
+ # imperal-sdk sits inside the MCP-Configs workspace checkout; standalone
24
+ # clones (CI on GitHub) won't have the workspace-level docs_guard inputs.
25
+ WORKSPACE_GUARD_INPUTS = REPO.parent / "scripts" / "docs_guard" / "inputs"
26
+
27
+
28
+ def test_committed_sdk_claims_match_generated() -> None:
29
+ committed = json.loads((REPO / "sdk_claims.json").read_text(encoding="utf-8"))
30
+ assert committed == generate_claims(), (
31
+ "sdk_claims.json is stale — regenerate with: "
32
+ "python -m imperal_sdk.devtools.generate_sdk_claims --output sdk_claims.json "
33
+ "(and copy to kernel tools/contract/sdk-claims.json on deploy)"
34
+ )
35
+
36
+
37
+ def test_docs_guard_api_surface_snapshot_fresh() -> None:
38
+ snap = WORKSPACE_GUARD_INPUTS / "api_surface.json"
39
+ if not snap.is_file():
40
+ pytest.skip("workspace docs_guard inputs not present (standalone SDK checkout)")
41
+ committed = json.loads(snap.read_text(encoding="utf-8"))
42
+ live = generate_surface()
43
+ assert {ns: sorted(m) for ns, m in committed.items()} == {
44
+ ns: sorted(m) for ns, m in live.items()
45
+ }, (
46
+ "scripts/docs_guard/inputs/api_surface.json is stale — refresh it from "
47
+ "imperal_sdk.devtools.generate_api_surface (manual-cp rot, conformance A9)"
48
+ )
49
+
50
+
51
+ def _live_ctx_surface() -> set[str]:
52
+ """Public Context surface: dataclass fields + properties + methods.
53
+
54
+ Mirrors the original ctx_surface.json snapshot semantics (everything an
55
+ extension author can legitimately write after ``ctx.`` at the top level,
56
+ excluding kernel-injected attrs like ``secrets`` which are not part of
57
+ the dataclass).
58
+ """
59
+ from imperal_sdk.context import Context
60
+
61
+ fields = {f.name for f in dataclasses.fields(Context) if not f.name.startswith("_")}
62
+ members = {
63
+ name
64
+ for name, value in vars(Context).items()
65
+ if not name.startswith("_") and (isinstance(value, property) or callable(value))
66
+ }
67
+ return fields | members
68
+
69
+
70
+ def test_kernel_contract_copies_mutually_consistent() -> None:
71
+ """A5: the two local copies of the kernel contract (the SDK contract-test
72
+ fixture and the docs_guard snapshot) must be identical — both are refreshed
73
+ from kernel ``tools/kernel-contract.json`` on a kernel contract change, and
74
+ one-sided rot (e.g. the pre-2026-06-12 state: guard copy carried the old
75
+ max_depth=3 while the fixture carried 6) is exactly how 'ALL GREEN'
76
+ overstates coverage."""
77
+ guard_copy = WORKSPACE_GUARD_INPUTS / "kernel-contract.json"
78
+ if not guard_copy.is_file():
79
+ pytest.skip("workspace docs_guard inputs not present (standalone SDK checkout)")
80
+ fixture = json.loads(
81
+ (REPO / "tests" / "fixtures" / "contract" / "kernel-contract.sample.json").read_text(
82
+ encoding="utf-8"
83
+ )
84
+ )
85
+ assert fixture == json.loads(guard_copy.read_text(encoding="utf-8")), (
86
+ "kernel-contract fixture and docs_guard snapshot diverged — refresh BOTH "
87
+ "from kernel tools/kernel-contract.json (conformance A5)"
88
+ )
89
+
90
+
91
+ def test_docs_guard_ctx_surface_snapshot_fresh() -> None:
92
+ snap = WORKSPACE_GUARD_INPUTS / "ctx_surface.json"
93
+ if not snap.is_file():
94
+ pytest.skip("workspace docs_guard inputs not present (standalone SDK checkout)")
95
+ committed = set(json.loads(snap.read_text(encoding="utf-8")))
96
+ live = _live_ctx_surface()
97
+ assert committed == live, (
98
+ f"scripts/docs_guard/inputs/ctx_surface.json is stale (conformance A9). "
99
+ f"missing_from_snapshot={sorted(live - committed)} "
100
+ f"gone_from_sdk={sorted(committed - live)}"
101
+ )