ConusAI vendor v0.1.0-dev.20261027+9b1131fb
ci / gate (push) Waiting to run

This commit is contained in:
ConusAI vendor v0.1.0-dev.20261027+9b1131fb
2026-10-27 18:05:17 +00:00
commit 3102273ec7
538 changed files with 80203 additions and 0 deletions
+123
View File
@@ -0,0 +1,123 @@
// The dependencies every use case of the module takes, and how a proposal becomes a line a reviewer
// sees. A line is not a row of this module until it is approved: until then it is a proposal of the
// proposals module, and this file is the one place the two are read together.
import type { TenantScope } from "@conusai-kit/core"
import type { DataSource, Filter } from "@conusai-kit/data"
import { type FilesDeps, sha256Hex } from "@conusai-kit/files"
import { type ProposalRow, type ProposalsDeps, type ProposalView, proposals, viewOf } from "@conusai-kit/proposals"
import { LINE_TARGET_KEY, type LineView, lineChangeSchema } from "../contract/index.js"
/** A project scope: the only level a document can live at. */
export type DocumentScope = TenantScope & { workspaceId: string; projectId: string }
export interface DocumentsDeps {
readonly data: DataSource
/** The files module's ports: a document is made from one of its files. */
readonly files: FilesDeps
/** The proposals module's ports: an extracted line is one of its proposals. */
readonly proposals: ProposalsDeps
}
/** The page size used to read this module's proposals. */
const PAGE = 200
/**
* Every proposal of this module's target matching the given record filter, any status, oldest
* first.
*
* The proposals module's own `listProposals` filters by status alone, so the filter by target is
* done here on its table, through the entity it exports: the `where` runs against the table's
* `(targetKey, targetRecordId)` index instead of paging the project's whole queue into memory and
* filtering it in JavaScript (one canonical copy of a line's state still — this adds a query, not
* a table). When the kit's list grows a `targetRecordId` filter of its own, this becomes a call
* of it again.
*/
async function proposalsWhere(
deps: DocumentsDeps,
scope: DocumentScope,
target: { recordId: string } | { recordIds: readonly string[] } | null,
): Promise<ProposalView[]> {
const byRecord: Filter<ProposalRow>[] =
target === null
? []
: "recordId" in target
? [{ field: "targetRecordId", op: "eq", value: target.recordId }]
: [{ field: "targetRecordId", op: "in", values: target.recordIds }]
const found: ProposalView[] = []
let cursor: string | undefined
do {
const page = await deps.proposals.data
.forScope(scope)
.of(proposals)
.list({
where: [{ field: "targetKey", op: "eq", value: LINE_TARGET_KEY }, ...byRecord],
order: [{ field: "createdAt", direction: "asc" }],
limit: PAGE,
...(cursor === undefined ? {} : { cursor }),
})
found.push(...page.items.map(viewOf))
cursor = page.hasMore ? page.cursor : undefined
} while (cursor !== undefined)
return found
}
/** Every proposal of this module's target in the project — or only one document's, when given. */
export function lineProposals(deps: DocumentsDeps, scope: DocumentScope, documentId?: string): Promise<ProposalView[]> {
return proposalsWhere(deps, scope, documentId === undefined ? null : { recordId: documentId })
}
/**
* This module's proposals of several documents at once, keyed by document id: the document list's
* join, one indexed page chain instead of one pass over the queue per document.
*/
export async function lineProposalsByDocument(
deps: DocumentsDeps,
scope: DocumentScope,
documentIds: readonly string[],
): Promise<Map<string, ProposalView[]>> {
const byDocument = new Map<string, ProposalView[]>()
if (documentIds.length === 0) return byDocument
for (const proposal of await proposalsWhere(deps, scope, { recordIds: documentIds })) {
if (proposal.targetRecordId === null) continue
const own = byDocument.get(proposal.targetRecordId)
if (own) own.push(proposal)
else byDocument.set(proposal.targetRecordId, [proposal])
}
return byDocument
}
/** A stable key for the line at a position of a document: what makes an extraction safe to run twice. */
export const lineKeyOf = (documentId: string, page: number, position: number): Promise<string> =>
sha256Hex(new TextEncoder().encode(`documents.line:${documentId}:${page}:${position}`))
/** The review screen's view of a proposal. */
export function lineViewOf(proposal: ProposalView): LineView {
const change = lineChangeSchema.parse(proposal.change)
return {
lineId: proposal.proposalId,
expectedVersion: proposal.version,
status: proposal.status,
label: change.label,
value: change.value,
page: change.page,
quote: change.quote,
box: proposal.evidence[0]?.box ?? null,
confidence: change.confidence,
verification: change.verification,
note: change.note ?? null,
blockId: proposal.evidence[0]?.blockId ?? null,
edited: change.edited,
model: proposal.model,
promptVersion: proposal.promptVersion,
decidedAt: proposal.decidedAt,
}
}
/** The lines a reviewer should see, in reading order, not creation order: the lines of one extraction are
* created within a millisecond of each other and their ids do not order them. */
export function visibleLines(proposals: readonly ProposalView[]): LineView[] {
return [...proposals]
.sort((a, b) => lineChangeSchema.parse(a.change).position - lineChangeSchema.parse(b.change).position)
.map(lineViewOf)
}