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.
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/CHANGELOG.md +59 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/PKG-INFO +1 -1
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/__init__.py +1 -1
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/cli/main.py +111 -30
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/extension.py +2 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/manifest.py +1 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/manifest_schema.py +6 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/schemas/imperal.schema.json +12 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/validator.py +27 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/test_cli.py +21 -6
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/test_validator.py +32 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/.github/workflows/identity-contract.yml +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/.github/workflows/publish.yml +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/.github/workflows/test.yml +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/.gitignore +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/LICENSE +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/README.md +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/api_surface.json +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/pyproject.toml +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/.codebase-index-cache.pkl +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/ai/__init__.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/ai/client.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/auth/__init__.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/auth/client.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/auth/middleware.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/billing/__init__.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/billing/client.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/cache/__init__.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/cache/client.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/cache/protocol.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/chat/__init__.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/chat/action_result.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/chat/error_codes.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/chat/extension.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/chat/filters.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/chat/guards.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/chat/handler.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/chat/kernel_primitives.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/chat/narration.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/chat/narration_guard.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/chat/prompt.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/chat/refusal.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/chat/retry.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/cli/__init__.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/config/__init__.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/config/client.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/context.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/db/__init__.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/db/client.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/errors.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/extensions/__init__.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/extensions/client.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/http/__init__.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/http/client.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/notify/__init__.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/notify/client.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/prompts/__init__.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/prompts/icnli_integrity_rules.txt +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/prompts/kernel_formatting_rule.txt +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/prompts/kernel_proactivity_rule.txt +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/protocols.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/rpc/__init__.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/rpc/codec.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/rpc/contract.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/runtime/__init__.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/runtime/executor.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/runtime/llm_provider.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/runtime/message_adapter.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/schemas/action_result.schema.json +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/schemas/balance_info.schema.json +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/schemas/chat_result.schema.json +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/schemas/completion_result.schema.json +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/schemas/document.schema.json +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/schemas/event.schema.json +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/schemas/file_info.schema.json +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/schemas/function_call.schema.json +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/schemas/http_response.schema.json +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/schemas/limits_result.schema.json +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/schemas/subscription_info.schema.json +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/security/__init__.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/security/call_token.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/skeleton/__init__.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/skeleton/client.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/storage/__init__.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/storage/client.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/store/__init__.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/store/client.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/store/exceptions.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/testing/__init__.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/testing/mock_context.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/tools/__init__.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/tools/client.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/tools/generate_api_surface.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/tools/validate_identity_contract.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/types/__init__.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/types/action_result.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/types/chat_result.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/types/client_contracts.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/types/contracts.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/types/contributions.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/types/events.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/types/health.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/types/identity.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/types/models.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/types/pagination.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/types/store_contracts.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/ui/__init__.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/ui/actions.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/ui/base.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/ui/data.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/ui/display.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/ui/feedback.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/ui/graph.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/ui/input_components.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/ui/interactive.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/ui/layout.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/ui/theme.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/validator_v1_6_0.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/conftest.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/contracts/__init__.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/contracts/test_store_contracts.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/fixtures/openapi/auth-gateway.json +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/fixtures/openapi/registry.json +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/fixtures/openapi/sharelock-cases.json +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/rpc/__init__.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/rpc/test_codec.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/rpc/test_contract.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/runtime/__init__.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/runtime/test_llm_provider_config_store.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/runtime/test_llm_provider_ctx_injection.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/store/__init__.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/store/test_list_users_client.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/store/test_query_all_client.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/test_action_result_typed.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/test_as_user.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/test_auth.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/test_billing.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/test_cache_client.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/test_cache_model.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/test_call_token.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/test_chat_extension_deprecation.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/test_chat_filters.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/test_chat_guards.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/test_chat_guards_bleed.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/test_chat_prompt.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/test_chat_pydantic_retry.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/test_chat_result.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/test_client_contracts.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/test_config_client.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/test_context.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/test_context_guards.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/test_contracts.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/test_contracts_live.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/test_contributions.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/test_document_contract.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/test_emits_decorator.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/test_error_codes.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/test_errors.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/test_event_schema_v2.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/test_events_health.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/test_extension.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/test_extension_v2.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/test_extensions_emit.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/test_handler_p2.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/test_id_shape_guard.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/test_identity_contract.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/test_imperal_schema_v2.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/test_kernel_primitives.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/test_manifest.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/test_manifest_roundtrip_gate.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/test_manifest_schema.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/test_manifest_v2_events.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/test_manifest_v2_other_sections.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/test_manifest_v2_webhooks.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/test_manifest_validator_v2.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/test_mock_context.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/test_models.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/test_narration_emission.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/test_narration_guard.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/test_openai_max_completion_tokens.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/test_pagination.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/test_panel_rendering_contract.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/test_panels.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/test_skeleton_decorator.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/test_spec_validation.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/test_tools_client.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/test_ui.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/test_ui_fileupload_enhanced.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/test_ui_html.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/test_ui_image_enhanced.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/test_ui_open.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/test_ui_theme.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/test_user.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/test_v7_emit_refusal.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/test_validator_drift.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/test_validator_pep563.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/test_validator_v1_6_0_rules.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/test_write_arg_bleed.py +0 -0
- {imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/tests/tools/__init__.py +0 -0
- {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
|
|
@@ -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'''"""{
|
|
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
|
-
|
|
80
|
-
|
|
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
|
-
|
|
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(
|
|
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
|
-
"""
|
|
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'''"""{
|
|
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
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
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>=
|
|
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 =
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
from
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
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(
|
|
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}'
|
|
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
|
|
138
|
-
click.echo(f" imperal
|
|
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'
|
|
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 '
|
|
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>=
|
|
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 '
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{imperal_sdk-4.1.8 → imperal_sdk-4.2.0}/src/imperal_sdk/schemas/completion_result.schema.json
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|