Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 8f9b02af4a |
@@ -20,6 +20,13 @@ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
||||
|
||||
### Changed
|
||||
|
||||
- **`agents actor add` reads name from config file instead of positional argument** (#8640): The
|
||||
``NAME`` positional argument has been removed from the ``agents actor add`` command. The actor
|
||||
name is now read from the ``name`` field in the YAML/JSON configuration file specified with
|
||||
``--config``. If no ``name`` field exists in the config, a clear error message is shown. This
|
||||
simplifies the CLI interface by having the config file be the single source of truth for actor
|
||||
settings including the namespaced name (e.g., ``local/my-actor``). Examples: `agents actor add --config ./actors/my-actor.yaml`.
|
||||
|
||||
- **`agents session list` now displays full 26-character session ULIDs** (#10970): The Rich table
|
||||
and Summary panel ("Most Recent" / "Oldest") previously showed only the first 8 characters of
|
||||
each session ULID. This made the output unusable for copy-paste into `session tell`,
|
||||
|
||||
@@ -21,6 +21,7 @@ Below are some of the specific details of various contributions.
|
||||
* HAL 9000 has contributed the plugin entry point security hardening fix (#7476): enforced entry point allowlist validation before importing plugin modules to prevent malicious plugin loading.
|
||||
* HAL 9000 has contributed the benchmark workflow separation (#9040): moved the benchmark-regression job out of the default PR workflow into a dedicated scheduled workflow, reducing median PR CI turnaround time from 99-132 minutes to under 30 minutes.
|
||||
* HAL 9000 has contributed the agent-evolution-pool-supervisor PR metadata assignment (#7888): the supervisor now automatically looks up the Type/Automation label and earliest open milestone before dispatching improvement PR creation workers, ensuring all generated improvement PRs have correct Type labels and milestone assignments.
|
||||
* HAL 9000 has contributed the actors actor add positional NAME removal (PR #8640): removed the required positional NAME argument from `agents actor add`, now reads name from the config file's `name` field with a clear error message when missing.
|
||||
* This project was made possible thanks to considerable donation of time, money, and resources by CleverThis, Inc.
|
||||
* HAL 9000 has contributed automated bug fixes, CLI output formatting improvements, and ongoing maintenance as part of the CleverAgents automation system.
|
||||
* HAL 9000 has contributed the file edit encoding parameter fix (PR #8258 / issue #7559).
|
||||
|
||||
@@ -1,24 +1,16 @@
|
||||
Feature: agents actor add NAME positional argument
|
||||
Feature: agents actor add reads name from config file
|
||||
As a user of the CleverAgents CLI
|
||||
I want to pass the actor name as a positional argument to `agents actor add`
|
||||
So that the CLI matches the spec synopsis: agents actor add <NAME> --config <FILE>
|
||||
I want to read the actor name from the YAML/JSON config file instead of passing it as a positional argument
|
||||
So that the CLI uses the config file for all actor configuration including the namespaced name
|
||||
|
||||
@tdd_issue @tdd_issue_4230 @tdd_expected_fail
|
||||
Scenario: actor add accepts NAME as positional argument
|
||||
Scenario: actor add reads name from config file successfully
|
||||
Given an actor CLI runner
|
||||
And I have an actor JSON config file with a name field
|
||||
When I run actor add with config file
|
||||
Then the actor add should succeed using the name from the config file
|
||||
|
||||
Scenario: actor add fails without name in config file
|
||||
Given an actor CLI runner
|
||||
And I have an actor JSON config file without a name field
|
||||
When I run actor add with NAME positional argument and config
|
||||
Then the actor add should succeed with the positional name
|
||||
|
||||
@tdd_issue @tdd_issue_4230 @tdd_expected_fail
|
||||
Scenario: actor add NAME positional argument takes precedence over config name
|
||||
Given an actor CLI runner
|
||||
And I have an actor JSON config file with a different name
|
||||
When I run actor add with NAME positional argument overriding config name
|
||||
Then the actor add should use the positional NAME not the config name
|
||||
|
||||
Scenario: actor add without NAME positional argument fails
|
||||
Given an actor CLI runner
|
||||
And I have an actor JSON config file without a name field
|
||||
When I run actor add with config but no NAME positional argument
|
||||
Then the actor command should fail with missing argument error
|
||||
When I run actor add with config file but no name
|
||||
Then the actor add should fail with an error about missing name
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
"""Step definitions for actor add NAME positional argument feature."""
|
||||
"""Step definitions for actor add name-from-config feature."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -43,6 +43,23 @@ def _register_cleanup(context: Any, path: Path) -> None:
|
||||
context._cleanup_handlers.append(lambda: path.unlink(missing_ok=True))
|
||||
|
||||
|
||||
@given("I have an actor JSON config file with a name field")
|
||||
def step_impl(context: Any) -> None:
|
||||
context.actor_config_data = {
|
||||
"name": "local/my-actor",
|
||||
"provider": "openai",
|
||||
"model": "gpt-4o-mini",
|
||||
"temperature": 0.5,
|
||||
}
|
||||
with tempfile.NamedTemporaryFile(
|
||||
delete=False, suffix=".json", mode="w", encoding="utf-8"
|
||||
) as handle:
|
||||
json.dump(context.actor_config_data, handle)
|
||||
handle.flush()
|
||||
context.actor_config_path = Path(handle.name)
|
||||
_register_cleanup(context, context.actor_config_path)
|
||||
|
||||
|
||||
@given("I have an actor JSON config file without a name field")
|
||||
def step_impl(context: Any) -> None:
|
||||
context.actor_config_data = {
|
||||
@@ -59,26 +76,12 @@ def step_impl(context: Any) -> None:
|
||||
_register_cleanup(context, context.actor_config_path)
|
||||
|
||||
|
||||
@given("I have an actor JSON config file with a different name")
|
||||
@when("I run actor add with config file")
|
||||
def step_impl(context: Any) -> None:
|
||||
context.actor_config_data = {
|
||||
"name": "local/config-name-actor",
|
||||
"provider": "openai",
|
||||
"model": "gpt-4o-mini",
|
||||
}
|
||||
with tempfile.NamedTemporaryFile(
|
||||
delete=False, suffix=".json", mode="w", encoding="utf-8"
|
||||
) as handle:
|
||||
json.dump(context.actor_config_data, handle)
|
||||
handle.flush()
|
||||
context.actor_config_path = Path(handle.name)
|
||||
_register_cleanup(context, context.actor_config_path)
|
||||
|
||||
|
||||
@when("I run actor add with NAME positional argument and config")
|
||||
def step_impl(context: Any) -> None:
|
||||
context.positional_name = "local/my-actor"
|
||||
mock_actor = _make_actor(name=context.positional_name)
|
||||
config_name = context.actor_config_data.get(
|
||||
"name", context.actor_config_data.get("_fallback_name", "local/default-actor")
|
||||
)
|
||||
mock_actor = _make_actor(name=config_name)
|
||||
with patch("cleveragents.cli.commands.actor._get_services") as mock_svc:
|
||||
registry = MagicMock()
|
||||
registry.upsert_actor.return_value = mock_actor
|
||||
@@ -87,7 +90,6 @@ def step_impl(context: Any) -> None:
|
||||
actor_app,
|
||||
[
|
||||
"add",
|
||||
context.positional_name,
|
||||
"--config",
|
||||
str(context.actor_config_path),
|
||||
],
|
||||
@@ -95,28 +97,9 @@ def step_impl(context: Any) -> None:
|
||||
context.mock_registry = registry
|
||||
|
||||
|
||||
@when("I run actor add with NAME positional argument overriding config name")
|
||||
def step_impl(context: Any) -> None:
|
||||
context.positional_name = "local/positional-name-actor"
|
||||
mock_actor = _make_actor(name=context.positional_name)
|
||||
with patch("cleveragents.cli.commands.actor._get_services") as mock_svc:
|
||||
registry = MagicMock()
|
||||
registry.upsert_actor.return_value = mock_actor
|
||||
mock_svc.return_value = (MagicMock(), registry)
|
||||
context.result = context.runner.invoke(
|
||||
actor_app,
|
||||
[
|
||||
"add",
|
||||
context.positional_name,
|
||||
"--config",
|
||||
str(context.actor_config_path),
|
||||
],
|
||||
)
|
||||
context.mock_registry = registry
|
||||
|
||||
|
||||
@when("I run actor add with config but no NAME positional argument")
|
||||
@when("I run actor add with config file but no name")
|
||||
def step_impl(context: Any) -> None:
|
||||
mock_actor = _make_actor()
|
||||
with patch("cleveragents.cli.commands.actor._get_services") as mock_svc:
|
||||
registry = MagicMock()
|
||||
mock_svc.return_value = (MagicMock(), registry)
|
||||
@@ -130,13 +113,13 @@ def step_impl(context: Any) -> None:
|
||||
)
|
||||
|
||||
|
||||
@then("the actor add should succeed with the positional name")
|
||||
@then("the actor add should succeed using the name from the config file")
|
||||
def step_impl(context: Any) -> None:
|
||||
assert context.result.exit_code == 0, (
|
||||
f"Expected exit_code=0, got {context.result.exit_code}.\n"
|
||||
f"Output:\n{context.result.output}"
|
||||
)
|
||||
# Verify the registry was called with the positional name
|
||||
# Verify the registry was called with the name from config file
|
||||
assert context.mock_registry.upsert_actor.called, (
|
||||
"Expected upsert_actor to be called on the registry"
|
||||
)
|
||||
@@ -144,34 +127,19 @@ def step_impl(context: Any) -> None:
|
||||
actual_name = call_kwargs.kwargs.get("name") or (
|
||||
call_kwargs.args[0] if call_kwargs.args else None
|
||||
)
|
||||
assert actual_name == context.positional_name, (
|
||||
f"Expected upsert_actor called with name={context.positional_name!r}, "
|
||||
expected_name = context.actor_config_data.get("name", "local/default-actor")
|
||||
assert actual_name == expected_name, (
|
||||
f"Expected upsert_actor called with name={expected_name!r}, "
|
||||
f"got name={actual_name!r}"
|
||||
)
|
||||
|
||||
|
||||
@then("the actor add should use the positional NAME not the config name")
|
||||
def step_impl(context: Any) -> None:
|
||||
assert context.result.exit_code == 0, (
|
||||
f"Expected exit_code=0, got {context.result.exit_code}.\n"
|
||||
f"Output:\n{context.result.output}"
|
||||
)
|
||||
assert context.mock_registry.upsert_actor.called, (
|
||||
"Expected upsert_actor to be called on the registry"
|
||||
)
|
||||
call_kwargs = context.mock_registry.upsert_actor.call_args
|
||||
actual_name = call_kwargs.kwargs.get("name") or (
|
||||
call_kwargs.args[0] if call_kwargs.args else None
|
||||
)
|
||||
assert actual_name == context.positional_name, (
|
||||
f"Expected upsert_actor called with positional name={context.positional_name!r}, "
|
||||
f"but got name={actual_name!r} (config name was 'local/config-name-actor')"
|
||||
)
|
||||
|
||||
|
||||
@then("the actor command should fail with missing argument error")
|
||||
@then("the actor add should fail with an error about missing name")
|
||||
def step_impl(context: Any) -> None:
|
||||
assert context.result.exit_code != 0, (
|
||||
f"Expected non-zero exit_code, got {context.result.exit_code}.\n"
|
||||
f"Output:\n{context.result.output}"
|
||||
)
|
||||
assert "name" in context.result.output.lower(), (
|
||||
f"Expected error message to mention 'name', got:\n{context.result.output}"
|
||||
)
|
||||
|
||||
@@ -126,7 +126,6 @@ def step_impl(context: Any) -> None:
|
||||
|
||||
@when("I run actor add with the typed config")
|
||||
def step_impl(context: Any) -> None:
|
||||
actor_name = context.actor_config_data.get("name", "local/rich-actor")
|
||||
with patch("cleveragents.cli.commands.actor._get_services") as mock_svc:
|
||||
registry = MagicMock()
|
||||
# registry.add() is the YAML-first path used by the add command
|
||||
@@ -134,13 +133,12 @@ def step_impl(context: Any) -> None:
|
||||
mock_svc.return_value = (MagicMock(), registry)
|
||||
context.result = context.runner.invoke(
|
||||
actor_app,
|
||||
["add", actor_name, "--config", str(context.actor_config_path)],
|
||||
["add", "--config", str(context.actor_config_path)],
|
||||
)
|
||||
|
||||
|
||||
@when("I run actor add with the capabilities config")
|
||||
def step_impl(context: Any) -> None:
|
||||
actor_name = context.actor_config_data.get("name", "local/cap-actor")
|
||||
with patch("cleveragents.cli.commands.actor._get_services") as mock_svc:
|
||||
registry = MagicMock()
|
||||
# registry.add() is the YAML-first path used by the add command
|
||||
@@ -148,13 +146,12 @@ def step_impl(context: Any) -> None:
|
||||
mock_svc.return_value = (MagicMock(), registry)
|
||||
context.result = context.runner.invoke(
|
||||
actor_app,
|
||||
["add", actor_name, "--config", str(context.actor_config_path)],
|
||||
["add", "--config", str(context.actor_config_path)],
|
||||
)
|
||||
|
||||
|
||||
@when("I run actor add with the tools config")
|
||||
def step_impl(context: Any) -> None:
|
||||
actor_name = context.actor_config_data.get("name", "local/tool-actor")
|
||||
with patch("cleveragents.cli.commands.actor._get_services") as mock_svc:
|
||||
registry = MagicMock()
|
||||
# registry.add() is the YAML-first path used by the add command
|
||||
@@ -162,13 +159,12 @@ def step_impl(context: Any) -> None:
|
||||
mock_svc.return_value = (MagicMock(), registry)
|
||||
context.result = context.runner.invoke(
|
||||
actor_app,
|
||||
["add", actor_name, "--config", str(context.actor_config_path)],
|
||||
["add", "--config", str(context.actor_config_path)],
|
||||
)
|
||||
|
||||
|
||||
@when("I run actor add with the typed config in json format")
|
||||
def step_impl(context: Any) -> None:
|
||||
actor_name = context.actor_config_data.get("name", "local/rich-actor")
|
||||
with patch("cleveragents.cli.commands.actor._get_services") as mock_svc:
|
||||
registry = MagicMock()
|
||||
# registry.add() is the YAML-first path used by the add command
|
||||
@@ -178,7 +174,6 @@ def step_impl(context: Any) -> None:
|
||||
actor_app,
|
||||
[
|
||||
"add",
|
||||
actor_name,
|
||||
"--config",
|
||||
str(context.actor_config_path),
|
||||
"--format",
|
||||
|
||||
@@ -424,7 +424,7 @@ def step_run_actor_add(context: Context, name: str) -> None:
|
||||
mock_get_services.return_value = (mock_service, mock_registry)
|
||||
result = runner.invoke(
|
||||
actor_app,
|
||||
["add", name, "--config", str(context.config_file)],
|
||||
["add", "--config", str(context.config_file)],
|
||||
catch_exceptions=True,
|
||||
)
|
||||
|
||||
@@ -903,7 +903,7 @@ def step_run_actor_add_no_name(context: Context) -> None:
|
||||
mock_get_services.return_value = (mock_service, mock_registry)
|
||||
result = runner.invoke(
|
||||
actor_app,
|
||||
["add", name, "--config", str(context.config_file)],
|
||||
["add", "--config", str(context.config_file)],
|
||||
catch_exceptions=True,
|
||||
)
|
||||
|
||||
|
||||
@@ -63,7 +63,7 @@ def step_run_actor_add_yaml_first(context: Any) -> None:
|
||||
|
||||
context.result = context.runner.invoke(
|
||||
actor_app,
|
||||
["add", "local/test-actor", "--config", str(context.actor_config_path)],
|
||||
["add", "--config", str(context.actor_config_path)],
|
||||
)
|
||||
context.mock_actor_registry = mock_registry
|
||||
|
||||
@@ -86,10 +86,9 @@ def step_run_actor_add_update_yaml_first(context: Any) -> None:
|
||||
actor_app,
|
||||
[
|
||||
"add",
|
||||
"local/test-actor",
|
||||
"--config",
|
||||
str(context.actor_config_path),
|
||||
"--update",
|
||||
"--update","
|
||||
],
|
||||
)
|
||||
context.mock_actor_registry = mock_registry
|
||||
@@ -115,10 +114,9 @@ def step_run_actor_add_set_default_yaml_first(context: Any) -> None:
|
||||
actor_app,
|
||||
[
|
||||
"add",
|
||||
"local/test-actor",
|
||||
"--config",
|
||||
str(context.actor_config_path),
|
||||
"--set-default",
|
||||
"--set-default","
|
||||
],
|
||||
)
|
||||
context.mock_actor_registry = mock_registry
|
||||
@@ -146,7 +144,6 @@ def step_run_actor_add_option_override_yaml_first(context: Any) -> None:
|
||||
actor_app,
|
||||
[
|
||||
"add",
|
||||
"local/test-actor",
|
||||
"--config",
|
||||
str(context.actor_config_path),
|
||||
"--option",
|
||||
|
||||
@@ -193,7 +193,7 @@ def step_impl(context):
|
||||
mock_actor_service = MagicMock()
|
||||
mock_actor_registry = MagicMock()
|
||||
mock_get_services.return_value = (mock_actor_service, mock_actor_registry)
|
||||
context.result = context.runner.invoke(actor_app, ["add", "local/test-actor"])
|
||||
context.result = context.runner.invoke(actor_app, ["add"])
|
||||
context.mock_actor_registry = mock_actor_registry
|
||||
context.expected_error = "Config file is required"
|
||||
|
||||
@@ -216,16 +216,11 @@ def step_impl(context):
|
||||
# Return the mocked services
|
||||
mock_get_services.return_value = (mock_actor_service, mock_actor_registry)
|
||||
|
||||
actor_name = (
|
||||
context.actor_config_data.get("name", "local/test-actor")
|
||||
if isinstance(context.actor_config_data, dict)
|
||||
else "local/test-actor"
|
||||
)
|
||||
|
||||
context.result = context.runner.invoke(
|
||||
actor_app,
|
||||
[
|
||||
"add",
|
||||
actor_name,
|
||||
"--config",
|
||||
str(context.actor_config_path),
|
||||
],
|
||||
@@ -260,12 +255,11 @@ def step_impl(context):
|
||||
# Return the mocked services
|
||||
mock_get_services.return_value = (mock_actor_service, mock_actor_registry)
|
||||
|
||||
actor_name = context.actor_config_data.get("name", "local/test-actor")
|
||||
|
||||
context.result = context.runner.invoke(
|
||||
actor_app,
|
||||
[
|
||||
"add",
|
||||
actor_name,
|
||||
"--config",
|
||||
str(context.actor_config_path),
|
||||
*option_args,
|
||||
@@ -289,12 +283,11 @@ def step_impl(context):
|
||||
# Return the mocked services
|
||||
mock_get_services.return_value = (mock_actor_service, mock_actor_registry)
|
||||
|
||||
actor_name = context.actor_config_data.get("name", "local/test-actor")
|
||||
|
||||
context.result = context.runner.invoke(
|
||||
actor_app,
|
||||
[
|
||||
"add",
|
||||
actor_name,
|
||||
"--config",
|
||||
str(context.actor_config_path),
|
||||
*option_args,
|
||||
@@ -318,12 +311,11 @@ def step_impl(context):
|
||||
# Return the mocked services
|
||||
mock_get_services.return_value = (mock_actor_service, mock_actor_registry)
|
||||
|
||||
actor_name = context.actor_config_data.get("name", "local/test-actor")
|
||||
|
||||
context.result = context.runner.invoke(
|
||||
actor_app,
|
||||
[
|
||||
"add",
|
||||
actor_name,
|
||||
"--config",
|
||||
str(context.actor_config_path),
|
||||
*option_args,
|
||||
@@ -335,18 +327,12 @@ def step_impl(context):
|
||||
|
||||
@when("I run actor add with malformed option")
|
||||
def step_impl(context):
|
||||
actor_name = (
|
||||
context.actor_config_data.get("name", "local/test-actor")
|
||||
if isinstance(context.actor_config_data, dict)
|
||||
else "local/test-actor"
|
||||
)
|
||||
with patch("cleveragents.cli.commands.actor._get_services") as mock_get_services:
|
||||
mock_get_services.return_value = (MagicMock(), MagicMock())
|
||||
context.result = context.runner.invoke(
|
||||
actor_app,
|
||||
[
|
||||
"add",
|
||||
actor_name,
|
||||
"--config",
|
||||
str(context.actor_config_path),
|
||||
"--option",
|
||||
@@ -358,18 +344,12 @@ def step_impl(context):
|
||||
|
||||
@when("I run actor add with empty option key")
|
||||
def step_impl(context):
|
||||
actor_name = (
|
||||
context.actor_config_data.get("name", "local/test-actor")
|
||||
if isinstance(context.actor_config_data, dict)
|
||||
else "local/test-actor"
|
||||
)
|
||||
with patch("cleveragents.cli.commands.actor._get_services") as mock_get_services:
|
||||
mock_get_services.return_value = (MagicMock(), MagicMock())
|
||||
context.result = context.runner.invoke(
|
||||
actor_app,
|
||||
[
|
||||
"add",
|
||||
actor_name,
|
||||
"--config",
|
||||
str(context.actor_config_path),
|
||||
"--option",
|
||||
@@ -397,16 +377,11 @@ def step_impl(context):
|
||||
# Return the mocked services
|
||||
mock_get_services.return_value = (mock_actor_service, mock_actor_registry)
|
||||
|
||||
actor_name = (
|
||||
context.actor_config_data.get("name", "local/test-actor")
|
||||
if isinstance(context.actor_config_data, dict)
|
||||
else "local/test-actor"
|
||||
)
|
||||
|
||||
context.result = context.runner.invoke(
|
||||
actor_app,
|
||||
[
|
||||
"add",
|
||||
actor_name,
|
||||
"--config",
|
||||
str(context.actor_config_path),
|
||||
"--unsafe",
|
||||
@@ -435,12 +410,10 @@ def step_impl(context):
|
||||
)
|
||||
mock_get_services.return_value = (actor_service, None)
|
||||
|
||||
actor_name = context.actor_config_data.get("name", "service/graph-actor")
|
||||
context.result = context.runner.invoke(
|
||||
actor_app,
|
||||
[
|
||||
"add",
|
||||
actor_name,
|
||||
"--config",
|
||||
str(context.actor_config_path),
|
||||
],
|
||||
@@ -459,7 +432,6 @@ def step_impl(context):
|
||||
actor_app,
|
||||
[
|
||||
"add",
|
||||
"local/test-actor",
|
||||
"--config",
|
||||
str(missing_path),
|
||||
],
|
||||
@@ -493,7 +465,6 @@ def step_impl(context):
|
||||
actor_app,
|
||||
[
|
||||
"add",
|
||||
"local/test-actor",
|
||||
"--config",
|
||||
str(config_path),
|
||||
],
|
||||
@@ -507,16 +478,11 @@ def step_impl(context):
|
||||
actor_service = MagicMock()
|
||||
mock_get_services.return_value = (actor_service, None)
|
||||
|
||||
actor_name = (
|
||||
context.actor_config_data.get("name", "local/test-actor")
|
||||
if isinstance(context.actor_config_data, dict)
|
||||
else "local/test-actor"
|
||||
)
|
||||
|
||||
context.result = context.runner.invoke(
|
||||
actor_app,
|
||||
[
|
||||
"add",
|
||||
actor_name,
|
||||
"--config",
|
||||
str(context.actor_config_path),
|
||||
],
|
||||
@@ -551,16 +517,11 @@ def step_impl(context):
|
||||
actor_service = MagicMock()
|
||||
mock_get_services.return_value = (actor_service, None)
|
||||
|
||||
actor_name = (
|
||||
context.actor_config_data.get("name", "local/test-actor")
|
||||
if isinstance(context.actor_config_data, dict)
|
||||
else "local/test-actor"
|
||||
)
|
||||
|
||||
context.result = context.runner.invoke(
|
||||
actor_app,
|
||||
[
|
||||
"add",
|
||||
actor_name,
|
||||
"--config",
|
||||
str(context.actor_config_path),
|
||||
],
|
||||
|
||||
@@ -216,12 +216,10 @@ def step_add_update(context: Any) -> None:
|
||||
# registry.add() is the YAML-first path used by the add command
|
||||
mock_registry.add.return_value = mock_actor
|
||||
mock_svc.return_value = (mock_service, mock_registry)
|
||||
actor_name = context.actor_config_data.get("name", "local/test-actor")
|
||||
context.result = context.runner.invoke(
|
||||
actor_app,
|
||||
[
|
||||
"add",
|
||||
actor_name,
|
||||
"--config",
|
||||
str(context.actor_config_path),
|
||||
"--update",
|
||||
@@ -251,12 +249,10 @@ def step_add_format_json(context: Any) -> None:
|
||||
# registry.add() is the YAML-first path used by the add command
|
||||
mock_registry.add.return_value = mock_actor
|
||||
mock_svc.return_value = (mock_service, mock_registry)
|
||||
actor_name = context.actor_config_data.get("name", "local/test-actor")
|
||||
context.result = context.runner.invoke(
|
||||
actor_app,
|
||||
[
|
||||
"add",
|
||||
actor_name,
|
||||
"--config",
|
||||
str(context.actor_config_path),
|
||||
"--format",
|
||||
@@ -274,12 +270,10 @@ def step_add_format_yaml(context: Any) -> None:
|
||||
# registry.add() is the YAML-first path used by the add command
|
||||
mock_registry.add.return_value = mock_actor
|
||||
mock_svc.return_value = (mock_service, mock_registry)
|
||||
actor_name = context.actor_config_data.get("name", "local/test-actor")
|
||||
context.result = context.runner.invoke(
|
||||
actor_app,
|
||||
[
|
||||
"add",
|
||||
actor_name,
|
||||
"--config",
|
||||
str(context.actor_config_path),
|
||||
"--format",
|
||||
|
||||
@@ -533,13 +533,6 @@ def _print_role_warnings(config_blob: dict[str, Any]) -> None:
|
||||
|
||||
@app.command()
|
||||
def add(
|
||||
name: Annotated[
|
||||
str,
|
||||
typer.Argument(
|
||||
help="Namespaced actor name (e.g. local/my-actor)",
|
||||
metavar="NAME",
|
||||
),
|
||||
],
|
||||
config: Annotated[
|
||||
Path | None,
|
||||
typer.Option("--config", "-c", help="Path to JSON/YAML actor config"),
|
||||
@@ -569,19 +562,18 @@ def add(
|
||||
) -> None:
|
||||
"""Add a new actor configuration.
|
||||
|
||||
The actor name is provided as a required positional argument. The YAML/JSON
|
||||
configuration file specified with ``--config`` supplies the actor settings.
|
||||
The positional ``NAME`` takes precedence over any ``name`` field in the
|
||||
config file.
|
||||
The actor name is read from the ``name`` field in the YAML/JSON
|
||||
configuration file specified with ``--config``. The config file supplies
|
||||
all actor settings including the namespaced name.
|
||||
|
||||
Signature:
|
||||
``agents actor add <NAME> --config <FILE> [--update] [--unsafe]
|
||||
``agents actor add --config <FILE> [--update] [--unsafe]
|
||||
[--set-default] [--option key=value] [--format FORMAT]``
|
||||
|
||||
Examples:
|
||||
agents actor add local/my-actor --config ./actors/my-actor.yaml
|
||||
agents actor add local/my-actor --config ./actors/my-actor.yaml --update
|
||||
agents actor add local/my-actor --config actor.yaml --format json
|
||||
agents actor add --config ./actors/my-actor.yaml
|
||||
agents actor add --config ./actors/my-actor.yaml --update
|
||||
agents actor add --config actor.yaml --format json
|
||||
"""
|
||||
service, registry = _get_services()
|
||||
option_overrides = _parse_option_overrides(option)
|
||||
@@ -597,6 +589,16 @@ def add(
|
||||
assert loaded is not None, "unreachable: config is not None"
|
||||
yaml_text, config_blob = loaded
|
||||
|
||||
# Read actor name from config file.
|
||||
if not config_blob.get("name"):
|
||||
raise typer.BadParameter(
|
||||
f"Actor config must contain a 'name' field. "
|
||||
f"Use a namespaced format like 'local/my-actor'.
|
||||
"
|
||||
f"Config file: {config}"
|
||||
)
|
||||
name = config_blob["name"]
|
||||
|
||||
# Validate v3 config via ActorConfigSchema if detected.
|
||||
# This ensures v3 actors are fully validated (cycle detection, required
|
||||
# fields, enum values) before the registry stores them.
|
||||
|
||||
Reference in New Issue
Block a user