Context
Found during a manual observability test campaign on consolidation (C3 target: docs/swift/platform/OBSERVABILITY-AND-AUDIT.md). Related to #1972 (ReAct execution loop migration to create_agent + platform middleware frame) and OBSERV-03 (id-legend.yaml).
Problem
ContextAwareTool (libs/fred-runtime/fred_runtime/common/context_aware_tool.py) is the only place that emits:
- KPI:
agent.tool_latency_ms (histogram) / agent.tool_failed_total (counter)
- Audit:
agent.tool.invocation.{started,completed} (the guarantee documented in OBSERVABILITY-AND-AUDIT.md §9 as "True today — emitted... for every actually-executed tool call")
But ContextAwareTool is only constructed in libs/fred-runtime/fred_runtime/common/mcp_toolkit.py:144, i.e. only for MCP-catalog-sourced tools. Native capability tools (e.g. DocumentAccessCapability's search_documents_using_vectorization, a plain @tool-decorated function shipped on the capability's own AgentMiddleware.tools) never pass through ContextAwareTool at all.
Confirmed live (2026-07-18): a chat turn that called search_documents_using_vectorization produced zero agent.tool_latency_ms samples and zero agent.tool.invocation.* audit events, while an MCP-sourced tool call would have produced both. So today:
- The documented "every tool invocation is audited" guarantee is false for capability-native tools.
- A production Grafana panel for "MCP/tool call latency" is blind to capability-native tool calls.
Target fix (not a per-tool patch)
LangChain's AgentMiddleware.awrap_tool_call(request: ToolCallRequest, handler) (langchain/agents/middleware/types.py) intercepts every tool call the graph executes, regardless of source. Add one new platform middleware implementing this hook, insert it into the fixed frame (libs/fred-runtime/fred_runtime/react/middleware/frame.py::build_react_platform_middleware_frame), and remove the now-redundant KPI/audit emission from ContextAwareTool (keep everything else there — context injection, MCP content normalization, error-message formatting stay MCP-specific and untouched).
See the follow-up prompt/PR for the full implementation spec.
Context
Found during a manual observability test campaign on
consolidation(C3 target: docs/swift/platform/OBSERVABILITY-AND-AUDIT.md). Related to #1972 (ReAct execution loop migration tocreate_agent+ platform middleware frame) and OBSERV-03 (id-legend.yaml).Problem
ContextAwareTool(libs/fred-runtime/fred_runtime/common/context_aware_tool.py) is the only place that emits:agent.tool_latency_ms(histogram) /agent.tool_failed_total(counter)agent.tool.invocation.{started,completed}(the guarantee documented in OBSERVABILITY-AND-AUDIT.md §9 as "True today — emitted... for every actually-executed tool call")But
ContextAwareToolis only constructed inlibs/fred-runtime/fred_runtime/common/mcp_toolkit.py:144, i.e. only for MCP-catalog-sourced tools. Native capability tools (e.g.DocumentAccessCapability'ssearch_documents_using_vectorization, a plain@tool-decorated function shipped on the capability's ownAgentMiddleware.tools) never pass throughContextAwareToolat all.Confirmed live (2026-07-18): a chat turn that called
search_documents_using_vectorizationproduced zeroagent.tool_latency_mssamples and zeroagent.tool.invocation.*audit events, while an MCP-sourced tool call would have produced both. So today:Target fix (not a per-tool patch)
LangChain's
AgentMiddleware.awrap_tool_call(request: ToolCallRequest, handler)(langchain/agents/middleware/types.py) intercepts every tool call the graph executes, regardless of source. Add one new platform middleware implementing this hook, insert it into the fixed frame (libs/fred-runtime/fred_runtime/react/middleware/frame.py::build_react_platform_middleware_frame), and remove the now-redundant KPI/audit emission fromContextAwareTool(keep everything else there — context injection, MCP content normalization, error-message formatting stay MCP-specific and untouched).See the follow-up prompt/PR for the full implementation spec.