The two RichText classes are not mutually assignable - `UnicodeString` has a
private field and the SDK brands `did`/`uri` as template literal types - so
every producer and consumer of a RichText instance has to move in one step.
`detectFacets` now takes a lex client instead of the legacy agent, which is
what removes the last hard agent dependency from these files. Handle
resolution is an appview job, so the appview client is threaded in: through
`useAppviewClient` in the hooks and dialogs, and through a new
`appviewClient` option on `apilib.post` (Composer already had the client to
hand). The rest of the post pipeline still writes through the agent.
Facet feature checks move from the `AppBskyRichtextFacet` validators to the
generated `#/lexicons` schemas, matching how the rest of the app narrows
lexicon types.
Display sinks still read facets off `@atproto/api` view types, which are the
same lexicon but typed with plain strings. `asSdkFacets` widens them at those
call sites and goes away once the view types come from the SDK too.
App preferences live on the appview and chat preferences on the chat
service, so the combined update mutation holds both clients.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The logged-out join link preview path built an ad-hoc AtpAgent pointed at
CHAT_SERVICE. It is now a module-level unauthenticated lex client against
the same service, so the client is built once rather than per call.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Comments should explain the code as it is, not narrate the change that produced
it. Drops "the old X" / "now" / "no longer" framing and a stale version pin in
favour of concrete names and the live constraints.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
`createAccount` and `login` abandoned the factory's returned bundle when a
newer task had already aborted them. The factories return an ARMED bundle, so
that session kept auto-refreshing and rotating refresh tokens server-side for
an account the app was no longer tracking; for signup the void-fired
post-signup writes kept its agent live too. Dispose in both abort branches,
matching `resumeSession`.
Also document why disposal of a replaced bundle is deferred to the post-commit
effect rather than done inline.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Replace the AtpAgent-owned session lifecycle with PasswordSession-backed
bundles, dispatched through the bridge agent. The provider, reducer and
factories now hold a `{session, agent, service}` bundle whose identity gates
session events, so a stale session can no longer log out the current account
or restore its tokens after a switch.
Behavioural changes that come with the new auth core:
- token rotation is read from the hook payload (PasswordSession fires before
committing its live getter), so refreshes persist the new tokens
- replaced bundles are disposed rather than mutated, since PasswordSession has
no in-place patch; cross-tab syncs rebuild instead
- the expiry rescue path prefers a newer persisted generation over logging
every tab out
Post-signup writes keep main's agent.* call style; createAccount synthesizes
the email/active fields the thinner lex output omits.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Session-account conversion, the network-aware fetch wrapper, and the
expiry-rescue candidate picker were tangled into agent.ts and util.ts.
Pulling them into agent-agnostic modules lets the upcoming session
layer share them.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The per-account labeler cache was the only async dependency in
configureModerationForAccount. MMKV-backed sync reads let session
setup apply labelers in the same tick. No AsyncStorage backfill: the
cache is rewritten on every preferences fetch.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>