simplify session dependent tasks and add TTL support (#91729)
## Summary
Three related improvements to turbo-tasks session-dependent task handling:
### 1. Make `session_dependent` a function attribute
Previously, tasks called `mark_session_dependent()` at runtime to flag themselves. This PR makes it a compile-time `#[turbo_tasks::function(session_dependent)]` attribute instead. Benefits:
- **Enables eager aggregation number selection**: Session-dependent tasks change on every session restore, behaving like dirty leaf nodes. By knowing at task creation time (not mid-execution) that a task is session-dependent, the backend can assign a high initial aggregation number, preventing long dirty-propagation chains through intermediate aggregated nodes.
- **Simpler API**: No runtime `mark_session_dependent()` call needed — the attribute is declarative and statically checked.
- Removes `mark_session_dependent()`, `mark_own_task_as_session_dependent()`, and the `session_dependent` field from `InProgressStateInner`. The backend now reads `is_session_dependent` directly from the `NativeFunction` metadata via `TaskGuard::is_session_dependent()`.
### 2. Fix fetch to respect HTTP `Cache-Control` headers
Previously, `fetch` results were cached indefinitely meaning results would never be refreshed (unless the cache was invalidated). Now they are `session_dependent` with a TTL to ensure we respect the http settings (e.g. Google Fonts with `max-age=86400`).
New two-task pattern:
- **`fetch_inner`** ( NOT `session_dependent`): Performs the HTTP request, grabs an `Invalidator` for itself, and returns the response + invalidator + absolute deadline. Cached across sessions.
- **`fetch`** (`network`, `session_dependent`): Reads the cached `fetch_inner` result and spawns a timer to invalidate when the TTL expires.
On warm cache restore, `fetch` re-executes (session-dependent), reads the persisted deadline from `fetch_inner`'s cached result, computes remaining TTL, and spawns a timer — no HTTP request unless the TTL has already expired. Mid-session, the timer fires and triggers a re-fetch.
Error handling: On fetch failure, `fetch_inner` takes a dependency on `Completion::session_dependent()` so transient errors (network down, DNS failure) are retried on the next session without busy-looping.
### 3. Drop `TransientState` in favor of inline solution
`TransientState` was only used in one place (`EcmascriptModuleAsset::last_successful_parse`) and brought unnecessary complexity — it registered invalidators and called `mark_session_dependent()` on every read. Replaced with a simple `TransientCache<T>` local to `turbopack-ecmascript` that is just a `Mutex<Option<T>>` with `#[bincode(skip)]`.
## Test Plan
- 3 new integration tests in `turbo-tasks-fetch/tests/fetch.rs`:
- `ttl_invalidates_within_session` — mock server returns `max-age=1`, body changes, verifies re-fetch after TTL
- `ttl_invalidates_on_session_restore` — fetches with TTL, stops TT, waits for expiry, warm restores with new TT, verifies re-fetch
- `errors_retried_on_session_restore` — server returns 500, stops TT, fixes server, warm restores, verifies success
- Existing 6 fetch tests continue to pass
- `cargo check -p turbo-tasks -p turbo-tasks-backend -p turbo-tasks-fetch`