Experimental Bluesky client for agents
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235export interface Source { service: string; mode: 'public' | 'authenticated'; viewerDid?: string;}
export interface Author { did: string; handle: string; displayName: string; avatar?: string; pronouns?: string; following?: boolean; followedBy?: boolean;}
export interface FeedReason { kind: 'repost' | 'pin' | 'unknown'; by?: Author; indexedAt?: string; type?: string;}
export interface FeedOccurrence { id: string; postUri: string; reason?: FeedReason; parentUri?: string; rootUri?: string;}
export interface FeedOptions { limit?: number; cursor?: string;}
export interface FeedEncounter { feedViewId: string; occurrenceId: string; postUri: string; reason?: FeedReason;}
export type TargetKind = 'post' | 'profile' | 'link' | 'tag' | 'media' | 'occurrence' | 'author-feed';export interface Target { kind: TargetKind; value: string; label: string; ownerUri?: string; index?: number;}
export interface Facet { byteStart: number; byteEnd: number; text: string; targets: Target[]; unsupported: string[];}
export interface Media { kind: 'image' | 'video'; url?: string; thumbnail?: string; alt?: string; width?: number; height?: number;}
export interface LinkCard { uri: string; title: string; description: string; thumbnail?: string;}
export interface PostNode { uri: string; cid?: string; availability: 'available' | 'not-found' | 'blocked' | 'unsupported'; author?: Author; text?: string; createdAt?: string; indexedAt?: string; parentUri?: string; rootUri?: string; replies: string[]; repliesFetched: boolean; facets: Facet[]; media: Media[]; quotes: string[]; cards: LinkCard[]; labels: string[]; unsupported: string[]; counts: { replies?: number; likes?: number; reposts?: number; quotes?: number };}
export interface FeedSnapshot { kind: 'feed'; feed: 'following' | 'author'; fetchedAt: string; source: Source; actor?: Author; entries: FeedOccurrence[]; nodes: PostNode[]; limit: number; cursor?: string; requestCursor?: string; maxNodes: number; locallyTruncated: boolean; warnings: string[];}
export interface ThreadSnapshot { kind: 'thread'; requested: string; focusUri: string; fetchedAt: string; source: Source; nodes: PostNode[]; /** Oldest available ancestor first; excludes focus. */ ancestors: string[]; replyOrder: 'source'; limits: { depth: number; parentHeight: number; maxNodes: number }; /** Local node omission is distinct from unknown remote coverage. */ locallyTruncated: boolean; warnings: string[]; encounter?: FeedEncounter;}
export interface ProfileLabel { value: string; issuerDid: string; uri: string; cid?: string; negated?: boolean; createdAt?: string; expiresAt?: string; signature?: string;}
export interface ProfileEnrichment { id: string; title: string; status: 'available' | 'absent' | 'unavailable' | 'unsupported'; items: Array<{ label: string; value: string; uri?: string }>; source: { service: string; actorDid: string; fetchedAt: string; uri?: string; cid?: string; }; note?: string;}
export interface ProfileSnapshot { kind: 'profile'; requested: string; fetchedAt: string; source: Source; author: Author; description?: string; banner?: string; website?: string; createdAt?: string; indexedAt?: string; associated?: Record<string, unknown>; verification?: Record<string, unknown>; pinnedPost?: { uri: string; cid: string }; counts: { followers?: number; follows?: number; posts?: number }; labels: Array<string | ProfileLabel>; warnings?: string[]; enrichments?: ProfileEnrichment[];}
export interface TargetSnapshot { kind: 'target'; fetchedAt: string; source: Source; target: Target; /** Link/tag/media inspection is not an external article fetch. */ note: string;}
export type Snapshot = ThreadSnapshot | ProfileSnapshot | TargetSnapshot | FeedSnapshot;export interface ReadingView { /** Monotonically allocated by the local store, never reused. */ id: string; snapshot: Snapshot; refs: Record<string, Target>;}
export interface SessionInfo { name: string; viewId: string | null; history: string[];}
export interface ThreadOptions { depth?: number; parentHeight?: number; maxNodes?: number;}
export interface ReaderOptions { service?: string; /** Explicit opt-in to existing OMP atproto env, never persisted. */ authConfig?: string; pds?: string;}
export interface Reader { readonly source: Source; thread(input: string, options?: ThreadOptions): Promise<ThreadSnapshot>; profile(actor: string): Promise<ProfileSnapshot>; following(options?: FeedOptions): Promise<FeedSnapshot>; authorFeed(actor: string, options?: FeedOptions): Promise<FeedSnapshot>;}
export interface RenderFiles { html: string; text: string; json: string; images: string[];}
export function targetKey(target: Target): string { return JSON.stringify([target.kind, target.value, target.ownerUri ?? null, target.index ?? null]);}
export function refFor(view: ReadingView, kind: TargetKind, value: string): string | undefined { return Object.entries(view.refs).find(([, target]) => target.kind === kind && target.value === value)?.[0];}