agentnode-sdk 0.11.2__tar.gz → 0.11.3__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.
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/CHANGELOG.md +19 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/PKG-INFO +1 -1
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/agentnode_sdk/__init__.py +1 -1
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/agentnode_sdk/installer.py +19 -1
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/agentnode_sdk/runtimes/python_runner.py +5 -2
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/pyproject.toml +1 -1
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/tests/test_lock_integrity.py +8 -1
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/tests/test_toolpack_sandbox.py +10 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/tests/test_v02.py +25 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/.env.example +0 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/.gitignore +0 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/README.md +0 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/REGISTRY_SIGNING_ACTIVATION.md +0 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/REGISTRY_SIGNING_SPEC.md +0 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/THREAT_MODEL.md +0 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/TRUST_STACK.md +0 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/agentnode.lock +0 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/agentnode_sdk/_fileutil.py +0 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/agentnode_sdk/async_client.py +0 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/agentnode_sdk/capability_graph.py +0 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/agentnode_sdk/capability_taxonomy.py +0 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/agentnode_sdk/cli/__init__.py +0 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/agentnode_sdk/cli/__main__.py +0 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/agentnode_sdk/cli/audit.py +0 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/agentnode_sdk/cli/auth.py +0 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/agentnode_sdk/cli/cassette_audit.py +0 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/agentnode_sdk/cli/commands.py +0 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/agentnode_sdk/cli/complements.py +0 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/agentnode_sdk/cli/init.py +0 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/agentnode_sdk/cli/main.py +0 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/agentnode_sdk/cli/mcp_commands.py +0 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/agentnode_sdk/cli/mcp_status.py +0 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/agentnode_sdk/cli/mcp_submit.py +0 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/agentnode_sdk/cli/mcp_verify.py +0 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/agentnode_sdk/cli/output.py +0 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/agentnode_sdk/cli/publish.py +0 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/agentnode_sdk/cli/record_cases.py +0 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/agentnode_sdk/cli/sandbox_commands.py +0 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/agentnode_sdk/cli/serve.py +0 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/agentnode_sdk/cli/setup_wizard.py +0 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/agentnode_sdk/cli/smart_run.py +0 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/agentnode_sdk/cli/templates.py +0 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/agentnode_sdk/cli/validate.py +0 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/agentnode_sdk/cli/verify_local.py +0 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/agentnode_sdk/client.py +0 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/agentnode_sdk/compatibility.py +0 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/agentnode_sdk/config.py +0 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/agentnode_sdk/credential_handle.py +0 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/agentnode_sdk/credential_resolver.py +0 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/agentnode_sdk/credential_store.py +0 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/agentnode_sdk/detect.py +0 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/agentnode_sdk/exceptions.py +0 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/agentnode_sdk/guard.py +0 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/agentnode_sdk/input_guard.py +0 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/agentnode_sdk/key_status.py +0 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/agentnode_sdk/lock_integrity.py +0 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/agentnode_sdk/mcp_server.py +0 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/agentnode_sdk/models.py +0 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/agentnode_sdk/planner.py +0 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/agentnode_sdk/policy.py +0 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/agentnode_sdk/references.py +0 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/agentnode_sdk/registry_trust.py +0 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/agentnode_sdk/resolve.py +0 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/agentnode_sdk/resource_provider.py +0 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/agentnode_sdk/risk_profile.py +0 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/agentnode_sdk/run_log.py +0 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/agentnode_sdk/runner.py +0 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/agentnode_sdk/runtime.py +0 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/agentnode_sdk/runtimes/__init__.py +0 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/agentnode_sdk/runtimes/agent_runner.py +0 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/agentnode_sdk/runtimes/mcp_runner.py +0 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/agentnode_sdk/runtimes/remote_runner.py +0 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/agentnode_sdk/sandbox/__init__.py +0 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/agentnode_sdk/sandbox/backend.py +0 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/agentnode_sdk/sandbox/container_backend.py +0 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/agentnode_sdk/sandbox/policy.py +0 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/agentnode_sdk/sandbox/types.py +0 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/agentnode_sdk/signature.py +0 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/agentnode_sdk/signing_key.py +0 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/agentnode_sdk/skill.py +0 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/sandbox-image/Dockerfile +0 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/sandbox-image/README.md +0 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/scripts/analyze_scores.py +0 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/scripts/batch_verify.py +0 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/scripts/ci_smoke_test.py +0 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/scripts/generate_compatibility_artifacts.py +0 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/scripts/verify_toolcalls.py +0 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/scripts/weekly_retest.sh +0 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/tests/__init__.py +0 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/tests/conftest.py +0 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/tests/test_agent_runner.py +0 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/tests/test_async_client.py +0 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/tests/test_audit_ux.py +0 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/tests/test_auto_upgrade_policy.py +0 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/tests/test_cli.py +0 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/tests/test_cli_lock.py +0 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/tests/test_cli_run_resolution.py +0 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/tests/test_client.py +0 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/tests/test_client_json_guard.py +0 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/tests/test_client_sprint_b.py +0 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/tests/test_config.py +0 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/tests/test_credential_handle.py +0 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/tests/test_credential_integration.py +0 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/tests/test_credential_resolver.py +0 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/tests/test_credential_store.py +0 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/tests/test_detect.py +0 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/tests/test_detect_and_install.py +0 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/tests/test_e2e_runtime.py +0 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/tests/test_edge_cases.py +0 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/tests/test_guard.py +0 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/tests/test_guard_check.py +0 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/tests/test_guard_config_cache.py +0 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/tests/test_guard_policy.py +0 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/tests/test_guard_preview.py +0 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/tests/test_guard_schema.py +0 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/tests/test_guard_set.py +0 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/tests/test_guard_status.py +0 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/tests/test_guard_tool_override.py +0 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/tests/test_guard_tool_override_audit.py +0 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/tests/test_guard_tool_override_cli.py +0 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/tests/test_guard_ux.py +0 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/tests/test_input_guard.py +0 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/tests/test_input_guard_escalation.py +0 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/tests/test_install_hardening.py +0 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/tests/test_installer_sprint_b.py +0 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/tests/test_intelligence.py +0 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/tests/test_key_status.py +0 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/tests/test_llm_binding.py +0 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/tests/test_llm_call_runlog.py +0 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/tests/test_lock_runtime.py +0 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/tests/test_mcp_audit.py +0 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/tests/test_mcp_doctor.py +0 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/tests/test_mcp_sandbox.py +0 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/tests/test_mcp_server.py +0 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/tests/test_observability.py +0 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/tests/test_planner.py +0 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/tests/test_policy.py +0 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/tests/test_policy_integration.py +0 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/tests/test_prompt_specs.py +0 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/tests/test_provider_matrix.py +0 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/tests/test_publish.py +0 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/tests/test_references.py +0 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/tests/test_registry_trust.py +0 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/tests/test_remote_hardening.py +0 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/tests/test_remote_runner.py +0 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/tests/test_resource_provider.py +0 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/tests/test_resource_specs.py +0 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/tests/test_risk_profile.py +0 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/tests/test_run_log.py +0 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/tests/test_runner.py +0 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/tests/test_runtime.py +0 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/tests/test_runtime_audit.py +0 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/tests/test_sandbox_backend.py +0 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/tests/test_sandbox_doctor.py +0 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/tests/test_sandbox_e2e.py +0 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/tests/test_sandbox_gate.py +0 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/tests/test_security_hardening.py +0 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/tests/test_signature.py +0 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/tests/test_signing_key.py +0 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/tests/test_skill.py +0 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/tests/test_smart.py +0 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/tests/test_stability.py +0 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/tests/test_validate.py +0 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/tests/test_validate_skill.py +0 -0
- {agentnode_sdk-0.11.2 → agentnode_sdk-0.11.3}/tests/validation_lockfile.json +0 -0
|
@@ -1,5 +1,24 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.11.3 — Test hygiene + multi-tool run guidance
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
|
|
7
|
+
- **Stale lock-integrity field-classification test.** `test_real_lockfile_fields_classified`
|
|
8
|
+
used the V1 `CANONICAL_FIELDS` set, so it flagged the V3 field `publisher_slug`
|
|
9
|
+
as "unclassified" on real lockfiles and failed. The integrity model itself
|
|
10
|
+
already seals `publisher_slug` (`CANONICAL_FIELDS_V3`); only the test was stale.
|
|
11
|
+
Updated to `CANONICAL_FIELDS_V3` — no change to the integrity model.
|
|
12
|
+
|
|
13
|
+
### Changed
|
|
14
|
+
|
|
15
|
+
- **Clearer `agentnode run <slug>` error for multi-tool packs.** When a package
|
|
16
|
+
exposes more than one tool and resolution fails without an explicit tool name,
|
|
17
|
+
the error now lists the available tools and points at
|
|
18
|
+
`agentnode run <slug>:<tool>` instead of a generic "no entrypoint" / "Function
|
|
19
|
+
'run' not found". Message-only — single-tool auto-select and multi-tool dispatch
|
|
20
|
+
behaviour are unchanged. Applied consistently to the host and container paths.
|
|
21
|
+
|
|
3
22
|
## 0.11.2 — CLI run resolution fixes
|
|
4
23
|
|
|
5
24
|
### Fixed
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: agentnode-sdk
|
|
3
|
-
Version: 0.11.
|
|
3
|
+
Version: 0.11.3
|
|
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
|
|
@@ -1051,6 +1051,20 @@ def _default_tool_entrypoint(entry: dict) -> str | None:
|
|
|
1051
1051
|
return entry.get("entrypoint")
|
|
1052
1052
|
|
|
1053
1053
|
|
|
1054
|
+
def _multi_tool_hint(entry: dict, slug: str = "<slug>") -> str:
|
|
1055
|
+
"""Trailing hint for resolution-failure errors: if the pack exposes MORE than
|
|
1056
|
+
one tool, point the user at ``agentnode run <slug>:<tool>`` and list the tools.
|
|
1057
|
+
Empty string for single-tool / no-tools packs. Message-only — does not change
|
|
1058
|
+
which entrypoint is chosen."""
|
|
1059
|
+
names = [t.get("name") for t in (entry.get("tools") or []) if t.get("name")]
|
|
1060
|
+
if len(names) > 1:
|
|
1061
|
+
return (
|
|
1062
|
+
f" This package exposes multiple tools: {names}. "
|
|
1063
|
+
f"Run a specific one with: agentnode run {slug}:<tool>."
|
|
1064
|
+
)
|
|
1065
|
+
return ""
|
|
1066
|
+
|
|
1067
|
+
|
|
1054
1068
|
def load_tool(slug: str, tool_name: str | None = None, *, _internal: bool = False) -> Any:
|
|
1055
1069
|
"""Load an installed package's tool function.
|
|
1056
1070
|
|
|
@@ -1120,7 +1134,10 @@ def load_tool(slug: str, tool_name: str | None = None, *, _internal: bool = Fals
|
|
|
1120
1134
|
# else fall back to the package-level entrypoint (multi-tool unchanged).
|
|
1121
1135
|
entrypoint = _default_tool_entrypoint(pkg)
|
|
1122
1136
|
if not entrypoint:
|
|
1123
|
-
raise ImportError(
|
|
1137
|
+
raise ImportError(
|
|
1138
|
+
f"Package '{slug}' has no entrypoint in lockfile."
|
|
1139
|
+
+ _multi_tool_hint(pkg, slug)
|
|
1140
|
+
)
|
|
1124
1141
|
|
|
1125
1142
|
module_path, func_name = _resolve_entrypoint(entrypoint)
|
|
1126
1143
|
mod = _import_module(module_path, slug)
|
|
@@ -1129,6 +1146,7 @@ def load_tool(slug: str, tool_name: str | None = None, *, _internal: bool = Fals
|
|
|
1129
1146
|
raise ImportError(
|
|
1130
1147
|
f"Function '{func_name}' not found in module '{module_path}' "
|
|
1131
1148
|
f"for package '{slug}'."
|
|
1149
|
+
+ _multi_tool_hint(pkg, slug)
|
|
1132
1150
|
)
|
|
1133
1151
|
return func
|
|
1134
1152
|
|
|
@@ -445,7 +445,9 @@ def _resolve_container_target(entry: dict, tool_name: str | None) -> tuple[str,
|
|
|
445
445
|
first then the default function (``run``), matching load_tool's getattr order;
|
|
446
446
|
- no tool_name → package entrypoint module + its function.
|
|
447
447
|
"""
|
|
448
|
-
from agentnode_sdk.installer import
|
|
448
|
+
from agentnode_sdk.installer import (
|
|
449
|
+
_resolve_entrypoint, _default_tool_entrypoint, _multi_tool_hint,
|
|
450
|
+
)
|
|
449
451
|
|
|
450
452
|
tools = entry.get("tools") or []
|
|
451
453
|
if tool_name:
|
|
@@ -466,7 +468,8 @@ def _resolve_container_target(entry: dict, tool_name: str | None) -> tuple[str,
|
|
|
466
468
|
ep = _default_tool_entrypoint(entry)
|
|
467
469
|
if not ep:
|
|
468
470
|
raise AgentNodeToolError(
|
|
469
|
-
"Package has no entrypoint in the lockfile."
|
|
471
|
+
"Package has no entrypoint in the lockfile." + _multi_tool_hint(entry),
|
|
472
|
+
tool_name=None,
|
|
470
473
|
)
|
|
471
474
|
module, func = _resolve_entrypoint(ep)
|
|
472
475
|
return module, [func]
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "agentnode-sdk"
|
|
7
|
-
version = "0.11.
|
|
7
|
+
version = "0.11.3"
|
|
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"
|
|
@@ -595,7 +595,14 @@ class TestFieldClassification:
|
|
|
595
595
|
"""Every field in a real lockfile entry must be either canonical,
|
|
596
596
|
mutable, or the _integrity seal itself."""
|
|
597
597
|
|
|
598
|
-
|
|
598
|
+
# V3 = canonical V1 + _signatures + publisher_slug (all integrity-sealed).
|
|
599
|
+
KNOWN_FIELDS = set(CANONICAL_FIELDS_V3) | set(MUTABLE_FIELDS) | {"_integrity"}
|
|
600
|
+
|
|
601
|
+
def test_known_fields_cover_v3(self):
|
|
602
|
+
"""Regression: V3 canonical fields (incl. _signatures, publisher_slug)
|
|
603
|
+
must be classified, else a real lockfile carrying them trips the check."""
|
|
604
|
+
assert "publisher_slug" in self.KNOWN_FIELDS
|
|
605
|
+
assert "_signatures" in self.KNOWN_FIELDS
|
|
599
606
|
|
|
600
607
|
def test_toolpack_entry_fields_classified(self):
|
|
601
608
|
e = seal_entry(_make_entry())
|
|
@@ -354,6 +354,16 @@ def test_resolve_target_multi_tool_keeps_package_default():
|
|
|
354
354
|
assert _resolve_container_target(entry, None) == ("mod.tool", ["dispatch"])
|
|
355
355
|
|
|
356
356
|
|
|
357
|
+
def test_resolve_target_multi_tool_no_entrypoint_hints():
|
|
358
|
+
# 0.11.3: multi-tool pack with NO package-level entrypoint → helpful hint
|
|
359
|
+
# listing tools + slug:tool (message-only; container path mirrors the host).
|
|
360
|
+
from agentnode_sdk.exceptions import AgentNodeToolError
|
|
361
|
+
entry = {"tools": [{"name": "a", "entrypoint": "m:a"},
|
|
362
|
+
{"name": "b", "entrypoint": "m:b"}]}
|
|
363
|
+
with pytest.raises(AgentNodeToolError, match="multiple tools"):
|
|
364
|
+
_resolve_container_target(entry, None)
|
|
365
|
+
|
|
366
|
+
|
|
357
367
|
def test_resolve_target_v02_colon_function():
|
|
358
368
|
entry = {"entrypoint": "mod.tool:describe", "tools": []}
|
|
359
369
|
assert _resolve_container_target(entry, None) == ("mod.tool", ["describe"])
|
|
@@ -111,6 +111,31 @@ class TestLoadTool:
|
|
|
111
111
|
func = load_tool("multi-pack")
|
|
112
112
|
assert func == mock_module.dispatch
|
|
113
113
|
|
|
114
|
+
def test_load_multi_tool_no_toolname_lists_tools(self, tmp_path):
|
|
115
|
+
"""0.11.3: multi-tool pack run without a tool name and an unresolvable
|
|
116
|
+
package entrypoint → helpful error listing tools + slug:tool hint.
|
|
117
|
+
Message-only; dispatch behaviour unchanged."""
|
|
118
|
+
self._write_lockfile(tmp_path, {
|
|
119
|
+
"multi-pack": {
|
|
120
|
+
"version": "1.0.0",
|
|
121
|
+
"entrypoint": "multi.tool", # colon-less → 'run', which won't exist
|
|
122
|
+
"tools": [
|
|
123
|
+
{"name": "a", "entrypoint": "multi.tool:a"},
|
|
124
|
+
{"name": "b", "entrypoint": "multi.tool:b"},
|
|
125
|
+
],
|
|
126
|
+
}
|
|
127
|
+
})
|
|
128
|
+
mock_module = MagicMock(spec=["a", "b"]) # no 'run' attribute
|
|
129
|
+
|
|
130
|
+
with patch("agentnode_sdk.installer._lockfile_path", return_value=tmp_path / "agentnode.lock"):
|
|
131
|
+
with patch("agentnode_sdk.installer.importlib.import_module", return_value=mock_module):
|
|
132
|
+
with pytest.raises(ImportError) as ei:
|
|
133
|
+
load_tool("multi-pack")
|
|
134
|
+
msg = str(ei.value)
|
|
135
|
+
assert "multiple tools" in msg
|
|
136
|
+
assert "'a'" in msg and "'b'" in msg
|
|
137
|
+
assert "multi-pack:<tool>" in msg
|
|
138
|
+
|
|
114
139
|
def test_load_v02_with_tool_name(self, tmp_path):
|
|
115
140
|
"""v0.2 pack — load_tool(slug, tool_name) returns specific function."""
|
|
116
141
|
self._write_lockfile(tmp_path, {
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|