zelos.credential_injector

Credential Injector — Injects credentials into Task.constraints at dispatch time.

v1.1.0: Called by ExecutionEngine.dispatch() before Agent execution. Ensures credentials never appear in EventBus events (only in memory).

 1"""
 2Credential Injector — Injects credentials into Task.constraints at dispatch time.
 3
 4v1.1.0: Called by ExecutionEngine.dispatch() before Agent execution.
 5Ensures credentials never appear in EventBus events (only in memory).
 6"""
 7
 8from .credential_store import CredentialStore
 9
10
11class CredentialNotFoundError(Exception):
12    """Credential not available for the requested agent."""
13    pass
14
15
16class CredentialExpiredError(Exception):
17    """Credential has expired."""
18    pass
19
20
21class CredentialInjector:
22    """Injects credentials into Task.constraints at dispatch time.
23
24    Design principles:
25    - Credentials are injected in-memory only, not persisted to EventBus
26    - Agent A never sees Agent B's credentials
27    - Expired credentials trigger Task failure for retry evaluation
28    """
29
30    def __init__(self, store: CredentialStore | None = None):
31        self._store = store
32
33    @property
34    def store(self) -> CredentialStore | None:
35        return self._store
36
37    def set_store(self, store: CredentialStore) -> None:
38        self._store = store
39
40    def inject(self, task, agent_id: str, required_credentials: list[str]) -> dict:
41        """Inject credentials into Task.constraints.
42
43        Args:
44            task: The Task being dispatched.
45            agent_id: The agent receiving the task.
46            required_credentials: List of credential names this agent needs.
47
48        Returns:
49            dict of credential_name -> Credential.
50
51        Raises:
52            CredentialNotFoundError: if a required credential is missing.
53            CredentialExpiredError: if a required credential has expired.
54        """
55        if not self._store or not required_credentials:
56            return {}
57
58        credentials = {}
59        for cred_name in required_credentials:
60            cred = self._store.get(cred_name, agent_id)
61            if cred is None:
62                raise CredentialNotFoundError(
63                    f"Credential '{cred_name}' not available for agent '{agent_id}'"
64                )
65            if not self._store.validate(cred_name, agent_id):
66                # Try refresh (for OAuth2 etc.)
67                refreshed = self._store.refresh(cred_name, agent_id)
68                if refreshed and not refreshed.is_expired():
69                    cred = refreshed
70                else:
71                    raise CredentialExpiredError(
72                        f"Credential '{cred_name}' has expired for agent '{agent_id}'"
73                    )
74            credentials[cred_name] = cred
75
76        # Inject into task.constraints (in-memory only)
77        task.constraints = task.constraints or {}
78        task.constraints["credential_refs"] = {
79            name: cred.to_dict() for name, cred in credentials.items()
80        }
81
82        return credentials
class CredentialNotFoundError(builtins.Exception):
12class CredentialNotFoundError(Exception):
13    """Credential not available for the requested agent."""
14    pass

Credential not available for the requested agent.

class CredentialExpiredError(builtins.Exception):
17class CredentialExpiredError(Exception):
18    """Credential has expired."""
19    pass

Credential has expired.

class CredentialInjector:
22class CredentialInjector:
23    """Injects credentials into Task.constraints at dispatch time.
24
25    Design principles:
26    - Credentials are injected in-memory only, not persisted to EventBus
27    - Agent A never sees Agent B's credentials
28    - Expired credentials trigger Task failure for retry evaluation
29    """
30
31    def __init__(self, store: CredentialStore | None = None):
32        self._store = store
33
34    @property
35    def store(self) -> CredentialStore | None:
36        return self._store
37
38    def set_store(self, store: CredentialStore) -> None:
39        self._store = store
40
41    def inject(self, task, agent_id: str, required_credentials: list[str]) -> dict:
42        """Inject credentials into Task.constraints.
43
44        Args:
45            task: The Task being dispatched.
46            agent_id: The agent receiving the task.
47            required_credentials: List of credential names this agent needs.
48
49        Returns:
50            dict of credential_name -> Credential.
51
52        Raises:
53            CredentialNotFoundError: if a required credential is missing.
54            CredentialExpiredError: if a required credential has expired.
55        """
56        if not self._store or not required_credentials:
57            return {}
58
59        credentials = {}
60        for cred_name in required_credentials:
61            cred = self._store.get(cred_name, agent_id)
62            if cred is None:
63                raise CredentialNotFoundError(
64                    f"Credential '{cred_name}' not available for agent '{agent_id}'"
65                )
66            if not self._store.validate(cred_name, agent_id):
67                # Try refresh (for OAuth2 etc.)
68                refreshed = self._store.refresh(cred_name, agent_id)
69                if refreshed and not refreshed.is_expired():
70                    cred = refreshed
71                else:
72                    raise CredentialExpiredError(
73                        f"Credential '{cred_name}' has expired for agent '{agent_id}'"
74                    )
75            credentials[cred_name] = cred
76
77        # Inject into task.constraints (in-memory only)
78        task.constraints = task.constraints or {}
79        task.constraints["credential_refs"] = {
80            name: cred.to_dict() for name, cred in credentials.items()
81        }
82
83        return credentials

Injects credentials into Task.constraints at dispatch time.

Design principles:

  • Credentials are injected in-memory only, not persisted to EventBus
  • Agent A never sees Agent B's credentials
  • Expired credentials trigger Task failure for retry evaluation
CredentialInjector(store: zelos.credential_store.CredentialStore | None = None)
31    def __init__(self, store: CredentialStore | None = None):
32        self._store = store
store: zelos.credential_store.CredentialStore | None
34    @property
35    def store(self) -> CredentialStore | None:
36        return self._store
def set_store(self, store: zelos.credential_store.CredentialStore) -> None:
38    def set_store(self, store: CredentialStore) -> None:
39        self._store = store
def inject(self, task, agent_id: str, required_credentials: list[str]) -> dict:
41    def inject(self, task, agent_id: str, required_credentials: list[str]) -> dict:
42        """Inject credentials into Task.constraints.
43
44        Args:
45            task: The Task being dispatched.
46            agent_id: The agent receiving the task.
47            required_credentials: List of credential names this agent needs.
48
49        Returns:
50            dict of credential_name -> Credential.
51
52        Raises:
53            CredentialNotFoundError: if a required credential is missing.
54            CredentialExpiredError: if a required credential has expired.
55        """
56        if not self._store or not required_credentials:
57            return {}
58
59        credentials = {}
60        for cred_name in required_credentials:
61            cred = self._store.get(cred_name, agent_id)
62            if cred is None:
63                raise CredentialNotFoundError(
64                    f"Credential '{cred_name}' not available for agent '{agent_id}'"
65                )
66            if not self._store.validate(cred_name, agent_id):
67                # Try refresh (for OAuth2 etc.)
68                refreshed = self._store.refresh(cred_name, agent_id)
69                if refreshed and not refreshed.is_expired():
70                    cred = refreshed
71                else:
72                    raise CredentialExpiredError(
73                        f"Credential '{cred_name}' has expired for agent '{agent_id}'"
74                    )
75            credentials[cred_name] = cred
76
77        # Inject into task.constraints (in-memory only)
78        task.constraints = task.constraints or {}
79        task.constraints["credential_refs"] = {
80            name: cred.to_dict() for name, cred in credentials.items()
81        }
82
83        return credentials

Inject credentials into Task.constraints.

Arguments:
  • task: The Task being dispatched.
  • agent_id: The agent receiving the task.
  • required_credentials: List of credential names this agent needs.
Returns:

dict of credential_name -> Credential.

Raises:
  • CredentialNotFoundError: if a required credential is missing.
  • CredentialExpiredError: if a required credential has expired.