- What's next is the server's: state.advance counts down (10 s) when the
media finishes with an Up next item first, holds while its performer or
warm source is gone, and sets queuePlay at its end; cancel is shared, a
seek or swap ends it; the server skips items nobody can put up. Shared
rules in core whats_next.dart; the app's janitor, per-screen countdown
and end-of-episode card are gone.
- The continuation (next Jellyfin episode, next file of a local show
with its gaps) is an auto item under "Continues with", prepared like
Up next's first item.
- Watch tracking by time In (D3): only time played while this viewer is
in counts; away, paused, held, jumps and stalls never do (P7, P9).
WatchTracker + device WatchHistory (files, links); Jellyfin progress
reads the same watch; records are the AniList seam.
- A server restart no longer loses what played: apps hold their media
and one offers it back (swap.restore, first offer wins), Up next is
re-added, the source re-feeds, and the watch carries on.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- PlayerHost port (MediaKitHost): players by role, pins, hwdec policy,
render probe, log routing, disposal order; the only media_kit user.
- MediaPipeline: plan (one pure switch over state, origin, caps,
presence) -> resolve -> open (prepared player promoted, else fresh)
-> ready; commands queued during a switch apply once (P15); viewers
play the buffer and wait for a dropped source without seek loops (D6).
- Every web page resolved by our yt-dlp into an EDL with headers and
cookies (danmaku excluded); mpv's ytdl_hook no longer used (ytdl=no).
- D11: sign-in errors say which cookies were used and whether they hold
any for the site.
- Relay: 256 KiB slices read as they fill, superseded pulls released
(seek-to-play ~4.2 s -> ~2.8 s, P29); own Jellyfin relay channel only
when someone needs it (P28).
- STARSYNC_SMOKE run and a Windows CI smoke step after the build.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- L1 (core, offline): release names with kinds (episode, movie, extra:
OP/ED/Opening/Ending/NC/PV/OVA…), folder hints (season and name from
parentheticals, ranges), one show across folders, gaps and next with
skipped numbers, junk ignored, stable keys.
- L2 (app, device-owned cache): AniList match with synonyms and
sequel walking, episode mapping across cours (AnimeFlow runs when
signed in), art and episode titles, "Wrong show?" pins per show or
folder, episode offsets; lazy, spaced requests, works offline.
- Files tab as a library: poster cards, show pages with banner, gap
prompt, extras, movies, Wrong show? and Episode numbers.
- media.ref produced for local files, Jellyfin (provider ids) and links,
carried in swaps and Up next; receivers name media from it.
- Hosting relays each Jellyfin item through its own server's login.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- One server per app run (loopback, no password); Invite attaches an
all-interfaces listener to the same server and Stop sharing detaches
it (guests told with 4016); members, media and Up next never reset.
No solo mode: you're home on your own server unless in a friend's.
- Navigation: none / yours / friend; a join passes a version gate on
/health first (clear "they/you need to update", update offer).
- Invite as a value (host, port, name, password flag, version, fp
reserved); addresses filtered (loopback, link-local, 198.18/15,
bridges) and ranked; Sharing chip, Copy invite, Stop sharing.
- Alone looks like a plain player (alone replaces solo).
- AppGraph builds and disposes systems; widgets use AppScope;
SessionState deleted; a test caps the remaining singletons.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Status: computed from connection, presence, hold, relay, player facts;
no setters (health writes and HealthStatus gone); pill, strip and
source line read it; transitions go to the Notifier.
- Notifier: noticeFor(event, context) with self presence, alone, window
activity, playing and prefs; toasts for joins/leaves/returns/swaps/
Up next adds (with picture)/problems/chat, the rest logged; while
sitting out only chat, nudges, starting, Up next (+ joins if chosen);
alone no social notices; replayed lines logged; owns its toast timer.
- System notifications (flutter_local_notifications: D-Bus on Linux,
WinRT on Windows) when the window isn't focused; a click focuses it.
- Toasts sit above the control bar and move with it.
- Versions: each member's in the members card and avatar tooltip,
the server's in the card header; the strip no longer shows it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Server and core: version ranges on both sides with a coded refusal; a
client id maps to the same member and token for the server's life; a
drop is `gone` for a 10-minute grace keeping ready, presence, queue
items, relay and avatar; `bye` leaves at once; ordered takeover (seq,
replaced/stale); presence joining/in/away/gone with one PresencePolicy;
joiners never pause playback and hold a held gate at most ~15 s;
state.hold; feed ids, seq and replay; the relay lives through its
source's grace and continues on re-dial; queue `by` lasting, auto
items and cancel; MediaRef carried; client ids never logged.
App: persisted client id and seq; Connection with unstable/lost/
replaced states, backoff with jitter, no lost/back pair per flap; a drop
keeps the session (state, player, media, relay source); presence in the
strip and members card; sit out = away intent; feed matched by id;
auto sit-out (idle when not playing, minimised for a while; never when
alone) replaces auto-unready; bye on close and Leave, not on update
restart; relay source re-dials through its grace.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Linux app id dev.foxyz.starsync, binary starsync; Windows starsync.exe,
company foxyz; server starsync-server; Dart package starsync; archives
starsync-<ver>-*.
- 1.2.x compatibility: watch_together and watch-together-server
launchers (Linux) / copies (Windows) in the archive; the updater
accepts both names and relaunches the new binary; update.sh/.bat use
whichever server exists.
- First start copies (never moves) the old support folder and this
app's Hive boxes out of ~/Documents into <support>/hive; a marker
makes it run once; AppDirs is a port; settings schema version with
ordered migrations.
- Launcher entry dev.foxyz.starsync.desktop; the old one removed.
- Repository name and URLs unchanged.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Close: hide the window, dispose every player, thumbnail job and the
sound engine, leave, then close normally; Linux runner _exit (the
NVIDIA EGL exit handler crashed 10/10 closes, 0/41 now); Windows tears
down inside the message loop, then TerminateProcess; watchdogs.
- Version under the Settings tab; an update band instead of a dot,
never over a video; checks at start and every 4 hours.
- Chat pop-ups default to All again.
- Shader read-back compared as a list (old mpv joins with ','), so
Windows stops turning shaders off; Windows hwdec auto-copy.
- Render diagnostics (`mpv: render` lines); RAM-only demuxer cache pin
silences the file-cache warning; a volume readout test.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
PlayerControlsModel holds the controls' state and actions once; the
Standard skin (our own, no more media_kit controls or private-state
reach-ins) and Minimal (the former uosc design, made the app's own and
credited) only draw it. One layout spec for both: play first, then
previous/next, volume, time; subtitles, audio, settings, chat,
fullscreen last; everything else in the settings menu. Settings ->
Player -> Controls: time display, size, hide delay, optional buttons.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Every app streams its redacted diagnostic lines (server time, batched
every 10 s, gzip, buffered while offline) to POST /l/; the server merges
them with its own into session-YYYY-MM-DD.log in time order, tagged by
who. Opt-out in Settings > About; Mark a problem (Ctrl+Shift+B, the
player menus, About) writes a marker with an optional note. Copy
diagnostics can include the server's session log. Chat text never
reaches any log.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Logs: one redaction pass (query strings to names, member tokens,
passwords, cookies, auth headers) for every line; daily files with size
and age caps in the app (hosted server included) and the dedicated
server (--log-dir); swaps, relay channels, per-member /r /m /j outcomes,
server fetches, mpv load errors, yt-dlp runs. Settings > About: Copy
diagnostics, Open logs folder.
Bilibili through someone else: the viewer opened /r/ before the source
dialled in; the server holds that request and mpv gives up after 5 s
("could not be opened"). Viewers now open a member relay once it is
live, the putter starts its source before its own player, and track
sizes are read in parallel.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The server keeps one small relay (32 MiB ahead, 8 behind) for Up next's
first relayed item, keyed by its queue id at a URL that stays the same
after the swap: fetched by the server for server-fetch links, fed by
the adder's app for through-me links and files, by a member with an
account for member-passed Jellyfin items. Every app preloads from it;
the swap promotes it with its buffer. Dropped when the item stops
being first or its adder leaves. Measured switch: ~1.6 s cold, 20-40 ms
preloaded. ROADMAP gains the research item on server-side fetching of
member-resolved URLs.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Every app gets the first Up next item ready itself (own Jellyfin plan,
direct links, guests via /j when state.relayNext says the server's
login can pass it on) and opens it in a hidden, muted, paused second
player; the swap promotes that player instead of opening cold, so its
buffer counts at once. Reports go out on open and when the buffer
reaches the gate's need. SwitchTrace times each phase. Measured in the
e2e harness: 1.5-3 s before, 35-103 ms with a preload. Relayed items
still open cold for everyone but their source (ROADMAP).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Overlay: one layout for both designs (top bar with Up next as one of
its buttons; one action band above the controls for the status pill,
skips, next episode and end cards), one glass recipe (AppGlass) and
one pill (FloatingPill); hovering any of them, or the open queue,
holds the controls up.
- Up next: the first item (or the first continuation episode) is kept
resolved on the app that puts it up, refreshed before it expires;
Schedule and drop/paste can queue too.
- Jellyfin: WatchSession (core) counts real watched coverage; an
episode is watched at 80 % or at its ending after half; a played one
is re-marked after Playing (10.11 unmarks on open) and never gets a
position; a peek leaves the old resume point; part-way resume points
stay under 85 %.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Up next: a server-side queue anyone edits by item id (add at end or
next, move, remove, play now, skip); each item says who added it and
how it plays. At the real end the first item swaps in after the end
card's countdown, put up by whoever can (its adder for through-me links
and files, someone with access for Jellyfin, else anyone); items that
can't play are skipped with the reason. The next item's link is
resolved ahead. Greyed "Continues with" episodes below. A chip with the
controls opens it as a popover (leaf_widgets, local path dependency for
now); "Add to queue" next to Put it up everywhere.
Delivery: the server measures the source's rate, the media's bitrate
and each member's read rate; the members card shows a source line
(who passes it on, through what, keeping up or not, source left) and
each member's route and buffer. A slow source is told once per media.
A failed server fetch is announced to everyone.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- AppSelect / AppMenuButton (widgets/app_menu.dart): glass menu anchored
to its button, check on the current item, keyboard and Esc; replaces
the Material dropdowns in Settings, player settings, Jellyfin and the
member menu. Material menus elsewhere get the same surface by theme.
- Link page: Through me is the main choice (you can't know which sites
need cookies); Everyone / Server fetches it sit under "Other ways".
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Next episode (known ending, else the last two minutes) and Skip ED
show only while the controls are up, like Skip 1:30 (PillGate).
- A web link's name and wide cover slot show from the first state, so
nothing flips when its thumbnail arrives.
- Chat pop-ups default to Only when paused.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- A swap stops the previous media at once; buffer counts only for the
media the player holds (client) and the current mediaId (server), so
the old video can no longer start the new one.
- The automatic frozen-picture reload is gone (false alarms on still
scenes); Reload video stays.
- The "Next episode" pill shows only from a known ending.
- A web link's thumbnail is its cover, under the video's own name.
- While playing, the unread chip stays off the picture; chat pop-ups
can be All / Only when paused / Off.
- Server fetch failures say when the server has no cookies.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
report.error / state.members[].error (optional, no protocol bump): an
errored member is out of the ready gate until its player opens the media
or the media changes; others see "Bob can't play this: ..." and a mark
on the avatar; your own pill offers Retry (reload).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The server's version is stamped like the app's (APP_VERSION) when packaged,
so it can compare against the manifest; --upgrade swaps the whole install
folder (app and server) and asks for a restart. update.sh/.bat no longer
need GTK.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A track can be served as pieces: the server writes a VOD playlist whose
entries are its own piece URLs (segments, init maps, AES-128 keys; byte
ranges folded in; yt-dlp's DASH fragments become the same list). A piece
buffer per track fills like the byte buffer. Picks a single file, then a
split byte pair, then pieces. DRM, live, master playlists are refused.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Putting anything up offline starts a solo session: an in-app server bound
to 127.0.0.1 only, joined like hosting, so tracking, next episode and the
player all work. Invite friends re-hosts on the Host card's port and
password with the same media and position; Stop needs no confirmation.
Offline, the Library opens with a welcome band (Join a friend, Host, Watch
solo); without a Jellyfin account it leads with Files and Link, Jellyfin
is an optional card. Play buttons work offline.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Anyone can sit anyone out (host included); only you bring yourself back.
Out members stay in chat and the party, never block or count, and load no
media; a card with the live position and Join back (Space) replaces the
video. The mini player's x sits you out; optionally idle does too. Nudge:
the server forwards it to the target only (3/s, 20/min per sender); the
target hears the nudge sound, gets a toast and a taskbar attention
request. Kick, its messages, errors and dialog are gone.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Relayed media can carry 2-4 byte tracks (video, audio): yt-dlp picks a
split pair when no single-file format exists (h264 first), the source
channel serves each track, the server keeps a buffer per track (budget
split by size, 8 MiB floor) and viewers open an mpv edl:// of the track
URLs. Expired URLs re-resolve once for all tracks. Upstream GETs are cut
into <= http_chunk_size (10 MiB) ranges: YouTube throttles larger ones.
Live streams and DRM are refused; HLS/DASH pieces are a follow-up.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
yt-dlp: a managed, checksum-verified, daily-updated yt-dlp in the app and
the dedicated server (--[no-]yt-dlp, --yt-dlp-dir). Pasted links are
probed: direct files stay links, pages are resolved for title, uploader,
duration and thumbnail. Everyone fetches it: mpv's ytdl hook on the
managed copy. I pass it on / the server fetches it: the best single-file
format, re-resolved once when its URL expires. Relay names never split
the /r/ path; viewers of server-fetched pages wait for the relay to go
live. No wire change.
Icon: two stars orbiting a play button (assets/icon/icon.svg, PNGs, the
Windows .ico). GTK app id moe.senkochan.watch_together; release builds
install their .desktop file and icons into ~/.local/share so Wayland
shows the icon; the window icon is set for X11. README shows the icon.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Ready gate: whoever is in the server while it is not playing counts (joining
while playing does not pause anyone); a swap counts everyone present and
keeps ready flags; the quorum rule is gone. Optional 'Unready when the
video changes'. No 'Skip 1:30' guess when the media has chapters.
Drag & drop and Ctrl+V anywhere (fox_utils): files, folders and links get a
small card asking how to open them. Files series start collapsed.
UI: Tab never moves focus and Space is always ready outside text fields;
chat focuses its field; unread badge on uosc and a chip in fullscreen;
people-only chat with an Activity toggle; skip pills show 5 s then with
the controls; volume up to 150%; own avatar same size as the party.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Eleven sounds for events that carry news (ready, unready, everyone ready,
join, leave, kick, new video, others' chat, idle warning, connection
lost/back); none for what the video already says. New voices: koto
(Karplus-Strong, bendable), furin chime, rin bowl, bamboo, hyoshigi,
shoji slide, in the Yo scale on D. Played by SynthSfx at the Settings
volume with per-sound switches and previews. The debug layout mock is
gone.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The swapper sends the media's poster and banner once (`art`, Jellyfin
images or AniList by parsed name); state carries their hashes and every
member fetches them from /i/ like avatars. state.media.swapBy names who
put the media up. uosc and the standard bar show cover, title and
episode; a banner card shows before playback starts. The top-left health
pill and the waiting pill become one frosted status pill in plain words.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Library: source switch, selectable cards with a details panel, one merged
Continue row, Latest grouped per series without re-releases, Link page with
previews and recent links, Files embedded. Player: gear popover in both
designs, progress line while controls hide, live hwdec. Settings rebuilt as
cards. State marks the host member (proto stays 9, unreleased); hosting
shows LAN invite addresses; window buttons in fullscreen. fadeThrough keeps
the outgoing view's state.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The shell (lib/src/shell/) replaces the Home and session screens:
- Party strip: profile, Library / Schedule / Settings tabs, Join and
Host cards (hosting no longer takes media), status-ring avatar stack
with the member list, chat & activity panel, Leave.
- The real session player is mounted once and shown full, as a
draggable corner-snapping mini player, or fullscreen (window_manager,
party and toasts overlaid); the Video is never mounted twice.
- Profile popover with inline rename, colour and a crop picker (256 px
PNG), pushed live to the session; avatar toasts; waiting pill.
- Motion kit (fastEaseInToSlowEaseOut, fade-through, popovers,
mouse-drag/wheel horizontal scrolling).
- Library = the existing picker for now (restyled next round); Schedule
= the new SchedulePage; Settings = left nav over the existing panels.
Protocol 9 (packages/core, packages/server, sync client):
- {"t":"profile"} renames / changes the picture mid-session ("rename",
"avatar" feed actions); names are cleaned the same way at hello.
- state.members carry avatarHash; pictures are served once from
/a/<sid>/<token>/<hash> (PNG/JPEG/WebP/GIF by magic bytes only) and
cached by hash in the app (AvatarCache).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Viewers who cannot reach the media get it through the server: a file on
any member's computer, a link passed on by a member ("Through me") or
fetched by the server ("Server fetches it", password-protected servers
only), and Jellyfin items for guests without an account, sourced by
whoever put them up with their own sign-in (or a pasted link's key),
which never leaves their machine. Fixes guests on a dedicated server
without a Jellyfin login being blocked.
The source dials /r/.../source (works behind NAT) and answers pulls with
an adaptive in-flight window (2 x rate x min RTT, 256 KiB..8 MiB). The
server keeps a memory-only buffer of 1 MiB slices sized like a viewer's
mpv buffer (pinned head/tail, 64 MiB back, 256 MiB ahead), fills it with
one sequential pull at a time (blocked viewers first, newest request
only), fetches nothing while nobody watches through it, and drops it on
the next swap or stop. Measured with mpv through shaped links
(tool/relay_e2e.dart): 400 Mbit/s seeks ~0.03 s; 40 Mbit/s no stalls,
3 viewers cost the source ~1x the bytes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
tool/package_windows.ps1 builds watch-together-<ver>-windows-x64.zip
(same layout as the Linux tarball, libmpv bundled, update.bat and
watch-together-server.exe). The release workflow gets a windows job;
ci runs the updater tests on Windows. Publishing moves to
tool/release_publish.dart (dart: only, no python/bash on Windows), which
tolerates both jobs racing to create the release and replaces an asset
on re-runs. release.sh writes both asset URLs into the manifest.
Windows jobs check out with plain git (the host's cached
actions/checkout clone was corrupt).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Thick timeline with hatched cache range, chapter diamonds and
chapter/time hover boxes; collapses to a thin progress line when
hidden. Flat icon row with track count badges, shader preset badge,
flat dark menus. Own show/hide state, live switch in window and
fullscreen. New buffered signal from demuxer-cache-time.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- PlaybackController exposes chapters, segments (Jellyfin
MediaSegments for account holders), tracks, position/duration.
- Own seek bar with chapter diamonds and chapter title on hover,
seeking through the sync user-seek path.
- Skip OP / Skip ED pills from Intro/Outro segments or chapter names
(ED skips to its end only); Skip 1:30 fallback for >20 min videos
without OP info; next-episode pill now starts at the ED.
- PgUp/PgDn chapters, Shift+Right +1:30; Audio and Subtitles menus
with count badges via the existing track path.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Host/swap send refs only; pasted Jellyfin links become refs; the host
hands the relay its own app-obtained credential.
- Each viewer resolves with their own account (token in a header) or
via the relay, else a clear blocked card with Link account.
- Per-account progress reporting (Playing/Progress/Stopped), 401 marks
the account expired.
- Previous/next episode in the controls (< > / Shift+P/N), floating
Next episode pill near the end, auto-advance countdown (one
designated swapper), track carry-over per series.
- Swap panel uses the shared MediaPicker; browser location remembered
per server and opens on the current series.
- Fix: states are applied one at a time (a stale pause could land
after a newer play).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Integrations links several Jellyfin servers (Quick Connect or
password, app-obtained tokens only, status/reconnect/sign out). The
Host tab's Jellyfin source is a full browser: continue watching, next
up, latest per library, library grids with sort/filter/infinite
scroll, series pages with smart resume/next-up, details, grouped
search, inspector pane on wide screens, keyboard navigation.
MediaPicker selection is now a sealed PickedMedia. Hosting a Jellyfin
pick is interim (host's stream URL) until per-viewer playback lands.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Vendored Anime4K v4.0.1 GLSL (MIT), extracted to app support once per
version. Presets A/B/C/A+A/B+B/C+A in HQ and Fast per Anime4K's mpv
templates, plus custom files. Shader button in the video controls
(works in fullscreen), Ctrl+1..6 / Ctrl+0, live apply with read-back,
failures fall back to Off with a toast. Render mode detected from
mpv's GL log; warns when on the software path. Docs include the
Jellyfin client section.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Top bar merges window chrome, profile, tabs and page icons. Join: one
compact row plus recent-server tiles with live /health status and a
needs-update badge. Host: reusable MediaPicker (Jellyfin placeholder,
Link, File) filling the width, server settings in a narrow column.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- serve(Map config, events) is the one server entrypoint: the app
hosts it via Isolate.spawn (crash surfaced on the Host card, logs
forwarded as [server]); bin/server.dart is the dedicated server,
compiled into the bundle as watch-together-server. --headless gone.
- Host identity via a per-hosting hostKey in hello, replacing the
loopback heuristic.
- Rename room -> server across code, files and wire (t:"state",
/health name); protocol bumped to 5.
- CI compiles the server binary; isolate smoke tests in server and
desktop suites. Docs updated.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
README for the repo page; HOW-IT-WORKS overview; ARCHITECTURE,
PROTOCOL, DEVELOPMENT checked against the code; ROADMAP is backlog
only (isolate/dedicated server, sync fixes, rendering+shaders,
Windows build, Jellyfin epic). HANDOFF.md and mpv-http-contract.md
folded in and deleted.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>