Skip to content
Merged
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
49 changes: 0 additions & 49 deletions .pr/4694/automated-tests.txt

This file was deleted.

7 changes: 0 additions & 7 deletions .pr/4694/concurrency.json

This file was deleted.

16 changes: 0 additions & 16 deletions .pr/4694/live-official-sdk.json

This file was deleted.

7 changes: 0 additions & 7 deletions .pr/4694/restart-replay.json

This file was deleted.

78 changes: 0 additions & 78 deletions .pr/4694/validation.md

This file was deleted.

44 changes: 40 additions & 4 deletions openhands-sdk/openhands/sdk/agent/acp_agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -1957,7 +1957,7 @@ def activate_file_credential_binding(
self._file_credential_bindings[secret_name] = binding

def restart_for_updated_credentials(self, secret_names: Collection[str]) -> None:
configured = {spec.secret_name for spec in self.acp_file_secrets}
configured = {spec.secret_name for spec in self._active_file_secrets()}
with self._file_credential_lock:
self._replace_file_credentials_on_next_materialisation.update(
configured.intersection(secret_names)
Expand Down Expand Up @@ -2452,7 +2452,7 @@ def _present_file_secret_names(self, state: ConversationState) -> set[str]:
(their values are file blobs, not env vars the subprocess can reference
by name).
"""
configured = {spec.secret_name for spec in self.acp_file_secrets}
configured = {spec.secret_name for spec in self._active_file_secrets()}
if not configured:
return set()
return set(state.secret_registry.secret_sources) & configured
Expand Down Expand Up @@ -2587,6 +2587,42 @@ def _resolved_provider(self) -> ACPProviderInfo | None:
self.acp_server or ""
) or detect_acp_provider_by_command(self.acp_command)

def _active_file_secrets(self) -> list[ACPFileSecretSpec]:
"""The file-secret specs that apply to the provider this agent runs.

Drops the specs that are *another registered provider's* reserved
credential and keeps everything else, so a harness added upstream cannot
change how this provider's conversation treats a secret carrying the new
reserved name (see #4923). A name several providers share stays: it is
this provider's too.

Deliberately no provenance test. :attr:`acp_file_secrets` defaults to the
union across the registry, but a persisted conversation carries whatever
that union was when it was written, so comparing against today's default
would read an older list as a caller override and silently stop scoping
after an upgrade. Filtering by ownership needs no such distinction, and a
spec for a CLI outside the registry is owned by nobody and always applies.

An unrecognised server keeps every spec, matching
:meth:`_strip_conflicting_env`: without an identity we cannot tell whose
credential a reserved name belongs to.
"""
provider = self._resolved_provider()
if provider is None:
return list(self.acp_file_secrets)
own = {spec.secret_name for spec in provider.file_secrets}
owned_elsewhere = {
spec.secret_name
for key, info in ACP_PROVIDERS.items()
if key != provider.key
for spec in info.file_secrets
} - own
return [
spec
for spec in self.acp_file_secrets
if spec.secret_name not in owned_elsewhere
]

def _strip_conflicting_env(self, env: dict[str, str]) -> None:
"""Remove env vars that would defeat this provider's own credential.

Expand Down Expand Up @@ -2615,7 +2651,7 @@ def _strip_conflicting_env(self, env: dict[str, str]) -> None:
def _materialise_file_secrets(
self, state: ConversationState, env: dict[str, str]
) -> None:
for spec in self.acp_file_secrets:
for spec in self._active_file_secrets():
name = spec.secret_name
with self._file_credential_lock:
replace_existing = (
Expand Down Expand Up @@ -3091,7 +3127,7 @@ async def _init() -> tuple[
or detect_acp_provider_by_agent_name(agent_name)
)
configured = _preconfigured_credentials(
auth_provider, self.acp_file_secrets, env
auth_provider, self._active_file_secrets(), env
)
if configured:
logger.info(
Expand Down
Loading
Loading