imperal-sdk 5.4.2__tar.gz → 5.4.3__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 (278) hide show
  1. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/CHANGELOG.md +24 -0
  2. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/PKG-INFO +1 -1
  3. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/sdk_claims.json +1 -1
  4. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/src/imperal_sdk/__init__.py +1 -1
  5. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/src/imperal_sdk/chat/extension.py +4 -1
  6. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/src/imperal_sdk/cli/main.py +13 -8
  7. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/src/imperal_sdk/secrets/panel_handler.py +1 -1
  8. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/src/imperal_sdk/testing/mock_context.py +11 -7
  9. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/src/imperal_sdk/types/models.py +6 -4
  10. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/src/imperal_sdk/validator.py +9 -3
  11. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/tests/test_cli.py +3 -2
  12. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/tests/test_data_model_kwarg.py +41 -2
  13. imperal_sdk-5.4.3/tests/test_http_json_and_mock.py +37 -0
  14. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/.github/workflows/identity-contract.yml +0 -0
  15. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/.github/workflows/publish.yml +0 -0
  16. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/.github/workflows/test.yml +0 -0
  17. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/.gitignore +0 -0
  18. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/LICENSE +0 -0
  19. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/README.md +0 -0
  20. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/api_surface.json +0 -0
  21. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/docs/sdl-facets.md +0 -0
  22. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/pyproject.toml +0 -0
  23. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/sdl_roles.json +0 -0
  24. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/src/.codebase-index-cache.pkl +0 -0
  25. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/src/imperal_sdk/ai/__init__.py +0 -0
  26. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/src/imperal_sdk/ai/client.py +0 -0
  27. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/src/imperal_sdk/auth/__init__.py +0 -0
  28. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/src/imperal_sdk/auth/client.py +0 -0
  29. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/src/imperal_sdk/auth/middleware.py +0 -0
  30. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/src/imperal_sdk/billing/__init__.py +0 -0
  31. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/src/imperal_sdk/billing/client.py +0 -0
  32. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/src/imperal_sdk/cache/__init__.py +0 -0
  33. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/src/imperal_sdk/cache/client.py +0 -0
  34. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/src/imperal_sdk/cache/protocol.py +0 -0
  35. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/src/imperal_sdk/chat/__init__.py +0 -0
  36. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/src/imperal_sdk/chat/action_result.py +0 -0
  37. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/src/imperal_sdk/chat/error_codes.py +0 -0
  38. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/src/imperal_sdk/chat/exceptions.py +0 -0
  39. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/src/imperal_sdk/chat/filters.py +0 -0
  40. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/src/imperal_sdk/chat/guards.py +0 -0
  41. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/src/imperal_sdk/chat/kernel_primitives.py +0 -0
  42. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/src/imperal_sdk/chat/narration.py +0 -0
  43. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/src/imperal_sdk/chat/narration_guard.py +0 -0
  44. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/src/imperal_sdk/chat/prompt.py +0 -0
  45. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/src/imperal_sdk/chat/refusal.py +0 -0
  46. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/src/imperal_sdk/cli/__init__.py +0 -0
  47. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/src/imperal_sdk/config/__init__.py +0 -0
  48. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/src/imperal_sdk/config/client.py +0 -0
  49. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/src/imperal_sdk/context.py +0 -0
  50. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/src/imperal_sdk/devtools/__init__.py +0 -0
  51. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/src/imperal_sdk/devtools/contract_checks.py +0 -0
  52. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/src/imperal_sdk/devtools/generate_api_surface.py +0 -0
  53. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/src/imperal_sdk/devtools/generate_sdk_claims.py +0 -0
  54. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/src/imperal_sdk/devtools/validate_identity_contract.py +0 -0
  55. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/src/imperal_sdk/errors.py +0 -0
  56. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/src/imperal_sdk/extension.py +0 -0
  57. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/src/imperal_sdk/extensions/__init__.py +0 -0
  58. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/src/imperal_sdk/extensions/client.py +0 -0
  59. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/src/imperal_sdk/http/__init__.py +0 -0
  60. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/src/imperal_sdk/http/client.py +0 -0
  61. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/src/imperal_sdk/manifest.py +0 -0
  62. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/src/imperal_sdk/manifest_schema.py +0 -0
  63. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/src/imperal_sdk/notify/__init__.py +0 -0
  64. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/src/imperal_sdk/notify/client.py +0 -0
  65. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/src/imperal_sdk/prompts/__init__.py +0 -0
  66. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/src/imperal_sdk/prompts/icnli_integrity_rules.txt +0 -0
  67. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/src/imperal_sdk/prompts/kernel_formatting_rule.txt +0 -0
  68. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/src/imperal_sdk/prompts/kernel_proactivity_rule.txt +0 -0
  69. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/src/imperal_sdk/protocols.py +0 -0
  70. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/src/imperal_sdk/rpc/__init__.py +0 -0
  71. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/src/imperal_sdk/rpc/codec.py +0 -0
  72. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/src/imperal_sdk/rpc/contract.py +0 -0
  73. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/src/imperal_sdk/runtime/__init__.py +0 -0
  74. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/src/imperal_sdk/runtime/executor.py +0 -0
  75. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/src/imperal_sdk/runtime/llm_provider.py +0 -0
  76. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/src/imperal_sdk/runtime/message_adapter.py +0 -0
  77. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/src/imperal_sdk/schemas/action_result.schema.json +0 -0
  78. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/src/imperal_sdk/schemas/balance_info.schema.json +0 -0
  79. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/src/imperal_sdk/schemas/chat_result.schema.json +0 -0
  80. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/src/imperal_sdk/schemas/completion_result.schema.json +0 -0
  81. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/src/imperal_sdk/schemas/document.schema.json +0 -0
  82. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/src/imperal_sdk/schemas/event.schema.json +0 -0
  83. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/src/imperal_sdk/schemas/file_info.schema.json +0 -0
  84. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/src/imperal_sdk/schemas/function_call.schema.json +0 -0
  85. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/src/imperal_sdk/schemas/http_response.schema.json +0 -0
  86. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/src/imperal_sdk/schemas/imperal.schema.json +0 -0
  87. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/src/imperal_sdk/schemas/limits_result.schema.json +0 -0
  88. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/src/imperal_sdk/schemas/subscription_info.schema.json +0 -0
  89. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/src/imperal_sdk/sdl/__init__.py +0 -0
  90. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/src/imperal_sdk/sdl/_generate_roles_json.py +0 -0
  91. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/src/imperal_sdk/sdl/entity.py +0 -0
  92. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/src/imperal_sdk/sdl/facets/__init__.py +0 -0
  93. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/src/imperal_sdk/sdl/facets/catalog.py +0 -0
  94. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/src/imperal_sdk/sdl/facets/comm.py +0 -0
  95. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/src/imperal_sdk/sdl/facets/content.py +0 -0
  96. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/src/imperal_sdk/sdl/facets/device.py +0 -0
  97. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/src/imperal_sdk/sdl/facets/event.py +0 -0
  98. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/src/imperal_sdk/sdl/facets/geo.py +0 -0
  99. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/src/imperal_sdk/sdl/facets/identity.py +0 -0
  100. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/src/imperal_sdk/sdl/facets/media.py +0 -0
  101. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/src/imperal_sdk/sdl/facets/metric.py +0 -0
  102. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/src/imperal_sdk/sdl/facets/money.py +0 -0
  103. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/src/imperal_sdk/sdl/facets/net.py +0 -0
  104. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/src/imperal_sdk/sdl/facets/people.py +0 -0
  105. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/src/imperal_sdk/sdl/facets/quantity.py +0 -0
  106. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/src/imperal_sdk/sdl/facets/rating.py +0 -0
  107. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/src/imperal_sdk/sdl/facets/security.py +0 -0
  108. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/src/imperal_sdk/sdl/facets/task.py +0 -0
  109. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/src/imperal_sdk/sdl/facets/time.py +0 -0
  110. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/src/imperal_sdk/sdl/field.py +0 -0
  111. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/src/imperal_sdk/sdl/roles.py +0 -0
  112. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/src/imperal_sdk/secrets/__init__.py +0 -0
  113. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/src/imperal_sdk/secrets/client.py +0 -0
  114. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/src/imperal_sdk/secrets/exceptions.py +0 -0
  115. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/src/imperal_sdk/secrets/spec.py +0 -0
  116. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/src/imperal_sdk/security/__init__.py +0 -0
  117. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/src/imperal_sdk/security/call_token.py +0 -0
  118. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/src/imperal_sdk/skeleton/__init__.py +0 -0
  119. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/src/imperal_sdk/skeleton/client.py +0 -0
  120. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/src/imperal_sdk/storage/__init__.py +0 -0
  121. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/src/imperal_sdk/storage/client.py +0 -0
  122. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/src/imperal_sdk/store/__init__.py +0 -0
  123. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/src/imperal_sdk/store/client.py +0 -0
  124. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/src/imperal_sdk/store/exceptions.py +0 -0
  125. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/src/imperal_sdk/testing/__init__.py +0 -0
  126. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/src/imperal_sdk/testing/mock_secrets.py +0 -0
  127. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/src/imperal_sdk/types/__init__.py +0 -0
  128. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/src/imperal_sdk/types/action_result.py +0 -0
  129. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/src/imperal_sdk/types/chat_result.py +0 -0
  130. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/src/imperal_sdk/types/client_contracts.py +0 -0
  131. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/src/imperal_sdk/types/contracts.py +0 -0
  132. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/src/imperal_sdk/types/contributions.py +0 -0
  133. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/src/imperal_sdk/types/events.py +0 -0
  134. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/src/imperal_sdk/types/health.py +0 -0
  135. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/src/imperal_sdk/types/identity.py +0 -0
  136. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/src/imperal_sdk/types/pagination.py +0 -0
  137. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/src/imperal_sdk/types/store_contracts.py +0 -0
  138. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/src/imperal_sdk/ui/__init__.py +0 -0
  139. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/src/imperal_sdk/ui/actions.py +0 -0
  140. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/src/imperal_sdk/ui/base.py +0 -0
  141. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/src/imperal_sdk/ui/data.py +0 -0
  142. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/src/imperal_sdk/ui/display.py +0 -0
  143. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/src/imperal_sdk/ui/feedback.py +0 -0
  144. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/src/imperal_sdk/ui/graph.py +0 -0
  145. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/src/imperal_sdk/ui/input_components.py +0 -0
  146. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/src/imperal_sdk/ui/interactive.py +0 -0
  147. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/src/imperal_sdk/ui/layout.py +0 -0
  148. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/src/imperal_sdk/ui/theme.py +0 -0
  149. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/src/imperal_sdk/validator_v1_6_0.py +0 -0
  150. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/tests/conftest.py +0 -0
  151. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/tests/contract/__init__.py +0 -0
  152. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/tests/contract/test_artifacts_freshness.py +0 -0
  153. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/tests/contract/test_contract_checks_selftest.py +0 -0
  154. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/tests/contract/test_generate_sdk_claims.py +0 -0
  155. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/tests/contract/test_sample_contract_shape.py +0 -0
  156. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/tests/contract/test_sdk_matches_kernel_contract.py +0 -0
  157. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/tests/contracts/__init__.py +0 -0
  158. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/tests/contracts/test_store_contracts.py +0 -0
  159. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/tests/fixtures/contract/kernel-contract.sample.json +0 -0
  160. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/tests/fixtures/openapi/auth-gateway.json +0 -0
  161. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/tests/fixtures/openapi/registry.json +0 -0
  162. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/tests/fixtures/openapi/sharelock-cases.json +0 -0
  163. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/tests/rpc/__init__.py +0 -0
  164. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/tests/rpc/test_codec.py +0 -0
  165. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/tests/rpc/test_contract.py +0 -0
  166. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/tests/runtime/__init__.py +0 -0
  167. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/tests/runtime/test_llm_provider_config_store.py +0 -0
  168. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/tests/runtime/test_llm_provider_ctx_injection.py +0 -0
  169. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/tests/store/__init__.py +0 -0
  170. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/tests/store/test_list_users_client.py +0 -0
  171. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/tests/store/test_query_all_client.py +0 -0
  172. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/tests/test_action_result_typed.py +0 -0
  173. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/tests/test_as_user.py +0 -0
  174. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/tests/test_auth.py +0 -0
  175. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/tests/test_billing.py +0 -0
  176. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/tests/test_cache_client.py +0 -0
  177. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/tests/test_cache_model.py +0 -0
  178. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/tests/test_call_token.py +0 -0
  179. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/tests/test_chat_extension_deprecation.py +0 -0
  180. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/tests/test_chat_extension_no_llm_router.py +0 -0
  181. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/tests/test_chat_filters.py +0 -0
  182. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/tests/test_chat_function_background_flag.py +0 -0
  183. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/tests/test_chat_guards.py +0 -0
  184. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/tests/test_chat_guards_bleed.py +0 -0
  185. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/tests/test_chat_prompt.py +0 -0
  186. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/tests/test_chat_result.py +0 -0
  187. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/tests/test_client_contracts.py +0 -0
  188. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/tests/test_config_client.py +0 -0
  189. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/tests/test_context.py +0 -0
  190. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/tests/test_context_background_task.py +0 -0
  191. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/tests/test_context_deliver_chat_message.py +0 -0
  192. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/tests/test_context_guards.py +0 -0
  193. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/tests/test_contracts.py +0 -0
  194. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/tests/test_contracts_live.py +0 -0
  195. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/tests/test_contributions.py +0 -0
  196. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/tests/test_document_contract.py +0 -0
  197. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/tests/test_emits_decorator.py +0 -0
  198. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/tests/test_error_codes.py +0 -0
  199. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/tests/test_errors.py +0 -0
  200. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/tests/test_event_schema_v2.py +0 -0
  201. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/tests/test_events_health.py +0 -0
  202. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/tests/test_extension.py +0 -0
  203. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/tests/test_extension_v2.py +0 -0
  204. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/tests/test_extensions_emit.py +0 -0
  205. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/tests/test_http_timeout_override.py +0 -0
  206. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/tests/test_id_shape_guard.py +0 -0
  207. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/tests/test_identity_contract.py +0 -0
  208. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/tests/test_imperal_schema_v2.py +0 -0
  209. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/tests/test_import_light.py +0 -0
  210. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/tests/test_kernel_primitives.py +0 -0
  211. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/tests/test_manifest.py +0 -0
  212. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/tests/test_manifest_no_orchestrator_tool.py +0 -0
  213. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/tests/test_manifest_roundtrip_gate.py +0 -0
  214. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/tests/test_manifest_schema.py +0 -0
  215. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/tests/test_manifest_v2_events.py +0 -0
  216. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/tests/test_manifest_v2_other_sections.py +0 -0
  217. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/tests/test_manifest_v2_webhooks.py +0 -0
  218. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/tests/test_manifest_validator_v2.py +0 -0
  219. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/tests/test_mock_context.py +0 -0
  220. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/tests/test_models.py +0 -0
  221. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/tests/test_narration_emission.py +0 -0
  222. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/tests/test_narration_guard.py +0 -0
  223. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/tests/test_openai_max_completion_tokens.py +0 -0
  224. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/tests/test_pagination.py +0 -0
  225. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/tests/test_panel_rendering_contract.py +0 -0
  226. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/tests/test_panels.py +0 -0
  227. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/tests/test_phase_a_dead_removal.py +0 -0
  228. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/tests/test_phase_a_drift.py +0 -0
  229. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/tests/test_phase_a_text.py +0 -0
  230. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/tests/test_sdk_version_stamp.py +0 -0
  231. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/tests/test_sdl_entity.py +0 -0
  232. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/tests/test_sdl_entity_marker.py +0 -0
  233. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/tests/test_sdl_exports.py +0 -0
  234. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/tests/test_sdl_facet_catalog.py +0 -0
  235. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/tests/test_sdl_facet_collisions.py +0 -0
  236. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/tests/test_sdl_facet_comm.py +0 -0
  237. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/tests/test_sdl_facet_content.py +0 -0
  238. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/tests/test_sdl_facet_device.py +0 -0
  239. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/tests/test_sdl_facet_event.py +0 -0
  240. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/tests/test_sdl_facet_exports.py +0 -0
  241. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/tests/test_sdl_facet_field.py +0 -0
  242. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/tests/test_sdl_facet_geo.py +0 -0
  243. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/tests/test_sdl_facet_identity.py +0 -0
  244. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/tests/test_sdl_facet_media.py +0 -0
  245. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/tests/test_sdl_facet_metric.py +0 -0
  246. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/tests/test_sdl_facet_money.py +0 -0
  247. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/tests/test_sdl_facet_net.py +0 -0
  248. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/tests/test_sdl_facet_people.py +0 -0
  249. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/tests/test_sdl_facet_quantity.py +0 -0
  250. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/tests/test_sdl_facet_rating.py +0 -0
  251. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/tests/test_sdl_facet_security.py +0 -0
  252. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/tests/test_sdl_facet_task.py +0 -0
  253. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/tests/test_sdl_facet_time.py +0 -0
  254. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/tests/test_sdl_facets_catalog.py +0 -0
  255. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/tests/test_sdl_facets_doc.py +0 -0
  256. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/tests/test_sdl_facets_pkg.py +0 -0
  257. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/tests/test_sdl_field.py +0 -0
  258. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/tests/test_sdl_roles.py +0 -0
  259. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/tests/test_sdl_roles_json.py +0 -0
  260. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/tests/test_sdl_roles_of_facets.py +0 -0
  261. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/tests/test_skeleton_decorator.py +0 -0
  262. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/tests/test_spec_validation.py +0 -0
  263. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/tests/test_ui.py +0 -0
  264. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/tests/test_ui_fileupload_enhanced.py +0 -0
  265. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/tests/test_ui_html.py +0 -0
  266. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/tests/test_ui_image_enhanced.py +0 -0
  267. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/tests/test_ui_open.py +0 -0
  268. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/tests/test_ui_theme.py +0 -0
  269. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/tests/test_user.py +0 -0
  270. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/tests/test_v7_emit_refusal.py +0 -0
  271. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/tests/test_validator.py +0 -0
  272. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/tests/test_validator_drift.py +0 -0
  273. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/tests/test_validator_pep563.py +0 -0
  274. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/tests/test_validator_v1_6_0_rules.py +0 -0
  275. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/tests/test_validator_v25.py +0 -0
  276. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/tests/test_write_arg_bleed.py +0 -0
  277. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/tests/tools/__init__.py +0 -0
  278. {imperal_sdk-5.4.2 → imperal_sdk-5.4.3}/tests/tools/test_generate_api_surface.py +0 -0
@@ -2,6 +2,30 @@
2
2
 
3
3
  All notable changes to `imperal-sdk` are documented here.
4
4
 
5
+ ## 5.4.3 — 2026-06-18 — Fix secrets-panel render crash
6
+
7
+ Bugfix — **nothing to migrate**.
8
+
9
+ ### Fixed
10
+ - Auto-generated `__panel__secrets` panel (created for any extension declaring
11
+ `@ext.secret`) called `ui.Heading(...)`, which does not exist — the display
12
+ element is exported as `ui.Header`. Every secrets-panel render raised
13
+ `AttributeError: module 'imperal_sdk.ui' has no attribute 'Heading'`. Switched
14
+ to `ui.Header`. No public API change.
15
+
16
+ ## 5.4.2 — 2026-06-16 — BillingClient renew_subscription
17
+
18
+ Additive — **nothing to migrate**.
19
+
20
+ ### Added
21
+ - `ctx.billing.renew_subscription()` — renews an **expired** subscription for
22
+ the same plan: charges the saved default card for one fresh period and
23
+ restores access immediately (POST `/v1/billing/renew`). Surfaces errors
24
+ (`402` no card / SCA required, `409` not expired). Returns the gateway result
25
+ dict (`{status, plan, expires_at, payment_intent_id}`).
26
+
27
+ `BillingProtocol` 18 → 19 methods.
28
+
5
29
  ## 5.4.1 — 2026-06-16 — BillingClient resume + cancel_at_period_end
6
30
 
7
31
  Additive — **nothing to migrate**.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: imperal-sdk
3
- Version: 5.4.2
3
+ Version: 5.4.3
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
@@ -1,5 +1,5 @@
1
1
  {
2
- "_sdk_version": "5.4.2",
2
+ "_sdk_version": "5.4.3",
3
3
  "constants": {
4
4
  "max_call_depth": {
5
5
  "counts_root": true,
@@ -1,7 +1,7 @@
1
1
  """Imperal Cloud SDK — build extensions for the Imperal platform."""
2
2
  from typing import TYPE_CHECKING
3
3
 
4
- __version__ = "5.4.2"
4
+ __version__ = "5.4.3"
5
5
 
6
6
  # 5.2.2 (2026-06-11): the package root resolves its public surface lazily
7
7
  # (PEP 562). The eager imports pulled the HTTP transport (Context / client
@@ -61,6 +61,7 @@ class FunctionDef:
61
61
  _pydantic_model: type | None = None # auto-detected Pydantic BaseModel class
62
62
  _pydantic_param: str = "" # parameter name that receives the model instance
63
63
  _return_model: type | None = None # data_model kwarg OR autodetected return Pydantic model
64
+ _ui_builder: bool = False # @chat.function(ui_builder=True): Declarative-UI builder; exempt from the V23 data_model rule
64
65
 
65
66
 
66
67
  class ChatExtension:
@@ -117,7 +118,8 @@ class ChatExtension:
117
118
  id_projection: str | None = None,
118
119
  background: bool = False,
119
120
  long_running: bool = False,
120
- data_model: type | None = None):
121
+ data_model: type | None = None,
122
+ ui_builder: bool = False):
121
123
  """Register a chat function.
122
124
 
123
125
  Args:
@@ -266,6 +268,7 @@ class ChatExtension:
266
268
  long_running=long_running,
267
269
  _pydantic_model=_detected_model, _pydantic_param=_detected_param,
268
270
  _return_model=_detected_return_model,
271
+ _ui_builder=ui_builder,
269
272
  )
270
273
  return func
271
274
  return decorator
@@ -9,6 +9,11 @@ import configparser
9
9
  import click
10
10
  import httpx
11
11
 
12
+ try:
13
+ from imperal_sdk import __version__ as SDK_VERSION
14
+ except Exception: # pragma: no cover - defensive
15
+ SDK_VERSION = "5.x"
16
+
12
17
 
13
18
  def _load_credentials() -> dict:
14
19
  """Load credentials from .imperal/credentials or environment variables."""
@@ -57,7 +62,7 @@ def _validate_manifest(manifest: dict) -> list[str]:
57
62
 
58
63
 
59
64
  @click.group()
60
- @click.version_option(version="1.0.0")
65
+ @click.version_option(version=SDK_VERSION)
61
66
  def cli():
62
67
  """Imperal Cloud SDK — build extensions for the Imperal platform."""
63
68
  pass
@@ -67,7 +72,7 @@ def cli():
67
72
  @click.argument("name")
68
73
  @click.option("--template", type=click.Choice(["chat", "tool"]), default="chat", help="Extension template")
69
74
  def init(name: str, template: str):
70
- """Scaffold a new extension project (federal v4 contract)."""
75
+ """Scaffold a new extension project (federal v5 contract)."""
71
76
  os.makedirs(name, exist_ok=True)
72
77
  os.makedirs(f"{name}/tests", exist_ok=True)
73
78
 
@@ -88,7 +93,7 @@ from pydantic import BaseModel, Field
88
93
  from imperal_sdk import Extension, ChatExtension, ActionResult
89
94
 
90
95
 
91
- # Federal v4 Extension surface — V14 (description ≥40 chars), V15
96
+ # Federal v5 Extension surface — V14 (description ≥40 chars), V15
92
97
  # (display_name ≥3 chars, ≠ app_id), V19 (actions_explicit=True), and
93
98
  # V21 (icon.svg required, XML <svg> root + viewBox) are all satisfied
94
99
  # by these defaults. Edit display_name + description before publish.
@@ -165,7 +170,7 @@ async def fn_default(ctx, **kwargs):
165
170
  )
166
171
 
167
172
  with open(f"{name}/requirements.txt", "w") as f:
168
- f.write("imperal-sdk>=4.0.0\n")
173
+ f.write("imperal-sdk>=5.0.0\n")
169
174
 
170
175
  with open(f"{name}/tests/__init__.py", "w") as f:
171
176
  pass
@@ -212,9 +217,9 @@ async def fn_default(ctx, **kwargs):
212
217
  click.echo(f"")
213
218
  click.echo(f"Next steps:")
214
219
  click.echo(f" cd {name}")
215
- click.echo(f" pip install 'imperal-sdk>=4.0.0'")
220
+ click.echo(f" pip install 'imperal-sdk>=5.0.0'")
216
221
  click.echo(f" imperal build # generates imperal.json")
217
- click.echo(f" imperal validate # runs V14-V22+V24 federal validators")
222
+ click.echo(f" imperal validate # runs V1-V24+V31 federal validators")
218
223
  click.echo(f" imperal test # smoke-test handlers via MockContext")
219
224
  click.echo(f" imperal deploy # upload to panel.imperal.io/developer")
220
225
 
@@ -297,7 +302,7 @@ def build(path: str):
297
302
  @cli.command()
298
303
  @click.argument("path", default=".")
299
304
  def validate(path: str):
300
- """Validate extension against SDK v1.0.0 rules."""
305
+ """Validate extension against the current SDK federal rules (V1-V24+V31)."""
301
306
  original_dir = os.getcwd()
302
307
  try:
303
308
  os.chdir(path)
@@ -346,7 +351,7 @@ def validate(path: str):
346
351
  fix="Fix the JSON syntax error at the reported line",
347
352
  ))
348
353
 
349
- click.echo(f"\n── Imperal Extension Validator v1.0 {'─' * 40}")
354
+ click.echo(f"\n── Imperal Extension Validator (SDK {SDK_VERSION}) {'─' * 36}")
350
355
  click.echo(f"\nExtension: {report.app_id} v{report.version}")
351
356
  click.echo(f"Tools: {report.tool_count}, Functions: {report.function_count}, Events: {report.event_count}")
352
357
 
@@ -114,7 +114,7 @@ async def builtin_secrets_panel_handler(ctx: Any, **_params: Any) -> dict:
114
114
  ))
115
115
 
116
116
  root = ui.Stack(children=[
117
- ui.Heading(f"Secrets · {ext_id}", level=2),
117
+ ui.Header(f"Secrets · {ext_id}", level=2),
118
118
  ui.Text(
119
119
  "Credentials this extension needs — API keys, OAuth tokens. "
120
120
  "Stored encrypted in Vault; never visible to admins or in logs."
@@ -207,18 +207,22 @@ class MockHTTP:
207
207
  """Mock HTTP client. Register responses per URL pattern."""
208
208
 
209
209
  def __init__(self):
210
- self._mocks: list[tuple[str, str, dict, int]] = []
210
+ self._mocks: list[tuple[str, str, dict, int, dict]] = []
211
211
 
212
- def mock_get(self, url_pattern: str, response: dict, status: int = 200) -> None:
213
- self._mocks.append(("GET", url_pattern, response, status))
212
+ def mock_get(self, url_pattern: str, response: dict, status: int = 200,
213
+ headers: dict | None = None) -> None:
214
+ self._mocks.append(("GET", url_pattern, response, status, headers or {}))
214
215
 
215
- def mock_post(self, url_pattern: str, response: dict, status: int = 200) -> None:
216
- self._mocks.append(("POST", url_pattern, response, status))
216
+ def mock_post(self, url_pattern: str, response: dict, status: int = 200,
217
+ headers: dict | None = None) -> None:
218
+ self._mocks.append(("POST", url_pattern, response, status, headers or {}))
217
219
 
218
220
  async def _find(self, method: str, url: str) -> HTTPResponse:
219
- for m, pattern, resp, status in self._mocks:
221
+ for entry in self._mocks:
222
+ m, pattern, resp, status = entry[0], entry[1], entry[2], entry[3]
223
+ hdrs = entry[4] if len(entry) > 4 else {} # tolerate legacy 4-tuples
220
224
  if m == method and pattern in url:
221
- return HTTPResponse(status_code=status, body=resp)
225
+ return HTTPResponse(status_code=status, body=resp, headers=dict(hdrs))
222
226
  return HTTPResponse(status_code=404, body={"error": "No mock registered"})
223
227
 
224
228
  async def get(self, url: str, **kwargs) -> HTTPResponse:
@@ -91,13 +91,15 @@ class HTTPResponse:
91
91
  body: dict | str | bytes = ""
92
92
  headers: dict = field(default_factory=dict)
93
93
 
94
- def json(self) -> dict:
95
- if isinstance(self.body, dict):
94
+ def json(self) -> dict | list:
95
+ # Already-parsed JSON (the prod http client parses on content-type: json,
96
+ # so an array endpoint like WP /wp/v2/posts yields a list body).
97
+ if isinstance(self.body, (dict, list)):
96
98
  return self.body
97
- if isinstance(self.body, str):
99
+ if isinstance(self.body, (str, bytes, bytearray)):
98
100
  import json
99
101
  return json.loads(self.body)
100
- raise ValueError("Body is bytes, cannot parse as JSON")
102
+ raise ValueError(f"Cannot parse {type(self.body).__name__} body as JSON")
101
103
 
102
104
  def text(self) -> str:
103
105
  if isinstance(self.body, str):
@@ -728,7 +728,7 @@ def validate_extension(ext) -> ValidationReport:
728
728
  # can be promoted post-soak.
729
729
  #
730
730
  # Soak severity is env-toggled via IMPERAL_VALIDATOR_V23_SEVERITY
731
- # (default "warn"; flip to "error" after third-party adoption).
731
+ # (default "error" since 2026-06-17 P5-final; set "warn" to opt out).
732
732
  try:
733
733
  _chat_exts = getattr(ext, "_chat_extensions", {}) or {}
734
734
  for _tool_name, _chat_ext in _chat_exts.items():
@@ -744,14 +744,20 @@ def validate_extension(ext) -> ValidationReport:
744
744
  def _v23_v24_check_data_model_presence(report, extensions, functions):
745
745
  """Apply V23 (read) + V24 (write/destructive) ``data_model`` rules to each
746
746
  @chat.function in the given functions dict (name -> FunctionDef)."""
747
- _v23_severity = os.environ.get("IMPERAL_VALIDATOR_V23_SEVERITY", "warn").lower()
748
- _v23_level = "ERROR" if _v23_severity == "error" else "WARN"
747
+ _v23_severity = os.environ.get("IMPERAL_VALIDATOR_V23_SEVERITY", "error").lower()
748
+ # P5-final (2026-06-17): V23 defaults to ERROR. Only an explicit env "warn"
749
+ # downgrades it; anything else (including unset) resolves to ERROR.
750
+ _v23_level = "WARN" if _v23_severity == "warn" else "ERROR"
749
751
  for fn_name, fn_def in (functions or {}).items():
750
752
  _action_type = getattr(fn_def, "action_type", "read")
751
753
  _return_model = getattr(fn_def, "_return_model", None)
752
754
  if _return_model is not None:
753
755
  continue
754
756
  if _action_type == "read":
757
+ # Declarative-UI builders (@chat.function(ui_builder=True)) return ui.*
758
+ # output, not an SDL entity — exempt from the data_model requirement.
759
+ if getattr(fn_def, "_ui_builder", False):
760
+ continue
755
761
  report.issues.append(ValidationIssue(
756
762
  rule="V23", level=_v23_level,
757
763
  message=(
@@ -3,6 +3,7 @@
3
3
  import os
4
4
  import pytest
5
5
  from click.testing import CliRunner
6
+ from imperal_sdk import __version__
6
7
  from imperal_sdk.cli.main import cli
7
8
 
8
9
 
@@ -10,7 +11,7 @@ def test_cli_version():
10
11
  runner = CliRunner()
11
12
  result = runner.invoke(cli, ["--version"])
12
13
  assert result.exit_code == 0
13
- assert "1.0.0" in result.output
14
+ assert __version__ in result.output # CLI reports the real SDK version
14
15
 
15
16
 
16
17
  def test_cli_init_chat_template():
@@ -40,7 +41,7 @@ def test_cli_init_chat_template():
40
41
  assert "actions_explicit=True" in content
41
42
 
42
43
  with open("my-test-ext/requirements.txt") as f:
43
- assert "imperal-sdk>=4.0.0" in f.read() # v4.1.9 — was >=1.0.0
44
+ assert "imperal-sdk>=5.0.0" in f.read() # scaffold pins current major
44
45
 
45
46
  with open("my-test-ext/icon.svg") as f:
46
47
  svg = f.read()
@@ -139,8 +139,8 @@ def test_data_model_none_means_no_return_model():
139
139
 
140
140
 
141
141
  def test_v23_warn_when_read_missing_data_model(monkeypatch):
142
- """``IMPERAL_VALIDATOR_V23_SEVERITY=warn`` (default) MUST emit WARN."""
143
- monkeypatch.delenv("IMPERAL_VALIDATOR_V23_SEVERITY", raising=False)
142
+ """``IMPERAL_VALIDATOR_V23_SEVERITY=warn`` (explicit opt-out) MUST emit WARN."""
143
+ monkeypatch.setenv("IMPERAL_VALIDATOR_V23_SEVERITY", "warn")
144
144
  ext = _make_ext()
145
145
  chat = _make_chat(ext)
146
146
 
@@ -159,6 +159,26 @@ def test_v23_warn_when_read_missing_data_model(monkeypatch):
159
159
  assert "data_model" in v23[0].message
160
160
 
161
161
 
162
+ def test_v23_error_by_default(monkeypatch):
163
+ """P5-final (2026-06-17): with the env unset, V23 defaults to ERROR."""
164
+ monkeypatch.delenv("IMPERAL_VALIDATOR_V23_SEVERITY", raising=False)
165
+ ext = _make_ext()
166
+ chat = _make_chat(ext)
167
+
168
+ @chat.function(
169
+ name="list_notes",
170
+ description="List notes — read tool deliberately missing data_model",
171
+ action_type="read",
172
+ )
173
+ async def list_notes(ctx, params: ListNotesParams) -> ActionResult:
174
+ return ActionResult.success(data={}, summary="ok")
175
+
176
+ report = validate_extension(ext)
177
+ v23 = [i for i in report.errors if i.rule == "V23"]
178
+ assert len(v23) == 1
179
+ assert "data_model" in v23[0].message
180
+
181
+
162
182
  def test_v23_error_when_severity_env_set(monkeypatch):
163
183
  """``IMPERAL_VALIDATOR_V23_SEVERITY=error`` MUST promote to ERROR."""
164
184
  monkeypatch.setenv("IMPERAL_VALIDATOR_V23_SEVERITY", "error")
@@ -179,6 +199,25 @@ def test_v23_error_when_severity_env_set(monkeypatch):
179
199
  assert "data_model" in v23[0].message
180
200
 
181
201
 
202
+ def test_v23_skips_ui_builder(monkeypatch):
203
+ """``ui_builder=True`` read tools are exempt from V23 even at severity=error."""
204
+ monkeypatch.setenv("IMPERAL_VALIDATOR_V23_SEVERITY", "error")
205
+ ext = _make_ext()
206
+ chat = _make_chat(ext)
207
+
208
+ @chat.function(
209
+ name="get_panel_data",
210
+ description="Declarative-UI builder — exempt from V23 via ui_builder flag",
211
+ action_type="read",
212
+ ui_builder=True,
213
+ )
214
+ async def get_panel_data(ctx, params: ListNotesParams) -> ActionResult:
215
+ return ActionResult.success(data={}, summary="ok")
216
+
217
+ report = validate_extension(ext)
218
+ assert not [i for i in report.issues if i.rule == "V23"]
219
+
220
+
182
221
  def test_v23_passes_when_data_model_declared():
183
222
  """Reads with ``data_model=`` MUST NOT trigger V23."""
184
223
  ext = _make_ext()
@@ -0,0 +1,37 @@
1
+ """Regression tests: HTTPResponse.json() on JSON-array bodies + MockHTTP headers."""
2
+ import asyncio
3
+
4
+ from imperal_sdk.types.models import HTTPResponse
5
+ from imperal_sdk.testing.mock_context import MockHTTP
6
+
7
+
8
+ def test_json_returns_list_for_array_body():
9
+ """An array endpoint (e.g. WP /wp/v2/posts) yields a list body — must not raise."""
10
+ r = HTTPResponse(status_code=200, body=[{"id": 1}, {"id": 2}])
11
+ assert r.json() == [{"id": 1}, {"id": 2}]
12
+
13
+
14
+ def test_json_dict_body():
15
+ assert HTTPResponse(status_code=200, body={"a": 1}).json() == {"a": 1}
16
+
17
+
18
+ def test_json_parses_str_and_bytes():
19
+ assert HTTPResponse(status_code=200, body="[1, 2]").json() == [1, 2]
20
+ assert HTTPResponse(status_code=200, body=b'{"a": 1}').json() == {"a": 1}
21
+
22
+
23
+ def test_mockhttp_get_sets_response_headers():
24
+ mh = MockHTTP()
25
+ mh.mock_get("/wp/v2/posts", [{"id": 1}], status=200, headers={"X-WP-Total": "57"})
26
+ resp = asyncio.run(mh.get("https://example.com/wp/v2/posts"))
27
+ assert resp.status_code == 200
28
+ assert resp.headers.get("X-WP-Total") == "57"
29
+ assert resp.json() == [{"id": 1}]
30
+
31
+
32
+ def test_mockhttp_post_headers_default_empty():
33
+ mh = MockHTTP()
34
+ mh.mock_post("/x", {"ok": True})
35
+ resp = asyncio.run(mh.post("https://example.com/x"))
36
+ assert resp.headers == {}
37
+ assert resp.json() == {"ok": True}
File without changes
File without changes
File without changes
File without changes
File without changes