Something went wrong. Try again.
Where attention appears to go across agent threads: collector, web explorer, and native iOS client
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340// @ts-check// Validated shapes of the private REST API. Parse at the network boundary;// everything past these functions can trust the types.
/** @typedef {{repo: string, project: string, repo_source: string}} RepoContext *//** * @typedef {{ * title: string, project: string, project_key: string, repo: string, provider: string, * display_title?: string, worktree?: string, host?: string, branch?: string, * linked_pr?: number, linked_issue?: number, workspace_status?: string, * worktree_archived?: boolean, repo_source?: string, repositories?: RepoContext[] * }} Thread *//** @typedef {{at: number, label: string, text: string, voice?: boolean, cut?: boolean}} SessionEvent *//** @typedef {"recent" | "completed" | "quiet"} SessionStatus *//** * @typedef {{ * id: string, provider: string, title: string, display_title: string, project: string, * cwd: string, model: string, branch: string, repo: string, repo_source: string, * host: string, worktree: string, workspace_status: string, linked_pr?: number, * linked_issue?: number, archived: boolean, subagent: boolean, updated: number, * status: SessionStatus, repositories: RepoContext[], events?: SessionEvent[] * }} Session *//** @typedef {{sessions: Session[], errors: string[], at: number}} SessionList *//** @typedef {{session: Session | null, errors: string[], at: number}} SessionItem *//** @typedef {{thread: string, at: number, kind: "voice" | "message"}} AttentionEvent *//** * @typedef {{at: number, since: number, threads: Record<string, Thread>, * events: AttentionEvent[], errors: string[], coverage: string}} Attention *//** @typedef {{id: string, thread: string, at: number, kind: string, text: string}} Source *//** @typedef {{start: number, end: number, thread: string, count: number}} Visit *//** * @typedef {{id: string, title: string, summary: string, threads: string[], count: number, * first: number, last: number, sources: Source[], visits: Visit[]}} WorkGroup *//** * @typedef {{pending: true, day: string, count: number, threads: number, * events: AttentionEvent[], error?: string}} PendingDay *//** * @typedef {{pending: false, day: string, recap: string, groups: WorkGroup[], count: number, * threads: number, updating: boolean, new_count: number, error?: string}} RecapDay *//** @typedef {PendingDay | RecapDay} DigestDay *//** * @typedef {{at: number, timezone: string, days: Record<string, DigestDay>, * threads: Record<string, Thread>, errors: string[]}} Digest */
export class ContractError extends Error {}
/** @typedef {{thread: string, at: number, kind: string, text: string, cut: boolean}} WorkEvidence *//** @typedef {{key: string, title: string, provider: string, project: string, repo: string}} WorkSession *//** @typedef {{id: string, aliases: string[], title: string, intent: string, first: number, last: number, revision: string, provisional: boolean, sources: WorkEvidence[], reports: WorkEvidence[], unresolved: WorkEvidence[], sessions: WorkSession[]}} Workstream *//** @typedef {{at: number, snapshot_at: number, digest_at: number, streams: Workstream[], errors: string[], total: number, coverage: string}} Workstreams */
/** @param {unknown} input @returns {WorkEvidence[]} */function workEvidence(input) { if (!Array.isArray(input)) throw new ContractError("Invalid work evidence"); return input.map((value) => { const e = object(value, "work evidence"); if (!finite(e.at) || !isString(e.text) || !isString(e.thread)) throw new ContractError("Invalid work evidence"); return { thread: e.thread, at: Number(e.at), kind: str(e.kind), text: e.text, cut: e.cut === true }; });}
/** @param {unknown} input @returns {Workstreams} */export function parseWorkstreams(input) { const data = object(input, "workstreams"); if (!Array.isArray(data.streams) || !finite(data.at)) throw new ContractError("Invalid workstreams"); return { at: Number(data.at), snapshot_at: Number(data.snapshot_at), digest_at: Number(data.digest_at), errors: strings(data.errors), total: Number(data.total), coverage: str(data.coverage), streams: data.streams.map((value) => { const s = object(value, "workstream"); if (!isString(s.id) || !isString(s.title) || !finite(s.first) || !finite(s.last) || !Array.isArray(s.sessions)) throw new ContractError("Invalid workstream"); return { id: s.id, aliases: strings(s.aliases), title: s.title, intent: str(s.intent), first: Number(s.first), last: Number(s.last), revision: str(s.revision), provisional: s.provisional === true, sources: workEvidence(s.sources), reports: workEvidence(s.reports), unresolved: workEvidence(s.unresolved), sessions: s.sessions.map((value) => { const session = object(value, "work session"); return { key: str(session.key), title: str(session.title), provider: str(session.provider), project: str(session.project), repo: str(session.repo) }; }), }; }), };}
// The primitive decoders below hold the only `typeof` checks in the codebase: this// module is the I/O boundary that anti-slop's no-runtime-typeof asks for, and JSDoc// type predicates are invisible to its allowInTypeGuards option (see .oxlintrc.json).
/** @param {unknown} value @returns {value is Record<string, unknown>} */const isObject = (value) => typeof value === "object" && value !== null && !Array.isArray(value);/** @param {unknown} value @returns {value is string} */const isString = (value) => typeof value === "string";/** @param {unknown} value */const str = (value) => (isString(value) ? value : "");/** @param {unknown} value */const finite = (value) => typeof value === "number" && Number.isFinite(value);/** @param {unknown} value */const positiveInt = (value) => Number.isSafeInteger(value) && /** @type {number} */ (value) > 0 ? /** @type {number} */ (value) : undefined;/** @param {unknown} value */const strings = (value) => Array.isArray(value) ? value.filter(isString) : [];/** @param {unknown} value @param {string} what */function object(value, what) { if (!isObject(value)) throw new ContractError(`Invalid ${what}`); return value;}
/** @param {unknown} value @returns {RepoContext[]} */function repoContexts(value) { if (!Array.isArray(value)) return []; return value.filter(isObject).map((r) => ({ repo: str(r.repo), project: str(r.project), repo_source: str(r.repo_source), }));}
/** @param {unknown} value @returns {Thread} */export function parseThread(value) { const t = object(value, "thread"); return { title: str(t.title), project: str(t.project), project_key: str(t.project_key), repo: str(t.repo), provider: str(t.provider), display_title: str(t.display_title) || undefined, worktree: str(t.worktree) || undefined, host: str(t.host) || undefined, branch: str(t.branch) || undefined, linked_pr: positiveInt(t.linked_pr), linked_issue: positiveInt(t.linked_issue), workspace_status: str(t.workspace_status) || undefined, worktree_archived: t.worktree_archived === true, repo_source: str(t.repo_source) || undefined, repositories: repoContexts(t.repositories), };}
/** @param {unknown} value */function threadMap(value) { const raw = object(value, "threads"); /** @type {Record<string, Thread>} */ const threads = {}; for (const [id, thread] of Object.entries(raw)) if (isObject(thread)) threads[id] = parseThread(thread); return threads;}
/** @param {unknown} value @returns {Session} */export function parseSession(value) { const s = object(value, "session"); if (!str(s.provider).trim() || !str(s.id) || !finite(s.updated)) throw new ContractError("Invalid session"); if ("events" in s && !Array.isArray(s.events)) throw new ContractError("Invalid session events"); const status = s.status === "recent" || s.status === "completed" ? s.status : "quiet"; return { id: str(s.id), provider: str(s.provider), title: str(s.title), display_title: str(s.display_title), project: str(s.project), cwd: str(s.cwd), model: str(s.model), branch: str(s.branch), repo: str(s.repo), repo_source: str(s.repo_source), host: str(s.host), worktree: str(s.worktree), workspace_status: str(s.workspace_status), linked_pr: positiveInt(s.linked_pr), linked_issue: positiveInt(s.linked_issue), archived: s.archived === true, subagent: s.subagent === true, updated: /** @type {number} */ (s.updated), status, repositories: repoContexts(s.repositories), events: Array.isArray(s.events) ? s.events.filter(isObject).map((e) => ({ at: finite(e.at) ? /** @type {number} */ (e.at) : 0, label: str(e.label), text: str(e.text), voice: e.voice === true || undefined, cut: e.cut === true || undefined, })) : undefined, };}
/** @param {unknown} value @returns {SessionList} */export function parseSessionList(value) { const data = object(value, "sessions"); if (!Array.isArray(data.sessions)) throw new ContractError("Invalid sessions"); return { sessions: data.sessions.map(parseSession), errors: strings(data.errors), at: finite(data.at) ? /** @type {number} */ (data.at) : 0, };}
/** @param {unknown} value @returns {SessionItem} */export function parseSessionItem(value) { const data = object(value, "session"); return { session: data.session === null ? null : parseSession(data.session), errors: strings(data.errors), at: finite(data.at) ? /** @type {number} */ (data.at) : 0, };}
/** @param {unknown} value @param {Record<string, Thread>} threads @returns {AttentionEvent[]} */function events(value, threads) { if (!Array.isArray(value)) throw new ContractError("Invalid events"); /** @type {AttentionEvent[]} */ const out = []; for (const e of value) { if (!isObject(e) || !finite(e.at) || !str(e.thread) || !threads[str(e.thread)]) continue; if (e.kind !== "voice" && e.kind !== "message") continue; out.push({ thread: str(e.thread), at: /** @type {number} */ (e.at), kind: e.kind }); } return out;}
/** @param {unknown} value @returns {Attention} */export function parseAttention(value) { const data = object(value, "history"); if (!finite(data.at)) throw new ContractError("Invalid history"); const threads = threadMap(data.threads); return { at: /** @type {number} */ (data.at), since: finite(data.since) ? /** @type {number} */ (data.since) : 0, threads, events: events(data.events, threads), errors: strings(data.errors), coverage: str(data.coverage), };}
/** @param {unknown} value @returns {WorkGroup} */function parseGroup(value) { const g = object(value, "group"); return { id: str(g.id), title: str(g.title), summary: str(g.summary), threads: strings(g.threads), count: finite(g.count) ? /** @type {number} */ (g.count) : 0, first: finite(g.first) ? /** @type {number} */ (g.first) : 0, last: finite(g.last) ? /** @type {number} */ (g.last) : 0, sources: Array.isArray(g.sources) ? g.sources.filter(isObject).map((s) => ({ id: str(s.id), thread: str(s.thread), at: finite(s.at) ? /** @type {number} */ (s.at) : 0, kind: str(s.kind), text: str(s.text), })) : [], visits: Array.isArray(g.visits) ? g.visits.filter(isObject).map((v) => ({ start: finite(v.start) ? /** @type {number} */ (v.start) : 0, end: finite(v.end) ? /** @type {number} */ (v.end) : 0, thread: str(v.thread), count: finite(v.count) ? /** @type {number} */ (v.count) : 0, })) : [], };}
/** @param {string} key @param {unknown} value @param {Record<string, Thread>} threads @returns {DigestDay} */function parseDay(key, value, threads) { const d = object(value, "day"); const count = finite(d.count) ? /** @type {number} */ (d.count) : 0; const threadCount = finite(d.threads) ? /** @type {number} */ (d.threads) : 0; const error = str(d.error) || undefined; if (d.pending === true) return { pending: true, day: key, count, threads: threadCount, events: Array.isArray(d.events) ? events(d.events, threads) : [], error, }; return { pending: false, day: key, recap: str(d.recap), groups: Array.isArray(d.groups) ? d.groups.map(parseGroup) : [], count, threads: threadCount, updating: d.updating === true, new_count: finite(d.new_count) ? /** @type {number} */ (d.new_count) : 0, error, };}
/** @param {unknown} value @returns {Digest} */export function parseDigest(value) { const data = object(value, "digest"); if (!finite(data.at) || !isObject(data.days)) throw new ContractError("Invalid history"); const threads = threadMap(data.threads); /** @type {Record<string, DigestDay>} */ const days = {}; for (const [key, day] of Object.entries(data.days)) if (/^\d{4}-\d{2}-\d{2}$/.test(key) && isObject(day)) days[key] = parseDay(key, day, threads); return { at: /** @type {number} */ (data.at), timezone: str(data.timezone), days, threads, errors: strings(data.errors), };}
/** @typedef {{token: string, did: string}} AppSession */
/** A stored or freshly issued app session, or null when the value is not one. @param {unknown} value @returns {AppSession | null} */export function parseAppSession(value) { if (!isObject(value) || !str(value.token) || !str(value.did)) return null; return { token: str(value.token), did: str(value.did) };}
/** The `error` code of a JSON error body, if any. @param {unknown} value */export const parseErrorCode = (value) => (isObject(value) ? str(value.error) : "");
/** The first string of a URL query value (string or repeated). @param {unknown} value */export const queryString = (value) => str(Array.isArray(value) ? value[0] : value);