Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,14 @@

<!-- version list -->

## Unreleased

### Changed

- **sdk**: Strict `runtime_auth_mode="jwt"` evaluation requests now require both
`target_type` and `target_id`; missing target context raises an error instead
of falling back to API-key auth.

## v7.7.0 (2026-05-07)

### Bug Fixes
Expand Down
16 changes: 15 additions & 1 deletion sdks/python/src/agent_control/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,7 @@ class _RefreshContext:
agent_name: str
server_url: str
api_key: str | None
api_key_header: str | None
target_type: str | None
target_id: str | None

Expand Down Expand Up @@ -221,6 +222,7 @@ def _snapshot_refresh_context() -> _RefreshContext:
agent = state.current_agent
server_url = state.server_url
api_key = state.api_key
api_key_header = state.api_key_header
target_type = state.target_type
target_id = state.target_id

Expand All @@ -234,6 +236,7 @@ def _snapshot_refresh_context() -> _RefreshContext:
agent_name=agent.agent_name,
server_url=server_url,
api_key=api_key,
api_key_header=api_key_header,
target_type=target_type,
target_id=target_id,
)
Expand All @@ -244,6 +247,7 @@ async def _fetch_controls_for_context_async(context: _RefreshContext) -> list[di
async with AgentControlClient(
base_url=context.server_url,
api_key=context.api_key,
api_key_header=context.api_key_header,
) as client:
response = await agents.list_agent_controls(
client,
Expand Down Expand Up @@ -430,6 +434,7 @@ def init(
agent_version: str | None = None,
server_url: str | None = None,
api_key: str | None = None,
api_key_header: str | None = None,
controls_file: str | None = None,
steps: list[StepSchemaDict] | None = None,
conflict_mode: Literal["strict", "overwrite"] = "overwrite",
Expand Down Expand Up @@ -468,6 +473,8 @@ def init(
server_url: Optional server URL (defaults to AGENT_CONTROL_URL env var
or http://localhost:8000)
api_key: Optional API key for authentication (defaults to AGENT_CONTROL_API_KEY env var)
api_key_header: Optional HTTP header name for API key authentication
(defaults to AGENT_CONTROL_API_KEY_HEADER env var or X-API-Key)
controls_file: Optional explicit path to controls.yaml (auto-discovered if not provided)
steps: Optional list of step schemas for registration:
[{"type": "tool", "name": "search", "input_schema": {...}, "output_schema": {...}}]
Expand Down Expand Up @@ -562,6 +569,8 @@ async def handle(message: str):
state.current_agent = next_agent
state.server_url = server_url or os.getenv('AGENT_CONTROL_URL') or 'http://localhost:8000'
state.api_key = api_key
state.api_key_header = api_key_header
state.runtime_token_cache.clear()
state.target_type = target_type
state.target_id = target_id

Expand Down Expand Up @@ -597,7 +606,9 @@ async def register() -> list[dict[str, Any]] | None:
assert state.current_agent is not None

async with AgentControlClient(
base_url=state.server_url, api_key=state.api_key
base_url=state.server_url,
api_key=state.api_key,
api_key_header=state.api_key_header,
) as client:
# Check server health first
try:
Expand Down Expand Up @@ -684,6 +695,7 @@ def run_in_thread() -> None:
batcher = init_observability(
server_url=state.server_url,
api_key=state.api_key,
api_key_header=state.api_key_header,
enabled=observability_enabled,
sink_name=observability_sink_name,
sink_config=observability_sink_config,
Expand Down Expand Up @@ -715,6 +727,8 @@ def _reset_state() -> None:
state.server_controls = None
state.server_url = None
state.api_key = None
state.api_key_header = None
state.runtime_token_cache.clear()
state.target_type = None
state.target_id = None

Expand Down
4 changes: 4 additions & 0 deletions sdks/python/src/agent_control/_state.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@

from typing import TYPE_CHECKING, Any

from .runtime_auth import RuntimeTokenCache

if TYPE_CHECKING:
from agent_control_models import Agent

Expand All @@ -24,6 +26,8 @@ def __init__(self) -> None:
self.server_controls: list[dict[str, Any]] | None = None
self.server_url: str | None = None
self.api_key: str | None = None
self.api_key_header: str | None = None
self.runtime_token_cache = RuntimeTokenCache()
# Optional target context fixed at init() time; both fields are set
# together or both remain None.
self.target_type: str | None = None
Expand Down
Loading
Loading