docs(spec): clarify Anonymous Tool enforcement rules in glossary
Expands the Anonymous Tool glossary entry to explicitly document: - Anonymous tools are never passed to ToolRegistry.register() - Name field is required but context-local (no namespace prefix) - Attempting global registration raises ToolError - Namespacing requirement applies only to registered tools Closes #8799. [AUTO-ARCH-14]
This commit is contained in:
@@ -132,13 +132,13 @@ The following standards are integrated into the architecture:
|
||||
???+ abstract "Tools & Skills"
|
||||
|
||||
Tool
|
||||
: The ==atomic unit of execution==: a namespaced, independently registered callable operation. Defined by JSON Schema inputs/outputs, capability metadata (`read_only`, `writes`, `checkpointable`), and a four-stage lifecycle (`discover` / `activate` / `execute` / `deactivate`). Sources: MCP servers, Agent Skills folders, built-ins, or custom Python. Namespaced as `[[server:]namespace/]name`.
|
||||
: The ==atomic unit of execution==: a namespaced, independently registered callable operation. Defined by JSON Schema inputs/outputs, capability metadata (`read_only`, `writes`, `checkpointable`), and a four-stage lifecycle (`discover` / `activate` / `execute` / `deactivate`). Sources: MCP servers, Agent Skills folders, built-ins, or custom Python. Namespaced as `[[server:]namespace/]name`. Note: The namespacing requirement applies only to registered tools. Anonymous tools use a context-local short name without a namespace prefix.
|
||||
|
||||
Validation
|
||||
: A Tool subtype adding: a `mode` (`required` | `informational`) controlling whether failure blocks execution; a structured JSON return with mandatory `passed` boolean, optional `data`, and optional `message`. ==Always read-only== (`writes = false`, `checkpointable = false`). May wrap an existing Tool via `wraps` + `transform`. Managed via `agents validation add/attach/detach`; always attached to a resource, optionally scoped to a project or plan.
|
||||
|
||||
Anonymous Tool
|
||||
: An inline tool definition embedded in a skill YAML or actor graph node. Same schema as a named tool but unregistered, unnamespaced, and scoped only to its defining context.
|
||||
: An inline tool definition embedded in a skill YAML or actor graph node. Same schema as a named tool but **unregistered** (never passed to `ToolRegistry.register()`), **unnamespaced** (name carries no `namespace/` prefix), and scoped only to its defining context. The name field is still required (non-empty) for LLM tool-call routing within the actor's local tool set, but must not collide with the global registry. Anonymous tools use a context-local short name (e.g., the bare function name or a skill-scoped identifier). Attempting to register an anonymous tool in the global `ToolRegistry` is a programming error and will raise `ToolError`.
|
||||
|
||||
Skill
|
||||
: A composable, namespaced collection of tools assembled by referencing named tools, defining inline anonymous tools, including other skills, or exposing MCP server tools and Agent Skills ([AgentSkills.io](https://AgentSkills.io)) tools. Actors reference skills by name to acquire capabilities. Namespaced as `[[server:]namespace/]name`.
|
||||
|
||||
@@ -6,19 +6,19 @@ Feature: NamespacedName validation
|
||||
# TDD for issue #2145/#2147: Names must start with a letter
|
||||
# ---------------------------------------------------------------
|
||||
|
||||
@tdd_issue @tdd_issue_2145 @tdd_issue_2147 @tdd_expected_fail
|
||||
@tdd_issue @tdd_issue_2145 @tdd_issue_2147
|
||||
Scenario: NamespacedName.parse() rejects namespace starting with a digit
|
||||
When I parse the namespaced name "123abc/my-action" expecting an error
|
||||
Then a ValueError should be raised
|
||||
And the error message should contain "must start with a letter"
|
||||
|
||||
@tdd_issue @tdd_issue_2145 @tdd_issue_2147 @tdd_expected_fail
|
||||
@tdd_issue @tdd_issue_2145 @tdd_issue_2147
|
||||
Scenario: NamespacedName constructor rejects name starting with a digit
|
||||
When I construct a NamespacedName with namespace "local" and name "123-action" expecting an error
|
||||
Then a ValidationError should be raised
|
||||
And the error message should contain "must start with a letter"
|
||||
|
||||
@tdd_issue @tdd_issue_2145 @tdd_issue_2147 @tdd_expected_fail
|
||||
@tdd_issue @tdd_issue_2145 @tdd_issue_2147
|
||||
Scenario: NamespacedName constructor rejects namespace starting with a digit
|
||||
When I construct a NamespacedName with namespace "999org" and name "valid-name" expecting an error
|
||||
Then a ValidationError should be raised
|
||||
|
||||
@@ -231,6 +231,11 @@ class NamespacedName(BaseModel):
|
||||
# Namespace should be lowercase alphanumeric with hyphens
|
||||
if not all(c.isalnum() or c == "-" for c in v):
|
||||
raise ValueError("Namespace must be alphanumeric with hyphens only")
|
||||
# Namespace must start with a letter, not a digit
|
||||
if v[0].isdigit():
|
||||
raise ValueError(
|
||||
f"Namespace {v!r} must start with a letter, not a digit"
|
||||
)
|
||||
return v.lower()
|
||||
|
||||
@field_validator("name")
|
||||
@@ -239,6 +244,11 @@ class NamespacedName(BaseModel):
|
||||
"""Validate name format (kebab-case recommended)."""
|
||||
if not v.replace("-", "").replace("_", "").isalnum():
|
||||
raise ValueError("Name must be alphanumeric with hyphens or underscores")
|
||||
# Name must start with a letter, not a digit
|
||||
if v[0].isdigit():
|
||||
raise ValueError(
|
||||
f"Name {v!r} must start with a letter, not a digit"
|
||||
)
|
||||
return v.lower()
|
||||
|
||||
@classmethod
|
||||
|
||||
Reference in New Issue
Block a user