// The two built-in requests, as one list. // // A `review` and an `answer` are the two things an agent works on that are never units: neither // produces an artifact, so `timeline()` skips both and a goal's pie does not move when somebody asks // for a judgement or a reply. That was always right about the pie and always wrong about the smart // lists — the daemon runs real turns for both (`dispatch.ts`), auto-review writes an open review // request, and the claim manager assigns its execution — so the most common shape of "an // agent is working right now" was the one shape "With an agent" could not see. // // This module exists so the two pages and the rail read ONE list rather than each unioning two // folds; it cannot live in `units.ts`, which both folds already import. The row shape is // `AskContext` there, and the predicate that decides which list a row belongs in is `askWithAgent` // there too — one predicate, so no row can be drawn in two lists at once and no count can overstate // what its page draws. import type { MaterializedIndex } from '@radial/core' import { answerAsks } from './replies.js' import { agentDids, askWithAgent, type AskContext } from './units.js' import { reviewAsks } from './verdicts.js' /** Oldest first, matching `reviewQueue` — the request's own instant, then its uri to break ties. */ const byAge = (left: AskContext, right: AskContext): number => left.request.value.createdAt.localeCompare(right.request.value.createdAt) || left.request.uri.localeCompare(right.request.uri) /** Every open review and answer request in the space, for the lists that do not care which it is. */ export const asks = (index: MaterializedIndex): AskContext[] => [...reviewAsks(index), ...answerAsks(index)].sort(byAge) /** With an agent, one way or the other — what "With an agent" draws beside its units. */ export function asksWithAgent(index: MaterializedIndex): AskContext[] { const agents = agentDids(index) return asks(index).filter((ask) => askWithAgent(ask, agents)) } /** * Stopped on a person: an ask whose turn asked a question back and ended. * * The pair to `asksWithAgent`, and not optional beside it. An ask that appears in "With an agent" * and then vanishes the moment its agent asks something would be a worse bug than the one this * fixes: the row would be in no list at all, and the person it is waiting on would never know. */ export const parkedAsks = (index: MaterializedIndex): AskContext[] => asks(index).filter((ask) => ask.state === 'awaiting') /** * Asks grouped by the target they hang off — what a per-goal badge reads, so a page folds the space * once and every row on it reads its own slice rather than re-folding. */ export function asksByTarget(rows: AskContext[]): Map { const byTarget = new Map() for (const ask of rows) { const key = ask.target.target.uri byTarget.set(key, [...(byTarget.get(key) ?? []), ask]) } return byTarget }