agentnode-sdk 0.14.0__tar.gz → 0.15.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (191) hide show
  1. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/CHANGELOG.md +44 -0
  2. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/PKG-INFO +1 -1
  3. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/agentnode_sdk/__init__.py +1 -1
  4. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/agentnode_sdk/config.py +9 -1
  5. agentnode_sdk-0.15.0/agentnode_sdk/llm_providers.py +116 -0
  6. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/agentnode_sdk/runtimes/agent_runner.py +65 -44
  7. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/pyproject.toml +1 -1
  8. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/tests/test_config.py +31 -0
  9. agentnode_sdk-0.15.0/tests/test_llm_providers.py +79 -0
  10. agentnode_sdk-0.15.0/tests/test_llm_vault_binding.py +306 -0
  11. agentnode_sdk-0.14.0/tests/test_llm_vault_binding.py +0 -143
  12. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/.env.example +0 -0
  13. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/.gitignore +0 -0
  14. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/README.md +0 -0
  15. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/REGISTRY_SIGNING_ACTIVATION.md +0 -0
  16. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/REGISTRY_SIGNING_SPEC.md +0 -0
  17. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/THREAT_MODEL.md +0 -0
  18. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/TRUST_STACK.md +0 -0
  19. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/agentnode.lock +0 -0
  20. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/agentnode_sdk/_fileutil.py +0 -0
  21. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/agentnode_sdk/async_client.py +0 -0
  22. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/agentnode_sdk/capability_graph.py +0 -0
  23. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/agentnode_sdk/capability_taxonomy.py +0 -0
  24. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/agentnode_sdk/cli/__init__.py +0 -0
  25. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/agentnode_sdk/cli/__main__.py +0 -0
  26. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/agentnode_sdk/cli/audit.py +0 -0
  27. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/agentnode_sdk/cli/auth.py +0 -0
  28. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/agentnode_sdk/cli/cassette_audit.py +0 -0
  29. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/agentnode_sdk/cli/commands.py +0 -0
  30. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/agentnode_sdk/cli/complements.py +0 -0
  31. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/agentnode_sdk/cli/init.py +0 -0
  32. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/agentnode_sdk/cli/main.py +0 -0
  33. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/agentnode_sdk/cli/mcp_commands.py +0 -0
  34. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/agentnode_sdk/cli/mcp_status.py +0 -0
  35. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/agentnode_sdk/cli/mcp_submit.py +0 -0
  36. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/agentnode_sdk/cli/mcp_verify.py +0 -0
  37. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/agentnode_sdk/cli/output.py +0 -0
  38. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/agentnode_sdk/cli/publish.py +0 -0
  39. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/agentnode_sdk/cli/record_cases.py +0 -0
  40. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/agentnode_sdk/cli/sandbox_commands.py +0 -0
  41. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/agentnode_sdk/cli/serve.py +0 -0
  42. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/agentnode_sdk/cli/setup_wizard.py +0 -0
  43. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/agentnode_sdk/cli/smart_run.py +0 -0
  44. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/agentnode_sdk/cli/templates.py +0 -0
  45. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/agentnode_sdk/cli/validate.py +0 -0
  46. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/agentnode_sdk/cli/verify_local.py +0 -0
  47. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/agentnode_sdk/client.py +0 -0
  48. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/agentnode_sdk/compatibility.py +0 -0
  49. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/agentnode_sdk/credential_handle.py +0 -0
  50. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/agentnode_sdk/credential_resolver.py +0 -0
  51. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/agentnode_sdk/credential_store.py +0 -0
  52. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/agentnode_sdk/detect.py +0 -0
  53. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/agentnode_sdk/exceptions.py +0 -0
  54. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/agentnode_sdk/guard.py +0 -0
  55. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/agentnode_sdk/input_guard.py +0 -0
  56. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/agentnode_sdk/installer.py +0 -0
  57. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/agentnode_sdk/key_status.py +0 -0
  58. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/agentnode_sdk/lock_integrity.py +0 -0
  59. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/agentnode_sdk/mcp_server.py +0 -0
  60. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/agentnode_sdk/models.py +0 -0
  61. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/agentnode_sdk/planner.py +0 -0
  62. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/agentnode_sdk/policy.py +0 -0
  63. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/agentnode_sdk/references.py +0 -0
  64. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/agentnode_sdk/registry_trust.py +0 -0
  65. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/agentnode_sdk/resolve.py +0 -0
  66. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/agentnode_sdk/resource_provider.py +0 -0
  67. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/agentnode_sdk/risk_profile.py +0 -0
  68. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/agentnode_sdk/run_log.py +0 -0
  69. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/agentnode_sdk/runner.py +0 -0
  70. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/agentnode_sdk/runtime.py +0 -0
  71. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/agentnode_sdk/runtimes/__init__.py +0 -0
  72. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/agentnode_sdk/runtimes/agent_llm_broker.py +0 -0
  73. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/agentnode_sdk/runtimes/agent_llm_policy.py +0 -0
  74. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/agentnode_sdk/runtimes/agent_sandbox.py +0 -0
  75. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/agentnode_sdk/runtimes/mcp_runner.py +0 -0
  76. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/agentnode_sdk/runtimes/python_runner.py +0 -0
  77. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/agentnode_sdk/runtimes/remote_runner.py +0 -0
  78. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/agentnode_sdk/sandbox/__init__.py +0 -0
  79. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/agentnode_sdk/sandbox/agent_container_wrapper.py +0 -0
  80. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/agentnode_sdk/sandbox/agent_rpc.py +0 -0
  81. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/agentnode_sdk/sandbox/agent_session.py +0 -0
  82. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/agentnode_sdk/sandbox/backend.py +0 -0
  83. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/agentnode_sdk/sandbox/container_backend.py +0 -0
  84. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/agentnode_sdk/sandbox/policy.py +0 -0
  85. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/agentnode_sdk/sandbox/types.py +0 -0
  86. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/agentnode_sdk/signature.py +0 -0
  87. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/agentnode_sdk/signing_key.py +0 -0
  88. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/agentnode_sdk/skill.py +0 -0
  89. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/sandbox-image/Dockerfile +0 -0
  90. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/sandbox-image/README.md +0 -0
  91. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/scripts/analyze_scores.py +0 -0
  92. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/scripts/batch_verify.py +0 -0
  93. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/scripts/ci_smoke_test.py +0 -0
  94. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/scripts/generate_compatibility_artifacts.py +0 -0
  95. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/scripts/verify_toolcalls.py +0 -0
  96. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/scripts/weekly_retest.sh +0 -0
  97. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/spikes/agent_sandbox_routing/README.md +0 -0
  98. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/spikes/agent_sandbox_routing/__init__.py +0 -0
  99. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/spikes/agent_sandbox_routing/container_agent_wrapper.py +0 -0
  100. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/spikes/agent_sandbox_routing/fake_llm.py +0 -0
  101. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/spikes/agent_sandbox_routing/host_driver.py +0 -0
  102. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/spikes/agent_sandbox_routing/trivial_agent.py +0 -0
  103. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/tests/__init__.py +0 -0
  104. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/tests/conftest.py +0 -0
  105. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/tests/test_agent_llm_broker.py +0 -0
  106. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/tests/test_agent_llm_policy.py +0 -0
  107. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/tests/test_agent_rpc.py +0 -0
  108. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/tests/test_agent_runner.py +0 -0
  109. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/tests/test_agent_sandbox_e2e.py +0 -0
  110. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/tests/test_agent_sandbox_routing.py +0 -0
  111. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/tests/test_agent_sandbox_spike.py +0 -0
  112. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/tests/test_agent_session_container.py +0 -0
  113. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/tests/test_async_client.py +0 -0
  114. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/tests/test_audit_ux.py +0 -0
  115. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/tests/test_auth_cli_vault.py +0 -0
  116. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/tests/test_auto_upgrade_policy.py +0 -0
  117. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/tests/test_cli.py +0 -0
  118. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/tests/test_cli_lock.py +0 -0
  119. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/tests/test_cli_run_resolution.py +0 -0
  120. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/tests/test_client.py +0 -0
  121. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/tests/test_client_json_guard.py +0 -0
  122. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/tests/test_client_sprint_b.py +0 -0
  123. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/tests/test_credential_handle.py +0 -0
  124. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/tests/test_credential_integration.py +0 -0
  125. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/tests/test_credential_resolver.py +0 -0
  126. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/tests/test_credential_store.py +0 -0
  127. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/tests/test_credential_vault.py +0 -0
  128. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/tests/test_detect.py +0 -0
  129. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/tests/test_detect_and_install.py +0 -0
  130. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/tests/test_e2e_runtime.py +0 -0
  131. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/tests/test_edge_cases.py +0 -0
  132. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/tests/test_guard.py +0 -0
  133. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/tests/test_guard_check.py +0 -0
  134. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/tests/test_guard_config_cache.py +0 -0
  135. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/tests/test_guard_policy.py +0 -0
  136. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/tests/test_guard_preview.py +0 -0
  137. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/tests/test_guard_schema.py +0 -0
  138. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/tests/test_guard_set.py +0 -0
  139. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/tests/test_guard_status.py +0 -0
  140. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/tests/test_guard_tool_override.py +0 -0
  141. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/tests/test_guard_tool_override_audit.py +0 -0
  142. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/tests/test_guard_tool_override_cli.py +0 -0
  143. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/tests/test_guard_ux.py +0 -0
  144. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/tests/test_input_guard.py +0 -0
  145. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/tests/test_input_guard_escalation.py +0 -0
  146. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/tests/test_install_hardening.py +0 -0
  147. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/tests/test_installer_sprint_b.py +0 -0
  148. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/tests/test_intelligence.py +0 -0
  149. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/tests/test_key_status.py +0 -0
  150. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/tests/test_llm_binding.py +0 -0
  151. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/tests/test_llm_call_runlog.py +0 -0
  152. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/tests/test_lock_integrity.py +0 -0
  153. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/tests/test_lock_runtime.py +0 -0
  154. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/tests/test_mcp_audit.py +0 -0
  155. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/tests/test_mcp_doctor.py +0 -0
  156. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/tests/test_mcp_sandbox.py +0 -0
  157. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/tests/test_mcp_server.py +0 -0
  158. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/tests/test_observability.py +0 -0
  159. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/tests/test_planner.py +0 -0
  160. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/tests/test_policy.py +0 -0
  161. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/tests/test_policy_integration.py +0 -0
  162. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/tests/test_prompt_specs.py +0 -0
  163. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/tests/test_provider_matrix.py +0 -0
  164. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/tests/test_publish.py +0 -0
  165. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/tests/test_references.py +0 -0
  166. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/tests/test_registry_trust.py +0 -0
  167. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/tests/test_remote_hardening.py +0 -0
  168. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/tests/test_remote_runner.py +0 -0
  169. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/tests/test_resource_provider.py +0 -0
  170. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/tests/test_resource_specs.py +0 -0
  171. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/tests/test_risk_profile.py +0 -0
  172. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/tests/test_run_log.py +0 -0
  173. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/tests/test_runner.py +0 -0
  174. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/tests/test_runtime.py +0 -0
  175. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/tests/test_runtime_audit.py +0 -0
  176. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/tests/test_sandbox_backend.py +0 -0
  177. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/tests/test_sandbox_doctor.py +0 -0
  178. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/tests/test_sandbox_e2e.py +0 -0
  179. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/tests/test_sandbox_gate.py +0 -0
  180. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/tests/test_security_hardening.py +0 -0
  181. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/tests/test_setup_wizard.py +0 -0
  182. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/tests/test_signature.py +0 -0
  183. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/tests/test_signing_key.py +0 -0
  184. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/tests/test_skill.py +0 -0
  185. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/tests/test_smart.py +0 -0
  186. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/tests/test_stability.py +0 -0
  187. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/tests/test_toolpack_sandbox.py +0 -0
  188. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/tests/test_v02.py +0 -0
  189. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/tests/test_validate.py +0 -0
  190. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/tests/test_validate_skill.py +0 -0
  191. {agentnode_sdk-0.14.0 → agentnode_sdk-0.15.0}/tests/validation_lockfile.json +0 -0
@@ -1,5 +1,49 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.15.0 — Generic OpenAI-compatible LLM providers
4
+
5
+ ### Added
6
+
7
+ - **Generic OpenAI-compatible provider registry.** The LLM runtime can now
8
+ bind any endpoint that speaks the OpenAI-compatible protocol. Custom
9
+ endpoints are plain config entries — `llm.providers.<name>` with
10
+ `base_url`, `model`, and optionally `api_key_env`/`requires_key` — no code
11
+ changes needed per provider.
12
+ - **Built-in presets** for OpenRouter, DeepSeek, Mistral, Qwen, Gemini
13
+ (Google's OpenAI-compatible endpoint) and Ollama, each with the canonical
14
+ base URL, API-key environment variable, and a sensible default model
15
+ (overridable via `llm.providers.<name>.model`).
16
+ - **Ollama as a key-free local provider** — the first agent path with no
17
+ account and no per-token cost. Opt-in only: select it explicitly with
18
+ `agentnode config set llm.default_provider ollama` (or configure it under
19
+ `llm.providers`); AgentNode never probes or binds it by surprise.
20
+ - Per-provider credentials: each provider's environment variable (e.g.
21
+ `DEEPSEEK_API_KEY`, `GEMINI_API_KEY`) and stored vault credentials both
22
+ work; the environment always overrides the stored key.
23
+
24
+ ### Changed
25
+
26
+ - The OpenRouter binding now goes through the shared provider registry
27
+ (same behavior, including the namespaced default model — one code path
28
+ instead of a special case).
29
+ - The sandboxed-agent LLM broker automatically inherits every compatible
30
+ provider: keys stay host-side, the container still sees only RPC
31
+ completions, and the C1/C2 access policy applies unchanged.
32
+
33
+ ### Hardened
34
+
35
+ - No provider keys in logs or error messages (client failures are reported
36
+ by exception type only).
37
+ - A custom provider without a configured model is skipped instead of
38
+ guessing — a wrong default model would fail confusingly at call time.
39
+ - Keyless providers are never attempted unless explicitly selected or
40
+ configured.
41
+
42
+ ### BREAKING / Upgrade Notes
43
+
44
+ - None. Existing OpenAI/Anthropic setups (env vars, vault, per-agent config)
45
+ behave identically; the new providers are purely additive.
46
+
3
47
  ## 0.14.0 — Setup wizard: guided credentials + sandbox onboarding
4
48
 
5
49
  ### Added
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: agentnode-sdk
3
- Version: 0.14.0
3
+ Version: 0.15.0
4
4
  Summary: Python SDK for AgentNode — the open upgrade and discovery infrastructure for AI agents.
5
5
  Project-URL: Homepage, https://agentnode.net
6
6
  Project-URL: Repository, https://github.com/agentnode-ai/agentnode
@@ -38,7 +38,7 @@ from agentnode_sdk.runtime import AgentNodeRuntime
38
38
  Client = AgentNodeClient
39
39
  ToolError = AgentNodeToolError
40
40
 
41
- __version__ = "0.14.0"
41
+ __version__ = "0.15.0"
42
42
  __all__ = [
43
43
  "AgentNode",
44
44
  "AsyncAgentNode",
@@ -46,6 +46,7 @@ DEFAULTS: dict[str, Any] = {
46
46
  },
47
47
  "llm": {
48
48
  "default_provider": "openai",
49
+ "providers": {},
49
50
  },
50
51
  }
51
52
 
@@ -68,7 +69,10 @@ VALID_VALUES: dict[str, tuple[str, ...]] = {
68
69
  "guard.compute": ("allow", "prompt", "deny"),
69
70
  "guard.unknown": ("allow", "prompt", "deny"),
70
71
  "agent_sandbox.enabled": ("true", "false"),
71
- "llm.default_provider": ("openai", "anthropic", "openrouter"),
72
+ "llm.default_provider": (
73
+ "openai", "anthropic", "openrouter", "deepseek", "mistral", "qwen",
74
+ "gemini", "ollama",
75
+ ),
72
76
  }
73
77
 
74
78
 
@@ -160,6 +164,10 @@ def _merge_defaults(data: dict) -> dict[str, Any]:
160
164
  for k in ("default_provider",):
161
165
  if k in data["llm"]:
162
166
  cfg["llm"][k] = data["llm"][k]
167
+ # llm.providers holds nested endpoint specs (base_url/model/...) —
168
+ # pass through verbatim, like agent_sandbox.llm above.
169
+ if isinstance(data["llm"].get("providers"), dict):
170
+ cfg["llm"]["providers"] = data["llm"]["providers"]
163
171
  return cfg
164
172
 
165
173
 
@@ -0,0 +1,116 @@
1
+ """OpenAI-compatible LLM provider registry (Endpoint-A).
2
+
3
+ One shared source of truth for the LLM endpoints AgentNode can bind: built-in
4
+ presets (OpenRouter, DeepSeek, Mistral, Qwen, Ollama — plus the official
5
+ OpenAI/Anthropic entries) and user-defined entries/overrides from
6
+ ``llm.providers.<name>`` in ~/.agentnode/config.json.
7
+
8
+ The runtime (``_auto_detect_llm``) builds standard OpenAI-compatible bindings
9
+ from these specs, so the host LLM broker — and therefore sandboxed agents —
10
+ inherit every provider automatically, with the key staying host-side and no
11
+ broker/sandbox change.
12
+
13
+ Compat presets MUST carry a default model: the broker's bare OpenAI default
14
+ ("gpt-4o-mini") would 404 on most compatible endpoints. ``requires_key:
15
+ False`` providers (Ollama) are bound only on explicit selection — never by
16
+ probing.
17
+ """
18
+ from __future__ import annotations
19
+
20
+ from typing import Any
21
+
22
+ # Built-in presets. base_url=None means the provider SDK's official endpoint.
23
+ # default_model="" lets the broker pick its own official default.
24
+ KNOWN_PROVIDERS: dict[str, dict[str, Any]] = {
25
+ "openai": {
26
+ "base_url": None,
27
+ "api_key_env": "OPENAI_API_KEY",
28
+ "default_model": "",
29
+ "requires_key": True,
30
+ },
31
+ "anthropic": {
32
+ "base_url": None,
33
+ "api_key_env": "ANTHROPIC_API_KEY",
34
+ "default_model": "",
35
+ "requires_key": True,
36
+ },
37
+ "openrouter": {
38
+ "base_url": "https://openrouter.ai/api/v1",
39
+ "api_key_env": "OPENROUTER_API_KEY",
40
+ "default_model": "openai/gpt-4o-mini",
41
+ "requires_key": True,
42
+ },
43
+ "deepseek": {
44
+ "base_url": "https://api.deepseek.com/v1",
45
+ "api_key_env": "DEEPSEEK_API_KEY",
46
+ "default_model": "deepseek-chat",
47
+ "requires_key": True,
48
+ },
49
+ "mistral": {
50
+ "base_url": "https://api.mistral.ai/v1",
51
+ "api_key_env": "MISTRAL_API_KEY",
52
+ "default_model": "mistral-small-latest",
53
+ "requires_key": True,
54
+ },
55
+ "qwen": {
56
+ "base_url": "https://dashscope-intl.aliyuncs.com/compatible-mode/v1",
57
+ "api_key_env": "DASHSCOPE_API_KEY",
58
+ "default_model": "qwen-plus",
59
+ "requires_key": True,
60
+ },
61
+ "gemini": {
62
+ # Google's own OpenAI-compatible endpoint (trailing slash required).
63
+ "base_url": "https://generativelanguage.googleapis.com/v1beta/openai/",
64
+ "api_key_env": "GEMINI_API_KEY",
65
+ "default_model": "gemini-2.0-flash",
66
+ "requires_key": True,
67
+ },
68
+ "ollama": {
69
+ # key-free local inference: the first agent path with no account and
70
+ # no per-token cost. Bound ONLY when explicitly selected (no probing).
71
+ "base_url": "http://localhost:11434/v1",
72
+ "api_key_env": "OLLAMA_API_KEY",
73
+ "default_model": "llama3.2",
74
+ "requires_key": False,
75
+ },
76
+ }
77
+
78
+ _SPEC_KEYS = ("base_url", "api_key_env", "default_model", "requires_key")
79
+
80
+
81
+ def _config_providers(host_config: dict | None) -> dict:
82
+ llm = (host_config or {}).get("llm") or {}
83
+ providers = llm.get("providers")
84
+ return providers if isinstance(providers, dict) else {}
85
+
86
+
87
+ def resolve_provider_spec(name: str, host_config: dict | None = None) -> dict | None:
88
+ """Effective spec for ``name`` = preset merged with the config override
89
+ (``llm.providers.<name>``), or None for an unknown name without a config
90
+ entry. ``model`` is accepted as a friendlier config alias for
91
+ ``default_model``."""
92
+ name = (name or "").lower().strip()
93
+ preset = KNOWN_PROVIDERS.get(name)
94
+ override = _config_providers(host_config).get(name)
95
+ if preset is None and not isinstance(override, dict):
96
+ return None
97
+
98
+ spec: dict[str, Any] = dict(preset) if preset else {
99
+ "base_url": None, "api_key_env": "", "default_model": "", "requires_key": True,
100
+ }
101
+ if isinstance(override, dict):
102
+ for k in _SPEC_KEYS:
103
+ if k in override:
104
+ spec[k] = override[k]
105
+ if "model" in override:
106
+ spec["default_model"] = override["model"]
107
+ return spec
108
+
109
+
110
+ def known_provider_names(host_config: dict | None = None) -> list[str]:
111
+ """Preset names first (stable order), then custom config-defined names."""
112
+ names = list(KNOWN_PROVIDERS)
113
+ for n in _config_providers(host_config):
114
+ if n not in names:
115
+ names.append(n)
116
+ return names
@@ -241,64 +241,83 @@ def _create_llm_from_config(llm_config: dict) -> dict | None:
241
241
  return None
242
242
 
243
243
 
244
- def _llm_binding_from_vault() -> dict | None:
245
- """Build an LLM binding from the credential store (UX-2 vault).
246
-
247
- LAST resolution step — env vars always override the store (explicit/CI
248
- intent wins; gh-CLI precedent). Tries the configured
249
- ``llm.default_provider`` first, then the remaining providers in fixed
250
- order. The key stays host-side; failures are logged by exception TYPE
244
+ def _llm_binding_from_providers() -> dict | None:
245
+ """Build an LLM binding from the provider registry + credential store
246
+ (UX-2 vault, generalized by Endpoint-A to any OpenAI-compatible endpoint:
247
+ OpenRouter/DeepSeek/Mistral/Qwen/Ollama/custom ``llm.providers.*``).
248
+
249
+ LAST resolution step the bare OPENAI/ANTHROPIC env vars and per-agent
250
+ config were already checked by the earlier steps. Within this step the
251
+ key resolves per provider env-var-first (explicit/CI intent), then vault.
252
+ Keyless providers (e.g. ollama) bind ONLY when explicitly selected via
253
+ ``llm.default_provider`` or configured under ``llm.providers`` — never by
254
+ probing. The key stays host-side; failures are logged by exception TYPE
251
255
  only (never the message — it could carry context)."""
256
+ import os
257
+
252
258
  try:
253
259
  from agentnode_sdk.config import load_config
254
- default = ((load_config() or {}).get("llm") or {}).get("default_provider", "openai")
260
+ host_cfg = load_config() or {}
255
261
  except Exception:
256
- default = "openai"
257
- order = [default] + [p for p in ("openai", "anthropic", "openrouter") if p != default]
262
+ host_cfg = {}
263
+ llm_cfg = host_cfg.get("llm") or {}
264
+ default = llm_cfg.get("default_provider", "openai")
265
+ configured = llm_cfg.get("providers") if isinstance(llm_cfg.get("providers"), dict) else {}
258
266
 
259
267
  try:
260
268
  from agentnode_sdk.credential_store import get_llm_api_key
261
269
  except Exception:
262
270
  return None
271
+ from agentnode_sdk.llm_providers import known_provider_names, resolve_provider_spec
263
272
 
264
- for provider in order:
265
- try:
266
- key = get_llm_api_key(provider)
267
- except Exception:
268
- key = None
269
- if not key:
273
+ order = [default] + [n for n in known_provider_names(host_cfg) if n != default]
274
+ for name in order:
275
+ spec = resolve_provider_spec(name, host_cfg)
276
+ if spec is None:
270
277
  continue
271
- if provider == "openai":
278
+
279
+ env_var = spec.get("api_key_env") or ""
280
+ key = os.environ.get(env_var) if env_var else None # env beats vault
281
+ if not key:
272
282
  try:
273
- from openai import OpenAI
274
- return {"client": OpenAI(api_key=key), "provider": "openai", "model": ""}
275
- except ImportError:
276
- logger.debug("vault key for openai set but openai package not installed")
277
- except Exception as exc:
278
- logger.debug("Failed to create OpenAI client from vault: %s", type(exc).__name__)
279
- elif provider == "anthropic":
283
+ key = get_llm_api_key(name)
284
+ except Exception:
285
+ key = None
286
+
287
+ if spec.get("requires_key", True):
288
+ if not key:
289
+ continue
290
+ elif not key and not (name == default or name in configured):
291
+ # keyless provider without explicit selection: never bind by surprise
292
+ continue
293
+
294
+ if name == "anthropic":
280
295
  try:
281
296
  from anthropic import Anthropic
282
297
  return {"client": Anthropic(api_key=key), "provider": "anthropic", "model": ""}
283
298
  except ImportError:
284
- logger.debug("vault key for anthropic set but anthropic package not installed")
299
+ logger.debug("key for anthropic set but anthropic package not installed")
285
300
  except Exception as exc:
286
- logger.debug("Failed to create Anthropic client from vault: %s", type(exc).__name__)
287
- elif provider == "openrouter":
288
- try:
289
- from openai import OpenAI
290
- # OpenRouter model ids are namespaced — the broker's bare
291
- # "gpt-4o-mini" default would 404 there, so pin the
292
- # namespaced equivalent explicitly in the binding.
293
- return {
294
- "client": OpenAI(api_key=key, base_url="https://openrouter.ai/api/v1"),
295
- "provider": "openai",
296
- "model": "openai/gpt-4o-mini",
297
- }
298
- except ImportError:
299
- logger.debug("vault key for openrouter set but openai package not installed")
300
- except Exception as exc:
301
- logger.debug("Failed to create OpenRouter client from vault: %s", type(exc).__name__)
301
+ logger.debug("Failed to create Anthropic client: %s", type(exc).__name__)
302
+ continue
303
+
304
+ base_url = spec.get("base_url")
305
+ model = spec.get("default_model") or ""
306
+ if base_url and not model:
307
+ # compat endpoint without a model: never guess — the broker's bare
308
+ # OpenAI default would 404 on most compatible providers.
309
+ logger.debug("provider %s has a base_url but no model configured — skipped", name)
310
+ continue
311
+ try:
312
+ from openai import OpenAI
313
+ kwargs: dict = {"api_key": key or "ollama"}
314
+ if base_url:
315
+ kwargs["base_url"] = base_url
316
+ return {"client": OpenAI(**kwargs), "provider": "openai", "model": model}
317
+ except ImportError:
318
+ logger.debug("key for %s set but openai package not installed", name)
319
+ except Exception as exc:
320
+ logger.debug("Failed to create client for %s: %s", name, type(exc).__name__)
302
321
  return None
303
322
 
304
323
 
@@ -310,8 +329,10 @@ def _auto_detect_llm(slug: str = "") -> dict | None:
310
329
  2. ~/.agentnode/.env (loaded into env, no overwrite)
311
330
  3. OPENAI_API_KEY -> OpenAI client
312
331
  4. ANTHROPIC_API_KEY -> Anthropic client
313
- 5. Credential store (vault): openai/anthropic/openrouter — env always
314
- overrides the store (UX-2)
332
+ 5. Provider registry + credential store (vault): openai/anthropic plus
333
+ any OpenAI-compatible endpoint (openrouter/deepseek/mistral/qwen/
334
+ ollama/custom ``llm.providers.*``) — env always overrides the store
335
+ (UX-2/Endpoint-A)
315
336
 
316
337
  Returns a dict-style LLM binding {client, provider, model} or None.
317
338
  """
@@ -349,7 +370,7 @@ def _auto_detect_llm(slug: str = "") -> dict | None:
349
370
  except Exception as exc:
350
371
  logger.debug("Failed to create Anthropic client: %s", exc)
351
372
 
352
- return _llm_binding_from_vault()
373
+ return _llm_binding_from_providers()
353
374
 
354
375
 
355
376
  # ---------------------------------------------------------------------------
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "agentnode-sdk"
7
- version = "0.14.0"
7
+ version = "0.15.0"
8
8
  description = "Python SDK for AgentNode — the open upgrade and discovery infrastructure for AI agents."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -343,3 +343,34 @@ class TestAgentSandboxConfigSection:
343
343
  assert loaded["guard"]["delete"] == "deny"
344
344
  assert loaded["guard"]["read"] == "allow" # defaults intact
345
345
  assert loaded["trust"]["minimum_trust_level"] == "verified"
346
+
347
+
348
+ # --- llm.providers section (Endpoint-A) ---
349
+
350
+
351
+ class TestLlmProvidersConfigSection:
352
+ def test_defaults_include_empty_providers(self):
353
+ cfg = default_config()
354
+ assert cfg["llm"]["providers"] == {}
355
+
356
+ def test_providers_survive_load(self, isolated_config):
357
+ providers = {"myvllm": {"base_url": "http://10.0.0.5:8000/v1",
358
+ "model": "llama", "requires_key": False}}
359
+ isolated_config.parent.mkdir(parents=True, exist_ok=True)
360
+ isolated_config.write_text(
361
+ json.dumps({"llm": {"default_provider": "deepseek",
362
+ "providers": providers}}), encoding="utf-8")
363
+ loaded = load_config()
364
+ assert loaded["llm"]["providers"] == providers
365
+ assert loaded["llm"]["default_provider"] == "deepseek"
366
+
367
+ def test_set_value_accepts_new_provider_names(self):
368
+ cfg = default_config()
369
+ for name in ("deepseek", "mistral", "qwen", "ollama"):
370
+ set_value(cfg, "llm.default_provider", name)
371
+ assert cfg["llm"]["default_provider"] == name
372
+
373
+ def test_set_value_rejects_garbage_provider(self):
374
+ cfg = default_config()
375
+ with pytest.raises(ValueError):
376
+ set_value(cfg, "llm.default_provider", "skynet")
@@ -0,0 +1,79 @@
1
+ """Endpoint-A — the OpenAI-compatible provider registry."""
2
+ from __future__ import annotations
3
+
4
+ from agentnode_sdk.llm_providers import (
5
+ KNOWN_PROVIDERS,
6
+ known_provider_names,
7
+ resolve_provider_spec,
8
+ )
9
+
10
+
11
+ def test_presets_are_complete():
12
+ for name, spec in KNOWN_PROVIDERS.items():
13
+ for key in ("base_url", "api_key_env", "default_model", "requires_key"):
14
+ assert key in spec, f"{name} missing {key}"
15
+ # compat endpoints (base_url set) MUST carry a model — the broker's
16
+ # bare OpenAI default would 404 there
17
+ if spec["base_url"]:
18
+ assert spec["default_model"], f"{name} needs a default model"
19
+
20
+
21
+ def test_expected_preset_names():
22
+ for name in ("openai", "anthropic", "openrouter", "deepseek",
23
+ "mistral", "qwen", "gemini", "ollama"):
24
+ assert name in KNOWN_PROVIDERS
25
+
26
+
27
+ def test_gemini_uses_googles_openai_compat_endpoint():
28
+ spec = resolve_provider_spec("gemini")
29
+ assert spec["base_url"] == "https://generativelanguage.googleapis.com/v1beta/openai/"
30
+ assert spec["base_url"].endswith("/") # trailing slash required
31
+ assert spec["api_key_env"] == "GEMINI_API_KEY"
32
+ assert spec["default_model"] == "gemini-2.0-flash"
33
+ assert spec["requires_key"] is True
34
+
35
+
36
+ def test_ollama_is_keyless_localhost():
37
+ spec = resolve_provider_spec("ollama")
38
+ assert spec["requires_key"] is False
39
+ assert spec["base_url"] == "http://localhost:11434/v1"
40
+ assert spec["default_model"] # never empty (broker default 404s)
41
+
42
+
43
+ def test_resolve_preset_without_config():
44
+ spec = resolve_provider_spec("deepseek")
45
+ assert spec["base_url"] == "https://api.deepseek.com/v1"
46
+ assert spec["api_key_env"] == "DEEPSEEK_API_KEY"
47
+ assert spec["default_model"] == "deepseek-chat"
48
+
49
+
50
+ def test_config_override_beats_preset():
51
+ cfg = {"llm": {"providers": {"deepseek": {
52
+ "model": "deepseek-reasoner", "base_url": "https://proxy.local/v1"}}}}
53
+ spec = resolve_provider_spec("deepseek", cfg)
54
+ assert spec["default_model"] == "deepseek-reasoner" # "model" alias works
55
+ assert spec["base_url"] == "https://proxy.local/v1"
56
+ assert spec["api_key_env"] == "DEEPSEEK_API_KEY" # preset rest intact
57
+
58
+
59
+ def test_custom_provider_from_config_only():
60
+ cfg = {"llm": {"providers": {"myvllm": {
61
+ "base_url": "http://10.0.0.5:8000/v1", "model": "llama",
62
+ "requires_key": False}}}}
63
+ spec = resolve_provider_spec("myvllm", cfg)
64
+ assert spec["base_url"] == "http://10.0.0.5:8000/v1"
65
+ assert spec["default_model"] == "llama"
66
+ assert spec["requires_key"] is False
67
+ assert spec["api_key_env"] == ""
68
+
69
+
70
+ def test_unknown_provider_is_none():
71
+ assert resolve_provider_spec("nope") is None
72
+ assert resolve_provider_spec("") is None
73
+
74
+
75
+ def test_known_names_include_custom():
76
+ cfg = {"llm": {"providers": {"myvllm": {"base_url": "x", "model": "m"}}}}
77
+ names = known_provider_names(cfg)
78
+ assert names.index("openai") == 0 # presets first, stable order
79
+ assert "myvllm" in names