import type { ToolDefinition } from "./types.js"; /** * `GET /api/v1/repository?near_duplicates=` as a tool — shipped in the * platform by SPGD-703 (`specguard` `c43dc19`, 2026-08-28), which added the * `near_duplicates` block to `RepositoryOverview` behind an opt-in ask. * * == What the block is, and why it is behind an ask at all (this section is * the pre-SPGD-1474 story — the cost was live on the request; the next section * is what changed) * * It is the suite-wide near-duplicate census: which tests READ alike — same * body text, not same file — clustered by the engine SPGD-369 shipped * (`specguard` `f7d5352`). It is the first block on that endpoint whose grain * is the REPOSITORY rather than a run or a window of runs, and the first one * gated on COST rather than rows: `NearDuplicateClusters` is linear but * measured in seconds (seven queries at every size; seconds at a few thousand * identities, extrapolating to tens of seconds at the 20,000-identity design * point). `?near_duplicates=` confines that cost to the client that named it — * no ask, key present and `null`, not one query — which is why this block is a * SEPARATE tool rather than a forwarded parameter on * `get_repository_overview`: an agent calling the overview should never pay * the census by accident, and an agent calling this tool has asked for nothing * else. Splitting the tools splits the cost along exactly the line the server * drew. * * == SPGD-1474: the census is computed at ingest, and this read is stored * * The minutes-scale compute no longer sits behind the request (and could never * fit this bridge's 30-second default deadline). The server computes the census * once per write that moves its inputs — at each ingest, after identity * resolution settles the run's identities, and again when a run is deleted from * the repository (which can move the run the weight figures are weighed on) — * persists it, and serves the stored artifact, so a call here costs one stored * read and returns in milliseconds. The opt-in ask is unchanged wire contract * (`?near_duplicates=` still opens the block; the plain overview still answers * `null` without it), and the tool split still stands: one call, one census, * no accidental cost. What changed is the latency story and the honesty * machinery around it: the block carries `computed_at` (when the stored * artifact was taken) and `weighed_run_id` (which run its weight figures are * from), so a consumer can always tell how fresh the census it is reading is. * A request arriving between a completed write and the finished recompute * serves the PREVIOUS stored census with its own stamp — never a live * computation, never an unstamped answer. Between those writes (an ingest, or a * run deletion) the stored census is exactly what a live computation would * return: the inputs change only there. * * == The default ask is always sent, and always spelled `"true"` * * The server reads only whether the parameter is PRESENT * (`RequestedNearDuplicatesParam`): `?near_duplicates=false` opens the block * exactly as `=true` does, and a non-String shape is read as no ask at all. * There is no "off" value for a client to send, so a call with no `summary` * and no `cluster` sends `near_duplicates=true` and gets the whole census. * What IS choosable is WHICH REPOSITORY is censused (SPGD-953): an optional * `repository` argument (a numeric id from `list_repositories`) moves the call * to the plural endpoint `GET /api/v1/repositories/:id` under either member * credential, whichever is set — the AGENT key preferred, and the USER key (a * person credential, whose accessible set is the boundary the id is resolved * inside) when no agent key is set; SPGD-1106 widened the plural arm from * agent-only, on the same agent-wins/user-fallback terms every * either-credential tool answers by. Same body (minus the `api_key` block, * which is ABSENT on that surface rather than nulled — see * `repository-overview.ts` for why the omission is the server's, deliberately), * same ask, same cost gate. Without it the request is byte-for-byte the * singular, `sgk_`-bound one this tool has always made — and the cost argument * above is exactly why the argument is OPTIONAL rather than required: an agent * that has only ever had one reachable repository should not be asked to learn * a second credential to keep reading it. * * == SPGD-1714: the bounded view (`summary`, `cluster`) * * The platform half (SPGD-1712) serves two further asks on the same endpoints: * `?near_duplicates_summary=` (the ranking, head keys first, no member lists) * and `?near_duplicate_cluster=` (ONE cluster, members once). Like the * default ask, the server reads only that the summary key is PRESENT, so * `summary: false` sends NOTHING rather than `=false` — "off" is spelled by * omission. When either bounded ask is made, ONLY the asks named are sent (not * also `near_duplicates=true`: that would open the whole census beside the * bounded view the caller chose it to avoid). `cluster` is not range-checked * here: an out-of-range rank is the server's to answer (a `null` cluster with * the ask echoed). * * Because a deployment that predates SPGD-1712 would silently ignore an * unknown parameter and answer 200 with the plain overview, an ask whose * matching key is ABSENT from the body (absent, not `null` — a present `null` * is the server's own "asked, nothing there") is turned into an error naming * the key. Otherwise "asked for the bounded view, got nothing" and "the * deployment ignored the ask" would share one wire shape. Nothing else is * inspected: the tool stays a pass-through. * * Every key is built conditionally rather than stringified for the same * reason `repository-overview.ts` builds its `unannotated_examples` key: * `getJson` omits only `undefined`, so a conditional send is the only honest * way to spell "only when asked". The schema stays CLOSED around its arguments: * `server.ts` forwards `arguments` unvalidated, and an open schema would let an * invented argument ride through and be silently dropped (see * `registrable-repositories.ts` for the same call). * * == The response is passed through, not re-modelled * * Same rule as `repository-overview.ts`: every figure in the block is * annotated in `RepositoryOverview#serialized_near_duplicates` with the reason * for its shape, and several of those reasons are about honesty rather than * convenience — `similarity_floor`/`similarity_basis` served FIRST because a * count without the statement of what the similarity means is a vacuous * figure; `member_count` (texts in the REPOSITORY, across every run) and * `example_count` (examples in the ONE run `weighed_run_id` names) served side * by side because a three-example table-driven loop is one member and three * examples, and flattening them would erase the figure the ranking is built * on; `unobserved_members` disclosing that the member list holds an identity * the weighed run did not observe; `similarity_range` as the `[strongest, * weakest]` pair because membership is transitive while similarity is not. * Reshaping here would discard distinctions the serializer spent that care * preserving, so the body goes back as it arrived — the whole body, which * carries the repository/run context the clusters sit inside. */ declare const nearDuplicateClusters: ToolDefinition; export default nearDuplicateClusters;