Tools carry no signal about what kind of side effect they have. AsTool already exposes an arbitrary $metadata array, but nothing standardizes an "effect" key or reads it anywhere in the framework, so every consumer who wants to gate tool calls (human approval, audit logging, execution safety) reinvents this classification from scratch.
Proposal: a standard effect key (read, write, external) on AsTool/Tool, defaulting to external when unset (fail cautious for unclassified tools). No enforcement built in, just a documented, first-class place to declare it, so a ToolCallRequested listener or a custom ToolExecutorInterface can consult Tool::getMetadata()['effect'] instead of maintaining a side table.
Context: came up building a durable/interruptible agent on top of Temporal (see https://github.com/gplanchat/durable-dev/blob/spike/agent-durable-symfony-ai/documentation/user/use-cases/durable-agent.md), where this classification decides which tool calls suspend the workflow for human approval and which pass through.
Tools carry no signal about what kind of side effect they have.
AsToolalready exposes an arbitrary$metadataarray, but nothing standardizes an "effect" key or reads it anywhere in the framework, so every consumer who wants to gate tool calls (human approval, audit logging, execution safety) reinvents this classification from scratch.Proposal: a standard
effectkey (read,write,external) onAsTool/Tool, defaulting toexternalwhen unset (fail cautious for unclassified tools). No enforcement built in, just a documented, first-class place to declare it, so aToolCallRequestedlistener or a customToolExecutorInterfacecan consultTool::getMetadata()['effect']instead of maintaining a side table.Context: came up building a durable/interruptible agent on top of Temporal (see https://github.com/gplanchat/durable-dev/blob/spike/agent-durable-symfony-ai/documentation/user/use-cases/durable-agent.md), where this classification decides which tool calls suspend the workflow for human approval and which pass through.