@dev.fast/whiteboard 0.3.4-preview.20261019.127 → 0.3.4

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 (115) hide show
  1. package/dist/{account-alias-B5-HYi1D.js → account-alias-CprZojXd.js} +8 -13
  2. package/dist/agent-cli-C6SPSII0.js +2 -0
  3. package/dist/{agent-cli-CohHf6Ua.js → agent-cli-CThN7Q59.js} +10 -15
  4. package/dist/{agent-client-Dtc-Kn_w.js → agent-client-BOOi7EUR.js} +3 -13
  5. package/dist/{authoring-tools-B7KU04f7.js → authoring-tools-BNwuMnz6.js} +27 -34
  6. package/dist/{background-server-C03RKxKS.js → background-server-CkvxA-6O.js} +5 -5
  7. package/dist/{background-server-CB9C4uoc.js → background-server-DLSDogkI.js} +1 -1
  8. package/dist/build-info.json +1 -1
  9. package/dist/{cli-CJaEb7zC.js → cli-R4yfa5DT.js} +1 -1
  10. package/dist/{cli-runner-DGDIqh3V.js → cli-runner-Cuc9FSzu.js} +26 -26
  11. package/dist/cli.js +3 -4
  12. package/dist/{client-lKVF_Elo.js → client-BN_FDxFr.js} +47 -253
  13. package/dist/{fuzzy-match-BmVJJm9I.js → fuzzy-match-BO9bK37N.js} +4 -6
  14. package/dist/{headless-host-DRtjf9WA.js → headless-host-CVBOQyRS.js} +1 -1
  15. package/dist/{headless-host-XhL1X4vX.js → headless-host-Cog6SpS9.js} +140 -513
  16. package/dist/{local-data-Cpl4ENbJ.js → local-data-D_9tvdsp.js} +323 -2419
  17. package/dist/{mcp-ovmiUE_x.js → mcp-D1hziANa.js} +9 -5
  18. package/dist/{package-paths-BWxO-UEL.js → package-paths-C--__jEv.js} +1 -1
  19. package/dist/profile-CYPmvBoA.js +2 -0
  20. package/dist/{profile-BadSkCkI.js → profile-DokFw782.js} +8 -20
  21. package/dist/{remote-attach-Dd-YobM6.js → remote-attach-CEJ-kvDY.js} +4 -4
  22. package/dist/{remote-extensions-CNXwqK0D.js → remote-extensions-Blcjr9Ze.js} +3 -3
  23. package/dist/{remote-extensions-VP-kfq0C.js → remote-extensions-GN8Gf_qG.js} +1 -1
  24. package/dist/{remote-language-server-BXk87KCr.js → remote-language-server-BTKBQ7x4.js} +5 -5
  25. package/dist/{remote-uninstall-DuOzsvaD.js → remote-uninstall-DW7Gdryh.js} +6 -6
  26. package/dist/{request-origin-B9qCPiVB.js → request-origin-Zcn_CjX4.js} +12 -45
  27. package/dist/{review-app-launcher-C7el2Uoy.js → review-app-launcher-CkwLclzR.js} +2 -2
  28. package/dist/{review-home-paths-D8AoCdd2.js → review-home-paths-j2BRAaNv.js} +17 -19
  29. package/dist/{review-telemetry-BsoRMC2w.js → review-telemetry-BLNwYnLV.js} +5 -5
  30. package/dist/runtime-CKiWhGrf.js +52 -0
  31. package/dist/runtime.d.ts +1 -1
  32. package/dist/runtime.js +1 -1
  33. package/dist/{s3-CsG2EWED.js → s3-DtJFWC0G.js} +1 -1
  34. package/dist/{s3-config-CjcLyFV5.js → s3-config-BTO12tCC.js} +2 -4
  35. package/dist/{s3-config-zz4GWh2T.js → s3-config-CSy3fx8H.js} +1 -1
  36. package/dist/server/desktop-host.js +76 -29
  37. package/dist/{server-discovery-CKjw3-6h.js → server-discovery-Bey8IqXH.js} +3 -3
  38. package/dist/sharing/index.d.ts +70 -484
  39. package/dist/sharing/index.js +1 -1
  40. package/dist/{src-WPaQn75m.js → src-BMPuxWv4.js} +73 -208
  41. package/dist/{src-BiWNbcF_.js → src-DsM3jO8G.js} +12 -89
  42. package/dist/{stored-document-migration-dFbewRTG.js → stored-document-migration-CChxtFAy.js} +269 -270
  43. package/dist/{thread-state-Do5LX10I.js → thread-state-5pYr6_zM.js} +2 -7
  44. package/dist/{tool-failure-DvI-gys2.js → tool-failure-C9MpbXJJ.js} +3 -10
  45. package/instructions/authoring.md +2 -3
  46. package/instructions/scratchpad.md +23 -0
  47. package/package.json +4 -4
  48. package/src/ask/agents.ts +1 -13
  49. package/src/ask/checkout-files.ts +5 -2
  50. package/src/ask/thread-state.ts +0 -4
  51. package/src/ask/thread.ts +0 -16
  52. package/src/ask/threads.ts +3 -24
  53. package/src/cli-install.ts +2 -13
  54. package/src/cli-runner.ts +1 -4
  55. package/src/cli.ts +1 -4
  56. package/src/diff-selection-migration.ts +34 -48
  57. package/src/lens-selection.ts +2 -38
  58. package/src/review-api/README.md +13 -52
  59. package/src/review-api/activity.ts +4 -1
  60. package/src/review-api/agent-cli.ts +1 -9
  61. package/src/review-api/anchor-quotes.ts +7 -14
  62. package/src/review-api/ask-history.ts +51 -137
  63. package/src/review-api/authoring-tools.ts +30 -45
  64. package/src/review-api/blocks/definition.ts +1 -1
  65. package/src/review-api/blocks/markdown.ts +2 -2
  66. package/src/review-api/document-headings.ts +27 -58
  67. package/src/review-api/document-text.ts +2 -31
  68. package/src/review-api/document.ts +34 -281
  69. package/src/review-api/github-provider.ts +13 -51
  70. package/src/review-api/http.ts +123 -702
  71. package/src/review-api/instructions.ts +19 -0
  72. package/src/review-api/local-data.ts +46 -310
  73. package/src/review-api/mcp.ts +11 -3
  74. package/src/review-api/onboarding.ts +1 -1
  75. package/src/review-api/profile.ts +6 -37
  76. package/src/review-api/public-tools.ts +2 -24
  77. package/src/review-api/pull-request.ts +49 -82
  78. package/src/review-api/request-origin.ts +0 -10
  79. package/src/review-api/review-progress.ts +27 -61
  80. package/src/review-api/store-schema.ts +29 -38
  81. package/src/review-api/store.ts +478 -2142
  82. package/src/review-api/worktree-events.ts +5 -3
  83. package/src/review-api/worktree-source.ts +7 -3
  84. package/src/review-api/worktree-structural.integration.ts +0 -72
  85. package/src/review-preferences.ts +88 -0
  86. package/src/review-stack.ts +23 -15
  87. package/src/runtime.ts +30 -4
  88. package/src/server/desktop-host.ts +1 -6
  89. package/src/server/desktop-server.ts +31 -1
  90. package/src/server/headless-host.ts +2 -0
  91. package/src/server/hono-http.ts +1 -2
  92. package/src/server/legacy-tutorial-cleanup.ts +3 -2
  93. package/src/server/review-gateway-list.ts +5 -3
  94. package/src/server/review-gateway-memory.ts +1 -1
  95. package/src/server/review-lifecycle-telemetry.ts +2 -2
  96. package/src/server/review-server-core.ts +5 -1
  97. package/src/server/structural-comparisons.ts +0 -47
  98. package/src/sharing/export.ts +7 -36
  99. package/src/sharing/host.ts +14 -34
  100. package/src/sharing/import.ts +24 -156
  101. package/src/sharing/repository.ts +20 -128
  102. package/src/startup-trace.ts +3 -3
  103. package/src/stored-document-migration.ts +19 -110
  104. package/src/ui-telemetry-events.ts +11 -33
  105. package/dist/agent-cli-_YyMIMRY.js +0 -2
  106. package/dist/lens-selection-CyJapcsl.js +0 -214
  107. package/dist/profile-DJvkBJNQ.js +0 -2
  108. package/dist/runtime-Bj2tD33R.js +0 -27
  109. package/src/heading-anchors.ts +0 -42
  110. package/src/review-api/board-order.ts +0 -144
  111. package/src/review-api/checkout-groups.ts +0 -560
  112. package/src/review-api/foreign-references.ts +0 -328
  113. package/src/review-api/link-preview.ts +0 -113
  114. package/src/review-api/scratchpad-plan-upgrade.ts +0 -250
  115. package/src/review-api/store-migrations.ts +0 -357
@@ -1,357 +0,0 @@
1
- import { existsSync } from "node:fs";
2
- import { DatabaseSync } from "node:sqlite";
3
-
4
- import type { JsonValue } from "@dev.fast/json";
5
-
6
- import { CheckoutGroups } from "./checkout-groups.js";
7
- import type { ReadAtCommit } from "./foreign-references.js";
8
- import {
9
- planFromScratchpad,
10
- readScratchpadQuotes,
11
- } from "./scratchpad-plan-upgrade.js";
12
- import { columnsOf, hasTable } from "./store-schema.js";
13
-
14
- // A store's one-time upgrades, in order. PRAGMA user_version holds the last
15
- // one a store has run, so each runs once per store and a later launch runs
16
- // none; a new store runs them all on its first open.
17
- //
18
- // A shipped step never changes: a fix is a new step at the end. Each step
19
- // upgrades a store as a release before user_version left it, which may
20
- // already have some of it done, or lack the tables it touches; so each
21
- // checks what it needs before changing anything.
22
-
23
- /** What a step may use besides the database. */
24
- export interface StoreUpgradeContext {
25
- db: DatabaseSync;
26
- /** Groups the registered checkouts, reading their git metadata. */
27
- prepareCheckouts(): void;
28
- repositories(): { id: string }[];
29
- repositoryPath(repositoryId: string): string;
30
- /** A checkout's name as its project labels it, else its folder. */
31
- repositoryName(repositoryId: string): string;
32
- /** Undefined when the file can't be read; each file is read once. */
33
- fileAtCommit: ReadAtCommit;
34
- /** A checkout's project, made on first use. */
35
- projectOfRepository(repositoryId: string): string;
36
- /** The project of the board opened last, else firstRepositoryProject's. */
37
- fallbackProject(): string;
38
- /** Saves a version over what it was; the current one re-indexes its
39
- * board's links, as every written version does. */
40
- rewriteVersion(reviewId: string, version: number, snapshot: JsonValue): void;
41
- /** The first checkout's project, else one with no repository. */
42
- firstRepositoryProject(): string;
43
- }
44
-
45
- interface StoreUpgrade {
46
- version: number;
47
- name: string;
48
- /** It groups the checkouts, which the store can work out before it
49
- * opens (`groupCheckoutsAhead`), else does before the write lock. */
50
- groupsCheckouts?: true;
51
- /** Slow reads to do before taking the write lock other hosts wait on. */
52
- beforeLock?(context: StoreUpgradeContext): void;
53
- /** For a table rebuild: foreign keys can't be turned off in a
54
- * transaction. */
55
- foreignKeysOff?: true;
56
- run(context: StoreUpgradeContext): void;
57
- }
58
-
59
- /** Keep versions.author_kind to its values, as a CHECK would. */
60
- const VERSION_AUTHOR_TRIGGERS = `
61
- CREATE TRIGGER IF NOT EXISTS versions_author_kind_insert BEFORE INSERT ON versions
62
- WHEN NEW.author_kind NOT IN ('person','agent','system')
63
- BEGIN SELECT RAISE(ABORT, 'versions.author_kind must be person, agent or system'); END;
64
- CREATE TRIGGER IF NOT EXISTS versions_author_kind_update BEFORE UPDATE OF author_kind ON versions
65
- WHEN NEW.author_kind NOT IN ('person','agent','system')
66
- BEGIN SELECT RAISE(ABORT, 'versions.author_kind must be person, agent or system'); END;`;
67
-
68
- export const STORE_UPGRADES: readonly StoreUpgrade[] = [
69
- {
70
- version: 1,
71
- name: "retired-tables",
72
- // Tables earlier releases kept and nothing reads any more. Presences
73
- // replaced authoring_sessions; both last minutes, so it isn't moved.
74
- run: ({ db }) =>
75
- db.exec(`DROP TABLE IF EXISTS receipts;
76
- DROP TABLE IF EXISTS review_viewed;
77
- DROP TABLE IF EXISTS authoring_drafts;
78
- DROP TABLE IF EXISTS authoring_sessions;`),
79
- },
80
- {
81
- version: 2,
82
- name: "archive-to-trash",
83
- // Archive became Trash: archived boards get Trash's full recovery
84
- // window from the upgrade, since Archive kept them forever.
85
- run: ({ db }) => {
86
- if (!columnsOf(db, "review_attention").has("dismissed_at")) return;
87
-
88
- db.exec(
89
- "ALTER TABLE review_attention RENAME COLUMN dismissed_at TO deleted_at",
90
- );
91
- db.prepare(
92
- "UPDATE review_attention SET deleted_at=? WHERE deleted_at IS NOT NULL",
93
- ).run(new Date().toISOString());
94
- },
95
- },
96
- {
97
- version: 3,
98
- name: "board-projects",
99
- // Every board belongs to a project: its repository's, else the one of
100
- // the board opened last. With hundreds of checkouts, grouping them
101
- // reads a good deal of git metadata.
102
- groupsCheckouts: true,
103
- foreignKeysOff: true,
104
- run: ({
105
- db,
106
- repositories,
107
- projectOfRepository,
108
- firstRepositoryProject,
109
- }) => {
110
- // A child's parent is found by it.
111
- const parents = () =>
112
- db.exec(
113
- "CREATE INDEX IF NOT EXISTS reviews_parent ON reviews(parent_id)",
114
- );
115
-
116
- // A new store has the columns from the start.
117
- if (columnsOf(db, "reviews").has("project_id")) return parents();
118
-
119
- // A checkout names its project; stores from before projects lack
120
- // the column.
121
- if (!columnsOf(db, "repositories").has("project_id"))
122
- db.exec(
123
- "ALTER TABLE repositories ADD COLUMN project_id TEXT REFERENCES projects(id)",
124
- );
125
-
126
- // In creation order, which the rebuild keeps. A board whose current
127
- // version is missing is kept too, in the fallback project.
128
- const boards = db
129
- .prepare(
130
- `SELECT reviews.id, CASE WHEN json_valid(versions.snapshot) THEN COALESCE(json_extract(versions.snapshot,'$.target.repositoryId'),json_extract(versions.snapshot,'$.pins.repositoryId')) END AS repository_id,
131
- review_attention.viewed_at
132
- FROM reviews LEFT JOIN versions ON versions.review_id=reviews.id AND versions.version=reviews.version
133
- LEFT JOIN review_attention ON review_attention.review_id=reviews.id
134
- ORDER BY reviews.rowid`,
135
- )
136
- .all();
137
-
138
- const registered = new Set(repositories().map(({ id }) => id));
139
- const projects = new Map<string, string>();
140
-
141
- for (const { id, repository_id: repository } of boards)
142
- if (repository && registered.has(String(repository)))
143
- projects.set(String(id), projectOfRepository(String(repository)));
144
-
145
- // The project of the board opened last.
146
- const [opened] = boards
147
- .filter(({ id, viewed_at }) => viewed_at && projects.has(String(id)))
148
- .sort((a, b) => String(b.viewed_at).localeCompare(String(a.viewed_at)));
149
-
150
- let fallback = opened && projects.get(String(opened.id));
151
-
152
- const brokenReferences = () =>
153
- db
154
- .prepare("PRAGMA foreign_key_check")
155
- .all()
156
- .filter(({ parent }) => parent === "reviews").length;
157
-
158
- // Only references this rebuild breaks; older dangling rows stay.
159
- const broken = brokenReferences();
160
-
161
- // A child names its parent; deleting the parent moves it to top
162
- // level. No board had one before projects.
163
- db.exec(`CREATE TABLE reviews_with_projects(id TEXT PRIMARY KEY, version INTEGER NOT NULL, next_id INTEGER NOT NULL,
164
- project_id TEXT NOT NULL REFERENCES projects(id),
165
- parent_id TEXT REFERENCES reviews(id) ON DELETE SET NULL)`);
166
-
167
- const insert = db.prepare(
168
- "INSERT INTO reviews_with_projects(id,version,next_id,project_id) SELECT id,version,next_id,? FROM reviews WHERE id=?",
169
- );
170
-
171
- for (const { id } of boards)
172
- insert.run(
173
- projects.get(String(id)) ?? (fallback ??= firstRepositoryProject()),
174
- String(id),
175
- );
176
- db.exec(`DROP TABLE reviews;
177
- ALTER TABLE reviews_with_projects RENAME TO reviews;`);
178
- parents();
179
-
180
- if (brokenReferences() > broken)
181
- throw new Error("Giving boards projects broke a reference.");
182
- },
183
- },
184
- {
185
- version: 4,
186
- name: "scratchpad-plan",
187
- beforeLock: readScratchpadQuotes,
188
- run: planFromScratchpad,
189
- },
190
- {
191
- version: 5,
192
- name: "ask-threads-by-project",
193
- // Threads were kept per review. Each is now anchored on the board it
194
- // started on, and is in that board's project.
195
- run: ({ db }) => {
196
- // The Threads tab's Project filter finds a project's boards by it.
197
- db.exec(
198
- "CREATE INDEX IF NOT EXISTS reviews_project ON reviews(project_id)",
199
- );
200
- const columns = columnsOf(db, "ask_conversations");
201
-
202
- if (columns.size === 0) return;
203
-
204
- if (columns.has("review_id"))
205
- db.exec(
206
- "ALTER TABLE ask_conversations RENAME COLUMN review_id TO anchor_board",
207
- );
208
-
209
- if (!columns.has("anchor_block"))
210
- db.exec("ALTER TABLE ask_conversations ADD COLUMN anchor_block TEXT");
211
- },
212
- },
213
- {
214
- version: 6,
215
- name: "version-threads",
216
- // A version names the Ask thread that caused it in its own column.
217
- // No release wrote one into a version, so earlier versions have none.
218
- run: ({ db }) => {
219
- if (!columnsOf(db, "versions").has("thread_id"))
220
- db.exec("ALTER TABLE versions ADD COLUMN thread_id TEXT");
221
-
222
- db.exec(
223
- "CREATE INDEX IF NOT EXISTS versions_thread_id ON versions(thread_id)",
224
- );
225
- },
226
- },
227
- {
228
- version: 7,
229
- name: "viewed-on-upgrade",
230
- // Every board counts as viewed at its latest version, so only later
231
- // changes badge it.
232
- run: ({ db }) =>
233
- db.exec(`INSERT OR IGNORE INTO review_attention(review_id) SELECT id FROM reviews;
234
- UPDATE review_attention SET viewed_at=COALESCE(NULLIF((
235
- SELECT MAX(COALESCE(review_attention.viewed_at, ''), COALESCE(CASE WHEN json_valid(versions.snapshot) THEN json_extract(versions.snapshot,'$.createdAt') END, ''))
236
- FROM reviews JOIN versions ON versions.review_id=reviews.id AND versions.version=reviews.version
237
- WHERE reviews.id=review_attention.review_id), ''), review_attention.viewed_at)
238
- WHERE review_id IN (SELECT id FROM reviews);`),
239
- },
240
- {
241
- version: 8,
242
- name: "ask-thread-viewed",
243
- // A thread keeps when it was last seen; one saved before has none.
244
- run: ({ db }) => {
245
- const columns = columnsOf(db, "ask_conversations");
246
-
247
- if (columns.size > 0 && !columns.has("viewed_at"))
248
- db.exec("ALTER TABLE ask_conversations ADD COLUMN viewed_at TEXT");
249
- },
250
- },
251
- {
252
- version: 9,
253
- name: "version-authors",
254
- // Who wrote each version, a person or an agent, in its own column.
255
- // Releases didn't record it: an agent's command and a person's edit on
256
- // the canvas saved alike, so earlier versions have none. Triggers keep
257
- // the column to its values; a CHECK added with it would have SQLite read
258
- // every version, all of their JSON, to check the empty column.
259
- run: ({ db }) => {
260
- if (!columnsOf(db, "versions").has("author_kind"))
261
- db.exec("ALTER TABLE versions ADD COLUMN author_kind TEXT");
262
-
263
- db.exec(VERSION_AUTHOR_TRIGGERS);
264
- },
265
- },
266
- {
267
- version: 10,
268
- name: "ask-permissions",
269
- // Auto mode keeps what an Ask thread may do without asking; threads
270
- // saved before it lack the column.
271
- run: ({ db }) => {
272
- const columns = columnsOf(db, "ask_conversations");
273
-
274
- if (columns.size > 0 && !columns.has("permissions"))
275
- db.exec("ALTER TABLE ask_conversations ADD COLUMN permissions TEXT");
276
- },
277
- },
278
- ];
279
-
280
- export const STORE_VERSION = STORE_UPGRADES.at(-1)!.version;
281
-
282
- export const storeVersion = (db: DatabaseSync) =>
283
- Number(db.prepare("PRAGMA user_version").get()!.user_version);
284
-
285
- /** Runs every step the store hasn't, each in its own transaction with the
286
- * version it reaches, so a failed step rolls back alone and stops the store
287
- * opening, naming the step; the ones before it stay done. A store at a
288
- * later version than this build knows is left as it is. */
289
- export function upgradeStore(context: StoreUpgradeContext) {
290
- const { db } = context;
291
-
292
- for (const step of STORE_UPGRADES) {
293
- if (storeVersion(db) >= step.version) continue;
294
-
295
- if (step.groupsCheckouts) context.prepareCheckouts();
296
- step.beforeLock?.(context);
297
-
298
- if (step.foreignKeysOff) db.exec("PRAGMA foreign_keys=OFF");
299
-
300
- db.exec("BEGIN IMMEDIATE");
301
-
302
- try {
303
- // Another host on this home may have run it while this one waited.
304
- if (storeVersion(db) < step.version) {
305
- step.run(context);
306
- db.exec(`PRAGMA user_version=${step.version}`);
307
- }
308
-
309
- db.exec("COMMIT");
310
- } catch (error) {
311
- // SQLite may have rolled back already, on a full disk say; the error
312
- // that made it is the one to report.
313
- if (db.isTransaction) db.exec("ROLLBACK");
314
- throw new Error(
315
- `Could not upgrade the whiteboard store: step ${step.version} (${step.name}) failed and was rolled back. ${error instanceof Error ? error.message : String(error)}`,
316
- { cause: error },
317
- );
318
- } finally {
319
- if (step.foreignKeysOff) db.exec("PRAGMA foreign_keys=ON");
320
- }
321
- }
322
- }
323
-
324
- /** How the store's checkouts group, worked out before it opens, asking
325
- * several repositories at once, when an upgrade still to run needs it;
326
- * undefined otherwise, or when the store can't be read so, and the step
327
- * works it out itself. */
328
- export async function groupCheckoutsAhead(
329
- databasePath: string,
330
- ): Promise<ReadonlyMap<string, string> | undefined> {
331
- if (!existsSync(databasePath)) return undefined;
332
-
333
- let db: DatabaseSync | undefined;
334
-
335
- try {
336
- db = new DatabaseSync(databasePath, { timeout: 5000 });
337
- const version = storeVersion(db);
338
-
339
- if (
340
- !STORE_UPGRADES.some(
341
- (step) => step.groupsCheckouts && step.version > version,
342
- ) ||
343
- !columnsOf(db, "repositories").size
344
- )
345
- return undefined;
346
-
347
- const groups = new CheckoutGroups(db);
348
-
349
- await groups.prepareConcurrently();
350
-
351
- return groups.prepared;
352
- } catch {
353
- return undefined;
354
- } finally {
355
- db?.close();
356
- }
357
- }