visualproof 0.0.0-stage → 0.1.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.
- package/LICENSE +202 -0
- package/README.md +99 -2
- package/dist/assets/fonts/JuliaMono-Regular.ttf +0 -0
- package/dist/assets/fonts/JuliaMono-RegularItalic.ttf +0 -0
- package/dist/assets/fonts/JuliaMono-SemiBold.ttf +0 -0
- package/dist/assets/fonts/LICENSE-JuliaMono.txt +93 -0
- package/dist/assets/latex/visualproof.sty +308 -0
- package/dist/extractor/Extract.lean +1180 -0
- package/dist/extractor/Usage.lean +900 -0
- package/dist/mcp/visualproof-mcp.mjs +476 -0
- package/dist/server/access.js +95 -0
- package/dist/server/agent.js +3235 -0
- package/dist/server/agentdocs.js +35 -0
- package/dist/server/agentlibrary.js +202 -0
- package/dist/server/agentnotes.js +701 -0
- package/dist/server/agentviews.js +703 -0
- package/dist/server/askfollow.js +286 -0
- package/dist/server/boardspots.js +159 -0
- package/dist/server/dirs.js +175 -0
- package/dist/server/doctor.js +186 -0
- package/dist/server/events.js +198 -0
- package/dist/server/export.js +420 -0
- package/dist/server/extract.js +204 -0
- package/dist/server/fetchurl.js +321 -0
- package/dist/server/figures.js +158 -0
- package/dist/server/history.js +741 -0
- package/dist/server/historyapi.js +304 -0
- package/dist/server/hover.js +595 -0
- package/dist/server/identity.js +114 -0
- package/dist/server/invites.js +241 -0
- package/dist/server/layoutthread.js +51 -0
- package/dist/server/library.js +600 -0
- package/dist/server/libraryroute.js +211 -0
- package/dist/server/live.js +1101 -0
- package/dist/server/lostfound.js +199 -0
- package/dist/server/main.js +1394 -0
- package/dist/server/mcptools.js +497 -0
- package/dist/server/pastdoc.js +73 -0
- package/dist/server/pipeline.js +156 -0
- package/dist/server/poppler.js +251 -0
- package/dist/server/presence.js +252 -0
- package/dist/server/project.js +222 -0
- package/dist/server/prompt.js +53 -0
- package/dist/server/readonly.js +152 -0
- package/dist/server/readonlygate.js +123 -0
- package/dist/server/renames.js +268 -0
- package/dist/server/review.js +334 -0
- package/dist/server/reviewapi.js +188 -0
- package/dist/server/snapshot.js +49 -0
- package/dist/server/sourcestore.js +473 -0
- package/dist/server/status.js +360 -0
- package/dist/server/talk.js +476 -0
- package/dist/server/tokens.js +221 -0
- package/dist/server/usage.js +501 -0
- package/dist/server/versions.js +82 -0
- package/dist/server/view.js +157 -0
- package/dist/server/views.js +1167 -0
- package/dist/shared/askfollow.js +46 -0
- package/dist/shared/board.js +332 -0
- package/dist/shared/build.js +2 -0
- package/dist/shared/collab.js +148 -0
- package/dist/shared/comment.js +285 -0
- package/dist/shared/context.js +157 -0
- package/dist/shared/doc/authors.js +98 -0
- package/dist/shared/doc/build.js +358 -0
- package/dist/shared/doc/latex.js +455 -0
- package/dist/shared/doc/markdown.js +159 -0
- package/dist/shared/doc/model.js +37 -0
- package/dist/shared/doc/order.js +107 -0
- package/dist/shared/doc/proof.js +272 -0
- package/dist/shared/doc/sections.js +93 -0
- package/dist/shared/doc/settings.js +75 -0
- package/dist/shared/edgecodec.js +102 -0
- package/dist/shared/editor.js +64 -0
- package/dist/shared/excerpt.js +87 -0
- package/dist/shared/graph.js +16 -0
- package/dist/shared/items.js +92 -0
- package/dist/shared/keys.js +203 -0
- package/dist/shared/layout.js +926 -0
- package/dist/shared/lexer.js +130 -0
- package/dist/shared/library.js +711 -0
- package/dist/shared/lostfound.js +150 -0
- package/dist/shared/moves.js +243 -0
- package/dist/shared/notecard.js +295 -0
- package/dist/shared/noteplace.js +505 -0
- package/dist/shared/palette.js +46 -0
- package/dist/shared/pastgraph.js +95 -0
- package/dist/shared/places.js +80 -0
- package/dist/shared/projectnotes.js +244 -0
- package/dist/shared/query.js +810 -0
- package/dist/shared/review.js +642 -0
- package/dist/shared/reviewqueue.js +333 -0
- package/dist/shared/ring.js +366 -0
- package/dist/shared/scope.js +255 -0
- package/dist/shared/scopeedit.js +81 -0
- package/dist/shared/search.js +191 -0
- package/dist/shared/separate.js +178 -0
- package/dist/shared/sharesnap.js +280 -0
- package/dist/shared/sticky.js +501 -0
- package/dist/shared/timeline.js +73 -0
- package/dist/shared/titles.js +298 -0
- package/dist/shared/viewgraph.js +139 -0
- package/dist/shared/views.js +859 -0
- package/dist/web/bundle/app.js +48733 -0
- package/dist/web/bundle/edges.js +409 -0
- package/dist/web/bundle/layout.js +1010 -0
- package/dist/web/bundle/pdfworker.js +48322 -0
- package/dist/web/index.html +239 -0
- package/dist/web/src/motion.js +430 -0
- package/dist/web/src/past.js +224 -0
- package/dist/web/src/scene.js +787 -0
- package/dist/web/style.css +2312 -0
- package/docs/agent-api/README.md +91 -0
- package/docs/agent-api/advice.md +108 -0
- package/docs/agent-api/export.md +120 -0
- package/docs/agent-api/following.md +140 -0
- package/docs/agent-api/history.md +142 -0
- package/docs/agent-api/library.md +65 -0
- package/docs/agent-api/mcp.md +104 -0
- package/docs/agent-api/notes.md +93 -0
- package/docs/agent-api/reading.md +166 -0
- package/docs/agent-api/requests.md +94 -0
- package/docs/agent-api/talking.md +100 -0
- package/docs/agent-api/tcb.md +135 -0
- package/docs/agent-api/views.md +124 -0
- package/docs/agent-api/words.md +56 -0
- package/package.json +39 -4
- package/test/toolchains.json +5 -0
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
// The lost-and-found as the server answers it (HistorySpec decisions 12–14): the entries of
|
|
2
|
+
// shared/lostfound.ts — what was said of a declaration the graph no longer has — grouped by the name
|
|
3
|
+
// they are under, each name with the newest build that had it and the renames of medium confidence
|
|
4
|
+
// suggested for it; every migration, the server's and those made by hand; and what is archived.
|
|
5
|
+
//
|
|
6
|
+
// The archive is the one thing kept: `<site>/history/archive.jsonl`, one line per archiving or
|
|
7
|
+
// putting back, `{key, on, by, agent?, person?, at}`, appended and never rewritten; the last line of
|
|
8
|
+
// a key is its state. Archiving hides an entry from the list and changes nothing about the thing
|
|
9
|
+
// itself, which stays where it is, in the review's logs or on its view. It is shared, as the review
|
|
10
|
+
// is: everyone on the server sees the same list.
|
|
11
|
+
//
|
|
12
|
+
// An archiving is recorded as the review's actions are, under the name of whoever made it, which it
|
|
13
|
+
// needs, a private reader's too (CollabSpec decision 8), with the personal link of someone invited
|
|
14
|
+
// by one (decision 9): it hides what someone said from everyone's list, and the review says who
|
|
15
|
+
// said what; Attach to…, the other thing done to an entry, is a move in the review's logs.
|
|
16
|
+
import { appendFileSync, closeSync, existsSync, fstatSync, mkdirSync, openSync, readFileSync, readSync } from "node:fs";
|
|
17
|
+
import { join } from "node:path";
|
|
18
|
+
import { lostEntries, lostKey } from "../shared/lostfound.js";
|
|
19
|
+
/** the most keys one request archives or puts back */
|
|
20
|
+
export const MAX_ARCHIVE_KEYS = 500;
|
|
21
|
+
const KEY = /^(opinion|target|key|pin|note|link|move):/;
|
|
22
|
+
/** Whether a string can be an entry's key: one of the kinds, and short enough to keep. */
|
|
23
|
+
export function validKey(k) {
|
|
24
|
+
return typeof k === "string" && k.length <= 5000 && KEY.test(k) && !/[\n\r]/.test(k);
|
|
25
|
+
}
|
|
26
|
+
export class Archive {
|
|
27
|
+
file;
|
|
28
|
+
/** the last line of each key */
|
|
29
|
+
last = new Map();
|
|
30
|
+
constructor(historyDir) {
|
|
31
|
+
this.file = join(historyDir, "archive.jsonl");
|
|
32
|
+
if (!existsSync(this.file))
|
|
33
|
+
return;
|
|
34
|
+
for (const l of readFileSync(this.file, "utf8").split("\n")) {
|
|
35
|
+
if (!l.trim())
|
|
36
|
+
continue;
|
|
37
|
+
try {
|
|
38
|
+
const o = JSON.parse(l);
|
|
39
|
+
if (validKey(o.key) && typeof o.on === "boolean" && typeof o.by === "string" && typeof o.at === "string")
|
|
40
|
+
this.last.set(o.key, o);
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
// a line cut short (the server stopped while writing): left out
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
/** The keys archived now, with the line that archived each. */
|
|
48
|
+
archived() {
|
|
49
|
+
return new Map([...this.last].filter(([, l]) => l.on));
|
|
50
|
+
}
|
|
51
|
+
/** Whether `key` is archived now: its line, or null. */
|
|
52
|
+
get(key) {
|
|
53
|
+
const l = this.last.get(key);
|
|
54
|
+
return l?.on ? l : null;
|
|
55
|
+
}
|
|
56
|
+
/** Archive the keys, or put them back (`on` false), under `who`: a line for each whose state
|
|
57
|
+
* changes; the lines written. */
|
|
58
|
+
set(keys, on, who, now = new Date().toISOString()) {
|
|
59
|
+
const lines = [];
|
|
60
|
+
for (const key of new Set(keys)) {
|
|
61
|
+
if (!!this.get(key) === on)
|
|
62
|
+
continue;
|
|
63
|
+
lines.push({ key, on, by: who.name, ...(who.agent ? { agent: true } : {}), ...(who.person ? { person: who.person } : {}), at: now });
|
|
64
|
+
}
|
|
65
|
+
if (!lines.length)
|
|
66
|
+
return lines;
|
|
67
|
+
mkdirSync(join(this.file, ".."), { recursive: true });
|
|
68
|
+
// after a line cut short (the server stopped while writing), on a line of its own
|
|
69
|
+
appendFileSync(this.file, (endsLine(this.file) ? "" : "\n") + lines.map((l) => JSON.stringify(l)).join("\n") + "\n");
|
|
70
|
+
for (const l of lines)
|
|
71
|
+
this.last.set(l.key, l);
|
|
72
|
+
return lines;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
/** Whether a file is empty, missing, or ends with a line break. */
|
|
76
|
+
function endsLine(file) {
|
|
77
|
+
let fd;
|
|
78
|
+
try {
|
|
79
|
+
fd = openSync(file, "r");
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
return true;
|
|
83
|
+
}
|
|
84
|
+
try {
|
|
85
|
+
const size = fstatSync(fd).size;
|
|
86
|
+
if (!size)
|
|
87
|
+
return true;
|
|
88
|
+
const b = Buffer.alloc(1);
|
|
89
|
+
readSync(fd, b, 0, 1, size - 1);
|
|
90
|
+
return b[0] === 0x0a;
|
|
91
|
+
}
|
|
92
|
+
finally {
|
|
93
|
+
closeSync(fd);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
/** The lost-and-found: the entries not archived by the name they are under, the migrations not
|
|
97
|
+
* archived, what is archived, and what is set aside — entries of a declaration that was, in the
|
|
98
|
+
* newest build that had it, in a file the graph on show could not read: it did not vanish
|
|
99
|
+
* (HistorySpec decision 6). */
|
|
100
|
+
export function lostAndFound(w) {
|
|
101
|
+
const archived = w.archive?.archived() ?? new Map();
|
|
102
|
+
// an opinion's archiving under a name its writer went by before stands
|
|
103
|
+
const lineOf = (e) => {
|
|
104
|
+
for (const k of [e.key, ...(e.aliases ?? [])]) {
|
|
105
|
+
const l = archived.get(k);
|
|
106
|
+
if (l)
|
|
107
|
+
return l;
|
|
108
|
+
}
|
|
109
|
+
return null;
|
|
110
|
+
};
|
|
111
|
+
const lasts = new Map();
|
|
112
|
+
const last = (name) => {
|
|
113
|
+
if (!lasts.has(name))
|
|
114
|
+
lasts.set(name, w.last(name));
|
|
115
|
+
return lasts.get(name);
|
|
116
|
+
};
|
|
117
|
+
// a note's mention counts where it names what was a declaration, in some build the history kept:
|
|
118
|
+
// anything else in backticks is code
|
|
119
|
+
const entries = lostEntries(w.review, w.views, w.has, (name) => last(name) !== null, w.pinWho, w.title, w.atBuild, w.notes, w.hasCluster ? (k, key) => w.hasCluster(k, key) : null);
|
|
120
|
+
const missing = new Set(w.missing);
|
|
121
|
+
const aside = { entries: 0, modules: new Set() };
|
|
122
|
+
const groups = new Map();
|
|
123
|
+
const out = { lost: [], migrations: [], archived: [], aside: { entries: 0, modules: [] }, count: 0 };
|
|
124
|
+
const shown = [];
|
|
125
|
+
for (const e of entries) {
|
|
126
|
+
const l = last(e.name);
|
|
127
|
+
if (l && missing.has(l.module)) {
|
|
128
|
+
aside.entries++;
|
|
129
|
+
aside.modules.add(l.module);
|
|
130
|
+
continue;
|
|
131
|
+
}
|
|
132
|
+
const line = lineOf(e);
|
|
133
|
+
if (line) {
|
|
134
|
+
shown.push({ key: e.key, line, entry: e });
|
|
135
|
+
continue;
|
|
136
|
+
}
|
|
137
|
+
let g = groups.get(e.name);
|
|
138
|
+
if (!g)
|
|
139
|
+
groups.set(e.name, (g = { name: e.name, last: l, suggestions: [], entries: [] }));
|
|
140
|
+
g.entries.push(e);
|
|
141
|
+
}
|
|
142
|
+
for (const s of w.suggestions) {
|
|
143
|
+
const g = groups.get(s.from);
|
|
144
|
+
if (g && !g.suggestions.some((x) => x.to === s.to))
|
|
145
|
+
g.suggestions.push({ to: s.to, score: s.score ?? 0, why: s.why, snap: s.snap });
|
|
146
|
+
}
|
|
147
|
+
for (const g of groups.values())
|
|
148
|
+
g.suggestions.sort((a, b) => b.score - a.score || (a.to < b.to ? -1 : 1));
|
|
149
|
+
out.lost = [...groups.values()];
|
|
150
|
+
for (const m of [...w.migrations].reverse()) {
|
|
151
|
+
const key = lostKey.move(m.id);
|
|
152
|
+
const line = archived.get(key);
|
|
153
|
+
if (line)
|
|
154
|
+
shown.push({ key, line, migration: m });
|
|
155
|
+
else
|
|
156
|
+
out.migrations.push({ ...m, key });
|
|
157
|
+
}
|
|
158
|
+
out.archived = shown.sort((a, b) => (a.line.at < b.line.at ? 1 : a.line.at > b.line.at ? -1 : 0));
|
|
159
|
+
out.aside = { entries: aside.entries, modules: [...aside.modules].sort() };
|
|
160
|
+
out.count = out.lost.reduce((n, g) => n + g.entries.length, 0) + out.migrations.length;
|
|
161
|
+
return out;
|
|
162
|
+
}
|
|
163
|
+
/** The keys an archiving or a putting back names in the lost-and-found as it is: archiving takes an
|
|
164
|
+
* entry or a migration listed and not archived, putting back one archived (an opinion under any
|
|
165
|
+
* of its keys). The keys to write, or why not. */
|
|
166
|
+
export function archiveKeys(a, keys, on, archive) {
|
|
167
|
+
const listed = new Map();
|
|
168
|
+
for (const g of a.lost)
|
|
169
|
+
for (const e of g.entries)
|
|
170
|
+
listed.set(e.key, e);
|
|
171
|
+
for (const m of a.migrations)
|
|
172
|
+
listed.set(m.key, {});
|
|
173
|
+
const held = new Map();
|
|
174
|
+
for (const x of a.archived)
|
|
175
|
+
held.set(x.key, x.entry ?? {});
|
|
176
|
+
const out = [];
|
|
177
|
+
for (const k of keys) {
|
|
178
|
+
if (on) {
|
|
179
|
+
if (held.has(k))
|
|
180
|
+
continue;
|
|
181
|
+
if (!listed.has(k))
|
|
182
|
+
return `There is no entry ${k.slice(0, 300)} in the lost-and-found now (GET /api/history/lost lists them).`;
|
|
183
|
+
out.push(k);
|
|
184
|
+
}
|
|
185
|
+
else {
|
|
186
|
+
const e = held.get(k);
|
|
187
|
+
if (!e) {
|
|
188
|
+
if (listed.has(k))
|
|
189
|
+
continue;
|
|
190
|
+
return `There is no archived entry ${k.slice(0, 300)} (GET /api/history/lost lists them under archived).`;
|
|
191
|
+
}
|
|
192
|
+
// the line that archived it may be under a name its writer went by before
|
|
193
|
+
for (const x of [k, ...(e.aliases ?? [])])
|
|
194
|
+
if (archive.get(x))
|
|
195
|
+
out.push(x);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
return out;
|
|
199
|
+
}
|