## Problem
Fixes#7953.
The v3 SSE and WebSocket stream transports build their default paths by
interpolating `thread_id` directly into the URL:
```python
self._commands_url = commands_path or f"/threads/{thread_id}/commands"
self._stream_url = stream_path or f"/threads/{thread_id}/stream/events"
```
This skips the `_quote_path_param` escaping that the rest of the SDK
adopted in #7893. A `thread_id` containing reserved characters or
dot-segments is then normalized by the HTTP/WebSocket stack before
transmission. For example, `thread_id = "../assistants/abc"`:
| | before |
|---|---|
| constructed | `/threads/../assistants/abc/commands` |
| wire path | `/assistants/abc/commands` |
So the value stops being one opaque identifier under
`/threads/{thread_id}/...` and silently hits a different resource.
## Fix
Reuse the existing `_quote_path_param` helper for the **default** paths
in all four transports (`http`, `sync_http`, `ws`, `sync_ws`). Explicit
`commands_path` / `stream_path` overrides are left untouched, so callers
that pass their own paths opt out of encoding as before.
`_quote_path_param("../assistants/abc")` → `..%2Fassistants%2Fabc`,
which the HTTP/WS stack no longer collapses.
## Tests
Adds `tests/streaming/test_transport_path_encoding.py` covering all four
transports:
- default `_commands_url` / `_stream_url` / `_stream_path` are
percent-encoded,
- the actual SSE wire path (async + sync) stays under `/threads/`,
- the built WebSocket URL (async + sync) stays under `/threads/`,
- explicit path overrides are left untouched.
`make format`, `make lint`, and `make test` all pass in `libs/sdk-py`
(491 passed).