imperal-sdk 6.5.0__tar.gz → 6.5.2__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-6.5.0 → imperal_sdk-6.5.2}/.github/workflows/test.yml +6 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/CHANGELOG.md +25 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/PKG-INFO +1 -1
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/sdk-reference.json +1 -1
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/sdk_claims.json +1 -1
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/__init__.py +1 -1
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/code_index.py +118 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/code_links.py +60 -10
- imperal_sdk-6.5.2/tests/test_code_links.py +170 -0
- imperal_sdk-6.5.0/tests/test_code_links.py +0 -87
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/.cm.txt +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/.github/workflows/identity-contract.yml +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/.github/workflows/publish.yml +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/.gitignore +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/LICENSE +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/README.md +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/docs/sdl-facets.md +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/pyproject.toml +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/sdl_roles.json +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/.codebase-index-cache.pkl +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/_gateway.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/_http_retry.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/_shared_http.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/adversarial_oracle.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/ai/__init__.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/ai/client.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/app.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/apps/__init__.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/apps/client.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/auth/__init__.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/auth/client.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/auth/middleware.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/auto_ui.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/billing/__init__.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/billing/client.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/bom_extractor.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/cache/__init__.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/cache/client.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/cache/protocol.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/catalog.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/chat/__init__.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/chat/action_result.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/chat/error_codes.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/chat/exceptions.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/chat/extension.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/chat/filters.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/chat/guards.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/chat/kernel_primitives.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/chat/narration.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/chat/narration_guard.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/chat/prompt.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/chat/refusal.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/cli/__init__.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/cli/linter.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/cli/main.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/code_langs.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/config/__init__.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/config/client.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/context.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/conversations/__init__.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/conversations/client.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/core.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/cross_surface_bus.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/devtools/__init__.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/devtools/contract_checks.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/devtools/generate_api_surface.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/devtools/generate_reference.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/devtools/generate_sdk_claims.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/devtools/reference/__init__.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/devtools/reference/_flags.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/devtools/reference/_introspect.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/devtools/reference/client_methods.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/devtools/reference/dataclasses_kind.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/devtools/reference/decorators.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/devtools/reference/sdl_funcs.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/devtools/reference/sdl_roles.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/devtools/reference/ui_components.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/devtools/validate_identity_contract.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/dwell_guard.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/errors.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/extension.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/extensions/__init__.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/extensions/client.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/gnc_guard.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/http/__init__.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/http/client.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/ir/__init__.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/ir/_impl.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/ir/actions.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/ir/custom_roles.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/ir/migrate.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/ir/produce.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/ir/schema.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/ir/skeleton.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/ir/ui.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/ir/ui_children.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/ir/ui_template.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/ir/validator.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/manifest.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/manifest_schema.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/manifest_schema_ui.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/notify/__init__.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/notify/client.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/oauth_state.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/oauth_tokens.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/pipes.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/prompts/__init__.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/prompts/icnli_integrity_rules.txt +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/prompts/kernel_formatting_rule.txt +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/prompts/kernel_proactivity_rule.txt +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/protocols.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/rbac/__init__.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/rbac/client.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/rpc/__init__.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/rpc/codec.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/rpc/contract.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/runtime/__init__.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/runtime/_platform.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/runtime/canon.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/runtime/engine.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/runtime/executor.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/runtime/hosted_client.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/runtime/interpreter.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/runtime/llm_provider.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/runtime/local_engine.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/runtime/message_adapter.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/runtime/template.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/runtime/verbs.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/schema/marathon_ram.sql +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/schemas/action_result.schema.json +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/schemas/actions/__init__.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/schemas/actions/ai_complete.json +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/schemas/actions/call.json +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/schemas/actions/conditional.json +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/schemas/actions/navigate.json +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/schemas/actions/open.json +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/schemas/actions/send.json +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/schemas/actions/store_create.json +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/schemas/actions/store_delete.json +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/schemas/actions/store_get.json +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/schemas/actions/store_list.json +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/schemas/actions/store_update.json +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/schemas/balance_info.schema.json +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/schemas/chat_result.schema.json +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/schemas/completion_result.schema.json +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/schemas/document.schema.json +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/schemas/event.schema.json +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/schemas/file_info.schema.json +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/schemas/function_call.schema.json +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/schemas/http_response.schema.json +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/schemas/imperal.schema.json +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/schemas/ir.schema.json +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/schemas/limits_result.schema.json +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/schemas/metered_event.schema.json +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/schemas/subscription_info.schema.json +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/schemas/ui_children/__init__.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/schemas/ui_children/accordion.json +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/schemas/ui_children/datatable_columns.json +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/schemas/ui_children/datatable_rows.json +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/schemas/ui_children/menu.json +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/schemas/ui_children/select.json +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/schemas/ui_children/tabs.json +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/schemas/ui_children/timeline.json +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/schemas/ui_children/tree.json +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/sdl/__init__.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/sdl/_generate_roles_json.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/sdl/entity.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/sdl/facet_resolve.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/sdl/facets/__init__.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/sdl/facets/catalog.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/sdl/facets/comm.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/sdl/facets/content.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/sdl/facets/device.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/sdl/facets/event.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/sdl/facets/geo.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/sdl/facets/identity.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/sdl/facets/media.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/sdl/facets/metric.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/sdl/facets/money.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/sdl/facets/net.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/sdl/facets/people.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/sdl/facets/quantity.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/sdl/facets/rating.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/sdl/facets/security.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/sdl/facets/task.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/sdl/facets/time.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/sdl/field.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/sdl/roles.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/search.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/secrets/__init__.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/secrets/client.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/secrets/exceptions.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/secrets/panel_handler.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/secrets/spec.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/security/__init__.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/security/call_token.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/security/deactivation_guard.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/signals/__init__.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/signals/client.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/skeleton/__init__.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/skeleton/client.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/storage/__init__.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/storage/client.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/store/__init__.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/store/client.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/store/exceptions.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/testing/__init__.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/testing/autonomous_mock.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/testing/mock_context.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/testing/mock_secrets.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/transport.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/typed_context.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/types/__init__.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/types/action_result.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/types/chat_result.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/types/client_contracts.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/types/contracts.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/types/contributions.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/types/events.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/types/health.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/types/identity.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/types/metered_event.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/types/models.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/types/pagination.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/types/store_contracts.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/ui/__init__.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/ui/actions.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/ui/base.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/ui/canvas.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/ui/data.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/ui/display.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/ui/feedback.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/ui/graph.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/ui/input_components.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/ui/interactive.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/ui/layout.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/ui/liquid.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/ui/signals.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/ui/stage.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/ui/theme.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/universal/__init__.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/universal/image.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/universal/index.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/universal/kinds.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/universal/legacy.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/universal/mail.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/universal/office.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/universal/pdf.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/universal/textual.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/universal/view.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/users/__init__.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/users/client.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/validator.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/validator_v1_6_0.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/vault_ram_client.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/conftest.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/contract/__init__.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/contract/test_action_schemas.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/contract/test_artifacts_freshness.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/contract/test_contract_checks_selftest.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/contract/test_generate_sdk_claims.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/contract/test_identity_nonempty.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/contract/test_ir_schema_fresh.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/contract/test_meter_lua_contract.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/contract/test_metered_event_contract.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/contract/test_notify_wire_payload.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/contract/test_public_docstrings_engine_neutral.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/contract/test_reference_completeness.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/contract/test_reference_no_leak.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/contract/test_sample_contract_shape.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/contract/test_sdk_matches_kernel_contract.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/contracts/__init__.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/contracts/test_store_contracts.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/fixtures/contract/kernel-contract.sample.json +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/fixtures/ir/README.md +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/fixtures/ir/archive_ended.ir.json +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/fixtures/ir/link_saver.ir.json +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/fixtures/ir/test_fixtures_valid.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/fixtures/openapi/auth-gateway.json +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/fixtures/openapi/registry.json +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/fixtures/openapi/sharelock-cases.json +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/fixtures/universal/exif.jpg +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/fixtures/universal/invoice.doc +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/fixtures/universal/locked.pdf +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/fixtures/universal/two_pages.pdf +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/fixtures/universal/with_attachment.eml +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/ir/__init__.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/ir/test_custom_roles.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/ir/test_ir_impl.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/ir/test_ir_mapping_real_ext.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/ir/test_ir_migrate.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/ir/test_ir_produce.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/ir/test_ir_schema.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/ir/test_ir_skeleton.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/ir/test_ir_ui.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/ir/test_ir_ui_produce.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/ir/test_ir_validator.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/ir/test_ir_versions.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/ir/test_ui_children.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/ir/test_ui_template.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/rpc/__init__.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/rpc/test_boundary_seal.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/rpc/test_codec.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/rpc/test_contract.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/runtime/__init__.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/runtime/test_canon.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/runtime/test_canon_template.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/runtime/test_engine_abc.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/runtime/test_engine_parity.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/runtime/test_executor_neutral.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/runtime/test_hosted_client.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/runtime/test_interpreter.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/runtime/test_llm_provider_config_store.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/runtime/test_llm_provider_ctx_injection.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/runtime/test_local_engine.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/runtime/test_platform_shim.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/runtime/test_template.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/runtime/test_verb_ai.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/runtime/test_verb_call_directives.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/runtime/test_verb_conditional.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/runtime/test_verb_store.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/store/__init__.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/store/test_list_users_client.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/store/test_query_all_client.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_action_result_time_travel.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_action_result_typed.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_agent_presence.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_app_engine.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_as_user.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_auth.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_autonomous_mock.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_billing.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_cache_client.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_cache_model.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_call_token.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_callable_surface_single_source.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_chat_extension_deprecation.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_chat_extension_no_llm_router.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_chat_filters.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_chat_function_background_flag.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_chat_guards.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_chat_guards_bleed.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_chat_prompt.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_chat_result.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_cli.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_cli_sdk_commands.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_client_contracts.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_client_emit_neutral.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_code_index_configs.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_code_index_contracts.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_code_index_indexer.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_code_langs.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_code_web.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_config_client.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_context.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_context_background_task.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_context_deliver_chat_message.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_context_guards.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_contracts.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_contracts_live.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_contributions.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_core_primitives.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_data_model_kwarg.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_document_contract.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_emits_decorator.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_error_codes.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_errors.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_event_schema_v2.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_events_health.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_extension.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_extension_pipes.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_extension_v2.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_extensions_emit.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_gateway_namespaces.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_http_json_and_mock.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_http_retry.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_http_timeout_override.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_id_shape_guard.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_identity_contract.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_imperal_schema_v2.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_import_light.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_init_onboarding.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_kernel_primitives.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_liquid_ui.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_manifest.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_manifest_file_sinks.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_manifest_linter.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_manifest_no_orchestrator_tool.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_manifest_oauth.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_manifest_panels.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_manifest_roundtrip_gate.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_manifest_schema.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_manifest_v2_events.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_manifest_v2_other_sections.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_manifest_v2_webhooks.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_manifest_validator_v2.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_mock_context.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_models.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_narration_emission.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_narration_guard.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_oauth_authorize_url.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_oauth_tokens_shared.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_openai_max_completion_tokens.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_pagination.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_panel_rendering_contract.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_panel_swr_caching.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_panels.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_phase_a_dead_removal.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_phase_a_drift.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_phase_a_text.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_sdk_version_stamp.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_sdl_entity.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_sdl_entity_marker.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_sdl_exports.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_sdl_facet_catalog.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_sdl_facet_collisions.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_sdl_facet_comm.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_sdl_facet_content.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_sdl_facet_device.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_sdl_facet_event.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_sdl_facet_exports.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_sdl_facet_field.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_sdl_facet_geo.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_sdl_facet_identity.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_sdl_facet_media.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_sdl_facet_metric.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_sdl_facet_money.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_sdl_facet_net.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_sdl_facet_people.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_sdl_facet_quantity.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_sdl_facet_rating.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_sdl_facet_resolve.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_sdl_facet_security.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_sdl_facet_task.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_sdl_facet_time.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_sdl_facets_catalog.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_sdl_facets_doc.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_sdl_facets_pkg.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_sdl_field.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_sdl_roles.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_sdl_roles_json.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_sdl_roles_of_facets.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_sdl_vocab_version.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_search_provider.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_secret_scope_decorator.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_secret_spec_scope.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_skeleton_decorator.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_spec_validation.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_stage.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_surface_adaptation.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_sync_async_parity.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_transport_isolation.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_ui.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_ui_canvas.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_ui_fileupload_enhanced.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_ui_html.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_ui_image_enhanced.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_ui_modal.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_ui_open.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_ui_signals.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_ui_theme.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_universal_code_langs.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_universal_index.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_universal_kinds.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_universal_office.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_universal_pdf_mail.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_universal_views_text.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_user.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_v7_emit_refusal.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_validator.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_validator_drift.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_validator_pep563.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_validator_v1_6_0_rules.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_validator_v25.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_write_arg_bleed.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/tools/__init__.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/tools/test_generate_api_surface.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/tools/test_generate_reference.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/tools/test_reference_description.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/tools/test_reference_flags.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/tools/test_reference_typegraph.py +0 -0
- {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/ui/test_input_type_enum.py +0 -0
|
@@ -40,6 +40,12 @@ jobs:
|
|
|
40
40
|
matrix:
|
|
41
41
|
os: [ubuntu-latest, macos-latest, windows-latest]
|
|
42
42
|
python-version: ["3.9", "3.10", "3.11", "3.12", "3.13", "3.14"]
|
|
43
|
+
exclude:
|
|
44
|
+
- os: ubuntu-latest # its image carries no 3.9 (end of life): the same Linux on 22.04 below
|
|
45
|
+
python-version: "3.9"
|
|
46
|
+
include:
|
|
47
|
+
- os: ubuntu-22.04
|
|
48
|
+
python-version: "3.9"
|
|
43
49
|
|
|
44
50
|
steps:
|
|
45
51
|
- uses: actions/checkout@v4
|
|
@@ -2,6 +2,31 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to `imperal-sdk` are documented here.
|
|
4
4
|
|
|
5
|
+
## 6.5.2 - 2026-11-06
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
- `impact()` depth grows only through the definition that uses the symbol. A module-level use no longer counts as
|
|
9
|
+
"the whole file", so depth 2 no longer lists every file that imports anything from that module (found on a live
|
|
10
|
+
check).
|
|
11
|
+
|
|
12
|
+
## 6.5.1 - 2026-11-06
|
|
13
|
+
|
|
14
|
+
Fixes from the second 06.11 field report (Webbee 0.7.2 in a folder of nine repositories).
|
|
15
|
+
|
|
16
|
+
### Fixed
|
|
17
|
+
- `code_links`: a folder of repositories indexed as one resolves each absolute import in its OWN repository first.
|
|
18
|
+
`Linker(facts, configs, roots)` takes the repository roots; config directories are project roots too. Before,
|
|
19
|
+
`from app.sessions import ...` inside `gateway/` resolved nowhere, and two repositories with the same module names
|
|
20
|
+
took each other's files.
|
|
21
|
+
- `impact()` carries a `summary`: one sentence that can't be misread. "0 resolved users, 11 files only mention the
|
|
22
|
+
name" is no longer read as "no callers".
|
|
23
|
+
- Python members are resolved through typed values:
|
|
24
|
+
- typed locals and parameters (`Optional[...]`, forward references);
|
|
25
|
+
- `self.x` attributes and dataclass fields;
|
|
26
|
+
- return annotations.
|
|
27
|
+
|
|
28
|
+
A member of an unannotated call result is never linked to the call.
|
|
29
|
+
|
|
5
30
|
## 6.5.0 - 2026-11-06
|
|
6
31
|
|
|
7
32
|
The code graph, fixed after the 06.11 report: one link algorithm for the vault and every client, every major
|
|
@@ -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__ = "6.5.
|
|
4
|
+
__version__ = "6.5.2"
|
|
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
|
|
@@ -1168,6 +1168,113 @@ def _py_walk(sc: _Scope, node, scope: list, ctx: str, local: set, exported_names
|
|
|
1168
1168
|
_py_walk(sc, c, scope, ctx, local, exported_names, is_init)
|
|
1169
1169
|
|
|
1170
1170
|
|
|
1171
|
+
def _py_type(sc: _Scope, node) -> list | None:
|
|
1172
|
+
"""A Python annotation as a chain: ``Repo``, ``db.Conn``, ``Optional[Repo]`` / ``Repo | None`` -> the class;
|
|
1173
|
+
a builtin generic (``list[X]``, ``dict``) or anything else -> None."""
|
|
1174
|
+
if node is None:
|
|
1175
|
+
return None
|
|
1176
|
+
if node.type == "type" and node.named_children:
|
|
1177
|
+
node = node.named_children[0]
|
|
1178
|
+
if node.type in ("identifier", "attribute"):
|
|
1179
|
+
parts = sc.text(node).replace(" ", "").split(".")
|
|
1180
|
+
return parts if all(_IDENT_SHAPE.match(x) for x in parts) and parts[-1] not in _PY_BUILTINS else None
|
|
1181
|
+
if node.type == "binary_operator": # X | None
|
|
1182
|
+
for side in ("left", "right"):
|
|
1183
|
+
got = _py_type(sc, node.child_by_field_name(side))
|
|
1184
|
+
if got and got != ["None"]:
|
|
1185
|
+
return got
|
|
1186
|
+
return None
|
|
1187
|
+
if node.type in ("subscript", "generic_type"): # Optional[X], Annotated[X, ...]
|
|
1188
|
+
head = node.child_by_field_name("value") or (node.named_children[0] if node.named_children else None)
|
|
1189
|
+
if head is not None and sc.text(head).rsplit(".", 1)[-1] in ("Optional", "Annotated"):
|
|
1190
|
+
inner = [c for c in node.named_children if c is not None and c.start_byte != head.start_byte]
|
|
1191
|
+
if inner and inner[0].type == "type_parameter":
|
|
1192
|
+
inner = list(inner[0].named_children)
|
|
1193
|
+
return _py_type(sc, inner[0]) if inner else None
|
|
1194
|
+
return None
|
|
1195
|
+
if node.type == "string": # a forward reference: 'Repo'
|
|
1196
|
+
body = "".join(sc.text(c) for c in node.named_children if c.type == "string_content").strip()
|
|
1197
|
+
parts = body.split(".")
|
|
1198
|
+
return parts if body and all(_IDENT_SHAPE.match(x) for x in parts) else None
|
|
1199
|
+
return None
|
|
1200
|
+
|
|
1201
|
+
|
|
1202
|
+
def _py_value_type(sc: _Scope, value) -> list | None:
|
|
1203
|
+
"""``Repo(...)`` / ``db.Conn(...)`` -> the class (a capitalized callee); ``make_repo(...)`` -> ``[make_repo, "()"]``
|
|
1204
|
+
(the value its declared return type names -- resolved through its ``returns`` binding)."""
|
|
1205
|
+
if value is None:
|
|
1206
|
+
return None
|
|
1207
|
+
if value.type == "await" and value.named_children:
|
|
1208
|
+
value = value.named_children[0]
|
|
1209
|
+
if value.type != "call":
|
|
1210
|
+
return None
|
|
1211
|
+
fn = value.child_by_field_name("function")
|
|
1212
|
+
chain = _py_chain(sc, fn) if fn is not None else None
|
|
1213
|
+
if not chain or chain[0] in _PY_BUILTINS:
|
|
1214
|
+
return None
|
|
1215
|
+
return chain if chain[-1][:1].isupper() else chain + ["()"]
|
|
1216
|
+
|
|
1217
|
+
|
|
1218
|
+
def _py_typed(sc: _Scope, fn) -> dict:
|
|
1219
|
+
"""``{local: type chain}`` of a Python function: annotated parameters, ``x: T = ...``, ``x = T(...)``,
|
|
1220
|
+
``x = make()`` (through make's return annotation)."""
|
|
1221
|
+
out: dict = {}
|
|
1222
|
+
params = fn.child_by_field_name("parameters")
|
|
1223
|
+
for p in (params.named_children if params is not None else []):
|
|
1224
|
+
if p.type in ("typed_parameter", "typed_default_parameter"):
|
|
1225
|
+
n = p.child_by_field_name("name") or next((c for c in p.named_children if c.type == "identifier"), None)
|
|
1226
|
+
ch = _py_type(sc, p.child_by_field_name("type"))
|
|
1227
|
+
if n is not None and ch:
|
|
1228
|
+
out[sc.text(n)] = ch
|
|
1229
|
+
stack = list((fn.child_by_field_name("body") or fn).children)
|
|
1230
|
+
while stack:
|
|
1231
|
+
n = stack.pop()
|
|
1232
|
+
if n.type in ("function_definition", "class_definition", "lambda"):
|
|
1233
|
+
continue
|
|
1234
|
+
if n.type == "assignment":
|
|
1235
|
+
left = n.child_by_field_name("left")
|
|
1236
|
+
if left is not None and left.type == "identifier":
|
|
1237
|
+
ch = _py_type(sc, n.child_by_field_name("type")) or _py_value_type(sc, n.child_by_field_name("right"))
|
|
1238
|
+
if ch:
|
|
1239
|
+
out[sc.text(left)] = ch
|
|
1240
|
+
stack.extend(n.children)
|
|
1241
|
+
return out
|
|
1242
|
+
|
|
1243
|
+
|
|
1244
|
+
def _py_fields(sc: _Scope, cls) -> dict:
|
|
1245
|
+
"""``{attribute: type chain}`` of a Python class: annotated class attributes (dataclasses, pydantic) and
|
|
1246
|
+
``self.x = T(...)`` / ``self.x: T`` / ``self.x = param`` (a typed parameter) in its methods."""
|
|
1247
|
+
out: dict = {}
|
|
1248
|
+
body = cls.child_by_field_name("body")
|
|
1249
|
+
for st in (body.named_children if body is not None else []):
|
|
1250
|
+
expr = st.named_children[0] if st.type == "expression_statement" and st.named_children else st
|
|
1251
|
+
if expr.type == "assignment":
|
|
1252
|
+
left = expr.child_by_field_name("left")
|
|
1253
|
+
ch = _py_type(sc, expr.child_by_field_name("type")) or _py_value_type(sc, expr.child_by_field_name("right"))
|
|
1254
|
+
if left is not None and left.type == "identifier" and ch:
|
|
1255
|
+
out[sc.text(left)] = ch
|
|
1256
|
+
fn = st.child_by_field_name("definition") if st.type == "decorated_definition" else st
|
|
1257
|
+
if fn is None or fn.type != "function_definition":
|
|
1258
|
+
continue
|
|
1259
|
+
params = _py_typed(sc, fn)
|
|
1260
|
+
stack = list((fn.child_by_field_name("body") or fn).children)
|
|
1261
|
+
while stack:
|
|
1262
|
+
n = stack.pop()
|
|
1263
|
+
if n.type in ("function_definition", "class_definition", "lambda"):
|
|
1264
|
+
continue
|
|
1265
|
+
if n.type == "assignment":
|
|
1266
|
+
left = n.child_by_field_name("left")
|
|
1267
|
+
if left is not None and left.type == "attribute" and sc.text(left).startswith("self."):
|
|
1268
|
+
attr = sc.text(left)[5:]
|
|
1269
|
+
right = n.child_by_field_name("right")
|
|
1270
|
+
ch = (_py_type(sc, n.child_by_field_name("type")) or _py_value_type(sc, right)
|
|
1271
|
+
or (params.get(sc.text(right)) if right is not None and right.type == "identifier" else None))
|
|
1272
|
+
if ch and "." not in attr:
|
|
1273
|
+
out.setdefault(attr, ch)
|
|
1274
|
+
stack.extend(n.children)
|
|
1275
|
+
return out
|
|
1276
|
+
|
|
1277
|
+
|
|
1171
1278
|
def _py_def(sc: _Scope, node, outer, scope: list, local: set, exported_names, is_init: bool) -> None:
|
|
1172
1279
|
name_node = node.child_by_field_name("name")
|
|
1173
1280
|
if name_node is None:
|
|
@@ -1183,6 +1290,17 @@ def _py_def(sc: _Scope, node, outer, scope: list, local: set, exported_names, is
|
|
|
1183
1290
|
exp = bool(parent[2]) and parent[1] == "class" and not name.startswith("_")
|
|
1184
1291
|
sc.defs.append(Def(name, qual, kind, _line_of(outer), node.end_point[0] + 1,
|
|
1185
1292
|
_first_line(sc.text(node)), exp))
|
|
1293
|
+
if is_class:
|
|
1294
|
+
fields = _py_fields(sc, node)
|
|
1295
|
+
if fields:
|
|
1296
|
+
sc.fields[qual] = fields
|
|
1297
|
+
else:
|
|
1298
|
+
typed = _py_typed(sc, node)
|
|
1299
|
+
if typed:
|
|
1300
|
+
sc.typed[qual] = typed
|
|
1301
|
+
rch = _py_type(sc, node.child_by_field_name("return_type"))
|
|
1302
|
+
if rch:
|
|
1303
|
+
sc.bindings.append(Binding(qual + "()", "", ".".join(rch), "returns", _line_of(outer)))
|
|
1186
1304
|
inner = (qual, "class" if is_class else "function", exp)
|
|
1187
1305
|
sup = node.child_by_field_name("superclasses")
|
|
1188
1306
|
if sup is not None:
|
|
@@ -64,19 +64,31 @@ def is_test_path(path: str) -> bool:
|
|
|
64
64
|
|
|
65
65
|
# ---- module resolution (the vault's stitching code, §4.6.3) ----
|
|
66
66
|
|
|
67
|
-
def module_index(paths, configs: list) -> dict:
|
|
67
|
+
def module_index(paths, configs: list, roots=()) -> dict:
|
|
68
68
|
"""The repo's files as the resolver probes them: every path (exact and case-blind), and every
|
|
69
69
|
Python module name under each package root -- the repo root, ``src/`` layouts, the roots the
|
|
70
70
|
configs declare (pyproject ``where`` / ``package-dir``, setup.cfg ``package_dir``) and the top
|
|
71
|
-
of each ``__init__.py`` chain.
|
|
71
|
+
of each ``__init__.py`` chain.
|
|
72
|
+
|
|
73
|
+
``roots``: the repositories inside a folder indexed as one (``core/wpanel-sdk``, ``gateway``). With the
|
|
74
|
+
directories of the project configs they are PROJECT roots: an absolute import is looked up first among the
|
|
75
|
+
names of the project holding the importing file (report 06.11 #2: ``from app.sessions import ...`` inside
|
|
76
|
+
``gateway/`` resolved nowhere, and two repos with the same module names took each other's files)."""
|
|
72
77
|
paths = sorted(set(paths), key=lambda p: (not p.endswith(".py"), p))
|
|
73
78
|
files = set(paths)
|
|
74
79
|
lower: dict = {}
|
|
75
80
|
for p in paths:
|
|
76
81
|
lower.setdefault(p.lower(), p)
|
|
77
|
-
|
|
82
|
+
projects = {""} | {_slashes(str(r)).strip("/") for r in roots or () if str(r).strip("/.")}
|
|
83
|
+
for c in configs or []:
|
|
84
|
+
if c.get("kind") in ("pyproject", "setup_cfg", "package_json", "go_mod", "cargo"):
|
|
85
|
+
projects.add(posixpath.dirname(_slashes(str(c.get("path") or "")).lstrip("/")))
|
|
86
|
+
roots = set(projects)
|
|
78
87
|
if any(p.startswith("src/") for p in paths):
|
|
79
88
|
roots.add("src")
|
|
89
|
+
for pr in projects:
|
|
90
|
+
if pr and any(p.startswith(pr + "/src/") for p in paths):
|
|
91
|
+
roots.add(pr + "/src")
|
|
80
92
|
for c in configs or []:
|
|
81
93
|
data = c.get("data") or {}
|
|
82
94
|
base = posixpath.dirname(str(c.get("path") or ""))
|
|
@@ -86,6 +98,7 @@ def module_index(paths, configs: list) -> dict:
|
|
|
86
98
|
if str(k) == "" and v:
|
|
87
99
|
roots.add(_slashes(posixpath.join(base, str(v))).lstrip("/"))
|
|
88
100
|
py: dict = {}
|
|
101
|
+
by_root: dict = {}
|
|
89
102
|
inits = {posixpath.dirname(p) for p in paths if posixpath.basename(p) in ("__init__.py", "__init__.pyi")}
|
|
90
103
|
for p in paths:
|
|
91
104
|
if not p.endswith(_PY_EXT):
|
|
@@ -99,12 +112,13 @@ def module_index(paths, configs: list) -> dict:
|
|
|
99
112
|
while top and top in inits:
|
|
100
113
|
top = posixpath.dirname(top)
|
|
101
114
|
mine.add(top)
|
|
102
|
-
for root in mine:
|
|
115
|
+
for root in sorted(mine):
|
|
103
116
|
if root and not (stem == root or stem.startswith(root + "/")):
|
|
104
117
|
continue
|
|
105
118
|
rel = stem[len(root) + 1:] if root else stem
|
|
106
119
|
if rel:
|
|
107
120
|
py.setdefault(rel.replace("/", "."), p)
|
|
121
|
+
by_root.setdefault(root, {}).setdefault(rel.replace("/", "."), p)
|
|
108
122
|
pkgs = {}
|
|
109
123
|
for c in configs or []:
|
|
110
124
|
if c.get("kind") == "package_json" and (c.get("data") or {}).get("name"):
|
|
@@ -112,7 +126,21 @@ def module_index(paths, configs: list) -> dict:
|
|
|
112
126
|
by_path = {_slashes(str(c.get("path") or "")).lstrip("/"): c for c in configs or []}
|
|
113
127
|
ts = [dict(c, eff=_ts_effective(c, by_path, pkgs, set()))
|
|
114
128
|
for c in configs or [] if c.get("kind") in ("tsconfig", "jsconfig")]
|
|
115
|
-
return {"files": files, "lower": lower, "py": py, "ts": ts, "pkgs": pkgs
|
|
129
|
+
return {"files": files, "lower": lower, "py": py, "ts": ts, "pkgs": pkgs, "py_roots": by_root,
|
|
130
|
+
"projects": sorted(projects, key=len, reverse=True)}
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def _py_absolute(modules: dict, from_path: str, spec: str) -> str | None:
|
|
134
|
+
"""An absolute Python module: first among the names of the project holding ``from_path`` (its own roots),
|
|
135
|
+
then anywhere (a package another project of the folder provides: ``wpanel_sdk`` from ``core/wpanel-sdk``)."""
|
|
136
|
+
proj = next((r for r in modules.get("projects") or () if not r or from_path.startswith(r + "/")), "")
|
|
137
|
+
if proj:
|
|
138
|
+
for root in sorted(modules.get("py_roots") or {}):
|
|
139
|
+
if root == proj or root.startswith(proj + "/"):
|
|
140
|
+
got = modules["py_roots"][root].get(spec)
|
|
141
|
+
if got:
|
|
142
|
+
return got
|
|
143
|
+
return modules["py"].get(spec)
|
|
116
144
|
|
|
117
145
|
|
|
118
146
|
def _ts_effective(c: dict, by_path: dict, pkgs: dict, seen: set) -> dict:
|
|
@@ -234,7 +262,7 @@ def resolve_module(modules: dict, from_path: str, spec: str, lang: str, configs:
|
|
|
234
262
|
parts = parts[:len(parts) - (level - 1)]
|
|
235
263
|
target = "/".join(parts + ([x for x in rest.split(".") if x] if rest else []))
|
|
236
264
|
return _probe(modules, target, "python") if target else _probe(modules, "__init__", "python")
|
|
237
|
-
return modules
|
|
265
|
+
return _py_absolute(modules, from_path, spec)
|
|
238
266
|
if spec.startswith("."):
|
|
239
267
|
return _probe(modules, posixpath.join(posixpath.dirname(from_path), spec), lang)
|
|
240
268
|
confs = sorted((c for c in modules.get("ts") or []
|
|
@@ -271,9 +299,10 @@ class Linker:
|
|
|
271
299
|
parsed resolution configs (``code_index.parse_config``: ``{path, kind, data}``)."""
|
|
272
300
|
facts: dict
|
|
273
301
|
configs: list = field(default_factory=list)
|
|
302
|
+
roots: list = field(default_factory=list) # the repositories of a folder indexed as one (module_index)
|
|
274
303
|
|
|
275
304
|
def __post_init__(self):
|
|
276
|
-
self.modules = module_index(list(self.facts), self.configs)
|
|
305
|
+
self.modules = module_index(list(self.facts), self.configs, self.roots)
|
|
277
306
|
self._defs: dict = {}
|
|
278
307
|
self._pkgs: dict = {} # module key -> (files, names before the asked ones)
|
|
279
308
|
self.ns: dict = {} # (family, package) -> files that declare it
|
|
@@ -586,10 +615,10 @@ class Linker:
|
|
|
586
615
|
ret = next((b.imported for b in self.bindings(got[0])
|
|
587
616
|
if b.kind == "returns" and b.local_name == got[1] + _CALL and b.imported), None)
|
|
588
617
|
if not ret:
|
|
589
|
-
return
|
|
618
|
+
return None # no declared return type: what ``.rest`` names is unproven (the call is its own ref)
|
|
590
619
|
tp = [x for x in ret.split(".") if x]
|
|
591
620
|
typed = self.resolve_ref(got[0], tp[0], ".".join(tp[1:] + rest), got[1], depth + 1)
|
|
592
|
-
return (typed[0], typed[1], "returns") if isinstance(typed, tuple) and typed[1] else
|
|
621
|
+
return (typed[0], typed[1], "returns") if isinstance(typed, tuple) and typed[1] else None
|
|
593
622
|
bound = [b for b in self.bindings(path) if b.local_name == head and b.kind != "star"]
|
|
594
623
|
lang = _lang(path)
|
|
595
624
|
if bound:
|
|
@@ -740,6 +769,8 @@ def impact(linker: Linker, symbols: list, depth: int = 2) -> dict:
|
|
|
740
769
|
if c not in it["callers"] and len(it["callers"]) < 20:
|
|
741
770
|
it["callers"].append(c)
|
|
742
771
|
nk = (src, user)
|
|
772
|
+
if not user: # a module-level use is no definition to follow -- never "the whole file"
|
|
773
|
+
continue
|
|
743
774
|
if nk not in seen:
|
|
744
775
|
seen.add(nk)
|
|
745
776
|
nxt.append(nk)
|
|
@@ -778,6 +809,25 @@ def impact(linker: Linker, symbols: list, depth: int = 2) -> dict:
|
|
|
778
809
|
same_file += [{"path": p, "user": user, "line": line} for src, user, _k, line, tp, tq, _o in links
|
|
779
810
|
if src == p and tp == p and tq == q and user != q][:20]
|
|
780
811
|
out = sorted(items.values(), key=lambda i: (i["group"] == "tests", i["depth"], i["id"]))
|
|
812
|
+
resolved = [f"{p}::{q}" if q else p for p, q in targets]
|
|
781
813
|
return {"ok": True, "data": {"items": out, "total": len(out), "has_more": False},
|
|
782
814
|
"by_name_count": len(by_name_files), "by_name": by_name[:BY_NAME_SHOWN], "same_file": same_file,
|
|
783
|
-
"resolved":
|
|
815
|
+
"resolved": resolved,
|
|
816
|
+
"summary": impact_summary(symbols, resolved, len(out), len(by_name_files), len(same_file))}
|
|
817
|
+
|
|
818
|
+
|
|
819
|
+
def impact_summary(symbols, resolved, users: int, by_name: int, same_file: int = 0) -> str:
|
|
820
|
+
"""One sentence an agent cannot misread (report 06.11 #2: ``items: 0`` beside 11 name matches was read as "no
|
|
821
|
+
callers"): how many files use the symbol by resolved links, and what the name-only matches are -- unproven."""
|
|
822
|
+
what = ", ".join(str(s) for s in symbols or []) or "the symbol"
|
|
823
|
+
if not resolved:
|
|
824
|
+
return f"{what}: not a definition in this index -- nothing to trace (check the name or pass path::name)"
|
|
825
|
+
head = (f"{users} file(s) use {what} by resolved links" if users else f"no file uses {what} by a resolved link")
|
|
826
|
+
if same_file:
|
|
827
|
+
head += f"; {same_file} use(s) inside its own file"
|
|
828
|
+
if by_name:
|
|
829
|
+
head += (f"; {by_name} more file(s) only mention the name (by_name) -- calls through an untyped value or a "
|
|
830
|
+
f"name clash: neither proven users nor proven unrelated, read them before concluding")
|
|
831
|
+
elif not users:
|
|
832
|
+
head += " and no other file mentions the name"
|
|
833
|
+
return head
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
"""imperal_sdk.code_links: the vault's link rules, computed locally (2026-11-06 report: a function with no caller
|
|
2
|
+
answered 481 files when links were made by bare name)."""
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import pytest
|
|
6
|
+
|
|
7
|
+
pytest.importorskip("tree_sitter")
|
|
8
|
+
pytest.importorskip("tree_sitter_python")
|
|
9
|
+
|
|
10
|
+
from imperal_sdk import code_index as ci # noqa: E402
|
|
11
|
+
from imperal_sdk.code_links import Linker, impact, resolve_module, module_index # noqa: E402
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def _linker(files: dict, configs: list | None = None) -> Linker:
|
|
15
|
+
return Linker({p: ci.scope_facts(p, t) for p, t in files.items()}, configs or [])
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
PY = {
|
|
19
|
+
"pkg/__init__.py": "from .core import compute as compute_total\n",
|
|
20
|
+
"pkg/core.py": (
|
|
21
|
+
"def compute(x):\n return helper(x)\n\n\ndef helper(x):\n return x\n\n\n"
|
|
22
|
+
"def lonely():\n return 1\n\n\nclass Cart:\n def total(self):\n return self.sum()\n\n"
|
|
23
|
+
" def sum(self):\n return 0\n"),
|
|
24
|
+
"app/use.py": "from pkg import compute_total\n\n\ndef run():\n return compute_total(1)\n",
|
|
25
|
+
"app/deep.py": "from app.use import run\n\n\ndef outer():\n return run()\n",
|
|
26
|
+
"app/other.py": "def go(obj):\n return obj.lonely()\n\n\ndef unrelated():\n return 2\n",
|
|
27
|
+
"tests/test_core.py": "from pkg.core import compute\n\n\ndef test_compute():\n assert compute(2) == 2\n",
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def test_a_function_nobody_calls_has_no_dependents():
|
|
32
|
+
out = impact(_linker(PY), ["lonely"])
|
|
33
|
+
assert out["data"]["items"] == [] and out["resolved"] == ["pkg/core.py::lonely"]
|
|
34
|
+
# obj.lonely() binds to nothing the resolver can prove: listed apart, by name only -- never a resolved link
|
|
35
|
+
assert [b["path"] for b in out["by_name"]] == ["app/other.py"] and out["by_name_count"] == 1
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def test_reexport_alias_and_depth_through_the_enclosing_def_only():
|
|
39
|
+
out = impact(_linker(PY), ["compute"], depth=2)
|
|
40
|
+
items = {i["id"]: i for i in out["data"]["items"]}
|
|
41
|
+
# pkg/__init__.py only re-exports (an import, no use): no user, as in the vault
|
|
42
|
+
assert set(items) == {"app/use.py", "tests/test_core.py", "app/deep.py"}
|
|
43
|
+
assert items["app/use.py"]["depth"] == 1 and items["app/use.py"]["user"] == "run"
|
|
44
|
+
assert items["app/deep.py"]["depth"] == 2 and items["app/deep.py"]["user"] == "outer"
|
|
45
|
+
assert "app/other.py" not in items # shares no link, never pulled in by a file's other defs
|
|
46
|
+
groups = [i["group"] for i in out["data"]["items"]]
|
|
47
|
+
assert groups == sorted(groups, key=lambda g: g == "tests") # code before tests
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def test_self_member_and_same_file_users():
|
|
51
|
+
lk = _linker(PY)
|
|
52
|
+
assert lk.resolve_ref("pkg/core.py", "self", "sum", "Cart.total") == ("pkg/core.py", "Cart.sum", "self")
|
|
53
|
+
out = impact(lk, ["helper"])
|
|
54
|
+
assert out["data"]["items"] == [] and out["same_file"][0]["user"] == "compute"
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def test_unknown_symbol_is_said_not_scanned():
|
|
58
|
+
out = impact(_linker(PY), ["does_not_exist"])
|
|
59
|
+
assert out["resolved"] == [] and out["data"]["items"] == [] and out["by_name"] == []
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def test_qualified_forms():
|
|
63
|
+
lk = _linker(PY)
|
|
64
|
+
assert lk.symbol_targets("pkg/core.py::Cart.sum") == [("pkg/core.py", "Cart.sum")]
|
|
65
|
+
assert lk.symbol_targets("Cart.sum") == [("pkg/core.py", "Cart.sum")]
|
|
66
|
+
assert lk.symbol_targets("pkg/core.py") == [("pkg/core.py", "")]
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def test_typescript_paths_and_index():
|
|
70
|
+
pytest.importorskip("tree_sitter_typescript")
|
|
71
|
+
files = {
|
|
72
|
+
"tsconfig.json": "",
|
|
73
|
+
"src/lib/index.ts": "export function fmt(x: number) { return String(x) }\nexport function unused() { return 1 }\n",
|
|
74
|
+
"src/app.ts": "import { fmt } from '@/lib'\nexport function show() { return fmt(1) }\n",
|
|
75
|
+
}
|
|
76
|
+
facts = {p: ci.scope_facts(p, t) for p, t in files.items() if p.endswith(".ts")}
|
|
77
|
+
lk = Linker(facts, [{"path": "tsconfig.json", "kind": "tsconfig",
|
|
78
|
+
"data": {"baseUrl": ".", "paths": {"@/*": ["src/*"]}}}])
|
|
79
|
+
assert [i["id"] for i in impact(lk, ["fmt"])["data"]["items"]] == ["src/app.ts"]
|
|
80
|
+
assert impact(lk, ["unused"])["data"]["items"] == []
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def test_resolve_module_is_the_vaults():
|
|
84
|
+
mods = module_index(["src/a/__init__.py", "src/a/b.py"], [])
|
|
85
|
+
assert resolve_module(mods, "src/a/__init__.py", ".b", "python") == "src/a/b.py"
|
|
86
|
+
assert resolve_module(mods, "x.py", "a.b", "python") == "src/a/b.py"
|
|
87
|
+
assert resolve_module(mods, "x.py", "requests", "python") is None
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
FOLDER = {
|
|
91
|
+
"core/wpanel-sdk/pyproject.toml": '[project]\nname = "wpanel-sdk"\n[tool.setuptools.packages.find]\nwhere = ["src"]\n',
|
|
92
|
+
"core/wpanel-sdk/src/wpanel_sdk/__init__.py": "",
|
|
93
|
+
"core/wpanel-sdk/src/wpanel_sdk/ui/__init__.py": "class IRNode:\n pass\n",
|
|
94
|
+
"modules/wpanel-tickets/wpanel_tickets/__init__.py": "",
|
|
95
|
+
"modules/wpanel-tickets/wpanel_tickets/screens.py": "from wpanel_sdk.ui import IRNode\n\n\ndef screen():\n"
|
|
96
|
+
" return IRNode()\n",
|
|
97
|
+
# two repos with the same top-level package name and no __init__ above it (namespace packages)
|
|
98
|
+
"gateway/app/sessions.py": "def session_for_sid(sid):\n return sid\n",
|
|
99
|
+
"gateway/app/routes.py": "from app.sessions import session_for_sid\n\n\ndef me(sid):\n return session_for_sid(sid)\n",
|
|
100
|
+
"billing/app/sessions.py": "def session_for_sid(sid):\n return None\n",
|
|
101
|
+
"billing/app/api.py": "from app.sessions import session_for_sid\n\n\ndef pay(sid):\n return session_for_sid(sid)\n",
|
|
102
|
+
"check-paths-agree.py": "print('IRNode')\n",
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def _folder_linker(roots):
|
|
107
|
+
facts, configs = {}, []
|
|
108
|
+
for p, t in FOLDER.items():
|
|
109
|
+
if p.endswith(".toml"):
|
|
110
|
+
kind, data = ci.parse_config(p, t)
|
|
111
|
+
configs.append({"path": p, "kind": kind, "data": data})
|
|
112
|
+
else:
|
|
113
|
+
facts[p] = ci.scope_facts(p, t)
|
|
114
|
+
return Linker(facts, configs, roots)
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def test_a_folder_of_repositories_resolves_each_import_in_its_own_repository():
|
|
118
|
+
"""06.11 #2: a folder indexed as one -- absolute imports from each repository's own root, a package another
|
|
119
|
+
repository provides by its name."""
|
|
120
|
+
lk = _folder_linker(["core/wpanel-sdk", "modules/wpanel-tickets", "gateway", "billing"])
|
|
121
|
+
assert [i["id"] for i in impact(lk, ["IRNode"])["data"]["items"]] == ["modules/wpanel-tickets/wpanel_tickets/screens.py"]
|
|
122
|
+
assert [i["id"] for i in impact(lk, ["gateway/app/sessions.py::session_for_sid"])["data"]["items"]] == \
|
|
123
|
+
["gateway/app/routes.py"]
|
|
124
|
+
assert [i["id"] for i in impact(lk, ["billing/app/sessions.py::session_for_sid"])["data"]["items"]] == \
|
|
125
|
+
["billing/app/api.py"]
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def test_the_summary_never_reads_as_no_callers_when_names_match():
|
|
130
|
+
out = impact(_linker(PY), ["lonely"])
|
|
131
|
+
assert out["data"]["items"] == [] and "no file uses lonely by a resolved link" in out["summary"]
|
|
132
|
+
assert "1 more file(s) only mention the name" in out["summary"] and "before concluding" in out["summary"]
|
|
133
|
+
assert impact(_linker(PY), ["compute"])["summary"].startswith("3 file(s) use compute by resolved links")
|
|
134
|
+
assert "not a definition" in impact(_linker(PY), ["nope"])["summary"]
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def test_python_members_through_typed_locals_fields_and_returns():
|
|
138
|
+
lk = _linker({
|
|
139
|
+
"repo.py": "class Repo:\n def save(self):\n return 1\n\n\ndef make_repo() -> 'Repo':\n return Repo()\n\n\n"
|
|
140
|
+
"def open_repo() -> Repo:\n return Repo()\n",
|
|
141
|
+
"svc.py": "from typing import Optional\nfrom repo import Repo, open_repo\n\n\nclass Svc:\n"
|
|
142
|
+
" def __init__(self, repo: Repo):\n self.repo = repo\n\n def run(self):\n"
|
|
143
|
+
" return self.repo.save()\n\n\ndef a():\n r = Repo()\n return r.save()\n\n\n"
|
|
144
|
+
"def b(r: Optional[Repo]):\n return r.save()\n\n\ndef c():\n r = open_repo()\n return r.save()\n\n\n"
|
|
145
|
+
"def d(x):\n return x.save()\n",
|
|
146
|
+
})
|
|
147
|
+
got = impact(lk, ["Repo.save"])
|
|
148
|
+
users = {c["user"] for i in got["data"]["items"] for c in i["callers"]}
|
|
149
|
+
assert users == {"Svc.run", "a", "b", "c"} # d's x is untyped: by name only, never a resolved link
|
|
150
|
+
assert got["by_name"] == [] or all(b["path"] != "svc.py" for b in got["by_name"])
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
def test_a_member_of_an_unannotated_call_result_is_never_linked_to_the_call():
|
|
155
|
+
"""A spot check of real links found ``vs.to_arrays()`` linked to ``VectorStore.from_arrays`` (vs came from it)."""
|
|
156
|
+
lk = _linker({"v.py": "class VectorStore:\n @classmethod\n def from_arrays(cls, a):\n return cls()\n\n"
|
|
157
|
+
" def to_arrays(self):\n return 1\n",
|
|
158
|
+
"u.py": "from v import VectorStore\n\n\ndef go():\n vs = VectorStore.from_arrays(1)\n"
|
|
159
|
+
" return vs.to_arrays()\n"})
|
|
160
|
+
got = {(l[3], l[5]) for l in lk.links() if l[0] == "u.py"}
|
|
161
|
+
assert (5, "VectorStore.from_arrays") in got and (6, "VectorStore.from_arrays") not in got
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
def test_depth_never_grows_through_a_module_level_use_as_the_whole_file():
|
|
166
|
+
lk = _linker({"q.py": "def linker():\n return 1\n",
|
|
167
|
+
"mod.py": "from q import linker\nVALUE = linker()\n\n\ndef unrelated():\n return 2\n",
|
|
168
|
+
"uses_mod.py": "from mod import unrelated\n\n\ndef f():\n return unrelated()\n"})
|
|
169
|
+
got = {(i["id"], i["depth"]) for i in impact(lk, ["linker"], depth=2)["data"]["items"]}
|
|
170
|
+
assert got == {("mod.py", 1)} # uses_mod.py only uses another definition of mod.py
|
|
@@ -1,87 +0,0 @@
|
|
|
1
|
-
"""imperal_sdk.code_links: the vault's link rules, computed locally (2026-11-06 report: a function with no caller
|
|
2
|
-
answered 481 files when links were made by bare name)."""
|
|
3
|
-
from __future__ import annotations
|
|
4
|
-
|
|
5
|
-
import pytest
|
|
6
|
-
|
|
7
|
-
pytest.importorskip("tree_sitter")
|
|
8
|
-
pytest.importorskip("tree_sitter_python")
|
|
9
|
-
|
|
10
|
-
from imperal_sdk import code_index as ci # noqa: E402
|
|
11
|
-
from imperal_sdk.code_links import Linker, impact, resolve_module, module_index # noqa: E402
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
def _linker(files: dict, configs: list | None = None) -> Linker:
|
|
15
|
-
return Linker({p: ci.scope_facts(p, t) for p, t in files.items()}, configs or [])
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
PY = {
|
|
19
|
-
"pkg/__init__.py": "from .core import compute as compute_total\n",
|
|
20
|
-
"pkg/core.py": (
|
|
21
|
-
"def compute(x):\n return helper(x)\n\n\ndef helper(x):\n return x\n\n\n"
|
|
22
|
-
"def lonely():\n return 1\n\n\nclass Cart:\n def total(self):\n return self.sum()\n\n"
|
|
23
|
-
" def sum(self):\n return 0\n"),
|
|
24
|
-
"app/use.py": "from pkg import compute_total\n\n\ndef run():\n return compute_total(1)\n",
|
|
25
|
-
"app/deep.py": "from app.use import run\n\n\ndef outer():\n return run()\n",
|
|
26
|
-
"app/other.py": "def go(obj):\n return obj.lonely()\n\n\ndef unrelated():\n return 2\n",
|
|
27
|
-
"tests/test_core.py": "from pkg.core import compute\n\n\ndef test_compute():\n assert compute(2) == 2\n",
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
def test_a_function_nobody_calls_has_no_dependents():
|
|
32
|
-
out = impact(_linker(PY), ["lonely"])
|
|
33
|
-
assert out["data"]["items"] == [] and out["resolved"] == ["pkg/core.py::lonely"]
|
|
34
|
-
# obj.lonely() binds to nothing the resolver can prove: listed apart, by name only -- never a resolved link
|
|
35
|
-
assert [b["path"] for b in out["by_name"]] == ["app/other.py"] and out["by_name_count"] == 1
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
def test_reexport_alias_and_depth_through_the_enclosing_def_only():
|
|
39
|
-
out = impact(_linker(PY), ["compute"], depth=2)
|
|
40
|
-
items = {i["id"]: i for i in out["data"]["items"]}
|
|
41
|
-
# pkg/__init__.py only re-exports (an import, no use): no user, as in the vault
|
|
42
|
-
assert set(items) == {"app/use.py", "tests/test_core.py", "app/deep.py"}
|
|
43
|
-
assert items["app/use.py"]["depth"] == 1 and items["app/use.py"]["user"] == "run"
|
|
44
|
-
assert items["app/deep.py"]["depth"] == 2 and items["app/deep.py"]["user"] == "outer"
|
|
45
|
-
assert "app/other.py" not in items # shares no link, never pulled in by a file's other defs
|
|
46
|
-
groups = [i["group"] for i in out["data"]["items"]]
|
|
47
|
-
assert groups == sorted(groups, key=lambda g: g == "tests") # code before tests
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
def test_self_member_and_same_file_users():
|
|
51
|
-
lk = _linker(PY)
|
|
52
|
-
assert lk.resolve_ref("pkg/core.py", "self", "sum", "Cart.total") == ("pkg/core.py", "Cart.sum", "self")
|
|
53
|
-
out = impact(lk, ["helper"])
|
|
54
|
-
assert out["data"]["items"] == [] and out["same_file"][0]["user"] == "compute"
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
def test_unknown_symbol_is_said_not_scanned():
|
|
58
|
-
out = impact(_linker(PY), ["does_not_exist"])
|
|
59
|
-
assert out["resolved"] == [] and out["data"]["items"] == [] and out["by_name"] == []
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
def test_qualified_forms():
|
|
63
|
-
lk = _linker(PY)
|
|
64
|
-
assert lk.symbol_targets("pkg/core.py::Cart.sum") == [("pkg/core.py", "Cart.sum")]
|
|
65
|
-
assert lk.symbol_targets("Cart.sum") == [("pkg/core.py", "Cart.sum")]
|
|
66
|
-
assert lk.symbol_targets("pkg/core.py") == [("pkg/core.py", "")]
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
def test_typescript_paths_and_index():
|
|
70
|
-
pytest.importorskip("tree_sitter_typescript")
|
|
71
|
-
files = {
|
|
72
|
-
"tsconfig.json": "",
|
|
73
|
-
"src/lib/index.ts": "export function fmt(x: number) { return String(x) }\nexport function unused() { return 1 }\n",
|
|
74
|
-
"src/app.ts": "import { fmt } from '@/lib'\nexport function show() { return fmt(1) }\n",
|
|
75
|
-
}
|
|
76
|
-
facts = {p: ci.scope_facts(p, t) for p, t in files.items() if p.endswith(".ts")}
|
|
77
|
-
lk = Linker(facts, [{"path": "tsconfig.json", "kind": "tsconfig",
|
|
78
|
-
"data": {"baseUrl": ".", "paths": {"@/*": ["src/*"]}}}])
|
|
79
|
-
assert [i["id"] for i in impact(lk, ["fmt"])["data"]["items"]] == ["src/app.ts"]
|
|
80
|
-
assert impact(lk, ["unused"])["data"]["items"] == []
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
def test_resolve_module_is_the_vaults():
|
|
84
|
-
mods = module_index(["src/a/__init__.py", "src/a/b.py"], [])
|
|
85
|
-
assert resolve_module(mods, "src/a/__init__.py", ".b", "python") == "src/a/b.py"
|
|
86
|
-
assert resolve_module(mods, "x.py", "a.b", "python") == "src/a/b.py"
|
|
87
|
-
assert resolve_module(mods, "x.py", "requests", "python") is None
|
|
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
|