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.
Files changed (482) hide show
  1. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/.github/workflows/test.yml +6 -0
  2. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/CHANGELOG.md +25 -0
  3. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/PKG-INFO +1 -1
  4. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/sdk-reference.json +1 -1
  5. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/sdk_claims.json +1 -1
  6. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/__init__.py +1 -1
  7. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/code_index.py +118 -0
  8. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/code_links.py +60 -10
  9. imperal_sdk-6.5.2/tests/test_code_links.py +170 -0
  10. imperal_sdk-6.5.0/tests/test_code_links.py +0 -87
  11. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/.cm.txt +0 -0
  12. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/.github/workflows/identity-contract.yml +0 -0
  13. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/.github/workflows/publish.yml +0 -0
  14. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/.gitignore +0 -0
  15. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/LICENSE +0 -0
  16. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/README.md +0 -0
  17. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/docs/sdl-facets.md +0 -0
  18. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/pyproject.toml +0 -0
  19. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/sdl_roles.json +0 -0
  20. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/.codebase-index-cache.pkl +0 -0
  21. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/_gateway.py +0 -0
  22. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/_http_retry.py +0 -0
  23. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/_shared_http.py +0 -0
  24. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/adversarial_oracle.py +0 -0
  25. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/ai/__init__.py +0 -0
  26. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/ai/client.py +0 -0
  27. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/app.py +0 -0
  28. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/apps/__init__.py +0 -0
  29. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/apps/client.py +0 -0
  30. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/auth/__init__.py +0 -0
  31. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/auth/client.py +0 -0
  32. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/auth/middleware.py +0 -0
  33. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/auto_ui.py +0 -0
  34. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/billing/__init__.py +0 -0
  35. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/billing/client.py +0 -0
  36. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/bom_extractor.py +0 -0
  37. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/cache/__init__.py +0 -0
  38. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/cache/client.py +0 -0
  39. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/cache/protocol.py +0 -0
  40. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/catalog.py +0 -0
  41. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/chat/__init__.py +0 -0
  42. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/chat/action_result.py +0 -0
  43. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/chat/error_codes.py +0 -0
  44. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/chat/exceptions.py +0 -0
  45. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/chat/extension.py +0 -0
  46. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/chat/filters.py +0 -0
  47. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/chat/guards.py +0 -0
  48. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/chat/kernel_primitives.py +0 -0
  49. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/chat/narration.py +0 -0
  50. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/chat/narration_guard.py +0 -0
  51. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/chat/prompt.py +0 -0
  52. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/chat/refusal.py +0 -0
  53. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/cli/__init__.py +0 -0
  54. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/cli/linter.py +0 -0
  55. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/cli/main.py +0 -0
  56. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/code_langs.py +0 -0
  57. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/config/__init__.py +0 -0
  58. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/config/client.py +0 -0
  59. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/context.py +0 -0
  60. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/conversations/__init__.py +0 -0
  61. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/conversations/client.py +0 -0
  62. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/core.py +0 -0
  63. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/cross_surface_bus.py +0 -0
  64. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/devtools/__init__.py +0 -0
  65. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/devtools/contract_checks.py +0 -0
  66. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/devtools/generate_api_surface.py +0 -0
  67. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/devtools/generate_reference.py +0 -0
  68. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/devtools/generate_sdk_claims.py +0 -0
  69. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/devtools/reference/__init__.py +0 -0
  70. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/devtools/reference/_flags.py +0 -0
  71. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/devtools/reference/_introspect.py +0 -0
  72. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/devtools/reference/client_methods.py +0 -0
  73. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/devtools/reference/dataclasses_kind.py +0 -0
  74. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/devtools/reference/decorators.py +0 -0
  75. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/devtools/reference/sdl_funcs.py +0 -0
  76. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/devtools/reference/sdl_roles.py +0 -0
  77. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/devtools/reference/ui_components.py +0 -0
  78. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/devtools/validate_identity_contract.py +0 -0
  79. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/dwell_guard.py +0 -0
  80. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/errors.py +0 -0
  81. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/extension.py +0 -0
  82. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/extensions/__init__.py +0 -0
  83. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/extensions/client.py +0 -0
  84. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/gnc_guard.py +0 -0
  85. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/http/__init__.py +0 -0
  86. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/http/client.py +0 -0
  87. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/ir/__init__.py +0 -0
  88. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/ir/_impl.py +0 -0
  89. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/ir/actions.py +0 -0
  90. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/ir/custom_roles.py +0 -0
  91. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/ir/migrate.py +0 -0
  92. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/ir/produce.py +0 -0
  93. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/ir/schema.py +0 -0
  94. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/ir/skeleton.py +0 -0
  95. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/ir/ui.py +0 -0
  96. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/ir/ui_children.py +0 -0
  97. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/ir/ui_template.py +0 -0
  98. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/ir/validator.py +0 -0
  99. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/manifest.py +0 -0
  100. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/manifest_schema.py +0 -0
  101. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/manifest_schema_ui.py +0 -0
  102. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/notify/__init__.py +0 -0
  103. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/notify/client.py +0 -0
  104. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/oauth_state.py +0 -0
  105. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/oauth_tokens.py +0 -0
  106. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/pipes.py +0 -0
  107. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/prompts/__init__.py +0 -0
  108. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/prompts/icnli_integrity_rules.txt +0 -0
  109. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/prompts/kernel_formatting_rule.txt +0 -0
  110. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/prompts/kernel_proactivity_rule.txt +0 -0
  111. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/protocols.py +0 -0
  112. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/rbac/__init__.py +0 -0
  113. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/rbac/client.py +0 -0
  114. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/rpc/__init__.py +0 -0
  115. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/rpc/codec.py +0 -0
  116. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/rpc/contract.py +0 -0
  117. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/runtime/__init__.py +0 -0
  118. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/runtime/_platform.py +0 -0
  119. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/runtime/canon.py +0 -0
  120. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/runtime/engine.py +0 -0
  121. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/runtime/executor.py +0 -0
  122. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/runtime/hosted_client.py +0 -0
  123. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/runtime/interpreter.py +0 -0
  124. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/runtime/llm_provider.py +0 -0
  125. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/runtime/local_engine.py +0 -0
  126. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/runtime/message_adapter.py +0 -0
  127. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/runtime/template.py +0 -0
  128. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/runtime/verbs.py +0 -0
  129. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/schema/marathon_ram.sql +0 -0
  130. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/schemas/action_result.schema.json +0 -0
  131. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/schemas/actions/__init__.py +0 -0
  132. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/schemas/actions/ai_complete.json +0 -0
  133. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/schemas/actions/call.json +0 -0
  134. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/schemas/actions/conditional.json +0 -0
  135. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/schemas/actions/navigate.json +0 -0
  136. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/schemas/actions/open.json +0 -0
  137. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/schemas/actions/send.json +0 -0
  138. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/schemas/actions/store_create.json +0 -0
  139. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/schemas/actions/store_delete.json +0 -0
  140. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/schemas/actions/store_get.json +0 -0
  141. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/schemas/actions/store_list.json +0 -0
  142. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/schemas/actions/store_update.json +0 -0
  143. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/schemas/balance_info.schema.json +0 -0
  144. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/schemas/chat_result.schema.json +0 -0
  145. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/schemas/completion_result.schema.json +0 -0
  146. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/schemas/document.schema.json +0 -0
  147. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/schemas/event.schema.json +0 -0
  148. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/schemas/file_info.schema.json +0 -0
  149. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/schemas/function_call.schema.json +0 -0
  150. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/schemas/http_response.schema.json +0 -0
  151. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/schemas/imperal.schema.json +0 -0
  152. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/schemas/ir.schema.json +0 -0
  153. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/schemas/limits_result.schema.json +0 -0
  154. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/schemas/metered_event.schema.json +0 -0
  155. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/schemas/subscription_info.schema.json +0 -0
  156. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/schemas/ui_children/__init__.py +0 -0
  157. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/schemas/ui_children/accordion.json +0 -0
  158. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/schemas/ui_children/datatable_columns.json +0 -0
  159. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/schemas/ui_children/datatable_rows.json +0 -0
  160. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/schemas/ui_children/menu.json +0 -0
  161. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/schemas/ui_children/select.json +0 -0
  162. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/schemas/ui_children/tabs.json +0 -0
  163. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/schemas/ui_children/timeline.json +0 -0
  164. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/schemas/ui_children/tree.json +0 -0
  165. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/sdl/__init__.py +0 -0
  166. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/sdl/_generate_roles_json.py +0 -0
  167. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/sdl/entity.py +0 -0
  168. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/sdl/facet_resolve.py +0 -0
  169. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/sdl/facets/__init__.py +0 -0
  170. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/sdl/facets/catalog.py +0 -0
  171. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/sdl/facets/comm.py +0 -0
  172. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/sdl/facets/content.py +0 -0
  173. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/sdl/facets/device.py +0 -0
  174. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/sdl/facets/event.py +0 -0
  175. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/sdl/facets/geo.py +0 -0
  176. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/sdl/facets/identity.py +0 -0
  177. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/sdl/facets/media.py +0 -0
  178. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/sdl/facets/metric.py +0 -0
  179. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/sdl/facets/money.py +0 -0
  180. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/sdl/facets/net.py +0 -0
  181. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/sdl/facets/people.py +0 -0
  182. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/sdl/facets/quantity.py +0 -0
  183. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/sdl/facets/rating.py +0 -0
  184. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/sdl/facets/security.py +0 -0
  185. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/sdl/facets/task.py +0 -0
  186. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/sdl/facets/time.py +0 -0
  187. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/sdl/field.py +0 -0
  188. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/sdl/roles.py +0 -0
  189. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/search.py +0 -0
  190. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/secrets/__init__.py +0 -0
  191. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/secrets/client.py +0 -0
  192. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/secrets/exceptions.py +0 -0
  193. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/secrets/panel_handler.py +0 -0
  194. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/secrets/spec.py +0 -0
  195. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/security/__init__.py +0 -0
  196. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/security/call_token.py +0 -0
  197. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/security/deactivation_guard.py +0 -0
  198. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/signals/__init__.py +0 -0
  199. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/signals/client.py +0 -0
  200. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/skeleton/__init__.py +0 -0
  201. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/skeleton/client.py +0 -0
  202. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/storage/__init__.py +0 -0
  203. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/storage/client.py +0 -0
  204. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/store/__init__.py +0 -0
  205. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/store/client.py +0 -0
  206. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/store/exceptions.py +0 -0
  207. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/testing/__init__.py +0 -0
  208. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/testing/autonomous_mock.py +0 -0
  209. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/testing/mock_context.py +0 -0
  210. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/testing/mock_secrets.py +0 -0
  211. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/transport.py +0 -0
  212. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/typed_context.py +0 -0
  213. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/types/__init__.py +0 -0
  214. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/types/action_result.py +0 -0
  215. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/types/chat_result.py +0 -0
  216. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/types/client_contracts.py +0 -0
  217. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/types/contracts.py +0 -0
  218. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/types/contributions.py +0 -0
  219. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/types/events.py +0 -0
  220. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/types/health.py +0 -0
  221. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/types/identity.py +0 -0
  222. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/types/metered_event.py +0 -0
  223. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/types/models.py +0 -0
  224. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/types/pagination.py +0 -0
  225. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/types/store_contracts.py +0 -0
  226. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/ui/__init__.py +0 -0
  227. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/ui/actions.py +0 -0
  228. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/ui/base.py +0 -0
  229. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/ui/canvas.py +0 -0
  230. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/ui/data.py +0 -0
  231. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/ui/display.py +0 -0
  232. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/ui/feedback.py +0 -0
  233. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/ui/graph.py +0 -0
  234. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/ui/input_components.py +0 -0
  235. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/ui/interactive.py +0 -0
  236. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/ui/layout.py +0 -0
  237. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/ui/liquid.py +0 -0
  238. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/ui/signals.py +0 -0
  239. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/ui/stage.py +0 -0
  240. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/ui/theme.py +0 -0
  241. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/universal/__init__.py +0 -0
  242. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/universal/image.py +0 -0
  243. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/universal/index.py +0 -0
  244. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/universal/kinds.py +0 -0
  245. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/universal/legacy.py +0 -0
  246. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/universal/mail.py +0 -0
  247. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/universal/office.py +0 -0
  248. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/universal/pdf.py +0 -0
  249. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/universal/textual.py +0 -0
  250. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/universal/view.py +0 -0
  251. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/users/__init__.py +0 -0
  252. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/users/client.py +0 -0
  253. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/validator.py +0 -0
  254. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/validator_v1_6_0.py +0 -0
  255. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/src/imperal_sdk/vault_ram_client.py +0 -0
  256. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/conftest.py +0 -0
  257. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/contract/__init__.py +0 -0
  258. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/contract/test_action_schemas.py +0 -0
  259. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/contract/test_artifacts_freshness.py +0 -0
  260. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/contract/test_contract_checks_selftest.py +0 -0
  261. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/contract/test_generate_sdk_claims.py +0 -0
  262. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/contract/test_identity_nonempty.py +0 -0
  263. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/contract/test_ir_schema_fresh.py +0 -0
  264. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/contract/test_meter_lua_contract.py +0 -0
  265. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/contract/test_metered_event_contract.py +0 -0
  266. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/contract/test_notify_wire_payload.py +0 -0
  267. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/contract/test_public_docstrings_engine_neutral.py +0 -0
  268. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/contract/test_reference_completeness.py +0 -0
  269. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/contract/test_reference_no_leak.py +0 -0
  270. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/contract/test_sample_contract_shape.py +0 -0
  271. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/contract/test_sdk_matches_kernel_contract.py +0 -0
  272. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/contracts/__init__.py +0 -0
  273. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/contracts/test_store_contracts.py +0 -0
  274. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/fixtures/contract/kernel-contract.sample.json +0 -0
  275. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/fixtures/ir/README.md +0 -0
  276. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/fixtures/ir/archive_ended.ir.json +0 -0
  277. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/fixtures/ir/link_saver.ir.json +0 -0
  278. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/fixtures/ir/test_fixtures_valid.py +0 -0
  279. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/fixtures/openapi/auth-gateway.json +0 -0
  280. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/fixtures/openapi/registry.json +0 -0
  281. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/fixtures/openapi/sharelock-cases.json +0 -0
  282. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/fixtures/universal/exif.jpg +0 -0
  283. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/fixtures/universal/invoice.doc +0 -0
  284. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/fixtures/universal/locked.pdf +0 -0
  285. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/fixtures/universal/two_pages.pdf +0 -0
  286. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/fixtures/universal/with_attachment.eml +0 -0
  287. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/ir/__init__.py +0 -0
  288. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/ir/test_custom_roles.py +0 -0
  289. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/ir/test_ir_impl.py +0 -0
  290. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/ir/test_ir_mapping_real_ext.py +0 -0
  291. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/ir/test_ir_migrate.py +0 -0
  292. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/ir/test_ir_produce.py +0 -0
  293. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/ir/test_ir_schema.py +0 -0
  294. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/ir/test_ir_skeleton.py +0 -0
  295. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/ir/test_ir_ui.py +0 -0
  296. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/ir/test_ir_ui_produce.py +0 -0
  297. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/ir/test_ir_validator.py +0 -0
  298. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/ir/test_ir_versions.py +0 -0
  299. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/ir/test_ui_children.py +0 -0
  300. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/ir/test_ui_template.py +0 -0
  301. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/rpc/__init__.py +0 -0
  302. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/rpc/test_boundary_seal.py +0 -0
  303. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/rpc/test_codec.py +0 -0
  304. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/rpc/test_contract.py +0 -0
  305. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/runtime/__init__.py +0 -0
  306. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/runtime/test_canon.py +0 -0
  307. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/runtime/test_canon_template.py +0 -0
  308. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/runtime/test_engine_abc.py +0 -0
  309. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/runtime/test_engine_parity.py +0 -0
  310. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/runtime/test_executor_neutral.py +0 -0
  311. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/runtime/test_hosted_client.py +0 -0
  312. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/runtime/test_interpreter.py +0 -0
  313. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/runtime/test_llm_provider_config_store.py +0 -0
  314. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/runtime/test_llm_provider_ctx_injection.py +0 -0
  315. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/runtime/test_local_engine.py +0 -0
  316. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/runtime/test_platform_shim.py +0 -0
  317. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/runtime/test_template.py +0 -0
  318. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/runtime/test_verb_ai.py +0 -0
  319. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/runtime/test_verb_call_directives.py +0 -0
  320. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/runtime/test_verb_conditional.py +0 -0
  321. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/runtime/test_verb_store.py +0 -0
  322. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/store/__init__.py +0 -0
  323. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/store/test_list_users_client.py +0 -0
  324. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/store/test_query_all_client.py +0 -0
  325. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_action_result_time_travel.py +0 -0
  326. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_action_result_typed.py +0 -0
  327. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_agent_presence.py +0 -0
  328. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_app_engine.py +0 -0
  329. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_as_user.py +0 -0
  330. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_auth.py +0 -0
  331. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_autonomous_mock.py +0 -0
  332. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_billing.py +0 -0
  333. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_cache_client.py +0 -0
  334. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_cache_model.py +0 -0
  335. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_call_token.py +0 -0
  336. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_callable_surface_single_source.py +0 -0
  337. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_chat_extension_deprecation.py +0 -0
  338. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_chat_extension_no_llm_router.py +0 -0
  339. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_chat_filters.py +0 -0
  340. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_chat_function_background_flag.py +0 -0
  341. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_chat_guards.py +0 -0
  342. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_chat_guards_bleed.py +0 -0
  343. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_chat_prompt.py +0 -0
  344. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_chat_result.py +0 -0
  345. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_cli.py +0 -0
  346. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_cli_sdk_commands.py +0 -0
  347. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_client_contracts.py +0 -0
  348. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_client_emit_neutral.py +0 -0
  349. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_code_index_configs.py +0 -0
  350. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_code_index_contracts.py +0 -0
  351. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_code_index_indexer.py +0 -0
  352. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_code_langs.py +0 -0
  353. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_code_web.py +0 -0
  354. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_config_client.py +0 -0
  355. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_context.py +0 -0
  356. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_context_background_task.py +0 -0
  357. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_context_deliver_chat_message.py +0 -0
  358. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_context_guards.py +0 -0
  359. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_contracts.py +0 -0
  360. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_contracts_live.py +0 -0
  361. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_contributions.py +0 -0
  362. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_core_primitives.py +0 -0
  363. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_data_model_kwarg.py +0 -0
  364. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_document_contract.py +0 -0
  365. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_emits_decorator.py +0 -0
  366. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_error_codes.py +0 -0
  367. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_errors.py +0 -0
  368. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_event_schema_v2.py +0 -0
  369. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_events_health.py +0 -0
  370. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_extension.py +0 -0
  371. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_extension_pipes.py +0 -0
  372. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_extension_v2.py +0 -0
  373. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_extensions_emit.py +0 -0
  374. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_gateway_namespaces.py +0 -0
  375. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_http_json_and_mock.py +0 -0
  376. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_http_retry.py +0 -0
  377. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_http_timeout_override.py +0 -0
  378. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_id_shape_guard.py +0 -0
  379. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_identity_contract.py +0 -0
  380. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_imperal_schema_v2.py +0 -0
  381. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_import_light.py +0 -0
  382. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_init_onboarding.py +0 -0
  383. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_kernel_primitives.py +0 -0
  384. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_liquid_ui.py +0 -0
  385. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_manifest.py +0 -0
  386. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_manifest_file_sinks.py +0 -0
  387. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_manifest_linter.py +0 -0
  388. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_manifest_no_orchestrator_tool.py +0 -0
  389. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_manifest_oauth.py +0 -0
  390. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_manifest_panels.py +0 -0
  391. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_manifest_roundtrip_gate.py +0 -0
  392. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_manifest_schema.py +0 -0
  393. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_manifest_v2_events.py +0 -0
  394. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_manifest_v2_other_sections.py +0 -0
  395. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_manifest_v2_webhooks.py +0 -0
  396. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_manifest_validator_v2.py +0 -0
  397. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_mock_context.py +0 -0
  398. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_models.py +0 -0
  399. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_narration_emission.py +0 -0
  400. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_narration_guard.py +0 -0
  401. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_oauth_authorize_url.py +0 -0
  402. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_oauth_tokens_shared.py +0 -0
  403. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_openai_max_completion_tokens.py +0 -0
  404. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_pagination.py +0 -0
  405. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_panel_rendering_contract.py +0 -0
  406. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_panel_swr_caching.py +0 -0
  407. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_panels.py +0 -0
  408. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_phase_a_dead_removal.py +0 -0
  409. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_phase_a_drift.py +0 -0
  410. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_phase_a_text.py +0 -0
  411. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_sdk_version_stamp.py +0 -0
  412. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_sdl_entity.py +0 -0
  413. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_sdl_entity_marker.py +0 -0
  414. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_sdl_exports.py +0 -0
  415. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_sdl_facet_catalog.py +0 -0
  416. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_sdl_facet_collisions.py +0 -0
  417. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_sdl_facet_comm.py +0 -0
  418. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_sdl_facet_content.py +0 -0
  419. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_sdl_facet_device.py +0 -0
  420. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_sdl_facet_event.py +0 -0
  421. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_sdl_facet_exports.py +0 -0
  422. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_sdl_facet_field.py +0 -0
  423. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_sdl_facet_geo.py +0 -0
  424. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_sdl_facet_identity.py +0 -0
  425. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_sdl_facet_media.py +0 -0
  426. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_sdl_facet_metric.py +0 -0
  427. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_sdl_facet_money.py +0 -0
  428. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_sdl_facet_net.py +0 -0
  429. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_sdl_facet_people.py +0 -0
  430. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_sdl_facet_quantity.py +0 -0
  431. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_sdl_facet_rating.py +0 -0
  432. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_sdl_facet_resolve.py +0 -0
  433. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_sdl_facet_security.py +0 -0
  434. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_sdl_facet_task.py +0 -0
  435. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_sdl_facet_time.py +0 -0
  436. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_sdl_facets_catalog.py +0 -0
  437. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_sdl_facets_doc.py +0 -0
  438. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_sdl_facets_pkg.py +0 -0
  439. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_sdl_field.py +0 -0
  440. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_sdl_roles.py +0 -0
  441. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_sdl_roles_json.py +0 -0
  442. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_sdl_roles_of_facets.py +0 -0
  443. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_sdl_vocab_version.py +0 -0
  444. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_search_provider.py +0 -0
  445. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_secret_scope_decorator.py +0 -0
  446. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_secret_spec_scope.py +0 -0
  447. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_skeleton_decorator.py +0 -0
  448. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_spec_validation.py +0 -0
  449. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_stage.py +0 -0
  450. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_surface_adaptation.py +0 -0
  451. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_sync_async_parity.py +0 -0
  452. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_transport_isolation.py +0 -0
  453. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_ui.py +0 -0
  454. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_ui_canvas.py +0 -0
  455. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_ui_fileupload_enhanced.py +0 -0
  456. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_ui_html.py +0 -0
  457. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_ui_image_enhanced.py +0 -0
  458. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_ui_modal.py +0 -0
  459. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_ui_open.py +0 -0
  460. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_ui_signals.py +0 -0
  461. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_ui_theme.py +0 -0
  462. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_universal_code_langs.py +0 -0
  463. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_universal_index.py +0 -0
  464. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_universal_kinds.py +0 -0
  465. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_universal_office.py +0 -0
  466. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_universal_pdf_mail.py +0 -0
  467. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_universal_views_text.py +0 -0
  468. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_user.py +0 -0
  469. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_v7_emit_refusal.py +0 -0
  470. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_validator.py +0 -0
  471. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_validator_drift.py +0 -0
  472. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_validator_pep563.py +0 -0
  473. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_validator_v1_6_0_rules.py +0 -0
  474. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_validator_v25.py +0 -0
  475. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/test_write_arg_bleed.py +0 -0
  476. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/tools/__init__.py +0 -0
  477. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/tools/test_generate_api_surface.py +0 -0
  478. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/tools/test_generate_reference.py +0 -0
  479. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/tools/test_reference_description.py +0 -0
  480. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/tools/test_reference_flags.py +0 -0
  481. {imperal_sdk-6.5.0 → imperal_sdk-6.5.2}/tests/tools/test_reference_typegraph.py +0 -0
  482. {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,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: imperal-sdk
3
- Version: 6.5.0
3
+ Version: 6.5.2
4
4
  Summary: SDK for building Imperal Cloud extensions
5
5
  Author: Imperal, Inc.
6
6
  Author-email: Valentin Scerbacov <valentin@imperal.io>
@@ -1,5 +1,5 @@
1
1
  {
2
- "sdk_version": "6.5.0",
2
+ "sdk_version": "6.5.2",
3
3
  "symbols": {
4
4
  "ActionResult": {
5
5
  "action_vocab_safe": false,
@@ -1,5 +1,5 @@
1
1
  {
2
- "_sdk_version": "6.5.0",
2
+ "_sdk_version": "6.5.2",
3
3
  "constants": {
4
4
  "max_call_depth": {
5
5
  "counts_root": true,
@@ -1,7 +1,7 @@
1
1
  """Imperal Cloud SDK — build extensions for the Imperal platform."""
2
2
  from typing import TYPE_CHECKING
3
3
 
4
- __version__ = "6.5.0"
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
- roots = {""}
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["py"].get(spec)
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 got
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 got
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": [f"{p}::{q}" if q else p for p, q in targets]}
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