imperal-sdk 4.1.8__tar.gz → 4.2.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 (200) hide show
  1. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/CHANGELOG.md +59 -0
  2. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/PKG-INFO +1 -1
  3. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/__init__.py +1 -1
  4. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/cli/main.py +111 -30
  5. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/extension.py +2 -0
  6. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/manifest.py +1 -0
  7. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/manifest_schema.py +6 -0
  8. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/schemas/imperal.schema.json +12 -0
  9. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/validator.py +27 -0
  10. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/test_cli.py +21 -6
  11. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/test_validator.py +32 -0
  12. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/.github/workflows/identity-contract.yml +0 -0
  13. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/.github/workflows/publish.yml +0 -0
  14. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/.github/workflows/test.yml +0 -0
  15. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/.gitignore +0 -0
  16. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/LICENSE +0 -0
  17. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/README.md +0 -0
  18. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/api_surface.json +0 -0
  19. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/pyproject.toml +0 -0
  20. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/.codebase-index-cache.pkl +0 -0
  21. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/ai/__init__.py +0 -0
  22. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/ai/client.py +0 -0
  23. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/auth/__init__.py +0 -0
  24. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/auth/client.py +0 -0
  25. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/auth/middleware.py +0 -0
  26. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/billing/__init__.py +0 -0
  27. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/billing/client.py +0 -0
  28. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/cache/__init__.py +0 -0
  29. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/cache/client.py +0 -0
  30. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/cache/protocol.py +0 -0
  31. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/chat/__init__.py +0 -0
  32. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/chat/action_result.py +0 -0
  33. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/chat/error_codes.py +0 -0
  34. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/chat/extension.py +0 -0
  35. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/chat/filters.py +0 -0
  36. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/chat/guards.py +0 -0
  37. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/chat/handler.py +0 -0
  38. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/chat/kernel_primitives.py +0 -0
  39. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/chat/narration.py +0 -0
  40. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/chat/narration_guard.py +0 -0
  41. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/chat/prompt.py +0 -0
  42. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/chat/refusal.py +0 -0
  43. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/chat/retry.py +0 -0
  44. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/cli/__init__.py +0 -0
  45. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/config/__init__.py +0 -0
  46. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/config/client.py +0 -0
  47. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/context.py +0 -0
  48. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/db/__init__.py +0 -0
  49. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/db/client.py +0 -0
  50. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/errors.py +0 -0
  51. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/extensions/__init__.py +0 -0
  52. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/extensions/client.py +0 -0
  53. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/http/__init__.py +0 -0
  54. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/http/client.py +0 -0
  55. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/notify/__init__.py +0 -0
  56. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/notify/client.py +0 -0
  57. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/prompts/__init__.py +0 -0
  58. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/prompts/icnli_integrity_rules.txt +0 -0
  59. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/prompts/kernel_formatting_rule.txt +0 -0
  60. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/prompts/kernel_proactivity_rule.txt +0 -0
  61. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/protocols.py +0 -0
  62. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/rpc/__init__.py +0 -0
  63. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/rpc/codec.py +0 -0
  64. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/rpc/contract.py +0 -0
  65. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/runtime/__init__.py +0 -0
  66. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/runtime/executor.py +0 -0
  67. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/runtime/llm_provider.py +0 -0
  68. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/runtime/message_adapter.py +0 -0
  69. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/schemas/action_result.schema.json +0 -0
  70. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/schemas/balance_info.schema.json +0 -0
  71. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/schemas/chat_result.schema.json +0 -0
  72. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/schemas/completion_result.schema.json +0 -0
  73. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/schemas/document.schema.json +0 -0
  74. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/schemas/event.schema.json +0 -0
  75. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/schemas/file_info.schema.json +0 -0
  76. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/schemas/function_call.schema.json +0 -0
  77. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/schemas/http_response.schema.json +0 -0
  78. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/schemas/limits_result.schema.json +0 -0
  79. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/schemas/subscription_info.schema.json +0 -0
  80. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/security/__init__.py +0 -0
  81. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/security/call_token.py +0 -0
  82. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/skeleton/__init__.py +0 -0
  83. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/skeleton/client.py +0 -0
  84. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/storage/__init__.py +0 -0
  85. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/storage/client.py +0 -0
  86. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/store/__init__.py +0 -0
  87. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/store/client.py +0 -0
  88. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/store/exceptions.py +0 -0
  89. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/testing/__init__.py +0 -0
  90. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/testing/mock_context.py +0 -0
  91. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/tools/__init__.py +0 -0
  92. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/tools/client.py +0 -0
  93. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/tools/generate_api_surface.py +0 -0
  94. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/tools/validate_identity_contract.py +0 -0
  95. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/types/__init__.py +0 -0
  96. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/types/action_result.py +0 -0
  97. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/types/chat_result.py +0 -0
  98. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/types/client_contracts.py +0 -0
  99. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/types/contracts.py +0 -0
  100. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/types/contributions.py +0 -0
  101. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/types/events.py +0 -0
  102. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/types/health.py +0 -0
  103. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/types/identity.py +0 -0
  104. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/types/models.py +0 -0
  105. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/types/pagination.py +0 -0
  106. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/types/store_contracts.py +0 -0
  107. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/ui/__init__.py +0 -0
  108. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/ui/actions.py +0 -0
  109. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/ui/base.py +0 -0
  110. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/ui/data.py +0 -0
  111. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/ui/display.py +0 -0
  112. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/ui/feedback.py +0 -0
  113. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/ui/graph.py +0 -0
  114. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/ui/input_components.py +0 -0
  115. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/ui/interactive.py +0 -0
  116. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/ui/layout.py +0 -0
  117. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/ui/theme.py +0 -0
  118. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/validator_v1_6_0.py +0 -0
  119. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/conftest.py +0 -0
  120. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/contracts/__init__.py +0 -0
  121. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/contracts/test_store_contracts.py +0 -0
  122. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/fixtures/openapi/auth-gateway.json +0 -0
  123. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/fixtures/openapi/registry.json +0 -0
  124. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/fixtures/openapi/sharelock-cases.json +0 -0
  125. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/rpc/__init__.py +0 -0
  126. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/rpc/test_codec.py +0 -0
  127. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/rpc/test_contract.py +0 -0
  128. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/runtime/__init__.py +0 -0
  129. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/runtime/test_llm_provider_config_store.py +0 -0
  130. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/runtime/test_llm_provider_ctx_injection.py +0 -0
  131. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/store/__init__.py +0 -0
  132. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/store/test_list_users_client.py +0 -0
  133. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/store/test_query_all_client.py +0 -0
  134. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/test_action_result_typed.py +0 -0
  135. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/test_as_user.py +0 -0
  136. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/test_auth.py +0 -0
  137. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/test_billing.py +0 -0
  138. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/test_cache_client.py +0 -0
  139. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/test_cache_model.py +0 -0
  140. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/test_call_token.py +0 -0
  141. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/test_chat_extension_deprecation.py +0 -0
  142. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/test_chat_filters.py +0 -0
  143. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/test_chat_guards.py +0 -0
  144. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/test_chat_guards_bleed.py +0 -0
  145. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/test_chat_prompt.py +0 -0
  146. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/test_chat_pydantic_retry.py +0 -0
  147. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/test_chat_result.py +0 -0
  148. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/test_client_contracts.py +0 -0
  149. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/test_config_client.py +0 -0
  150. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/test_context.py +0 -0
  151. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/test_context_guards.py +0 -0
  152. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/test_contracts.py +0 -0
  153. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/test_contracts_live.py +0 -0
  154. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/test_contributions.py +0 -0
  155. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/test_document_contract.py +0 -0
  156. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/test_emits_decorator.py +0 -0
  157. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/test_error_codes.py +0 -0
  158. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/test_errors.py +0 -0
  159. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/test_event_schema_v2.py +0 -0
  160. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/test_events_health.py +0 -0
  161. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/test_extension.py +0 -0
  162. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/test_extension_v2.py +0 -0
  163. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/test_extensions_emit.py +0 -0
  164. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/test_handler_p2.py +0 -0
  165. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/test_id_shape_guard.py +0 -0
  166. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/test_identity_contract.py +0 -0
  167. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/test_imperal_schema_v2.py +0 -0
  168. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/test_kernel_primitives.py +0 -0
  169. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/test_manifest.py +0 -0
  170. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/test_manifest_roundtrip_gate.py +0 -0
  171. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/test_manifest_schema.py +0 -0
  172. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/test_manifest_v2_events.py +0 -0
  173. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/test_manifest_v2_other_sections.py +0 -0
  174. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/test_manifest_v2_webhooks.py +0 -0
  175. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/test_manifest_validator_v2.py +0 -0
  176. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/test_mock_context.py +0 -0
  177. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/test_models.py +0 -0
  178. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/test_narration_emission.py +0 -0
  179. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/test_narration_guard.py +0 -0
  180. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/test_openai_max_completion_tokens.py +0 -0
  181. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/test_pagination.py +0 -0
  182. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/test_panel_rendering_contract.py +0 -0
  183. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/test_panels.py +0 -0
  184. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/test_skeleton_decorator.py +0 -0
  185. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/test_spec_validation.py +0 -0
  186. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/test_tools_client.py +0 -0
  187. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/test_ui.py +0 -0
  188. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/test_ui_fileupload_enhanced.py +0 -0
  189. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/test_ui_html.py +0 -0
  190. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/test_ui_image_enhanced.py +0 -0
  191. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/test_ui_open.py +0 -0
  192. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/test_ui_theme.py +0 -0
  193. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/test_user.py +0 -0
  194. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/test_v7_emit_refusal.py +0 -0
  195. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/test_validator_drift.py +0 -0
  196. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/test_validator_pep563.py +0 -0
  197. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/test_validator_v1_6_0_rules.py +0 -0
  198. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/test_write_arg_bleed.py +0 -0
  199. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/tools/__init__.py +0 -0
  200. {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/tools/test_generate_api_surface.py +0 -0
@@ -2,6 +2,65 @@
2
2
 
3
3
  All notable changes to `imperal-sdk` are documented here.
4
4
 
5
+ ## 4.2.0 — 2026-05-11
6
+
7
+ ### Added
8
+
9
+ - **`Extension(system: bool = False)` kwarg** declares an extension as
10
+ a platform-managed system app. System apps are auto-installed for
11
+ every user on registration, never shown in marketplace listings, and
12
+ cannot be uninstalled. Federal contract additions:
13
+ - `I-SYSTEM-APPS-NEVER-UNINSTALLABLE` — uninstall on `system=true`
14
+ returns 403 at the auth-gw level.
15
+ - `I-MARKETPLACE-HIDES-SYSTEM` — `/v1/marketplace/*` SELECT queries
16
+ filter `system = FALSE` at the SQL layer.
17
+ - `I-SYSTEM-FLAG-RESERVED-FOR-IMPERAL` — only first-party Imperal
18
+ authors may set `system=True`; Dev Portal rejects 3rd-party
19
+ publishes at the author allowlist.
20
+ - **Manifest field**: top-level `system: bool` is emitted by
21
+ `imperal build` whenever `Extension(system=…)` is set. `manifest.py`
22
+ and `manifest_schema.Manifest` both carry it; the JSON schema file
23
+ (`src/imperal_sdk/schemas/imperal.schema.json`) is regenerated.
24
+ - **Validator V31**: local check that rejects `system=True` for
25
+ non-Imperal authors. Triggered by `IMPERAL_AUTHOR_ID` env var so
26
+ local dev without the env continues to pass (Dev Portal is the
27
+ authoritative gate at publish time). 4 new unit tests pinning the
28
+ contract.
29
+
30
+ ### Migration notes
31
+
32
+ - The four first-party Imperal extensions (`admin`, `billing`,
33
+ `developer`, `automations`) should add `system=True` to their
34
+ `Extension(...)` declaration. Existing rows in `developer_apps.system`
35
+ were backfilled during the Sprint B platform deploy, so the live
36
+ marketplace already hides them — re-publishing them through the
37
+ Dev Portal will keep manifest and DB consistent.
38
+
39
+ 978 tests pass, 3 skipped.
40
+
41
+ ## 4.1.9 — 2026-05-10
42
+
43
+ ### Fixed
44
+
45
+ - **`imperal init <name>` template now passes federal validators
46
+ cleanly.** The previous scaffold generated `Extension(name, version="1.0.0")`
47
+ without `display_name=`/`description=`/`icon=`/`actions_explicit=` —
48
+ failing V14 (description ≥40 chars), V15 (display_name ≥3 chars
49
+ ≠ app_id), V16 (per-function description ≥20 chars), and V21 (icon.svg
50
+ required) on the very first `imperal validate`. New scaffold writes
51
+ v4-correct kwargs, a proper `ChatExtension(...)` declaration, an
52
+ `icon.svg` placeholder (V21-compliant XML root + viewBox),
53
+ `requirements.txt: imperal-sdk>=4.0.0` (was `>=1.0.0`), and a test
54
+ file that exercises Pydantic param validation + `MockContext`.
55
+
56
+ ### Changed
57
+
58
+ - CLI `init` next-steps message updated to the canonical workflow:
59
+ `pip install` → `imperal build` → `imperal validate` →
60
+ `imperal test` → `imperal deploy`.
61
+
62
+ 974 tests pass, 3 skipped.
63
+
5
64
  ## 4.1.8 — 2026-05-10
6
65
 
7
66
  ### Added
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: imperal-sdk
3
- Version: 4.1.8
3
+ Version: 4.2.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
@@ -33,7 +33,7 @@ from imperal_sdk.validator_v1_6_0 import (
33
33
  validate_manifest_v1_6_0,
34
34
  )
35
35
 
36
- __version__ = "4.1.8"
36
+ __version__ = "4.2.0"
37
37
 
38
38
  __all__ = [
39
39
  # Core
@@ -67,75 +67,156 @@ def cli():
67
67
  @click.argument("name")
68
68
  @click.option("--template", type=click.Choice(["chat", "tool"]), default="chat", help="Extension template")
69
69
  def init(name: str, template: str):
70
- """Scaffold a new extension project."""
70
+ """Scaffold a new extension project (federal v4 contract)."""
71
71
  os.makedirs(name, exist_ok=True)
72
72
  os.makedirs(f"{name}/tests", exist_ok=True)
73
73
 
74
+ title = name.replace("-", " ").replace("_", " ").title()
75
+ # V14 requires description ≥40 chars, V15 requires display_name ≥3 chars
76
+ # ≠ app_id, V16 requires per-function description ≥20 chars. We seed
77
+ # values that satisfy all three so `imperal build && imperal validate`
78
+ # passes immediately — no surprises for new authors.
79
+ display_name = f"{title} Extension"
80
+ description = (
81
+ f"{title} — a starter extension scaffolded by `imperal init`. "
82
+ f"Replace this description with something specific to your tool."
83
+ )
84
+
74
85
  if template == "chat":
75
- main_content = f'''"""{name} — Imperal Cloud Extension."""
76
- from pydantic import BaseModel
86
+ main_content = f'''"""{title} extension — Imperal Cloud."""
87
+ from pydantic import BaseModel, Field
77
88
  from imperal_sdk import Extension, ChatExtension, ActionResult
78
89
 
79
- ext = Extension("{name}", version="1.0.0")
80
- chat = ChatExtension(ext, tool_name="{name}", description="{name.replace('-', ' ').title()} extension")
90
+
91
+ # Federal v4 Extension surface — V14 (description ≥40 chars), V15
92
+ # (display_name ≥3 chars, ≠ app_id), V19 (actions_explicit=True), and
93
+ # V21 (icon.svg required, XML <svg> root + viewBox) are all satisfied
94
+ # by these defaults. Edit display_name + description before publish.
95
+ ext = Extension(
96
+ "{name}",
97
+ version="1.0.0",
98
+ display_name={display_name!r},
99
+ description={description!r},
100
+ icon="icon.svg",
101
+ actions_explicit=True,
102
+ capabilities=[{name!r} + ":read"],
103
+ )
104
+
105
+ chat = ChatExtension(
106
+ ext,
107
+ tool_name={name.replace('-', '_')!r},
108
+ description={f"{title} — chat tool entrypoint."!r},
109
+ )
81
110
 
82
111
 
83
112
  class GreetParams(BaseModel):
84
- name: str = "World"
113
+ """Pydantic params model — V17 federal: typed BaseModel param required."""
114
+ name: str = Field(default="World", description="Person to greet")
85
115
 
86
116
 
87
- @chat.function("greet", description="Say hello", params={{}}, action_type="read")
117
+ @chat.function(
118
+ "greet",
119
+ action_type="read",
120
+ description="Greet someone by name with a friendly message.",
121
+ )
88
122
  async def fn_greet(ctx, params: GreetParams) -> ActionResult:
89
- """Greet someone by name."""
123
+ """Echo a greeting to verify the extension loads + validators pass."""
90
124
  return ActionResult.success(
91
125
  data={{"message": f"Hello, {{params.name}}!"}},
92
126
  summary=f"Greeted {{params.name}}",
93
127
  )
94
128
  '''
95
129
  else:
96
- main_content = f'''"""{name} — Imperal Cloud Extension."""
130
+ main_content = f'''"""{title} extension — Imperal Cloud (tool template, no chat surface)."""
97
131
  from imperal_sdk import Extension
98
132
 
99
- ext = Extension("{name}", version="1.0.0")
100
133
 
134
+ ext = Extension(
135
+ "{name}",
136
+ version="1.0.0",
137
+ display_name={display_name!r},
138
+ description={description!r},
139
+ icon="icon.svg",
140
+ actions_explicit=True,
141
+ capabilities=[{name!r} + ":read"],
142
+ )
101
143
 
102
- @ext.tool("{name}", description="{name.replace('-', ' ').title()}")
103
- async def hello(ctx, name: str = "World"):
104
- """Say hello."""
105
- return {{"message": f"Hello, {{name}}!"}}
144
+
145
+ @ext.tool({name!r}, scopes=[{name!r} + ":read"], description={f"{title} — invoke from automations or chains."!r})
146
+ async def fn_default(ctx, **kwargs):
147
+ """Stub tool. Replace with your own."""
148
+ return {{"message": "ok"}}
106
149
  '''
107
150
 
108
151
  with open(f"{name}/main.py", "w") as f:
109
152
  f.write(main_content)
110
153
 
154
+ # V21 federal: extensions MUST ship a valid SVG icon. We seed a tiny
155
+ # placeholder that passes the validator (XML root + viewBox + ≤100 KB).
156
+ with open(f"{name}/icon.svg", "w") as f:
157
+ f.write(
158
+ '<?xml version="1.0" encoding="UTF-8"?>\n'
159
+ '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" '
160
+ 'fill="none" stroke="currentColor" stroke-width="2" '
161
+ 'stroke-linecap="round" stroke-linejoin="round" width="24" height="24">'
162
+ '<rect x="3" y="3" width="18" height="18" rx="2"/>'
163
+ '<path d="M9 9h6v6H9z"/>'
164
+ '</svg>\n'
165
+ )
166
+
111
167
  with open(f"{name}/requirements.txt", "w") as f:
112
- f.write("imperal-sdk>=1.0.0\n")
168
+ f.write("imperal-sdk>=4.0.0\n")
113
169
 
114
170
  with open(f"{name}/tests/__init__.py", "w") as f:
115
171
  pass
116
172
 
117
- test_content = f'''"""Tests for {name} extension."""
118
- import pytest
119
- from imperal_sdk.testing import MockContext
120
- from main import ext
121
-
122
-
123
- def test_extension_registered():
124
- assert ext.app_id == "{name}"
125
- assert ext.version == "1.0.0"
126
- '''
173
+ test_content = (
174
+ f'"""Tests for {name} extension."""\n'
175
+ f'import pytest\n'
176
+ f'from imperal_sdk.testing import MockContext\n'
177
+ f'from main import ext\n'
178
+ )
179
+ if template == "chat":
180
+ test_content += (
181
+ f'from main import GreetParams, fn_greet\n\n\n'
182
+ f'def test_extension_registered():\n'
183
+ f' assert ext.app_id == "{name}"\n'
184
+ f' assert ext.version == "1.0.0"\n'
185
+ f' assert ext.display_name and ext.display_name != ext.app_id\n'
186
+ f' assert len(ext.description) >= 40\n\n\n'
187
+ f'@pytest.mark.asyncio\n'
188
+ f'async def test_greet_returns_action_result():\n'
189
+ f' ctx = MockContext(user_id="imp_u_test")\n'
190
+ f' result = await fn_greet(ctx, GreetParams(name="Alex"))\n'
191
+ f' assert result.status == "success"\n'
192
+ f' assert "Alex" in result.summary\n'
193
+ )
194
+ else:
195
+ test_content += (
196
+ f'\n\ndef test_extension_registered():\n'
197
+ f' assert ext.app_id == "{name}"\n'
198
+ f' assert ext.version == "1.0.0"\n'
199
+ f' assert "{name}" in ext.tools\n'
200
+ )
127
201
 
128
202
  with open(f"{name}/tests/test_main.py", "w") as f:
129
203
  f.write(test_content)
130
204
 
131
205
  with open(f"{name}/.gitignore", "w") as f:
132
- f.write("venv/\n__pycache__/\n*.pyc\n.env\n.imperal/\n")
206
+ f.write(
207
+ "venv/\n.venv/\n__pycache__/\n*.pyc\n*.pyo\n.pytest_cache/\n"
208
+ ".env\n.imperal/\nimperal.json\n.DS_Store\n"
209
+ )
133
210
 
134
- click.echo(f"Extension '{name}' created! (template: {template})")
211
+ click.echo(f"Extension '{name}' scaffolded (template: {template})")
212
+ click.echo(f"")
213
+ click.echo(f"Next steps:")
135
214
  click.echo(f" cd {name}")
136
- click.echo(f" pip install imperal-sdk")
137
- click.echo(f" imperal validate")
138
- click.echo(f" imperal dev")
215
+ click.echo(f" pip install 'imperal-sdk>=4.0.0'")
216
+ click.echo(f" imperal build # generates imperal.json")
217
+ click.echo(f" imperal validate # runs V14-V22+V24 federal validators")
218
+ click.echo(f" imperal test # smoke-test handlers via MockContext")
219
+ click.echo(f" imperal deploy # upload to panel.imperal.io/developer")
139
220
 
140
221
 
141
222
  @cli.command()
@@ -115,6 +115,7 @@ class Extension:
115
115
  description: str = "",
116
116
  icon: str = "",
117
117
  actions_explicit: bool = True,
118
+ system: bool = False,
118
119
  ):
119
120
  self.app_id = app_id
120
121
  self.version = version
@@ -122,6 +123,7 @@ class Extension:
122
123
  self.description = description
123
124
  self.icon = icon
124
125
  self.actions_explicit = actions_explicit
126
+ self.system = system
125
127
  self.capabilities = capabilities or []
126
128
  self.migrations_dir = migrations_dir
127
129
  self._tools: dict[str, ToolDef] = {}
@@ -126,6 +126,7 @@ def generate_manifest(ext: Extension) -> dict:
126
126
  "description": ext.description or "",
127
127
  "icon": ext.icon or "",
128
128
  "actions_explicit": ext.actions_explicit,
129
+ "system": bool(getattr(ext, "system", False)),
129
130
  "capabilities": ext.capabilities,
130
131
  "tools": tools,
131
132
  "signals": signals,
@@ -284,6 +284,12 @@ class Manifest(BaseModel):
284
284
  icon_size_bytes: Optional[int] = None
285
285
  lifecycle_hooks: Optional[Dict[str, Dict[str, Any]]] = None
286
286
 
287
+ # Federal v4.2.0 — `system=True` marks platform-managed extensions
288
+ # (admin / billing / developer / automations). Auto-installed for every
289
+ # user on registration, hidden from marketplace, cannot be uninstalled.
290
+ # Reserved for first-party Imperal authors (validator V31).
291
+ system: Optional[bool] = None
292
+
287
293
  # --- Marketplace merge (docs/imperal-cloud/developer-portal.md) ---
288
294
  name: Optional[str] = None
289
295
  description: Optional[str] = None
@@ -692,6 +692,18 @@
692
692
  "default": null,
693
693
  "title": "Lifecycle Hooks"
694
694
  },
695
+ "system": {
696
+ "anyOf": [
697
+ {
698
+ "type": "boolean"
699
+ },
700
+ {
701
+ "type": "null"
702
+ }
703
+ ],
704
+ "default": null,
705
+ "title": "System"
706
+ },
695
707
  "name": {
696
708
  "anyOf": [
697
709
  {
@@ -7,6 +7,7 @@ Returns structured report with errors, warnings, and info.
7
7
  """
8
8
  from __future__ import annotations
9
9
 
10
+ import os
10
11
  import re
11
12
  from dataclasses import dataclass, field
12
13
 
@@ -706,4 +707,30 @@ def validate_extension(ext) -> ValidationReport:
706
707
  ),
707
708
  ))
708
709
 
710
+ # V31 — `system=True` reserved for first-party Imperal extensions.
711
+ # System apps are auto-installed for every user, never shown in the
712
+ # marketplace, and cannot be uninstalled. Allowing a third-party
713
+ # developer to set this flag would let them slip past discovery and
714
+ # claim platform-level trust they have not been granted. The Dev Portal
715
+ # enforces an author allowlist server-side; this validator catches the
716
+ # mistake locally so the developer sees the error before publish.
717
+ if bool(getattr(ext, "system", False)):
718
+ author = (os.environ.get("IMPERAL_AUTHOR_ID") or "").strip()
719
+ IMPERAL_AUTHOR_IDS = {"imp_u_oPpbwTWjm-"} # server@webhostmost.com
720
+ if author and author not in IMPERAL_AUTHOR_IDS:
721
+ report.issues.append(ValidationIssue(
722
+ rule="V31", level="ERROR",
723
+ message=(
724
+ f"Extension(system=True) is reserved for first-party "
725
+ f"Imperal extensions (admin / billing / developer / "
726
+ f"automations). Author {author!r} is not in the Imperal "
727
+ f"author allowlist."
728
+ ),
729
+ fix=(
730
+ "Drop `system=True` from your Extension(...) call. "
731
+ "Your app will publish through the normal marketplace "
732
+ "flow and users will install it explicitly."
733
+ ),
734
+ ))
735
+
709
736
  return report
@@ -14,43 +14,58 @@ def test_cli_version():
14
14
 
15
15
 
16
16
  def test_cli_init_chat_template():
17
- """Default chat template generates ChatExtension + ActionResult scaffold."""
17
+ """Default chat template generates v4-compliant ChatExtension + ActionResult scaffold."""
18
18
  runner = CliRunner()
19
19
  with runner.isolated_filesystem():
20
20
  result = runner.invoke(cli, ["init", "my-test-ext"])
21
21
  assert result.exit_code == 0
22
- assert "Extension 'my-test-ext' created!" in result.output
22
+ assert "Extension 'my-test-ext' scaffolded" in result.output
23
23
  assert "(template: chat)" in result.output
24
24
  assert os.path.exists("my-test-ext/main.py")
25
+ assert os.path.exists("my-test-ext/icon.svg") # V21 federal — required
25
26
  assert os.path.exists("my-test-ext/requirements.txt")
26
27
  assert os.path.exists("my-test-ext/tests/test_main.py")
27
28
  assert os.path.exists("my-test-ext/.gitignore")
28
29
 
29
30
  with open("my-test-ext/main.py") as f:
30
31
  content = f.read()
31
- assert 'Extension("my-test-ext"' in content
32
+ assert '"my-test-ext"' in content
32
33
  assert "ChatExtension" in content
33
34
  assert "ActionResult" in content
34
35
  assert 'version="1.0.0"' in content
36
+ # v4 federal kwargs that v4.1.9 fix added
37
+ assert "display_name=" in content
38
+ assert "description=" in content
39
+ assert "icon=" in content
40
+ assert "actions_explicit=True" in content
35
41
 
36
42
  with open("my-test-ext/requirements.txt") as f:
37
- assert "imperal-sdk>=1.0.0" in f.read()
43
+ assert "imperal-sdk>=4.0.0" in f.read() # v4.1.9 — was >=1.0.0
44
+
45
+ with open("my-test-ext/icon.svg") as f:
46
+ svg = f.read()
47
+ assert "<svg" in svg
48
+ assert "viewBox" in svg
38
49
 
39
50
 
40
51
  def test_cli_init_tool_template():
41
- """Tool template generates bare @ext.tool scaffold."""
52
+ """Tool template generates bare @ext.tool scaffold (v4-compliant)."""
42
53
  runner = CliRunner()
43
54
  with runner.isolated_filesystem():
44
55
  result = runner.invoke(cli, ["init", "my-tool-ext", "--template", "tool"])
45
56
  assert result.exit_code == 0
46
57
  assert "(template: tool)" in result.output
47
58
  assert os.path.exists("my-tool-ext/main.py")
59
+ assert os.path.exists("my-tool-ext/icon.svg")
48
60
 
49
61
  with open("my-tool-ext/main.py") as f:
50
62
  content = f.read()
51
- assert 'Extension("my-tool-ext"' in content
63
+ assert '"my-tool-ext"' in content
52
64
  assert "@ext.tool" in content
53
65
  assert 'version="1.0.0"' in content
66
+ # v4 federal kwargs (added in v4.1.9 init scaffold fix)
67
+ assert "display_name=" in content
68
+ assert "actions_explicit=True" in content
54
69
 
55
70
 
56
71
  def test_cli_init_invalid_template():
@@ -310,3 +310,35 @@ class TestV7NoDirectImports:
310
310
  v7_errors = [i for i in report.errors if i.rule == "V7"]
311
311
  # This test module does not import anthropic/openai so no V7 error expected
312
312
  assert len(v7_errors) == 0
313
+
314
+
315
+ class TestV31SystemFlag:
316
+ """V31 — Extension(system=True) reserved for first-party Imperal authors."""
317
+
318
+ def test_system_default_false_no_v31(self, monkeypatch):
319
+ monkeypatch.setenv("IMPERAL_AUTHOR_ID", "imp_u_random_dev")
320
+ ext = Extension("hello-3rd", version="1.0.0", display_name="Hello", description="x" * 60, icon="icon.svg")
321
+ report = validate_extension(ext)
322
+ assert not [i for i in report.errors if i.rule == "V31"]
323
+
324
+ def test_third_party_system_true_blocked(self, monkeypatch):
325
+ monkeypatch.setenv("IMPERAL_AUTHOR_ID", "imp_u_random_dev")
326
+ ext = Extension("rogue", version="1.0.0", display_name="Rogue", description="x" * 60, icon="icon.svg", system=True)
327
+ report = validate_extension(ext)
328
+ v31 = [i for i in report.errors if i.rule == "V31"]
329
+ assert len(v31) == 1
330
+ assert "reserved for first-party" in v31[0].message
331
+
332
+ def test_imperal_author_system_true_allowed(self, monkeypatch):
333
+ monkeypatch.setenv("IMPERAL_AUTHOR_ID", "imp_u_oPpbwTWjm-")
334
+ ext = Extension("billing", version="1.0.0", display_name="Billing", description="x" * 60, icon="icon.svg", system=True)
335
+ report = validate_extension(ext)
336
+ assert not [i for i in report.errors if i.rule == "V31"]
337
+
338
+ def test_no_author_env_does_not_block(self, monkeypatch):
339
+ # Local dev without IMPERAL_AUTHOR_ID set should not fail validation —
340
+ # Dev Portal enforces author allowlist server-side at publish time.
341
+ monkeypatch.delenv("IMPERAL_AUTHOR_ID", raising=False)
342
+ ext = Extension("anything", version="1.0.0", display_name="x", description="x" * 60, icon="icon.svg", system=True)
343
+ report = validate_extension(ext)
344
+ assert not [i for i in report.errors if i.rule == "V31"]
File without changes
File without changes
File without changes
File without changes