visualproof 0.0.0-stage → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (146) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +99 -2
  3. package/dist/assets/fonts/JuliaMono-Regular.ttf +0 -0
  4. package/dist/assets/fonts/JuliaMono-RegularItalic.ttf +0 -0
  5. package/dist/assets/fonts/JuliaMono-SemiBold.ttf +0 -0
  6. package/dist/assets/fonts/LICENSE-JuliaMono.txt +93 -0
  7. package/dist/assets/latex/visualproof.sty +308 -0
  8. package/dist/assets/webfonts/LICENSE-Jost.txt +93 -0
  9. package/dist/assets/webfonts/LICENSE-SourceSans3.txt +93 -0
  10. package/dist/assets/webfonts/LICENSE-SourceSerif4.txt +93 -0
  11. package/dist/assets/webfonts/jost-latin-ext-wght-normal.woff2 +0 -0
  12. package/dist/assets/webfonts/jost-latin-wght-normal.woff2 +0 -0
  13. package/dist/assets/webfonts/source-sans-3-greek-wght-italic.woff2 +0 -0
  14. package/dist/assets/webfonts/source-sans-3-greek-wght-normal.woff2 +0 -0
  15. package/dist/assets/webfonts/source-sans-3-latin-ext-wght-italic.woff2 +0 -0
  16. package/dist/assets/webfonts/source-sans-3-latin-ext-wght-normal.woff2 +0 -0
  17. package/dist/assets/webfonts/source-sans-3-latin-wght-italic.woff2 +0 -0
  18. package/dist/assets/webfonts/source-sans-3-latin-wght-normal.woff2 +0 -0
  19. package/dist/assets/webfonts/source-serif-4-greek-wght-italic.woff2 +0 -0
  20. package/dist/assets/webfonts/source-serif-4-greek-wght-normal.woff2 +0 -0
  21. package/dist/assets/webfonts/source-serif-4-latin-ext-wght-italic.woff2 +0 -0
  22. package/dist/assets/webfonts/source-serif-4-latin-ext-wght-normal.woff2 +0 -0
  23. package/dist/assets/webfonts/source-serif-4-latin-wght-italic.woff2 +0 -0
  24. package/dist/assets/webfonts/source-serif-4-latin-wght-normal.woff2 +0 -0
  25. package/dist/extractor/Extract.lean +1180 -0
  26. package/dist/extractor/Usage.lean +900 -0
  27. package/dist/mcp/visualproof-mcp.mjs +476 -0
  28. package/dist/server/access.js +95 -0
  29. package/dist/server/agent.js +3270 -0
  30. package/dist/server/agentdocs.js +35 -0
  31. package/dist/server/agentlibrary.js +202 -0
  32. package/dist/server/agentnotes.js +701 -0
  33. package/dist/server/agentviews.js +703 -0
  34. package/dist/server/askfollow.js +286 -0
  35. package/dist/server/boardspots.js +159 -0
  36. package/dist/server/dirs.js +175 -0
  37. package/dist/server/doctor.js +186 -0
  38. package/dist/server/events.js +198 -0
  39. package/dist/server/export.js +420 -0
  40. package/dist/server/extract.js +204 -0
  41. package/dist/server/fetchurl.js +321 -0
  42. package/dist/server/figures.js +158 -0
  43. package/dist/server/history.js +780 -0
  44. package/dist/server/historyapi.js +304 -0
  45. package/dist/server/hover.js +595 -0
  46. package/dist/server/identity.js +114 -0
  47. package/dist/server/invites.js +241 -0
  48. package/dist/server/layoutthread.js +51 -0
  49. package/dist/server/library.js +600 -0
  50. package/dist/server/libraryroute.js +211 -0
  51. package/dist/server/live.js +1101 -0
  52. package/dist/server/lostfound.js +199 -0
  53. package/dist/server/main.js +1406 -0
  54. package/dist/server/mcptools.js +496 -0
  55. package/dist/server/pastdoc.js +73 -0
  56. package/dist/server/pipeline.js +159 -0
  57. package/dist/server/poppler.js +251 -0
  58. package/dist/server/presence.js +252 -0
  59. package/dist/server/project.js +222 -0
  60. package/dist/server/prompt.js +53 -0
  61. package/dist/server/readonly.js +152 -0
  62. package/dist/server/readonlygate.js +123 -0
  63. package/dist/server/renames.js +268 -0
  64. package/dist/server/review.js +334 -0
  65. package/dist/server/reviewapi.js +188 -0
  66. package/dist/server/snapshot.js +50 -0
  67. package/dist/server/sourcestore.js +473 -0
  68. package/dist/server/status.js +360 -0
  69. package/dist/server/talk.js +476 -0
  70. package/dist/server/tokens.js +221 -0
  71. package/dist/server/usage.js +501 -0
  72. package/dist/server/versions.js +82 -0
  73. package/dist/server/view.js +164 -0
  74. package/dist/server/views.js +1167 -0
  75. package/dist/shared/askfollow.js +46 -0
  76. package/dist/shared/board.js +332 -0
  77. package/dist/shared/build.js +2 -0
  78. package/dist/shared/collab.js +241 -0
  79. package/dist/shared/comment.js +285 -0
  80. package/dist/shared/context.js +157 -0
  81. package/dist/shared/doc/authors.js +98 -0
  82. package/dist/shared/doc/build.js +358 -0
  83. package/dist/shared/doc/latex.js +455 -0
  84. package/dist/shared/doc/markdown.js +159 -0
  85. package/dist/shared/doc/model.js +37 -0
  86. package/dist/shared/doc/order.js +124 -0
  87. package/dist/shared/doc/outline.js +45 -0
  88. package/dist/shared/doc/proof.js +272 -0
  89. package/dist/shared/doc/sections.js +93 -0
  90. package/dist/shared/doc/settings.js +75 -0
  91. package/dist/shared/edgecodec.js +102 -0
  92. package/dist/shared/editor.js +64 -0
  93. package/dist/shared/excerpt.js +87 -0
  94. package/dist/shared/graph.js +16 -0
  95. package/dist/shared/items.js +92 -0
  96. package/dist/shared/keys.js +203 -0
  97. package/dist/shared/layout.js +926 -0
  98. package/dist/shared/lexer.js +130 -0
  99. package/dist/shared/library.js +711 -0
  100. package/dist/shared/lostfound.js +150 -0
  101. package/dist/shared/moves.js +244 -0
  102. package/dist/shared/notecard.js +295 -0
  103. package/dist/shared/noteplace.js +505 -0
  104. package/dist/shared/palette.js +46 -0
  105. package/dist/shared/pastgraph.js +103 -0
  106. package/dist/shared/places.js +80 -0
  107. package/dist/shared/projectnotes.js +244 -0
  108. package/dist/shared/query.js +818 -0
  109. package/dist/shared/review.js +642 -0
  110. package/dist/shared/reviewqueue.js +333 -0
  111. package/dist/shared/ring.js +366 -0
  112. package/dist/shared/scope.js +255 -0
  113. package/dist/shared/scopeedit.js +81 -0
  114. package/dist/shared/search.js +191 -0
  115. package/dist/shared/separate.js +178 -0
  116. package/dist/shared/sharesnap.js +281 -0
  117. package/dist/shared/sticky.js +501 -0
  118. package/dist/shared/timeline.js +73 -0
  119. package/dist/shared/titles.js +300 -0
  120. package/dist/shared/viewgraph.js +157 -0
  121. package/dist/shared/views.js +859 -0
  122. package/dist/web/bundle/app.js +52671 -0
  123. package/dist/web/bundle/edges.js +407 -0
  124. package/dist/web/bundle/layout.js +1010 -0
  125. package/dist/web/bundle/pdfworker.js +48322 -0
  126. package/dist/web/index.html +257 -0
  127. package/dist/web/src/motion.js +430 -0
  128. package/dist/web/src/past.js +224 -0
  129. package/dist/web/src/scene.js +804 -0
  130. package/dist/web/style.css +2548 -0
  131. package/docs/agent-api/README.md +91 -0
  132. package/docs/agent-api/advice.md +108 -0
  133. package/docs/agent-api/export.md +120 -0
  134. package/docs/agent-api/following.md +140 -0
  135. package/docs/agent-api/history.md +142 -0
  136. package/docs/agent-api/library.md +65 -0
  137. package/docs/agent-api/mcp.md +104 -0
  138. package/docs/agent-api/notes.md +93 -0
  139. package/docs/agent-api/reading.md +166 -0
  140. package/docs/agent-api/requests.md +94 -0
  141. package/docs/agent-api/talking.md +100 -0
  142. package/docs/agent-api/tcb.md +135 -0
  143. package/docs/agent-api/views.md +125 -0
  144. package/docs/agent-api/words.md +56 -0
  145. package/package.json +39 -4
  146. package/test/toolchains.json +5 -0
@@ -0,0 +1,304 @@
1
+ // The history's endpoints (HistorySpec.md): the snapshots of the graph, one snapshot's graph, what
2
+ // changed between two of them and the source text a module's build was compiled from — what the
3
+ // Time Machine reads — and the moves: the migrations
4
+ // and the suggestions the lost-and-found lists, a move made by hand (Attach to…) and a migration
5
+ // undone; the lost-and-found itself, the newest build that had a name (Show in history), and its
6
+ // archive. The page asks under /api/history, agents under /api/agent/history, for the same
7
+ // answers; a move, an archiving or a putting back is recorded under who asks, a person with a name
8
+ // or an agent at level full.
9
+ //
10
+ // The Time Machine steps from build to build, and a snapshot's graph is large (on a large project,
11
+ // megabytes of JSON and a noticeable time to make and compress: Sphere-Packing-Lean's, 7,851 declarations, is 13.7 MB, made
12
+ // in 0.6 s): the page takes it in two parts, its declarations as JSON and its
13
+ // edges packed as the graph on show's are (shared/edgecodec.ts), each compressed once and kept for
14
+ // the last few snapshots asked, under the renames in force, and tagged so that a page that has one
15
+ // is answered 304.
16
+ import { gzipSync } from "node:zlib";
17
+ import { packEdges } from "../shared/edgecodec.js";
18
+ import { MAX_ARCHIVE_KEYS, validKey } from "./lostfound.js";
19
+ import { diffGraphs, pageGraph } from "./history.js";
20
+ const no = (status, error) => ({ status, body: { error } });
21
+ const ok = (body) => ({ status: 200, body });
22
+ /** the parts of the last snapshots asked, per history, by snapshot, build read and renames */
23
+ const kept = new WeakMap();
24
+ /** how many snapshots' parts are kept: about a megabyte each, compressed, on a large project (Sphere-Packing-Lean's:
25
+ * 0.7 MB of declarations, 0.5 MB of edges) */
26
+ const KEEP_PARTS = 8;
27
+ /** what changed between two snapshots, as answered last (a step on the Time Machine's timeline asks
28
+ * it of each build against the latest, every reader the same), by the builds as read and the renames
29
+ * in force; a few hundred KB at most each */
30
+ const diffsKept = new WeakMap();
31
+ const KEEP_DIFFS = 32;
32
+ /** Snapshot `id`'s graph as the page takes it (`part`): its declarations — the answer of /graph
33
+ * without the edges, and how many there are — or its edges, packed. Made together, once. */
34
+ export function snapshotParts(w, id) {
35
+ const h = w.history;
36
+ const info = h.get(id);
37
+ // its texts, once backfilled, change what the declarations' part says
38
+ const key = `${id}\u0000${info.fingerprint}\u0000${info.read}\u0000${w.identityTag?.() ?? ""}\u0000${JSON.stringify(info.sources ?? null)}`;
39
+ let byKey = kept.get(h);
40
+ if (!byKey)
41
+ kept.set(h, (byKey = new Map()));
42
+ const have = byKey.get(key);
43
+ if (have) {
44
+ // most recently asked last: the oldest goes first
45
+ byKey.delete(key);
46
+ byKey.set(key, have);
47
+ return have;
48
+ }
49
+ const g = h.graph(id);
50
+ const { edges: _flat, ...rest } = pageGraph({ ...g, edges: [] }, w.identity());
51
+ const E = new Int32Array(3 * g.edges.length);
52
+ g.edges.forEach(([s, d, m], k) => E.set([s, d, m], 3 * k));
53
+ const parts = { nodes: gzipSync(JSON.stringify({ ...rest, edgeCount: g.edges.length, sources: keptTexts(h, id) })), edges: gzipSync(packEdges(E, g.nodes.length, 2), { level: 9 }) };
54
+ byKey.set(key, parts);
55
+ while (byKey.size > KEEP_PARTS)
56
+ byKey.delete(byKey.keys().next().value);
57
+ return parts;
58
+ }
59
+ /** The modules whose text snapshot `id` keeps, each with how it was matched to its build
60
+ * (server/sourcestore.ts): what the page says of a declaration's source then before it asks. */
61
+ function keptTexts(h, id) {
62
+ const out = {};
63
+ for (const [m, r] of Object.entries(h.sourcesOf(id) ?? {}))
64
+ if (r.blob && r.check)
65
+ out[m] = r.check;
66
+ return out;
67
+ }
68
+ /** Module `module`'s text at snapshot `id`'s build (GET /source; HistorySpec decision 23): the
69
+ * text its build was compiled from, with its Lake hash and how it was matched to the build, or
70
+ * why it is not kept. Never another text. */
71
+ export function pastSource(h, id, module) {
72
+ const s = h.get(id);
73
+ const t = h.textOf(id, module);
74
+ const build = { snapshot: id, at: s.at, commit: s.commit };
75
+ if (!t) {
76
+ const why = h.sourcesOf(id) === null ? "the texts of this build are still being looked for" : h.modulesOf(id).includes(module) ? "its module has no file of the project" : `the build has no module ${module.slice(0, 300)}`;
77
+ return { status: 404, body: { error: `The text of ${module.slice(0, 300)} at this build is not kept: ${why}.`, kept: false, why, ...build } };
78
+ }
79
+ if (t.text === null)
80
+ return { status: 404, body: { error: `The text of ${module} at this build is not kept: ${t.ref.why ?? "it was not found"}.`, kept: false, why: t.ref.why ?? null, file: t.ref.file, ...build } };
81
+ return { status: 200, body: { module, file: t.ref.file, text: t.text, lake: t.ref.lake ?? null, check: t.ref.check, from: t.ref.from ?? null, ...build } };
82
+ }
83
+ /** A snapshot named in the query, `id` (or another key): the snapshot, or why not. The latest
84
+ * build's where it is left out and `dflt` says so. */
85
+ function snapshotOf(w, a, key, dflt) {
86
+ const h = w.history;
87
+ const raw = a.query.get(key);
88
+ if (raw === null || raw === "" || raw === "now") {
89
+ const l = h.latest();
90
+ if (dflt && l)
91
+ return l.id;
92
+ return no(400, `Say which snapshot: ${key}=<its id> (GET /api/history lists them).`);
93
+ }
94
+ const id = Number(raw);
95
+ if (!Number.isInteger(id) || !h.get(id))
96
+ return no(404, `There is no snapshot ${raw.slice(0, 40)}.`);
97
+ return id;
98
+ }
99
+ /** A request under /api/history (or /api/agent/history). `refuse`: why the caller may not move
100
+ * things (an agent below level full), or null. */
101
+ export function historyAnswer(w, a, refuse = () => null) {
102
+ const h = w.history;
103
+ if (!h)
104
+ return no(404, "This server serves a saved graph: it reads no builds, and keeps no history.");
105
+ const path = a.path.replace(/^\/api(\/agent)?\/history/, "").replace(/\/+$/, "");
106
+ if (a.method === "POST" && (path === "/move" || path === "/unmove" || path === "/archive")) {
107
+ const no_ = refuse(a);
108
+ if (no_)
109
+ return no_;
110
+ const name = a.caller.name?.trim();
111
+ if (!name)
112
+ return { status: 401, body: { error: `Say who you are first: ${path === "/archive" ? "an archiving" : "a move"} is recorded under your name.`, who: true } };
113
+ // someone invited by a personal link moves under the link's id, in their own log
114
+ const who = { name, agent: a.caller.kind === "agent", ...(a.caller.person ? { person: a.caller.person } : {}) };
115
+ const b = (a.body && typeof a.body === "object" ? a.body : {});
116
+ const str = (x) => typeof x === "string" && x.length > 0 && x.length <= 2000;
117
+ if (path === "/move") {
118
+ if (!str(b.from) || !str(b.to))
119
+ return no(400, "Send {from, to}: the full name of a declaration that is gone, and of one in the graph.");
120
+ return w.moveByHand ? w.moveByHand(b.from, b.to, who) : no(404, "not found");
121
+ }
122
+ if (path === "/archive") {
123
+ // one entry, or several at once (a name's every entry)
124
+ const keys = b.keys === undefined ? [b.key] : b.keys;
125
+ if (!Array.isArray(keys) || !keys.length || keys.length > MAX_ARCHIVE_KEYS || !keys.every(validKey) || (b.on !== undefined && typeof b.on !== "boolean"))
126
+ return no(400, `Send {key, on}: an entry's key (GET /api/history/lost lists them) — or {keys: [at most ${MAX_ARCHIVE_KEYS}], on} — on true (the default) to archive, false to put back.`);
127
+ return w.archiveEntries ? w.archiveEntries(keys, b.on !== false, who) : no(404, "not found");
128
+ }
129
+ if (typeof b.id !== "string" || !b.id)
130
+ return no(400, "Send {id}: a migration's id (GET /api/history/moves lists them).");
131
+ return w.unmove ? w.unmove(b.id, who) : no(404, "not found");
132
+ }
133
+ if (a.method !== "GET")
134
+ return no(405, "The history is read with GET; a move is made, or undone, with POST /move or /unmove, and an entry archived with POST /archive.");
135
+ if (path === "/moves")
136
+ return ok(w.moves?.() ?? { migrations: [], suggestions: [] });
137
+ if (path === "/lost")
138
+ return ok(w.lostAndFound?.() ?? { lost: [], migrations: [], archived: [], aside: { entries: 0, modules: [] }, count: 0 });
139
+ if (path === "/archive") {
140
+ const l = w.lostAndFound?.();
141
+ return ok({ archived: l ? l.archived.map((x) => x.line) : [] });
142
+ }
143
+ if (path === "/last") {
144
+ const name = a.query.get("name") ?? "";
145
+ if (!name || name.length > 2000)
146
+ return no(400, "Say which declaration: name=<its full name>.");
147
+ const l = w.lastHad?.(name) ?? null;
148
+ return l ? ok(l) : no(404, `No build the history kept had ${name.slice(0, 300)}: it went before the history began, or was never a declaration.`);
149
+ }
150
+ if (path === "") {
151
+ const latest = h.latest();
152
+ return ok({ snapshots: h.list(), current: latest?.id ?? null, diskBytes: h.diskBytes() });
153
+ }
154
+ if (path === "/graph") {
155
+ const id = snapshotOf(w, a, "id", true);
156
+ if (typeof id !== "number")
157
+ return id;
158
+ const part = a.query.get("part");
159
+ if (part === "nodes")
160
+ return { status: 200, body: null, gz: { type: "application/json", body: snapshotParts(w, id).nodes } };
161
+ if (part === "edges")
162
+ return { status: 200, body: null, gz: { type: "application/octet-stream", body: snapshotParts(w, id).edges } };
163
+ if (part !== null)
164
+ return no(400, "part is nodes or edges; leave it out for the whole graph.");
165
+ return ok(pageGraph(h.graph(id), w.identity()));
166
+ }
167
+ if (path === "/source") {
168
+ const id = snapshotOf(w, a, "id", false);
169
+ if (typeof id !== "number")
170
+ return id;
171
+ const module = a.query.get("module") ?? "";
172
+ if (!module || module.length > 2000)
173
+ return no(400, "Say which module: module=<its name>.");
174
+ return pastSource(h, id, module);
175
+ }
176
+ if (path === "/diff") {
177
+ const from = snapshotOf(w, a, "from", false);
178
+ if (typeof from !== "number")
179
+ return from;
180
+ const to = snapshotOf(w, a, "to", true);
181
+ if (typeof to !== "number")
182
+ return to;
183
+ const most = Math.min(MOST_DIFF, Math.max(0, Number(a.query.get("most") ?? 2000) || 0));
184
+ return ok(snapshotDiff(w, from, to, most));
185
+ }
186
+ return no(404, "not found");
187
+ }
188
+ /** the most names a diff lists of each kind: what the Time Machine asks, so that its marks and its
189
+ * lists have every declaration gone, changed, renamed or added since (web/src/timemachine.ts) */
190
+ export const MOST_DIFF = 20000;
191
+ /** What changed from snapshot `from` to snapshot `to` (both in the history), at most `most` names of
192
+ * each kind, as GET /diff answers it: made once, then kept for every reader and for the export of a
193
+ * view at a past build (server/pastdoc.ts), which makes the same past graph the page does. */
194
+ export function snapshotDiff(w, from, to, most) {
195
+ const h = w.history;
196
+ const [fa, fb] = [h.get(from), h.get(to)];
197
+ const key = [from, fa.fingerprint, fa.read, to, fb.fingerprint, fb.read, most, w.identityTag?.() ?? ""].join("\u0000");
198
+ let byKey = diffsKept.get(h);
199
+ if (!byKey)
200
+ diffsKept.set(h, (byKey = new Map()));
201
+ let body = byKey.get(key);
202
+ if (body === undefined) {
203
+ body = { from, to, ...diffGraphs(h.graph(from), h.graph(to), w.identity(), w.renamesBetween?.(from, to) ?? new Map(), most) };
204
+ byKey.set(key, body);
205
+ while (byKey.size > KEEP_DIFFS)
206
+ byKey.delete(byKey.keys().next().value);
207
+ }
208
+ return body;
209
+ }
210
+ /** What the history's answers read, from what the agents' API has (server/agent.ts, World): what
211
+ * is not there answers as a server with nothing of it would. */
212
+ export function historyWorld(world) {
213
+ return {
214
+ get history() {
215
+ return world.history ?? null;
216
+ },
217
+ identity: () => world.identity?.() ?? ((u) => u),
218
+ identityTag: () => world.identityTag?.() ?? "",
219
+ renamesBetween: (from, to) => world.renamesBetween?.(from, to) ?? new Map(),
220
+ moves: () => world.moves?.() ?? { migrations: [], suggestions: [] },
221
+ moveByHand: (from, to, who) => world.moveByHand?.(from, to, who) ?? { status: 404, body: { error: "not found" } },
222
+ unmove: (id, who) => world.unmove?.(id, who) ?? { status: 404, body: { error: "not found" } },
223
+ lostAndFound: () => world.lostAndFound?.() ?? { lost: [], migrations: [], archived: [], aside: { entries: 0, modules: [] }, count: 0 },
224
+ lastHad: (name) => world.lastHad?.(name) ?? null,
225
+ archiveEntries: (keys, on, who) => world.archiveEntries?.(keys, on, who) ?? { status: 404, body: { error: "not found" } },
226
+ };
227
+ }
228
+ /** The same, as endpoints of the agents' API (server/agent.ts): reading only, at every level. */
229
+ export function historyEndpoints(world, refuseFull) {
230
+ const w = historyWorld(world);
231
+ const run = (a) => historyAnswer(w, a, refuseFull);
232
+ return [
233
+ {
234
+ method: "GET", path: "/api/agent/history",
235
+ about: "The snapshots of the graph (HistorySpec.md): one per build the server read, oldest first — when the build finished, the commit it was built from, how many declarations, what keeping it took, the renames found against the build before, and how many of its modules' source texts are kept (sources: kept of how many, places of them matched to the build by the places it recorded rather than by their Lake hash). The Time Machine's ticks.",
236
+ params: {},
237
+ returns: "{snapshots: [{id, at, approx?, read, fingerprint, commit: {hash, branch, dirty?, guessed?} | null, toolchain, missing, counts: {modules, nodes, edges, externals}, bytes: {new, all}, imported?, renames?: {high, medium}, sources?: {kept, of, places, bytes: {new, all}}}], current, diskBytes}",
238
+ run,
239
+ },
240
+ {
241
+ method: "GET", path: "/api/agent/history/graph",
242
+ about: "One snapshot's graph, as the page reads the graph on show without its layouts: each project declaration with its statement, doc, place, version (hash, under the renames in force) and the kernel's hash it was read with; the outside declarations it refers to; the edges as one flat list. Large: 0.8 MB of JSON for 300 declarations, 14 MB for 8,000. A declaration's source text at that build: GET /api/agent/source?name=…&snapshot=<id>.",
243
+ params: { id: "a snapshot's id (default: the latest build's)", part: "nodes: the same without its edges (and edgeCount), with sources (the modules whose text is kept, each lake or places), as the page takes it; edges: the edges alone, packed as /api/edges?pack=2 packs them (shared/edgecodec.ts); leave it out for the whole graph" },
244
+ returns: "{snapshot, modules: [{name, imports}], nodes: [{name, kind, module, ax, type, doc?, range?, also?, flags?, hash, kernel?} | {name, kind, module, ax, ext: true}], projectCount, axioms, axiomSets, edges: [src, dst, sites, …]}",
245
+ run,
246
+ },
247
+ {
248
+ method: "GET", path: "/api/agent/history/diff",
249
+ about: "What changed from one snapshot to another (the latest build's by default): the declarations added, removed, renamed, and changed (a new version, so a rename alone is not a change). What a module the later build could not read held is neither added nor removed.",
250
+ params: { from: "a snapshot's id", to: "a snapshot's id (default: the latest build's)", most: "at most this many names per list (default 2000)" },
251
+ returns: "{from, to, counts: {added, removed, changed, renamed}, added, removed, changed, renamed: [[was, is]]}",
252
+ run,
253
+ },
254
+ {
255
+ method: "GET", path: "/api/agent/history/moves",
256
+ about: "The moves: every migration — a rename the server found and carried over by itself (auto), or a move a person or agent made (Attach to…) — with what it carried and whether it was undone; and the suggestions not acted on, renames of medium confidence whose old name is gone and new one is there.",
257
+ params: {},
258
+ returns: "{migrations: [{id, at, from, to, snap?, by, agent?, auto?, rename, review, carried: {opinions, targets, keys, notes, pins, scopes, reviewViews}, views: [{view, ops, scope?}], undone?: {by, agent?, at}}], suggestions: [{from, to, confidence: medium, why, score, snap}]}",
259
+ run,
260
+ },
261
+ {
262
+ method: "POST", path: "/api/agent/history/move", needs: "full",
263
+ about: "Attach what was said of a declaration that is gone (from) to one in the graph (to): its opinions, target mark and key marks move in your review log, its pins, notes' mentions and views' scopes on the views. It says that to is from renamed where to was not there yet in the last build that had from. Recorded under your name, marked as an agent's; undone with /history/unmove.",
264
+ params: { from: "a full name the graph no longer has", to: "a full name in the graph" },
265
+ returns: "{ok, changed, migration?}",
266
+ run,
267
+ },
268
+ {
269
+ method: "POST", path: "/api/agent/history/unmove", needs: "full",
270
+ about: "Undo a migration (GET /api/agent/history/moves lists them): an un-move in your review log takes back exactly what it carried, and its changes to the views are undone. Nothing is deleted.",
271
+ params: { id: "a migration's id, m1, m2, …" },
272
+ returns: "{ok, migration}",
273
+ run,
274
+ },
275
+ {
276
+ method: "GET", path: "/api/agent/history/lost",
277
+ about: "The lost-and-found (HistorySpec decisions 12–14): what people and agents said and marked of declarations the graph no longer has, that no rename carried — opinions with their comments, target marks, key marks, pins on views, notes' mentions — by the name it is under, each name with the newest build that had it and the renames suggested for it; every migration, the server's and those made by hand, undone or not; and what is archived. Nothing in it is deleted: attach a name's entries to a declaration with /history/move, or archive an entry, which only hides it.",
278
+ params: {},
279
+ returns: "{lost: [{name, last: {name, snapshot, at, commit, module} | null, suggestions: [{to, score, why, snap}], entries: [{key, kind: opinion|target|key|pin|note, name, by, agent?, person?, at, verdict?, comment?, target?, member?, view?, title?, mode?, note?, text?}]}], migrations: [{key, id, at, from, to, by, auto?, rename, carried, undone?}], archived: [{key, line: {key, on, by, agent?, at}, entry? | migration?}], aside: {entries, modules}, count}",
280
+ run,
281
+ },
282
+ {
283
+ method: "GET", path: "/api/agent/history/last",
284
+ about: "The newest build whose graph had a declaration of this name, and the module it was in there: where the Time Machine shows it. 404 where no build the history kept had it.",
285
+ params: { name: "a full name" },
286
+ returns: "{name, snapshot, at, approx?, commit, module}",
287
+ run,
288
+ },
289
+ {
290
+ method: "GET", path: "/api/agent/history/archive",
291
+ about: "What is archived of the lost-and-found: each entry's or migration's key with the line that archived it (who, when). The lines are kept in the server's state, one per archiving or putting back; the last of a key is its state.",
292
+ params: {},
293
+ returns: "{archived: [{key, on: true, by, agent?, person?, at}]}",
294
+ run,
295
+ },
296
+ {
297
+ method: "POST", path: "/api/agent/history/archive", needs: "full",
298
+ about: "Archive an entry of the lost-and-found, or a migration — it leaves the list, is kept, and shows under archived for everyone — or put it back (on: false). Nothing about the thing itself changes. Recorded under your name, marked as an agent's.",
299
+ params: { key: "an entry's key, as /history/lost lists it (opinion:…, target:…, key:…, pin:…, note:…, move:m3) — or —", keys: `[keys], at most ${MAX_ARCHIVE_KEYS}`, on: "true (default): archive; false: put back" },
300
+ returns: "{ok, changed, lines: [{key, on, by, agent, at}]}",
301
+ run,
302
+ },
303
+ ];
304
+ }