Skip to content

Commit 807f155

Browse files
feat: simplify excludelist
1 parent ad852ef commit 807f155

File tree

6 files changed

+370
-322
lines changed

6 files changed

+370
-322
lines changed

src/agentex/lib/core/services/adk/acp/acp.py

Lines changed: 23 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
from typing import Any, List, cast
2-
import fnmatch
32

43
from agentex import AsyncAgentex
54
from agentex.lib.core.tracing.tracer import AsyncTracer
@@ -11,6 +10,10 @@
1110
from agentex.types.task_message import TaskMessage
1211
from agentex.types.task_message_content import TaskMessageContent
1312
from agentex.types.task_message_content_param import TaskMessageContentParam
13+
from agentex.types.agent_rpc_params import (
14+
ParamsCancelTaskRequest as RpcParamsCancelTaskRequest,
15+
ParamsSendEventRequest as RpcParamsSendEventRequest,
16+
)
1417

1518
logger = make_logger(__name__)
1619

@@ -24,77 +27,6 @@ def __init__(
2427
self._agentex_client = agentex_client
2528
self._tracer = tracer
2629

27-
async def _get_agent_header_allowlist(
28-
self, agent_name: str | None = None, agent_id: str | None = None
29-
) -> list[str] | None:
30-
"""
31-
Get agent's header allowlist from manifest.
32-
33-
Returns None for pass-through behavior (all headers forwarded).
34-
Returns list[str] of allowed header patterns if agent has filtering requirements.
35-
36-
Note: In a production system, this would load the agent's manifest from a registry/database
37-
based on agent_name/agent_id and return the header_allowlist. For now, we implement
38-
pass-through by default as requested.
39-
40-
TODO: Integrate with agent registry to load manifest-based header allowlists
41-
"""
42-
# Pass-through by default - forward all headers unless agent has specific filtering config
43-
# In production, this would query an agent registry/database:
44-
# 1. Look up agent by name/id
45-
# 2. Get agent's manifest configuration
46-
# 3. Return manifest.agent.header_allowlist
47-
return None
48-
49-
def _filter_headers(
50-
self,
51-
headers: dict[str, str] | None,
52-
allowlist: list[str] | None
53-
) -> dict[str, str]:
54-
"""
55-
Filter headers based on agent's allowlist.
56-
57-
Pass-through by default: if no allowlist provided, all headers are forwarded.
58-
If allowlist exists, only matching headers are forwarded.
59-
60-
Args:
61-
headers: Headers to filter
62-
allowlist: Agent's header allowlist (None = pass-through all)
63-
64-
Returns:
65-
Filtered headers dictionary
66-
"""
67-
if not headers:
68-
return {}
69-
70-
# Pass-through behavior: if no allowlist, forward all headers
71-
if allowlist is None:
72-
logger.info("No header allowlist found, passing through all %d headers", len(headers))
73-
return headers
74-
75-
# Apply filtering based on allowlist
76-
if not allowlist:
77-
logger.info("Empty allowlist, blocking all headers")
78-
return {}
79-
80-
filtered = {}
81-
for header_name, header_value in headers.items():
82-
# Check against allowlist patterns (case-insensitive)
83-
header_allowed = False
84-
for pattern in allowlist:
85-
if fnmatch.fnmatch(header_name.lower(), pattern.lower()):
86-
header_allowed = True
87-
break
88-
89-
if header_allowed:
90-
filtered[header_name] = header_value
91-
logger.info("Allowed header: %s", header_name)
92-
else:
93-
logger.info("Header '%s' not in allowlist, ignoring", header_name)
94-
95-
logger.info("Filtered %d headers from %d based on allowlist", len(filtered), len(headers))
96-
return filtered
97-
9830
async def task_create(
9931
self,
10032
name: str | None = None,
@@ -118,10 +50,9 @@ async def task_create(
11850
) as span:
11951
heartbeat_if_in_workflow("task create")
12052

121-
# Extract headers from request and filter them
53+
# Extract headers from request; pass-through to agent
12254
headers = request.get("headers") if request else None
123-
header_allowlist = await self._get_agent_header_allowlist(agent_name, agent_id)
124-
filtered_headers = self._filter_headers(headers, header_allowlist)
55+
filtered_headers: dict[str, str] = headers or {}
12556

12657
if agent_name:
12758
json_rpc_response = await self._agentex_client.agents.rpc_by_name(
@@ -176,10 +107,9 @@ async def message_send(
176107
) as span:
177108
heartbeat_if_in_workflow("message send")
178109

179-
# Extract headers from request and filter them
110+
# Extract headers from request; pass-through to agent
180111
headers = request.get("headers") if request else None
181-
header_allowlist = await self._get_agent_header_allowlist(agent_name, agent_id)
182-
filtered_headers = self._filter_headers(headers, header_allowlist)
112+
filtered_headers: dict[str, str] = headers or {}
183113

184114
if agent_name:
185115
json_rpc_response = await self._agentex_client.agents.rpc_by_name(
@@ -238,34 +168,33 @@ async def event_send(
238168
"agent_id": agent_id,
239169
"agent_name": agent_name,
240170
"task_id": task_id,
171+
"task_name": task_name,
241172
"content": content,
242173
},
243174
) as span:
244175
heartbeat_if_in_workflow("event send")
245176

246-
# Extract headers from request and filter them
177+
# Extract headers from request; pass-through to agent
247178
headers = request.get("headers") if request else None
248-
header_allowlist = await self._get_agent_header_allowlist(agent_name, agent_id)
249-
filtered_headers = self._filter_headers(headers, header_allowlist)
179+
filtered_headers: dict[str, str] = headers or {}
250180

181+
rpc_event_params: RpcParamsSendEventRequest = {
182+
"task_id": task_id,
183+
"task_name": task_name,
184+
"content": cast(TaskMessageContentParam, content.model_dump()),
185+
}
251186
if agent_name:
252187
json_rpc_response = await self._agentex_client.agents.rpc_by_name(
253188
agent_name=agent_name,
254189
method="event/send",
255-
params={
256-
"task_id": task_id,
257-
"content": cast(TaskMessageContentParam, content.model_dump()),
258-
},
190+
params=rpc_event_params,
259191
extra_headers=filtered_headers,
260192
)
261193
elif agent_id:
262194
json_rpc_response = await self._agentex_client.agents.rpc(
263195
agent_id=agent_id,
264196
method="event/send",
265-
params={
266-
"task_id": task_id,
267-
"content": cast(TaskMessageContentParam, content.model_dump()),
268-
},
197+
params=rpc_event_params,
269198
extra_headers=filtered_headers,
270199
)
271200
else:
@@ -296,7 +225,7 @@ async def task_cancel(
296225
agent_name: Name of the agent that owns the task
297226
trace_id: Trace ID for tracing
298227
parent_span_id: Parent span ID for tracing
299-
extra_headers: Headers to forward to the agent
228+
request: Additional request context including headers to forward to the agent
300229
301230
Returns:
302231
Task entry representing the cancelled task
@@ -325,13 +254,12 @@ async def task_cancel(
325254
) as span:
326255
heartbeat_if_in_workflow("task cancel")
327256

328-
# Extract headers from request and filter them
257+
# Extract headers from request; pass-through to agent
329258
headers = request.get("headers") if request else None
330-
header_allowlist = await self._get_agent_header_allowlist(agent_name, agent_id)
331-
filtered_headers = self._filter_headers(headers, header_allowlist)
259+
filtered_headers: dict[str, str] = headers or {}
332260

333261
# Build params for the agent (task identification)
334-
params = {}
262+
params: RpcParamsCancelTaskRequest = {}
335263
if task_id:
336264
params["task_id"] = task_id
337265
if task_name:
@@ -346,6 +274,7 @@ async def task_cancel(
346274
extra_headers=filtered_headers,
347275
)
348276
else: # agent_id is provided (validated above)
277+
assert agent_id is not None
349278
json_rpc_response = await self._agentex_client.agents.rpc(
350279
agent_id=agent_id,
351280
method="task/cancel",

src/agentex/lib/sdk/config/agent_config.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,6 @@ class AgentConfig(BaseModel):
2828
temporal: TemporalConfig | None = Field(
2929
default=None, description="Temporal workflow configuration for this agent"
3030
)
31-
header_allowlist: list[str] | None = Field(
32-
default=None, description="List of header patterns this agent is allowed to receive. If not provided, all headers are forwarded."
33-
)
3431

3532
def is_temporal_agent(self) -> bool:
3633
"""Check if this agent uses Temporal workflows"""

src/agentex/lib/sdk/fastacp/base/base_acp_server.py

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,10 @@
2626
from agentex.lib.utils.logging import make_logger
2727
from agentex.lib.utils.model_utils import BaseModel
2828
from agentex.lib.utils.registration import register_agent
29+
from agentex.lib.sdk.fastacp.base.constants import (
30+
FASTACP_HEADER_SKIP_EXACT,
31+
FASTACP_HEADER_SKIP_PREFIXES,
32+
)
2933

3034
logger = make_logger(__name__)
3135

@@ -128,11 +132,13 @@ async def _handle_jsonrpc(self, request: Request):
128132
),
129133
)
130134

131-
# Extract all custom headers (already filtered by AgentEx service layer if needed)
132-
# Pass through all headers since filtering happens at the service layer
135+
# Extract application headers, excluding sensitive/transport headers per FASTACP_* rules
136+
# Forward filtered headers via params.request.headers to agent handlers
133137
custom_headers = {
134-
key: value for key, value in request.headers.items()
135-
if not key.lower().startswith(('content-', 'host', 'user-agent', 'accept')) # Skip standard HTTP headers
138+
key: value
139+
for key, value in request.headers.items()
140+
if key.lower() not in FASTACP_HEADER_SKIP_EXACT
141+
and not any(key.lower().startswith(p) for p in FASTACP_HEADER_SKIP_PREFIXES)
136142
}
137143

138144
# Parse params into appropriate model based on method and include headers
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
from __future__ import annotations
2+
3+
# Header filtering rules for FastACP server
4+
5+
# Prefixes to skip (case-insensitive beginswith checks)
6+
FASTACP_HEADER_SKIP_PREFIXES: tuple[str, ...] = (
7+
"content-",
8+
"host",
9+
"user-agent",
10+
"x-forwarded-",
11+
"sec-",
12+
)
13+
14+
# Exact header names to skip (case-insensitive matching done by lowercasing keys)
15+
FASTACP_HEADER_SKIP_EXACT: set[str] = {
16+
"x-agent-api-key",
17+
"connection",
18+
"accept-encoding",
19+
"cookie",
20+
"content-length",
21+
"transfer-encoding",
22+
}
23+
24+

0 commit comments

Comments
 (0)