digitalkin 1.0.4.dev0__tar.gz → 1.0.4.dev2__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 (263) hide show
  1. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/PKG-INFO +2 -2
  2. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/pyproject.toml +2 -2
  3. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/__version__.py +1 -1
  4. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/community/agno/toolkits/registry/action.py +45 -26
  5. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/community/agno/toolkits/registry/base.py +10 -9
  6. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/community/agno/toolkits/registry/services/action.py +47 -23
  7. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/community/agno/toolkits/registry/services/kit.py +4 -2
  8. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/grpc_servers/module_servicer.py +24 -5
  9. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/services/setup/default_setup.py +63 -26
  10. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/services/setup/grpc_setup.py +37 -26
  11. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/services/setup/setup_strategy.py +32 -12
  12. digitalkin-1.0.4.dev2/src/digitalkin/utils/json_structure.py +169 -0
  13. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/utils/setup_content_validator.py +41 -0
  14. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin.egg-info/PKG-INFO +2 -2
  15. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin.egg-info/requires.txt +1 -1
  16. digitalkin-1.0.4.dev0/src/digitalkin/utils/json_structure.py +0 -363
  17. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/LICENSE +0 -0
  18. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/README.md +0 -0
  19. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/examples/base_server/__init__.py +0 -0
  20. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/examples/base_server/mock/__init__.py +0 -0
  21. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/examples/base_server/mock/mock_pb2.py +0 -0
  22. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/examples/base_server/mock/mock_pb2_grpc.py +0 -0
  23. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/examples/base_server/server_async_insecure.py +0 -0
  24. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/examples/base_server/server_async_secure.py +0 -0
  25. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/examples/base_server/server_sync_insecure.py +0 -0
  26. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/examples/base_server/server_sync_secure.py +0 -0
  27. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/examples/bench_module/__init__.py +0 -0
  28. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/examples/bench_module/echo_module.py +0 -0
  29. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/examples/bench_module/models/__init__.py +0 -0
  30. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/examples/bench_module/models/input.py +0 -0
  31. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/examples/bench_module/models/output.py +0 -0
  32. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/examples/bench_module/models/secret.py +0 -0
  33. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/examples/bench_module/models/setup.py +0 -0
  34. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/examples/bench_module/server.py +0 -0
  35. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/examples/bench_module/triggers/__init__.py +0 -0
  36. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/examples/bench_module/triggers/message_trigger.py +0 -0
  37. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/examples/modules/__init__.py +0 -0
  38. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/examples/modules/archetype_with_tools_module.py +0 -0
  39. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/examples/modules/cpu_intensive_module.py +0 -0
  40. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/examples/modules/dynamic_setup_module.py +0 -0
  41. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/examples/modules/minimal_llm_module.py +0 -0
  42. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/examples/modules/text_transform_module.py +0 -0
  43. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/examples/monitoring/digitalkin_observability/__init__.py +0 -0
  44. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/examples/monitoring/digitalkin_observability/http_server.py +0 -0
  45. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/examples/monitoring/digitalkin_observability/interceptors.py +0 -0
  46. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/examples/monitoring/digitalkin_observability/metrics.py +0 -0
  47. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/examples/monitoring/digitalkin_observability/prometheus.py +0 -0
  48. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/examples/monitoring/tests/test_metrics.py +0 -0
  49. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/examples/redis_demo/client.py +0 -0
  50. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/examples/redis_demo/echo_module.py +0 -0
  51. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/examples/redis_demo/models/__init__.py +0 -0
  52. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/examples/redis_demo/models/input.py +0 -0
  53. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/examples/redis_demo/models/output.py +0 -0
  54. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/examples/redis_demo/models/secret.py +0 -0
  55. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/examples/redis_demo/models/setup.py +0 -0
  56. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/examples/redis_demo/server.py +0 -0
  57. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/examples/redis_demo/triggers/__init__.py +0 -0
  58. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/examples/redis_demo/triggers/message_trigger.py +0 -0
  59. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/examples/services/filesystem_module.py +0 -0
  60. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/examples/services/storage_module.py +0 -0
  61. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/setup.cfg +0 -0
  62. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/__init__.py +0 -0
  63. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/community/__init__.py +0 -0
  64. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/community/agno/__init__.py +0 -0
  65. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/community/agno/agno_adapter.py +0 -0
  66. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/community/agno/agui_tools.py +0 -0
  67. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/community/agno/hitl.py +0 -0
  68. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/community/agno/models.py +0 -0
  69. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/community/agno/module_toolkit.py +0 -0
  70. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/community/agno/toolkits/__init__.py +0 -0
  71. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/community/agno/toolkits/base.py +0 -0
  72. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/community/agno/toolkits/chat_history.py +0 -0
  73. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/community/agno/toolkits/defaults.py +0 -0
  74. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/community/agno/toolkits/registry/__init__.py +0 -0
  75. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/community/agno/toolkits/registry/kins/__init__.py +0 -0
  76. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/community/agno/toolkits/registry/kins/action.py +0 -0
  77. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/community/agno/toolkits/registry/kins/kit.py +0 -0
  78. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/community/agno/toolkits/registry/loader/__init__.py +0 -0
  79. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/community/agno/toolkits/registry/loader/action.py +0 -0
  80. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/community/agno/toolkits/registry/loader/kit.py +0 -0
  81. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/community/agno/toolkits/registry/services/__init__.py +0 -0
  82. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/community/agno/toolkits/registry/tools/__init__.py +0 -0
  83. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/community/agno/toolkits/registry/tools/action.py +0 -0
  84. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/community/agno/toolkits/registry/tools/kit.py +0 -0
  85. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/community/agno/toolkits/user_profile.py +0 -0
  86. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/core/__init__.py +0 -0
  87. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/core/common/__init__.py +0 -0
  88. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/core/common/factories.py +0 -0
  89. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/core/exceptions.py +0 -0
  90. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/core/job_manager/__init__.py +0 -0
  91. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/core/job_manager/base_job_manager.py +0 -0
  92. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/core/job_manager/single_job_manager.py +0 -0
  93. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/core/profiling/__init__.py +0 -0
  94. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/core/profiling/step_timer.py +0 -0
  95. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/core/profiling/task_profiler.py +0 -0
  96. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/core/resilience/__init__.py +0 -0
  97. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/core/resilience/bulkhead.py +0 -0
  98. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/core/resilience/task_supervisor.py +0 -0
  99. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/core/task_manager/__init__.py +0 -0
  100. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/core/task_manager/base_task_manager.py +0 -0
  101. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/core/task_manager/local_task_manager.py +0 -0
  102. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/core/task_manager/module_runner.py +0 -0
  103. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/core/task_manager/redis/__init__.py +0 -0
  104. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/core/task_manager/redis/proto_streams.py +0 -0
  105. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/core/task_manager/redis/redis_client.py +0 -0
  106. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/core/task_manager/redis/redis_idempotency.py +0 -0
  107. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/core/task_manager/redis/redis_signal.py +0 -0
  108. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/core/task_manager/redis/redis_state.py +0 -0
  109. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/core/task_manager/remote_task_manager.py +0 -0
  110. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/core/task_manager/task_executor.py +0 -0
  111. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/core/task_manager/task_session.py +0 -0
  112. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/exceptions.py +0 -0
  113. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/grpc_servers/__init__.py +0 -0
  114. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/grpc_servers/_base_server.py +0 -0
  115. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/grpc_servers/exceptions.py +0 -0
  116. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/grpc_servers/gateway_servicer.py +0 -0
  117. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/grpc_servers/interceptors/__init__.py +0 -0
  118. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/grpc_servers/interceptors/permission.py +0 -0
  119. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/grpc_servers/interceptors/request_ids.py +0 -0
  120. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/grpc_servers/m2m_call_registry.py +0 -0
  121. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/grpc_servers/module_server.py +0 -0
  122. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/grpc_servers/stream_registry.py +0 -0
  123. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/grpc_servers/stream_session.py +0 -0
  124. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/grpc_servers/utils/__init__.py +0 -0
  125. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/grpc_servers/utils/circuit_breaker.py +0 -0
  126. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/grpc_servers/utils/grpc_client_wrapper.py +0 -0
  127. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/grpc_servers/utils/grpc_error_handler.py +0 -0
  128. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/grpc_servers/utils/utility_schema_extender.py +0 -0
  129. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/grpc_servers/utils/validators.py +0 -0
  130. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/logger.py +0 -0
  131. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/mixins/__init__.py +0 -0
  132. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/mixins/agui_mixin.py +0 -0
  133. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/mixins/base_mixin.py +0 -0
  134. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/mixins/cost_mixin.py +0 -0
  135. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/mixins/file_history_mixin.py +0 -0
  136. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/mixins/filesystem_mixin.py +0 -0
  137. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/mixins/logger_mixin.py +0 -0
  138. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/mixins/storage_mixin.py +0 -0
  139. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/models/__init__.py +0 -0
  140. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/models/core/__init__.py +0 -0
  141. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/models/core/job_manager_models.py +0 -0
  142. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/models/core/redis.py +0 -0
  143. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/models/core/task_monitor.py +0 -0
  144. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/models/events/__init__.py +0 -0
  145. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/models/events/agent_events.py +0 -0
  146. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/models/grpc_servers/__init__.py +0 -0
  147. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/models/grpc_servers/circuit_breaker.py +0 -0
  148. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/models/grpc_servers/m2m.py +0 -0
  149. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/models/grpc_servers/models.py +0 -0
  150. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/models/grpc_servers/stream_error_codes.py +0 -0
  151. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/models/grpc_servers/types.py +0 -0
  152. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/models/module/__init__.py +0 -0
  153. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/models/module/ag_ui.py +0 -0
  154. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/models/module/base_types.py +0 -0
  155. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/models/module/loaded_tools.py +0 -0
  156. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/models/module/module.py +0 -0
  157. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/models/module/module_context.py +0 -0
  158. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/models/module/module_types.py +0 -0
  159. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/models/module/request_metadata.py +0 -0
  160. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/models/module/select_schema.py +0 -0
  161. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/models/module/setup_types.py +0 -0
  162. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/models/module/tool_cache.py +0 -0
  163. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/models/module/tool_reference.py +0 -0
  164. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/models/module/utility.py +0 -0
  165. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/models/services/__init__.py +0 -0
  166. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/models/services/cost.py +0 -0
  167. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/models/services/registry.py +0 -0
  168. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/models/services/services.py +0 -0
  169. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/models/services/storage.py +0 -0
  170. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/models/settings/__init__.py +0 -0
  171. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/models/settings/gateway.py +0 -0
  172. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/models/settings/grpc_client.py +0 -0
  173. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/models/settings/log.py +0 -0
  174. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/models/settings/module.py +0 -0
  175. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/models/settings/profiling.py +0 -0
  176. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/models/settings/queue.py +0 -0
  177. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/models/settings/redis.py +0 -0
  178. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/models/settings/registry.py +0 -0
  179. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/models/settings/resilience.py +0 -0
  180. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/models/settings/server/__init__.py +0 -0
  181. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/models/settings/server/channel.py +0 -0
  182. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/models/settings/server/grpc.py +0 -0
  183. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/models/settings/server/server.py +0 -0
  184. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/models/settings/server/servicer.py +0 -0
  185. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/models/settings/task_manager.py +0 -0
  186. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/models/settings/utils/__init__.py +0 -0
  187. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/models/settings/utils/channel.py +0 -0
  188. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/models/utils/__init__.py +0 -0
  189. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/models/utils/dynamic_schema.py +0 -0
  190. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/modules/__init__.py +0 -0
  191. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/modules/_base_module.py +0 -0
  192. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/modules/archetype_module.py +0 -0
  193. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/modules/tool_module.py +0 -0
  194. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/modules/trigger_handler.py +0 -0
  195. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/modules/triggers/__init__.py +0 -0
  196. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/modules/triggers/healthcheck_ping_trigger.py +0 -0
  197. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/modules/triggers/healthcheck_services_trigger.py +0 -0
  198. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/modules/triggers/healthcheck_status_trigger.py +0 -0
  199. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/py.typed +0 -0
  200. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/services/__init__.py +0 -0
  201. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/services/base_strategy.py +0 -0
  202. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/services/communication/__init__.py +0 -0
  203. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/services/communication/communication_strategy.py +0 -0
  204. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/services/communication/default_communication.py +0 -0
  205. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/services/communication/exceptions.py +0 -0
  206. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/services/communication/grpc_communication.py +0 -0
  207. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/services/cost/__init__.py +0 -0
  208. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/services/cost/cost_strategy.py +0 -0
  209. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/services/cost/default_cost.py +0 -0
  210. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/services/cost/exceptions.py +0 -0
  211. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/services/cost/grpc_cost.py +0 -0
  212. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/services/filesystem/__init__.py +0 -0
  213. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/services/filesystem/default_filesystem.py +0 -0
  214. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/services/filesystem/exceptions.py +0 -0
  215. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/services/filesystem/filesystem_strategy.py +0 -0
  216. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/services/filesystem/grpc_filesystem.py +0 -0
  217. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/services/identity/__init__.py +0 -0
  218. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/services/identity/default_identity.py +0 -0
  219. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/services/identity/identity_strategy.py +0 -0
  220. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/services/registry/__init__.py +0 -0
  221. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/services/registry/default_registry.py +0 -0
  222. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/services/registry/exceptions.py +0 -0
  223. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/services/registry/grpc_registry.py +0 -0
  224. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/services/registry/registry_models.py +0 -0
  225. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/services/registry/registry_strategy.py +0 -0
  226. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/services/secret/__init__.py +0 -0
  227. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/services/secret/default_secret.py +0 -0
  228. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/services/secret/exceptions.py +0 -0
  229. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/services/secret/grpc_secret.py +0 -0
  230. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/services/secret/secret_strategy.py +0 -0
  231. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/services/services_config.py +0 -0
  232. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/services/services_models.py +0 -0
  233. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/services/setup/__init__.py +0 -0
  234. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/services/setup/exceptions.py +0 -0
  235. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/services/storage/__init__.py +0 -0
  236. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/services/storage/default_storage.py +0 -0
  237. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/services/storage/exceptions.py +0 -0
  238. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/services/storage/grpc_storage.py +0 -0
  239. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/services/storage/storage_strategy.py +0 -0
  240. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/services/task_manager/__init__.py +0 -0
  241. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/services/task_manager/default_task_manager.py +0 -0
  242. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/services/task_manager/exceptions.py +0 -0
  243. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/services/task_manager/redis_task_manager.py +0 -0
  244. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/services/task_manager/task_manager_strategy.py +0 -0
  245. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/services/user_profile/__init__.py +0 -0
  246. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/services/user_profile/default_user_profile.py +0 -0
  247. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/services/user_profile/exceptions.py +0 -0
  248. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/services/user_profile/grpc_user_profile.py +0 -0
  249. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/services/user_profile/user_profile_strategy.py +0 -0
  250. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/utils/__init__.py +0 -0
  251. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/utils/arg_parser.py +0 -0
  252. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/utils/conditional_schema.py +0 -0
  253. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/utils/development_mode_action.py +0 -0
  254. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/utils/dynamic_schema.py +0 -0
  255. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/utils/exceptions.py +0 -0
  256. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/utils/llm_ready_schema.py +0 -0
  257. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/utils/package_discover.py +0 -0
  258. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/utils/proto_utils.py +0 -0
  259. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin/utils/schema_splitter.py +0 -0
  260. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin.egg-info/SOURCES.txt +0 -0
  261. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin.egg-info/dependency_links.txt +0 -0
  262. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/src/digitalkin.egg-info/top_level.txt +0 -0
  263. {digitalkin-1.0.4.dev0 → digitalkin-1.0.4.dev2}/tests/test_exceptions.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: digitalkin
3
- Version: 1.0.4.dev0
3
+ Version: 1.0.4.dev2
4
4
  Summary: SDK to build kin used in DigitalKin
5
5
  Author-email: "DigitalKin.ai" <contact@digitalkin.ai>
6
6
  License: Attribution-NonCommercial-ShareAlike 4.0 International
@@ -453,7 +453,7 @@ Requires-Python: >=3.10
453
453
  Description-Content-Type: text/markdown
454
454
  License-File: LICENSE
455
455
  Requires-Dist: ag-ui-protocol==0.1.21.dev1787220377
456
- Requires-Dist: agentic-mesh-protocol==1.0.2.dev1
456
+ Requires-Dist: agentic-mesh-protocol==1.0.2.dev2
457
457
  Requires-Dist: agno<3,>=2.8.0
458
458
  Requires-Dist: anyio>=4.13.0
459
459
  Requires-Dist: grpcio-health-checking==1.82.1
@@ -31,7 +31,7 @@
31
31
  # which is how a nested run's output is attributed to its author. Pinned exactly
32
32
  # because it is a dated prerelease; move to >=0.1.21 once that ships stable.
33
33
  "ag-ui-protocol==0.1.21.dev1787220377",
34
- "agentic-mesh-protocol==1.0.2.dev1",
34
+ "agentic-mesh-protocol==1.0.2.dev2",
35
35
  "agno>=2.8.0,<3",
36
36
  "anyio>=4.13.0",
37
37
  "grpcio-health-checking==1.82.1",
@@ -41,7 +41,7 @@
41
41
  "pydantic>=2.12.4",
42
42
  "redis[hiredis]>=7.4.0,<9",
43
43
  ]
44
- version = "1.0.4.dev0"
44
+ version = "1.0.4.dev2"
45
45
 
46
46
  [project.optional-dependencies]
47
47
  agno = [ "agno>=2.6" ]
@@ -5,4 +5,4 @@ from importlib.metadata import PackageNotFoundError, version
5
5
  try:
6
6
  __version__ = version("digitalkin")
7
7
  except PackageNotFoundError:
8
- __version__ = "1.0.4.dev0"
8
+ __version__ = "1.0.4.dev2"
@@ -23,6 +23,7 @@ from digitalkin.models.services.registry import (
23
23
 
24
24
  if TYPE_CHECKING:
25
25
  from digitalkin.community.agno.toolkits.registry.base import RegistryActionCtx
26
+ from digitalkin.models.services.registry import SetupSummary
26
27
 
27
28
 
28
29
  class GetAction(RegistryAction):
@@ -124,26 +125,36 @@ class SearchAction(RegistryAction):
124
125
  # would both contradict ``total_returned`` and, at exactly ``cap`` rows, promise an empty
125
126
  # next page.
126
127
  truncated = len(usable) > cap
127
- rows = [
128
- {
129
- "setup_id": setup.setup_id,
130
- "name": setup.name,
131
- "module_name": setup.module_name,
132
- "version": setup.setup_version,
133
- # Echoed because they are filterable: a caller cannot use the ``tags``,
134
- # ``visibilities`` or ``statuses`` filters without first seeing the values in use.
135
- "tags": setup.tags,
136
- "visibility": setup.visibility.value if setup.visibility else None,
137
- "status": setup.status.value if setup.status else None,
138
- "description": (setup.documentation or "")[: self._DOC_PREVIEW_CHARS],
139
- # The shape of the configuration, so choosing a scope needs no second call:
140
- # read a key from here and pass it to ``load``.
141
- "structure": setup.structure,
142
- }
143
- for setup in usable[:cap]
144
- ]
128
+ rows = [self._row(setup) for setup in usable[:cap]]
145
129
  return {"total_returned": len(rows), "truncated": truncated, "offset": self.offset, "setups": rows}
146
130
 
131
+ def _row(self, setup: SetupSummary) -> dict[str, Any]:
132
+ """Trim one search hit to the fields a caller can act on.
133
+
134
+ Args:
135
+ setup: The summary returned by the registry.
136
+
137
+ Returns:
138
+ The rendered row.
139
+ """
140
+ row: dict[str, Any] = {
141
+ "setup_id": setup.setup_id,
142
+ "name": setup.name,
143
+ "module_name": setup.module_name,
144
+ "version": setup.setup_version,
145
+ # Echoed because they are filterable: a caller cannot use the ``tags``,
146
+ # ``visibilities`` or ``statuses`` filters without first seeing the values in use.
147
+ "tags": setup.tags,
148
+ "visibility": setup.visibility.value if setup.visibility else None,
149
+ "status": setup.status.value if setup.status else None,
150
+ "description": (setup.documentation or "")[: self._DOC_PREVIEW_CHARS],
151
+ }
152
+ # Only service setups carry a map. Omitting the key rather than rendering an empty
153
+ # one keeps the feature out of the tools and kins surfaces entirely.
154
+ if setup.structure:
155
+ row["structure"] = setup.structure
156
+ return row
157
+
147
158
 
148
159
  class UpdateAction(RegistryAction):
149
160
  """Update an instance: cut a new version of its configuration, and rename it.
@@ -169,16 +180,21 @@ class UpdateAction(RegistryAction):
169
180
  "without changing what the instance currently serves, then activate it later with "
170
181
  "``set_version``.",
171
182
  )
172
- structure: dict[str, str] | None = Field(
183
+ documentation: str | None = Field(
173
184
  default=None,
174
- description="Refreshed map of key path -> one-line summary of what lives at it, "
175
- "describing the content this call carries. Same form as on create: summarise what each "
176
- 'key is FOR, join nested keys with "." (llm.model), bracket-quote a key containing '
177
- '. [ ] " or \\ (limits["max.tokens"]), index a list element (tools[0]). Omitting it '
178
- "replaces the stored map with one derived from the raw values, losing every written "
179
- "summary — supply it whenever the instance had one.",
185
+ description="Free text describing what this instance is for, indexed by ``search``. "
186
+ "Omit to keep the text the instance already has; pass a string to replace it, or an "
187
+ "empty string to clear it.",
180
188
  )
181
189
 
190
+ def _type_payload(self) -> dict[str, Any]: # ruff: ignore[no-self-use]
191
+ """Fields this object type adds to the update.
192
+
193
+ Returns:
194
+ Nothing by default; ``services_manager`` overrides it to refresh the structure map.
195
+ """
196
+ return {}
197
+
182
198
  async def execute(self, ctx: RegistryActionCtx) -> Any:
183
199
  """Cut a new version of the setup's content and rename it.
184
200
 
@@ -196,7 +212,10 @@ class UpdateAction(RegistryAction):
196
212
  "name": self.name,
197
213
  "content": self.content,
198
214
  "set_as_current": self.set_as_current,
199
- "structure": self.structure,
215
+ "documentation": (
216
+ setup.current_setup_version.documentation if self.documentation is None else self.documentation
217
+ ),
218
+ **self._type_payload(),
200
219
  })
201
220
 
202
221
 
@@ -171,22 +171,23 @@ class RegistryAction(BaseAction[RegistryActionCtx], ABC):
171
171
 
172
172
  writes: ClassVar[bool] = False
173
173
 
174
- @field_validator("name", check_fields=False)
174
+ @field_validator("name", "documentation", check_fields=False)
175
175
  @classmethod
176
- def _name_has_no_control_chars(cls, value: str) -> str:
177
- """Reject control characters in a user-facing ``name`` so it fails loudly, not silently.
176
+ def _text_has_no_control_chars(cls, value: str | None) -> str | None:
177
+ """Reject control characters in user-facing free text so it fails loudly, not silently.
178
178
 
179
- The action's ``name`` bypasses the content validator, so without this a NUL byte or ANSI
180
- escape would reach persistence and be stripped there, altering the value without telling
181
- the caller. Applies to any action declaring ``name`` (update, service create).
179
+ ``name`` and ``documentation`` bypass the content validator, so without this a NUL byte
180
+ or ANSI escape would reach persistence and be stripped there, altering the value without
181
+ telling the caller. Applies to any action declaring either (update, service create).
182
+ ``None`` is the "leave unchanged" marker on update and has nothing to check.
182
183
 
183
184
  Returns:
184
- The name unchanged when clean.
185
+ The value unchanged when clean.
185
186
 
186
187
  Raises:
187
- ValueError: The name carries a control character.
188
+ ValueError: The value carries a control character.
188
189
  """
189
- return SetupContentValidator.reject_control_chars(value)
190
+ return value if value is None else SetupContentValidator.reject_control_chars(value)
190
191
 
191
192
  @field_validator("content", check_fields=False)
192
193
  @classmethod
@@ -24,7 +24,6 @@ from digitalkin.community.agno.toolkits.registry.action import (
24
24
  UpdateAction,
25
25
  )
26
26
  from digitalkin.community.agno.toolkits.registry.base import RegistryAction
27
- from digitalkin.utils.json_structure import JsonStructure
28
27
 
29
28
  if TYPE_CHECKING:
30
29
  from digitalkin.community.agno.toolkits.registry.base import RegistryActionCtx
@@ -50,6 +49,12 @@ class CreateServiceAction(RegistryAction):
50
49
  description="The service configuration (a non-empty JSON object). "
51
50
  "Note: JSON numbers round-trip as floats over the wire.",
52
51
  )
52
+ documentation: str = Field(
53
+ default="",
54
+ description="Free text describing what this service is for and when to use it. It is "
55
+ "what ``search`` matches on and previews back, so a service created without it is "
56
+ "findable only by name.",
57
+ )
53
58
  structure: dict[str, str] = Field(
54
59
  ...,
55
60
  description="Map of key path -> one-line summary of what lives at it. This is what "
@@ -58,17 +63,19 @@ class CreateServiceAction(RegistryAction):
58
63
  "content it describes. Cover each key worth finding on its own; point at a whole "
59
64
  "section instead when it is only ever read as a unit. Path syntax: join nested keys "
60
65
  'with "." (llm.model); bracket-quote a key containing . [ ] " or \\ '
61
- '(limits["max.tokens"]); index a list element (tools[0]). Paths that do not exist in '
62
- "content are dropped.",
66
+ '(limits["max.tokens"]); index a list element (tools[0]). Keep each description under '
67
+ "512 characters — longer ones are trimmed.",
63
68
  )
64
69
 
65
70
  async def execute(self, ctx: RegistryActionCtx) -> Any:
66
- """Create the service setup from its name, content and authored key map.
71
+ """Create the service setup from its name, content, documentation and authored key map.
67
72
 
68
73
  Returns:
69
74
  The created service setup.
70
75
  """
71
- return await ctx.setup.create_service_setup(self.name, self.content, self.structure)
76
+ return await ctx.setup.create_service_setup(
77
+ self.name, self.content, documentation=self.documentation, structure=self.structure
78
+ )
72
79
 
73
80
 
74
81
  class StructureServiceAction(RegistryAction):
@@ -106,8 +113,9 @@ class LoadServiceAction(RegistryAction):
106
113
  key: str | None = Field(
107
114
  default=None,
108
115
  description="One key path copied verbatim from the service's structure map, to read just "
109
- "that part of the configuration. Omit to load the whole document — do that only when the "
110
- "configuration is small or genuinely needed in full.",
116
+ "that part of the configuration. Copy it, do not compose it: a key the configuration "
117
+ "does not have is not an error, it returns the whole document. Omit the key to load "
118
+ "everything deliberately, when the configuration is small or genuinely needed in full.",
111
119
  )
112
120
 
113
121
  async def execute(self, ctx: RegistryActionCtx) -> Any:
@@ -115,25 +123,41 @@ class LoadServiceAction(RegistryAction):
115
123
 
116
124
  Guards the object type first: without it ``load`` would happily return a tool's
117
125
  internal configuration — the most dangerous type-confusion, since the response
118
- carries no field the caller could use to notice it read the wrong kind. That guard
119
- already reads the whole setup, so the key is applied to the content in hand rather
120
- than costing a second scoped fetch.
126
+ carries no field the caller could use to notice it read the wrong kind. The read
127
+ itself passes ``key`` to the setup service, so the narrowing happens where the
128
+ document lives; a key the configuration does not have returns the whole document
129
+ rather than an error, which is what the wire does.
121
130
 
122
131
  Returns:
123
- The value at ``key``, or the whole configuration JSON object when no key is given.
132
+ ``{key: value}`` for a key that resolves, otherwise the whole configuration.
133
+ """
134
+ await ctx.ensure_kind(self.setup_id)
135
+ setup = await ctx.setup.get_setup({"setup_id": self.setup_id, "structure_key": self.key or ""})
136
+ return setup.current_setup_version.content
137
+
138
+
139
+ class UpdateServiceAction(UpdateAction):
140
+ """Update a service, refreshing the key map alongside the configuration.
141
+
142
+ The map belongs to the content it describes, so a new revision needs a new one; the
143
+ stored map is replaced by whatever this call carries.
144
+ """
145
+
146
+ structure: dict[str, str] | None = Field(
147
+ default=None,
148
+ description="Refreshed map of key path -> one-line description of what lives at that "
149
+ "leaf, describing the content this call carries. Same form and same 512-character limit "
150
+ "as on create. Omitting it leaves the new revision without a map, so other agents can no "
151
+ "longer see the service's shape — resupply it whenever the service had one.",
152
+ )
153
+
154
+ def _type_payload(self) -> dict[str, Any]:
155
+ """Carry the refreshed map onto the new revision.
124
156
 
125
- Raises:
126
- ValueError: ``key`` names a path the configuration does not have.
157
+ Returns:
158
+ The ``structure`` entry for the update payload.
127
159
  """
128
- setup = await ctx.ensure_kind(self.setup_id)
129
- content = setup.current_setup_version.content
130
- if self.key is None:
131
- return content
132
- found = JsonStructure.resolve(content, [self.key])
133
- if self.key not in found:
134
- msg = f"{self.key!r} is not a key of this configuration; copy one from its structure"
135
- raise ValueError(msg)
136
- return found[self.key]
160
+ return {"structure": self.structure}
137
161
 
138
162
 
139
163
  ServiceActions = Annotated[
@@ -142,7 +166,7 @@ ServiceActions = Annotated[
142
166
  | SearchAction
143
167
  | StructureServiceAction
144
168
  | LoadServiceAction
145
- | UpdateAction
169
+ | UpdateServiceAction
146
170
  | DeleteAction
147
171
  | ChangeVisibilityAction
148
172
  | ListVersionsAction
@@ -43,8 +43,10 @@ class ServicesManager(RegistryObjectToolKit):
43
43
  "Configurations can be large, so read them in two steps: 'search' returns each "
44
44
  "result with a 'structure' map of key path -> what lives there ('structure' fetches "
45
45
  "the same map for an id you already hold), then 'load' with a key copied verbatim "
46
- "from it returns just that part. Load without a key only when the whole document is "
47
- "genuinely needed. On create and update you write that map yourself, describing what "
46
+ "from it returns just that part. Copy the key, never invent one: a key the "
47
+ "configuration does not have quietly returns everything. Load without a key only when "
48
+ "the whole document is genuinely needed. On create and update you write that map "
49
+ "yourself, describing what "
48
50
  "each key is for — it is how other agents find their way around the configuration."
49
51
  ),
50
52
  entrypoint=self.services_manager,
@@ -16,6 +16,7 @@ from agentic_mesh_protocol.module.v1 import (
16
16
  )
17
17
  from agentic_mesh_protocol.user_profile.v1 import user_profile_pb2
18
18
  from google.protobuf import json_format, struct_pb2
19
+ from pydantic import ValidationError
19
20
 
20
21
  from digitalkin.core.job_manager.base_job_manager import BaseJobManager
21
22
  from digitalkin.core.job_manager.single_job_manager import SingleJobManager
@@ -393,11 +394,29 @@ class ModuleServicer(module_service_pb2_grpc.ModuleServiceServicer, ArgParser):
393
394
  return lifecycle_pb2.ConfigSetupModuleResponse(success=False)
394
395
  # Invalidate cached setup so concurrent/subsequent starts refetch the reconfigured version
395
396
  self._setup_cache.pop(setup_version.setup_id, None)
396
- config_setup_data = self.module_class.create_config_setup_model(json_format.MessageToDict(request.content))
397
- setup_version_data = await self.module_class.create_setup_model(
398
- json_format.MessageToDict(request.setup_version.content),
399
- config_fields=True,
400
- )
397
+ try:
398
+ config_setup_data = self.module_class.create_config_setup_model(json_format.MessageToDict(request.content))
399
+ setup_version_data = await self.module_class.create_setup_model(
400
+ json_format.MessageToDict(request.setup_version.content),
401
+ config_fields=True,
402
+ )
403
+ except ValidationError as error:
404
+ # Without this the pydantic error escapes the servicer and grpc reports UNKNOWN
405
+ # with a stack trace, so a malformed setup reads as an SDK crash. Name the fields
406
+ # instead: the sender is the only one who can fix them.
407
+ fields = ", ".join(".".join(str(part) for part in item["loc"]) for item in error.errors())
408
+ msg = (
409
+ f"setup version {setup_version.id} does not match the module's setup model (missing/invalid: {fields})"
410
+ )
411
+ logger.error(
412
+ "ConfigSetupModule rejected setup_version=%s: %s",
413
+ setup_version.id,
414
+ error,
415
+ extra={"mission_id": request.mission_id},
416
+ )
417
+ context.set_code(grpc.StatusCode.INVALID_ARGUMENT)
418
+ context.set_details(msg)
419
+ return lifecycle_pb2.ConfigSetupModuleResponse(success=False)
401
420
 
402
421
  if not setup_version_data:
403
422
  msg = "No setup data returned."
@@ -18,6 +18,7 @@ from digitalkin.services.setup.setup_strategy import (
18
18
  SetupVersionPage,
19
19
  )
20
20
  from digitalkin.utils.json_structure import JsonStructure
21
+ from digitalkin.utils.setup_content_validator import SetupContentValidator
21
22
 
22
23
 
23
24
  class DefaultSetup(SetupStrategy):
@@ -70,30 +71,66 @@ class DefaultSetup(SetupStrategy):
70
71
  'structure_key'.
71
72
 
72
73
  Returns:
73
- The setup with its current version populated. With a non-empty
74
- 'structure_key', the version content is replaced by the {key path: value}
75
- projection of that one path — the local mirror of server-side projection.
76
- An empty key is treated as absent, because GetSetupRequest.structure_key has
77
- no proto3 presence and the wire cannot tell the two apart.
74
+ The setup shaped as ``GetSetupResponse`` would carry it — see
75
+ :meth:`_as_wire_read` for the three ways that differs from the stored record.
78
76
 
79
77
  Raises:
80
78
  SetupServiceError: setup_id does not exist.
81
79
  """
82
80
  setup = self._get_or_raise(setup_dict.get("setup_id", ""))
83
- key = setup_dict.get("structure_key")
81
+ return self._as_wire_read(setup, setup_dict.get("structure_key") or "")
82
+
83
+ @classmethod
84
+ def _as_wire_read(cls, setup: SetupData, key: str) -> SetupData:
85
+ """Copy a stored setup down to what a read can actually return.
86
+
87
+ Two ways a read differs from the record, both forced by the protocol:
88
+ the content is projected to ``key``, and the structure map is dropped because
89
+ ``GetSetupResponse`` has no field to carry it. Mirroring the second matters as much
90
+ as the first — serving a map here that the gRPC strategy cannot would let code work
91
+ locally and lose the map in production.
92
+
93
+ Args:
94
+ setup: The stored setup.
95
+ key: The requested key path; empty means the whole document.
96
+
97
+ Returns:
98
+ A copy safe to hand back; the stored record is untouched.
99
+ """
100
+ version = setup.current_setup_version
101
+ return setup.model_copy(
102
+ update={
103
+ "current_setup_version": version.model_copy(
104
+ update={"content": cls._project(version.content, key), "structure": {}}
105
+ )
106
+ }
107
+ )
108
+
109
+ @staticmethod
110
+ def _project(content: dict[str, Any], key: str) -> dict[str, Any]:
111
+ """Narrow a version's content to one key path.
112
+
113
+ Args:
114
+ content: The stored content.
115
+ key: The requested key path; empty means the whole document.
116
+
117
+ Returns:
118
+ ``{key: value}`` when the path resolves, otherwise the whole document — an
119
+ empty key and an unresolvable one both return everything, which is what the
120
+ backend sends and what ``structure_key``'s lack of proto3 presence forces.
121
+ """
84
122
  if not key:
85
- return setup
86
- projected = setup.model_copy(deep=True)
87
- projected.current_setup_version.content = JsonStructure.resolve(setup.current_setup_version.content, [key])
88
- return projected
123
+ return content
124
+ return JsonStructure.resolve(content, [key]) or content
89
125
 
90
126
  async def create_setup(self, setup_dict: dict[str, Any]) -> SetupData:
91
127
  """Create a new setup; identifiers are generated locally.
92
128
 
93
129
  Args:
94
- setup_dict: Dictionary with 'name', 'content' and optional 'structure' — the
95
- authored ``{key path: summary}`` map, filtered to the paths that resolve
96
- in ``content``. Absent, it is derived from ``content``.
130
+ setup_dict: Dictionary with 'name', 'content', optional 'documentation' and
131
+ optional 'structure' — the ``{key path: description}`` map the agent
132
+ wrote for ``content``, stored as written with only each description's
133
+ length bounded.
97
134
 
98
135
  Returns:
99
136
  The created setup with its initial version.
@@ -101,6 +138,7 @@ class DefaultSetup(SetupStrategy):
101
138
  Raises:
102
139
  ValueError: If name or content is invalid.
103
140
  """
141
+ SetupContentValidator.reject_oversized_output_format_spec(setup_dict.get("content") or {})
104
142
  setup_id = self._new_id()
105
143
  try:
106
144
  setup = SetupData(
@@ -115,12 +153,9 @@ class DefaultSetup(SetupStrategy):
115
153
  id=self._new_id(),
116
154
  setup_id=setup_id,
117
155
  version="1.0.0",
156
+ documentation=setup_dict.get("documentation") or "",
118
157
  content=setup_dict.get("content") or {},
119
- structure=(
120
- JsonStructure.check(setup_dict.get("content") or {}, setup_dict["structure"])
121
- if setup_dict.get("structure")
122
- else JsonStructure.describe(setup_dict.get("content") or {})
123
- ),
158
+ structure=JsonStructure.clip(setup_dict.get("structure") or {}),
124
159
  creation_date=datetime.datetime.now(datetime.timezone.utc),
125
160
  ),
126
161
  )
@@ -141,9 +176,11 @@ class DefaultSetup(SetupStrategy):
141
176
 
142
177
  Args:
143
178
  setup_dict: Dictionary with 'setup_id', 'name', 'content', optional
144
- 'set_as_current' (defaults to True) and optional 'structure' — the authored
145
- ``{key path: summary}`` map for the new content. Omitting it falls back to
146
- a derived map, discarding any authored summaries.
179
+ 'set_as_current' (defaults to True), optional 'documentation' and optional
180
+ 'structure'. The map belongs to the content it describes, so a revision
181
+ carries only the map its own call supplied; omitting it leaves the new
182
+ revision without one. Omitting the documentation clears
183
+ it, matching the wire.
147
184
 
148
185
  Returns:
149
186
  The updated setup with its current version.
@@ -158,6 +195,7 @@ class DefaultSetup(SetupStrategy):
158
195
  if not name or not isinstance(content, dict):
159
196
  msg = "setup_id, name and content (object) are required"
160
197
  raise ValueError(msg)
198
+ SetupContentValidator.reject_oversized_output_format_spec(content)
161
199
  setup.name = name
162
200
  # A new revision rather than an in-place edit, matching UpdateSetup on the wire.
163
201
  history = self.versions.setdefault(setup.id, [setup.current_setup_version])
@@ -165,12 +203,11 @@ class DefaultSetup(SetupStrategy):
165
203
  id=self._new_id(),
166
204
  setup_id=setup.id,
167
205
  version=f"1.0.{len(history)}",
206
+ # No presence on UpdateSetupRequest, so an omitted value clears it server-side.
207
+ # Mirror that rather than preserving the old text: same input, same result.
208
+ documentation=setup_dict.get("documentation") or "",
168
209
  content=content,
169
- structure=(
170
- JsonStructure.check(content, setup_dict["structure"])
171
- if setup_dict.get("structure")
172
- else JsonStructure.describe(content)
173
- ),
210
+ structure=JsonStructure.clip(setup_dict.get("structure") or {}),
174
211
  creation_date=datetime.datetime.now(datetime.timezone.utc),
175
212
  )
176
213
  history.append(version)
@@ -20,6 +20,7 @@ from digitalkin.services.setup.exceptions import SetupServiceError
20
20
  from digitalkin.services.setup.setup_strategy import SetupData, SetupStrategy, SetupVersionData, SetupVersionPage
21
21
  from digitalkin.utils.json_structure import JsonStructure
22
22
  from digitalkin.utils.proto_utils import ProtoUtils
23
+ from digitalkin.utils.setup_content_validator import SetupContentValidator
23
24
 
24
25
 
25
26
  class GrpcSetup(SetupStrategy, GrpcClientWrapper):
@@ -133,15 +134,30 @@ class GrpcSetup(SetupStrategy, GrpcClientWrapper):
133
134
  The validated ``SetupData``.
134
135
 
135
136
  Raises:
136
- SetupServiceError: If neither carries a setup version.
137
+ SetupServiceError: Neither field carries a setup version, or the one that does
138
+ carries no content.
137
139
  """
138
- if setup_msg.HasField("current_setup_version"):
139
- version_msg = setup_msg.current_setup_version
140
- elif not version_msg.id:
140
+ embedded = setup_msg.current_setup_version if setup_msg.HasField("current_setup_version") else None
141
+ chosen = version_msg if embedded is None else embedded
142
+ if not chosen.id:
141
143
  msg = f"setup '{setup_msg.id}' returned without a setup version"
142
144
  raise SetupServiceError(msg)
143
145
  data = ProtoUtils.proto_to_dict(setup_msg, with_defaults=True)
144
- data["current_setup_version"] = ProtoUtils.proto_to_dict(version_msg, with_defaults=True)
146
+ version = ProtoUtils.proto_to_dict(chosen, with_defaults=True)
147
+ # An unset content Struct is dropped by proto_to_dict rather than rendered as {},
148
+ # so SetupData would fail with a bare "Field required" naming neither the setup nor
149
+ # which of the response's two SetupVersion fields was read. Say both instead: the
150
+ # message carries the version twice and only one of them may hold the payload.
151
+ if "content" not in version:
152
+ source = "setup.current_setup_version" if embedded is not None else "setup_version"
153
+ other = version_msg if embedded is not None else None
154
+ sibling = "not populated" if other is None else ("carries content" if other.content.fields else "empty too")
155
+ msg = (
156
+ f"setup '{setup_msg.id}' version '{chosen.id}' arrived with no content "
157
+ f"(read from {source}; sibling setup_version {sibling})"
158
+ )
159
+ raise SetupServiceError(msg)
160
+ data["current_setup_version"] = version
145
161
  if structure is not None:
146
162
  data["current_setup_version"]["structure"] = dict(structure)
147
163
  return SetupData(**data)
@@ -183,34 +199,33 @@ class GrpcSetup(SetupStrategy, GrpcClientWrapper):
183
199
 
184
200
  Args:
185
201
  setup_dict: Dictionary with 'name', 'content', optional 'documentation' and
186
- optional 'structure' — the authored ``{key path: summary}`` map, filtered
187
- to the paths that resolve in ``content``. Absent, it is derived from
188
- ``content`` so a writer with no author still stores one.
202
+ optional 'structure' — the ``{key path: description}`` map the agent
203
+ wrote for ``content``, stored as written with only each description's
204
+ length bounded.
189
205
 
190
206
  Returns:
191
207
  The created setup with its initial version and structure.
192
208
 
193
209
  Raises:
194
- ValueError: If name or content is missing.
210
+ ValueError: If name or content is missing, or output_format_spec is oversized.
195
211
  ServerError: If gRPC operation fails.
196
212
  SetupServiceError: If the server reports failure or an unexpected error occurs.
197
213
  """
198
214
  if not setup_dict.get("name") or not isinstance(setup_dict.get("content"), dict):
199
215
  msg = "name and content (object) are required"
200
216
  raise ValueError(msg)
217
+ # Outside handle_grpc_errors: an input guard must stay a ValueError, not become a
218
+ # SetupServiceError via the catch-all.
219
+ SetupContentValidator.reject_oversized_output_format_spec(setup_dict["content"])
201
220
  async with self.handle_grpc_errors("Setup Creation"):
202
221
  content_struct = Struct()
203
222
  content_struct.update(setup_dict["content"])
204
223
  structure_struct = Struct()
205
- structure_struct.update(
206
- JsonStructure.check(setup_dict["content"], setup_dict["structure"])
207
- if setup_dict.get("structure")
208
- else JsonStructure.describe(setup_dict["content"])
209
- )
224
+ structure_struct.update(JsonStructure.clip(setup_dict.get("structure") or {}))
210
225
  request = setup_pb2.CreateSetupRequest(
211
226
  name=setup_dict["name"],
212
227
  content=content_struct,
213
- documentation=setup_dict.get("documentation", ""),
228
+ documentation=setup_dict.get("documentation") or "",
214
229
  structure=structure_struct,
215
230
  )
216
231
  response = await self.exec_grpc_query("CreateSetup", request)
@@ -226,16 +241,15 @@ class GrpcSetup(SetupStrategy, GrpcClientWrapper):
226
241
  Args:
227
242
  setup_dict: Dictionary with 'setup_id', 'name', 'content', optional
228
243
  'set_as_current' (defaults to True), optional 'documentation' and optional
229
- 'structure' — the authored ``{key path: summary}`` map for the new content.
230
- It is always rewritten, so it cannot drift from what is stored; omitting it
231
- falls back to a derived map, which discards any authored summaries the
232
- previous version carried.
244
+ 'structure'. The map belongs to the content it describes, so a revision
245
+ carries only the map its own call supplied; omitting it leaves the new
246
+ revision without one.
233
247
 
234
248
  Returns:
235
249
  The updated setup with its current version and structure.
236
250
 
237
251
  Raises:
238
- ValueError: If setup_id, name or content is missing.
252
+ ValueError: If setup_id, name or content is missing, or output_format_spec is oversized.
239
253
  ServerError: If gRPC operation fails.
240
254
  SetupServiceError: If the server reports failure or an unexpected error occurs.
241
255
  """
@@ -246,15 +260,12 @@ class GrpcSetup(SetupStrategy, GrpcClientWrapper):
246
260
  ):
247
261
  msg = "setup_id, name and content (object) are required"
248
262
  raise ValueError(msg)
263
+ SetupContentValidator.reject_oversized_output_format_spec(setup_dict["content"])
249
264
  async with self.handle_grpc_errors("Setup Update"):
250
265
  content_struct = Struct()
251
266
  content_struct.update(setup_dict["content"])
252
267
  structure_struct = Struct()
253
- structure_struct.update(
254
- JsonStructure.check(setup_dict["content"], setup_dict["structure"])
255
- if setup_dict.get("structure")
256
- else JsonStructure.describe(setup_dict["content"])
257
- )
268
+ structure_struct.update(JsonStructure.clip(setup_dict.get("structure") or {}))
258
269
  # UpdateSetup cuts a new version rather than editing in place; without
259
270
  # set_as_current the setup would keep serving the old content.
260
271
  request = setup_pb2.UpdateSetupRequest(
@@ -262,7 +273,7 @@ class GrpcSetup(SetupStrategy, GrpcClientWrapper):
262
273
  name=setup_dict["name"],
263
274
  content=content_struct,
264
275
  set_as_current=bool(setup_dict.get("set_as_current", True)),
265
- documentation=setup_dict.get("documentation", ""),
276
+ documentation=setup_dict.get("documentation") or "",
266
277
  structure=structure_struct,
267
278
  )
268
279
  response = await self.exec_grpc_query("UpdateSetup", request)