Fan out every lifecycle method to each wrapped hook in order. Exceptions from earlier hooks short-circuit the rest by design — hooks are integral to the run, not best-effort. PR6's archive hook should catch its own exceptions internally if it wants resilience.
| 84 | |
| 85 | |
| 86 | class _CompositeRunHooks(RunHooks[Any]): |
| 87 | """Fan out every lifecycle method to each wrapped hook in order. |
| 88 | |
| 89 | Exceptions from earlier hooks short-circuit the rest by design — hooks |
| 90 | are integral to the run, not best-effort. PR6's archive hook should |
| 91 | catch its own exceptions internally if it wants resilience. |
| 92 | """ |
| 93 | |
| 94 | def __init__(self, inner: list[RunHooks[Any]]) -> None: |
| 95 | self._inner = list(inner) |
| 96 | |
| 97 | async def on_llm_start(self, *args: Any, **kwargs: Any) -> None: |
| 98 | for h in self._inner: |
| 99 | await h.on_llm_start(*args, **kwargs) |
| 100 | |
| 101 | async def on_llm_end(self, *args: Any, **kwargs: Any) -> None: |
| 102 | for h in self._inner: |
| 103 | await h.on_llm_end(*args, **kwargs) |
| 104 | |
| 105 | async def on_agent_start(self, *args: Any, **kwargs: Any) -> None: |
| 106 | for h in self._inner: |
| 107 | await h.on_agent_start(*args, **kwargs) |
| 108 | |
| 109 | async def on_agent_end(self, *args: Any, **kwargs: Any) -> None: |
| 110 | for h in self._inner: |
| 111 | await h.on_agent_end(*args, **kwargs) |
| 112 | |
| 113 | async def on_handoff(self, *args: Any, **kwargs: Any) -> None: |
| 114 | for h in self._inner: |
| 115 | await h.on_handoff(*args, **kwargs) |
| 116 | |
| 117 | async def on_tool_start(self, *args: Any, **kwargs: Any) -> None: |
| 118 | for h in self._inner: |
| 119 | await h.on_tool_start(*args, **kwargs) |
| 120 | |
| 121 | async def on_tool_end(self, *args: Any, **kwargs: Any) -> None: |
| 122 | for h in self._inner: |
| 123 | await h.on_tool_end(*args, **kwargs) |
| 124 | |
| 125 | |
| 126 | def _archive_run_artifacts( |
no outgoing calls