@dev.fast/whiteboard 0.4.0 → 0.4.1-preview.20261026.138

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 (78) hide show
  1. package/dist/{account-alias-2yTAxoBx.js → account-alias-BPlPnzQM.js} +7 -7
  2. package/dist/{agent-cli-CHNDd4Ga.js → agent-cli-BbCf9uid.js} +11 -11
  3. package/dist/agent-cli-ipN_JmzG.js +2 -0
  4. package/dist/{agent-client-Dc0vHFJt.js → agent-client-DfpM8F5N.js} +3 -3
  5. package/dist/{authoring-tools-D8EkIDjm.js → authoring-tools-bm39DRoV.js} +23 -10
  6. package/dist/{background-server-DyWnPJyZ.js → background-server--0EeCCeV.js} +1 -1
  7. package/dist/{background-server-DHWHzpo5.js → background-server-B--5p72o.js} +6 -6
  8. package/dist/build-info.json +1 -1
  9. package/dist/{cli-qZomwJmK.js → cli-DBTQIroM.js} +2 -2
  10. package/dist/{cli-runner-DFBaVGij.js → cli-runner-CRFCdb7G.js} +25 -25
  11. package/dist/cli.js +3 -3
  12. package/dist/{fuzzy-match-CfSxqzBF.js → fuzzy-match-l3mJZ1Ge.js} +5 -5
  13. package/dist/{headless-host-dwlqhqMY.js → headless-host-5ei3nHS8.js} +1 -1
  14. package/dist/{headless-host-DJhcab6r.js → headless-host-BkJ9OUQY.js} +131 -299
  15. package/dist/{client-p9WB8Sj0.js → import-Bwvf9Wos.js} +505 -390
  16. package/dist/{lens-selection-9byzEgvU.js → lens-selection-EuZdag4Q.js} +1 -1
  17. package/dist/{local-data-DHedfhxw.js → local-data-BCSeTyic.js} +98 -154
  18. package/dist/{mcp-Br-r_wwR.js → mcp-_Xg1gQuA.js} +17 -8
  19. package/dist/{package-paths-BeYHYjeR.js → package-paths-Bg731FHS.js} +2 -2
  20. package/dist/profile-C8zL4u3L.js +2 -0
  21. package/dist/{profile-D9wdlPgw.js → profile-xzrUFgbT.js} +4 -4
  22. package/dist/{remote-attach-DSGpTOJ4.js → remote-attach-DF25Epoa.js} +4 -4
  23. package/dist/{remote-extensions-WtJO7pkb.js → remote-extensions-BJamgsCe.js} +1 -1
  24. package/dist/{remote-extensions-CsI_BJ_f.js → remote-extensions-CeywZL-Z.js} +4 -4
  25. package/dist/{remote-language-server-Dx13Mg2m.js → remote-language-server-CH8sG_CU.js} +6 -6
  26. package/dist/{remote-uninstall-Dp2o3qz0.js → remote-uninstall-Cq-lkBY7.js} +7 -7
  27. package/dist/{request-origin-CNSLVn7x.js → request-origin-CargJFvH.js} +14 -3
  28. package/dist/{review-app-launcher-BZAnR8Vp.js → review-app-launcher-D8ApNDUV.js} +3 -3
  29. package/dist/{review-home-paths-CG-sb9Cw.js → review-home-paths-DcB4xLD-.js} +2 -2
  30. package/dist/{review-telemetry-BM-_KdLi.js → review-telemetry-BW7gJrSo.js} +7 -7
  31. package/dist/{runtime-C6_1K-3Z.js → runtime-DY6Ctyog.js} +1 -1
  32. package/dist/runtime.js +1 -1
  33. package/dist/{s3-CsG2EWED.js → s3-BIWv4h1J.js} +2 -2
  34. package/dist/{s3-config-zz4GWh2T.js → s3-config-Du3RBIvv.js} +1 -1
  35. package/dist/server/desktop-host.js +51 -17
  36. package/dist/{server-discovery-DKLIOgg3.js → server-discovery-aHK_1r3U.js} +5 -5
  37. package/dist/sharing/index.d.ts +209 -98
  38. package/dist/sharing/index.js +1 -1
  39. package/dist/{src-DYHVzX3b.js → src-DEOc8bvC.js} +63 -2
  40. package/dist/{src-Z1x4-Qmb.js → src-DjRaCctk.js} +6 -6
  41. package/dist/{stored-document-migration-DEt8iwem.js → stored-document-migration-CwmTwTb8.js} +139 -13
  42. package/dist/{thread-state-CO97FUqK.js → thread-state-CbLB0jUA.js} +1 -1
  43. package/dist/{tool-failure-C1L1vJyz.js → tool-failure-DZaQzi4f.js} +2 -2
  44. package/instructions/animation.md +345 -0
  45. package/instructions/authoring.md +2 -2
  46. package/package.json +6 -6
  47. package/src/fixtures/blocks/animation.json +19 -0
  48. package/src/review-api/authoring-tools.ts +17 -6
  49. package/src/review-api/blocks/animation.ts +98 -0
  50. package/src/review-api/blocks/index.ts +3 -0
  51. package/src/review-api/blocks/sequence.ts +3 -1
  52. package/src/review-api/comparison-coverage.ts +120 -89
  53. package/src/review-api/component-reference.ts +16 -0
  54. package/src/review-api/document-text.ts +11 -0
  55. package/src/review-api/document.ts +26 -5
  56. package/src/review-api/http.ts +31 -2
  57. package/src/review-api/instructions.ts +11 -2
  58. package/src/review-api/local-data.ts +21 -4
  59. package/src/review-api/mcp.ts +22 -14
  60. package/src/review-api/onboarding.ts +100 -26
  61. package/src/review-preferences.ts +43 -0
  62. package/src/server/desktop-server.ts +24 -0
  63. package/src/server/json-review-reporting.ts +1 -1
  64. package/src/server/review-server-core.ts +2 -0
  65. package/src/server/structural-comparisons.ts +5 -11
  66. package/src/server/structural-diff.ts +8 -2
  67. package/src/sharing/cli.ts +1 -1
  68. package/src/sharing/client.ts +54 -222
  69. package/src/sharing/export.ts +172 -114
  70. package/src/sharing/host.ts +32 -36
  71. package/src/sharing/import.ts +93 -147
  72. package/src/sharing/index.ts +1 -6
  73. package/src/sharing/routes.ts +18 -17
  74. package/src/ui-telemetry-events.ts +11 -0
  75. package/dist/agent-cli-Cn4-16XA.js +0 -2
  76. package/dist/profile-BopGMZUh.js +0 -2
  77. package/dist/{s3-config-CjcLyFV5.js → s3-config-D7Yt0VhS.js} +1 -1
  78. package/dist/{src-X9ZebeOD.js → src-DXnn7RrI.js} +1 -1
@@ -1,4 +1,4 @@
1
- import "./src-DYHVzX3b.js";
1
+ import "./src-DEOc8bvC.js";
2
2
  import { z } from "zod";
3
3
  //#region src/viewed-coverage.ts
4
4
  const coverageSchema = z.object({
@@ -1,22 +1,22 @@
1
- import { T as runVcsSync, _ as listTrackedFilesAtCommit, a as diff, b as resolveRepoContext, c as diffFileSummariesWorkingTree, d as git, f as gitAt, g as listCommitRange, h as jjRevisionIsConflicted, i as detectLocalVcs, l as diffTrees, m as gitCommonDirSync, n as createBlobBatchReader, p as gitCommonDir, s as diffFileSummariesTrees, t as LocalVcsToolsMissingError, u as diffWorkingTree, v as parseGitRemote, w as runVcs, x as resolveRevision, y as readFileAtCommit } from "./src-X9ZebeOD.js";
1
+ import { T as runVcsSync, _ as listTrackedFilesAtCommit, a as diff, b as resolveRepoContext, c as diffFileSummariesWorkingTree, d as git, f as gitAt, g as listCommitRange, h as jjRevisionIsConflicted, i as detectLocalVcs, l as diffTrees, m as gitCommonDirSync, n as createBlobBatchReader, p as gitCommonDir, s as diffFileSummariesTrees, t as LocalVcsToolsMissingError, u as diffWorkingTree, v as parseGitRemote, w as runVcs, x as resolveRevision, y as readFileAtCommit } from "./src-DXnn7RrI.js";
2
2
  import { c as jsonString, m as isStringValue, n as isJsonObject, o as jsonObject, u as parseJsonText } from "./src-X9phtB2j.js";
3
- import { F as structuralChangeCounts, P as decodeStructuralDiffEvent, c as boardIconKind, l as boardTitle } from "./src-DYHVzX3b.js";
4
- import "./src-Z1x4-Qmb.js";
5
- import { b as updateCoverage, c as parseAnchor, f as sourceAnchors, g as intersectIntervals, h as emptyCoverage, p as coverageSchema, v as subtractIntervals, y as unionIntervals } from "./lens-selection-9byzEgvU.js";
6
- import { $ as lensEditSchema, A as editSchema, B as resourceReferences, C as applyEdit, D as boardRepositoryId, E as boardLinks, F as pinnedRepositoryIds, H as respellDocumentLinks, I as pinsSchema, J as checkReferences, K as sourceReferences, M as explicitPins, O as commitLinkCommits, P as pinDocumentReferences, Q as applyLensEdit, S as anchorPins, T as assignMissingIds, U as respellEditLinks, V as respellCommitLinks, W as reviewTargetSchema, Y as documentAdvice, _ as fetchPullRequest, a as SoftwareModelValidationError, at as requireVisibleSource, b as textIncludesQuote, c as traceSchema, ct as markdownText, d as setPullRequest, f as sourceBranchName, g as defaultPullRequestDeps, h as GitHubRefusedRead, i as readStoredDocument, it as fileLineRangeSchema, j as elements, k as documentSchema, l as pullRequestKey, lt as parseMarkdown, m as resolvePullRequest, n as migrateStoredDocument, nt as SourceRangeError, o as defineSoftwareMap, ot as sliceSourceRange, q as summarizeEdit, r as migrateStoredLenses, rt as checkSourcePath, st as markdownNodes, tt as lensSelections, u as pullRequestUrl, v as remoteUrls, w as assignFreshIds, x as adoptFlowUnits, y as azureRepositoryOf, z as resourceReference } from "./stored-document-migration-DEt8iwem.js";
3
+ import { F as decodeStructuralDiffEvent, I as structuralChangeCounts, P as applyStructuralUpdate, c as boardIconKind, l as boardTitle } from "./src-DEOc8bvC.js";
4
+ import "./src-DjRaCctk.js";
5
+ import { b as updateCoverage, c as parseAnchor, f as sourceAnchors, g as intersectIntervals, h as emptyCoverage, p as coverageSchema, v as subtractIntervals, y as unionIntervals } from "./lens-selection-EuZdag4Q.js";
6
+ import { A as commitLinkCommits, C as adoptFlowUnits, D as assignMissingIds, E as assignFreshIds, G as respellEditLinks, H as resourceReferences, I as pinDocumentReferences, J as sourceReferences, K as reviewTargetSchema, L as pinnedRepositoryIds, M as editSchema, N as elements, O as boardLinks, P as explicitPins, R as pinsSchema, S as parseUnifiedPatch, T as applyEdit, U as respellCommitLinks, V as resourceReference, W as respellDocumentLinks, X as checkReferences, Y as summarizeEdit, Z as documentAdvice, _ as fetchPullRequest, a as SoftwareModelValidationError, at as checkSourcePath, b as textIncludesQuote, c as traceSchema, ct as sliceSourceRange, d as setPullRequest, dt as parseMarkdown, et as applyLensEdit, f as sourceBranchName, g as defaultPullRequestDeps, h as GitHubRefusedRead, i as readStoredDocument, it as SourceRangeError, j as documentSchema, k as boardRepositoryId, l as pullRequestKey, lt as markdownNodes, m as resolvePullRequest, n as migrateStoredDocument, o as defineSoftwareMap, ot as fileLineRangeSchema, r as migrateStoredLenses, rt as lensSelections, st as requireVisibleSource, tt as lensEditSchema, u as pullRequestUrl, ut as markdownText, v as remoteUrls, w as anchorPins, x as textualRows, y as azureRepositoryOf } from "./stored-document-migration-CwmTwTb8.js";
7
7
  import { t as ReviewInputError } from "./input-error-OLgB_h31.js";
8
- import { A as processStartIdentity, L as writePrivateJsonAtomic, O as pidReused, j as withFileLock, k as processIsAlive } from "./s3-config-CjcLyFV5.js";
8
+ import { A as processStartIdentity, L as writePrivateJsonAtomic, O as pidReused, j as withFileLock, k as processIsAlive } from "./s3-config-D7Yt0VhS.js";
9
9
  import { t as errorMessage } from "./error-message-OtiDonty.js";
10
- import { s as safeStorageSegment } from "./review-home-paths-CG-sb9Cw.js";
10
+ import { s as safeStorageSegment } from "./review-home-paths-DcB4xLD-.js";
11
11
  import { r as isMissingFileError } from "./fs-utils-BMPLt0cr.js";
12
- import { a as askHistoryEntrySchema, i as askEntrySchema, o as askOfferSchema, s as askPermissionsSchema } from "./thread-state-CO97FUqK.js";
12
+ import { a as askHistoryEntrySchema, i as askEntrySchema, o as askOfferSchema, s as askPermissionsSchema } from "./thread-state-CbLB0jUA.js";
13
13
  import { createRequire } from "node:module";
14
14
  import crypto, { createHash, randomUUID } from "node:crypto";
15
15
  import { z } from "zod";
16
+ import path, { dirname, isAbsolute, relative, resolve, sep } from "node:path";
16
17
  import { existsSync, readFileSync, readdirSync, realpathSync, rmSync, statSync } from "node:fs";
17
18
  import * as fs$1 from "node:fs/promises";
18
19
  import { lstat, mkdir, readFile, readdir, realpath, rm, stat, writeFile } from "node:fs/promises";
19
- import path, { dirname, isAbsolute, relative, resolve, sep } from "node:path";
20
20
  import { homedir } from "node:os";
21
21
  import { spawn } from "node:child_process";
22
22
  import { isDeepStrictEqual } from "node:util";
@@ -819,7 +819,7 @@ async function* structuralDiff(input) {
819
819
  break;
820
820
  case "merge-base": args.push(`${comparison.base}...${comparison.head}`);
821
821
  }
822
- args.push("--", ...input.paths ?? []);
822
+ args.push("--defer", "--", ...input.paths ?? []);
823
823
  const startupAbort = new AbortController();
824
824
  const startup = setTimeout(() => startupAbort.abort(), 12e4);
825
825
  const signal = AbortSignal.any([startupAbort.signal, input.signal]);
@@ -850,6 +850,7 @@ async function* structuralDiff(input) {
850
850
  let completed = false;
851
851
  let aborted;
852
852
  let failed = 0;
853
+ const sent = /* @__PURE__ */ new Set();
853
854
  const lines = createInterface({
854
855
  input: child.stdout,
855
856
  crlfDelay: Infinity
@@ -868,7 +869,8 @@ async function* structuralDiff(input) {
868
869
  completed = true;
869
870
  failed = event.failed;
870
871
  aborted = event.aborted;
871
- } else if (event.type !== "file") throw new Error(`Unexpected diffr event: ${event.type}`);
872
+ } else if (event.type === "file") sent.add((event.file.rhs ?? event.file.lhs).path);
873
+ else if (event.type !== "update" || !sent.has((event.file.rhs ?? event.file.lhs).path)) throw new Error(`Unexpected diffr event: ${event.type}`);
872
874
  yield event;
873
875
  }
874
876
  if (!completed) {
@@ -908,11 +910,11 @@ var StructuralComparisons = class {
908
910
  yield* entry.read(input.signal);
909
911
  } finally {
910
912
  entry.readers--;
911
- if (!entry.done && !entry.initialReady && !entry.readers || entry.error || entry.failed) {
913
+ if (!entry.done && !entry.readers || entry.error || entry.failed) {
912
914
  entry.abort.abort();
913
915
  this.entries.delete(key);
914
916
  }
915
- const idle = [...this.entries].filter(([, value]) => (value.done || value.initialReady) && !value.readers);
917
+ const idle = [...this.entries].filter(([, value]) => value.done && !value.readers);
916
918
  for (const [oldKey, old] of idle.slice(0, -10)) {
917
919
  old.abort.abort();
918
920
  this.entries.delete(oldKey);
@@ -959,7 +961,6 @@ var Comparison = class {
959
961
  done = false;
960
962
  error;
961
963
  failed = false;
962
- initialReady = false;
963
964
  remaining;
964
965
  listeners = /* @__PURE__ */ new Set();
965
966
  constructor(input) {
@@ -971,13 +972,10 @@ var Comparison = class {
971
972
  ...input,
972
973
  signal: this.abort.signal
973
974
  })) {
974
- if (event.type === "start") {
975
- this.remaining = new Set(event.files.map((entry) => (entry.file.rhs ?? entry.file.lhs).path));
976
- this.initialReady = this.remaining.size === 0;
977
- } else if (event.type === "file") {
975
+ if (event.type === "start") this.remaining = new Set(event.files.map((entry) => (entry.file.rhs ?? entry.file.lhs).path));
976
+ else if (event.type === "file") {
978
977
  const path = (event.file.rhs ?? event.file.lhs).path;
979
978
  if (!this.remaining?.delete(path)) throw new Error(`Unexpected structural result: ${path}`);
980
- this.initialReady = this.remaining.size === 0;
981
979
  }
982
980
  if (event.type === "complete") this.failed = event.failed > 0 || !!event.aborted;
983
981
  this.events.push(event);
@@ -1015,65 +1013,6 @@ var Comparison = class {
1015
1013
  }
1016
1014
  };
1017
1015
  //#endregion
1018
- //#region src/unified-diff.ts
1019
- const HUNK_HEADER = /^@@ -(?<oldStart>\d+)(?:,(?<oldLines>\d+))? \+(?<newStart>\d+)(?:,(?<newLines>\d+))? @@/;
1020
- function parseUnifiedPatch(file, patch) {
1021
- if (!patch) return [];
1022
- const hunks = [];
1023
- let current = null;
1024
- let oldLine = 0;
1025
- let newLine = 0;
1026
- for (const row of patch.split(/\r?\n/)) {
1027
- const header = HUNK_HEADER.exec(row);
1028
- if (header?.groups) {
1029
- current = {
1030
- file,
1031
- oldStart: Number(header.groups.oldStart),
1032
- oldLines: Number(header.groups.oldLines ?? "1"),
1033
- newStart: Number(header.groups.newStart),
1034
- newLines: Number(header.groups.newLines ?? "1"),
1035
- lines: []
1036
- };
1037
- oldLine = current.oldStart;
1038
- newLine = current.newStart;
1039
- hunks.push(current);
1040
- continue;
1041
- }
1042
- if (!current || row.startsWith("\")) continue;
1043
- const marker = row[0];
1044
- const text = row.slice(1);
1045
- let line = null;
1046
- if (marker === " ") {
1047
- line = {
1048
- kind: "context",
1049
- oldLine,
1050
- newLine,
1051
- text
1052
- };
1053
- oldLine += 1;
1054
- newLine += 1;
1055
- } else if (marker === "+") {
1056
- line = {
1057
- kind: "add",
1058
- oldLine: null,
1059
- newLine,
1060
- text
1061
- };
1062
- newLine += 1;
1063
- } else if (marker === "-") {
1064
- line = {
1065
- kind: "remove",
1066
- oldLine,
1067
- newLine: null,
1068
- text
1069
- };
1070
- oldLine += 1;
1071
- }
1072
- if (line) current.lines.push(line);
1073
- }
1074
- return hunks;
1075
- }
1076
- //#endregion
1077
1016
  //#region src/software-map-diff-counts.ts
1078
1017
  async function resolveSoftwareMapDiffCounts(input) {
1079
1018
  const baseRef = input.baseRef?.trim();
@@ -1347,32 +1286,6 @@ function unquoteGitPath(value) {
1347
1286
  */
1348
1287
  const checkoutFs = process.versions.electron ? createRequire(import.meta.url)("original-fs").promises : fs$1;
1349
1288
  //#endregion
1350
- //#region src/review-api/lens-alignment.ts
1351
- /** Project the already-computed patch's runs, including unchanged gaps. */
1352
- function textualRows(path, patch, baseCount, headCount) {
1353
- const rows = [];
1354
- let base = 0, head = 0;
1355
- const append = (baseEnd, headEnd) => {
1356
- while (base < baseEnd || head < headEnd) rows.push([base < baseEnd ? base++ : null, head < headEnd ? head++ : null]);
1357
- };
1358
- for (const hunk of parseUnifiedPatch(path, patch)) {
1359
- append(hunk.oldLines ? hunk.oldStart - 1 : hunk.oldStart, hunk.newLines ? hunk.newStart - 1 : hunk.newStart);
1360
- let removed = 0, added = 0;
1361
- const flush = () => {
1362
- append(base + removed, head + added);
1363
- removed = added = 0;
1364
- };
1365
- for (const line of hunk.lines) if (line.kind === "context") {
1366
- flush();
1367
- append(base + 1, head + 1);
1368
- } else if (line.kind === "add") added++;
1369
- else removed++;
1370
- flush();
1371
- }
1372
- append(baseCount, headCount);
1373
- return rows;
1374
- }
1375
- //#endregion
1376
1289
  //#region src/review-api/comparison-coverage.ts
1377
1290
  const hash = (parts) => createHash("sha256").update(JSON.stringify(parts)).digest("hex");
1378
1291
  /** Immutable comparison facts shared by catalog totals and review progress. */
@@ -1436,53 +1349,73 @@ async function comparisonCoverage(data, reviewId, pins, signal, publish) {
1436
1349
  publish?.(current());
1437
1350
  const baseFiles = new Map(coverage.files.map((file, index) => [file.previousPath ?? file.path, index]));
1438
1351
  const headFiles = new Map(coverage.files.map((file, index) => [file.path, index]));
1439
- const remaining = /* @__PURE__ */ new Set();
1440
- for await (const event of data.structuralChanges({
1441
- reviewId,
1442
- pins,
1443
- signal
1444
- })) {
1445
- if (event.type === "start") {
1446
- for (const entry of event.files) remaining.add((entry.file.rhs ?? entry.file.lhs).path);
1447
- if (!remaining.size) break;
1448
- continue;
1449
- }
1450
- if (event.type === "complete" && (event.failed || event.aborted)) throw new Error(event.aborted?.message ?? "Structural coverage is incomplete.");
1451
- if (event.type !== "file") continue;
1452
- const path = (event.file.rhs ?? event.file.lhs).path;
1453
- if (!remaining.delete(path)) throw new Error(`Unexpected structural result: ${path}`);
1454
- const baseIndex = event.file.lhs && baseFiles.get(event.file.lhs.path);
1455
- const headIndex = event.file.rhs && headFiles.get(event.file.rhs.path);
1456
- if (event.error) {
1457
- if (event.error.code !== "unsupported_file_type" && event.error.code !== "not_utf8") throw new Error(`Cannot count ${path}: ${event.error.message}`);
1458
- for (const index of [baseIndex, headIndex]) if (index !== void 0) unsupported.add(coverage.files[index].path);
1459
- } else if (event.diff.type === "text") {
1460
- const diff = event.diff;
1461
- const folded = foldedChanges(diff);
1462
- for (const side of ["base", "head"]) {
1463
- const source = side === "base" ? event.file.lhs : event.file.rhs;
1464
- const index = source && (side === "base" ? baseFiles : headFiles).get(source.path);
1465
- if (index === void 0) continue;
1466
- const file = coverage.files[index];
1467
- const visible = subtractIntervals(diff.structural_changes[side], folded[side]);
1468
- coverage.files[index] = {
1469
- ...file,
1470
- folded: {
1471
- ...file.folded ?? emptyCoverage(),
1472
- [side]: subtractIntervals(file.changed[side], visible)
1352
+ return new Promise((resolve, reject) => {
1353
+ let resolved = false;
1354
+ const counted = () => {
1355
+ resolved = true;
1356
+ resolve(current());
1357
+ };
1358
+ (async () => {
1359
+ const remaining = /* @__PURE__ */ new Set();
1360
+ const diffs = /* @__PURE__ */ new Map();
1361
+ for await (const event of data.structuralChanges({
1362
+ reviewId,
1363
+ pins,
1364
+ signal
1365
+ })) {
1366
+ if (event.type === "start") {
1367
+ for (const entry of event.files) remaining.add((entry.file.rhs ?? entry.file.lhs).path);
1368
+ if (!remaining.size) break;
1369
+ continue;
1370
+ }
1371
+ if (event.type === "complete" && event.aborted) throw new Error(event.aborted.message);
1372
+ if (event.type !== "file" && event.type !== "update") continue;
1373
+ const path = (event.file.rhs ?? event.file.lhs).path;
1374
+ const baseIndex = event.file.lhs && baseFiles.get(event.file.lhs.path);
1375
+ const headIndex = event.file.rhs && headFiles.get(event.file.rhs.path);
1376
+ let diff;
1377
+ if (event.type === "update") {
1378
+ const previous = diffs.get(path);
1379
+ if (!previous) throw new Error(`Unexpected structural result: ${path}`);
1380
+ if (event.error) continue;
1381
+ diff = applyStructuralUpdate(previous, event);
1382
+ } else if (!remaining.delete(path)) throw new Error(`Unexpected structural result: ${path}`);
1383
+ else if (event.error) {
1384
+ if (event.error.code !== "unsupported_file_type" && event.error.code !== "not_utf8") throw new Error(`Cannot count ${path}: ${event.error.message}`);
1385
+ for (const index of [baseIndex, headIndex]) if (index !== void 0) unsupported.add(coverage.files[index].path);
1386
+ } else diff = event.diff;
1387
+ if (diff?.type === "text") {
1388
+ diffs.set(path, diff);
1389
+ const folded = foldedChanges(diff);
1390
+ for (const side of ["base", "head"]) {
1391
+ const source = side === "base" ? event.file.lhs : event.file.rhs;
1392
+ const index = source && (side === "base" ? baseFiles : headFiles).get(source.path);
1393
+ if (index === void 0) continue;
1394
+ const file = coverage.files[index];
1395
+ const visible = subtractIntervals(diff.structural_changes[side], folded[side]);
1396
+ coverage.files[index] = {
1397
+ ...file,
1398
+ folded: {
1399
+ ...file.folded ?? emptyCoverage(),
1400
+ [side]: subtractIntervals(file.changed[side], visible)
1401
+ }
1402
+ };
1473
1403
  }
1474
- };
1475
- }
1476
- const index = headIndex ?? baseIndex;
1477
- if (index !== void 0) {
1478
- if ((coverage.fileSources.get(coverage.files[index].path) ?? []).every((source) => source.file === (source.side === "base" ? event.file.lhs : event.file.rhs)?.path)) coverage.alignments = new Map(coverage.alignments).set(coverage.files[index].path, structuralRows(diff));
1404
+ const index = headIndex ?? baseIndex;
1405
+ if (event.type === "file" && index !== void 0) {
1406
+ if ((coverage.fileSources.get(coverage.files[index].path) ?? []).every((source) => source.file === (source.side === "base" ? event.file.lhs : event.file.rhs)?.path)) coverage.alignments = new Map(coverage.alignments).set(coverage.files[index].path, structuralRows(diff));
1407
+ }
1408
+ }
1409
+ publish?.(current());
1410
+ if (!remaining.size && !resolved) counted();
1479
1411
  }
1480
- }
1481
- publish?.(current());
1482
- if (!remaining.size) break;
1483
- }
1484
- if (remaining.size) throw new Error("Structural coverage is incomplete.");
1485
- return current();
1412
+ if (remaining.size) throw new Error("Structural coverage is incomplete.");
1413
+ if (!resolved) counted();
1414
+ })().catch((error) => {
1415
+ if (resolved) console.warn("Could not refine folded coverage:", error);
1416
+ else reject(error);
1417
+ });
1418
+ });
1486
1419
  }
1487
1420
  /**
1488
1421
  * The changed lines diffr folds by default. A file it hides (lockfiles,
@@ -6125,14 +6058,25 @@ var LocalReviewData = class {
6125
6058
  const key = JSON.stringify(pins);
6126
6059
  let entry = this.coverageCache.get(key);
6127
6060
  if (!entry || entry.state === "error") {
6128
- const promise = comparisonCoverage(this, reviewId, pins, this.coverageAbort.signal, (partial) => {
6061
+ const worktree = pins.worktreeRevision ? JSON.stringify({
6062
+ ...pins,
6063
+ worktreeRevision: void 0
6064
+ }) : void 0;
6065
+ for (const [cachedKey, cached] of this.coverageCache) if (worktree && cached.worktree === worktree) {
6066
+ cached.abort.abort();
6067
+ this.coverageCache.delete(cachedKey);
6068
+ }
6069
+ const abort = new AbortController();
6070
+ const promise = comparisonCoverage(this, reviewId, pins, AbortSignal.any([this.coverageAbort.signal, abort.signal]), (partial) => {
6129
6071
  const current = this.coverageCache.get(key);
6130
6072
  if (current) current.partial = partial;
6131
6073
  this.notifyCoverage();
6132
6074
  });
6133
6075
  entry = {
6134
6076
  promise,
6135
- state: "pending"
6077
+ state: "pending",
6078
+ abort,
6079
+ worktree
6136
6080
  };
6137
6081
  this.coverageCache.set(key, entry);
6138
6082
  const current = entry;
@@ -6146,7 +6090,7 @@ var LocalReviewData = class {
6146
6090
  }
6147
6091
  };
6148
6092
  promise.then((value) => {
6149
- current.partial = value;
6093
+ current.partial ??= value;
6150
6094
  if (!this.closed) this.store.setDiffStats(pins, {
6151
6095
  fileCount: value.files.length,
6152
6096
  additions: value.files.reduce((sum, file) => sum + structuralChangeCounts(file.changed).added, 0),
@@ -6155,7 +6099,7 @@ var LocalReviewData = class {
6155
6099
  settled("ready");
6156
6100
  }, (error) => settled("error", error));
6157
6101
  }
6158
- return entry.promise;
6102
+ return entry.state === "ready" && entry.partial ? Promise.resolve(entry.partial) : entry.promise;
6159
6103
  }
6160
6104
  async changes(pins, file, previousPath) {
6161
6105
  if (file === void 0) return this.summaries(pins);
@@ -6408,4 +6352,4 @@ async function readWorkspace(file) {
6408
6352
  }
6409
6353
  }
6410
6354
  //#endregion
6411
- export { projectOfCheckout as a, activityBeginSchema as c, textualRows as d, invalidateStructuralComparisons as f, CheckoutGroups as g, readGitHubPullRequests as h, inspectSnapshot as i, activityEndSchema as l, diffrMissingError as m, uploadSchema as n, groupCheckoutsAhead as o, diffrExecutable as p, commandSchema as r, initializeReviewStoreSchema as s, openLocalReviewStore as t, activityUpdateSchema as u };
6355
+ export { projectOfCheckout as a, activityBeginSchema as c, invalidateStructuralComparisons as d, diffrExecutable as f, CheckoutGroups as h, inspectSnapshot as i, activityEndSchema as l, readGitHubPullRequests as m, uploadSchema as n, groupCheckoutsAhead as o, diffrMissingError as p, commandSchema as r, initializeReviewStoreSchema as s, openLocalReviewStore as t, activityUpdateSchema as u };
@@ -1,7 +1,8 @@
1
- import { a as toolResultText, s as ReviewApiError } from "./agent-client-Dc0vHFJt.js";
1
+ import { n as isJsonObject } from "./src-X9phtB2j.js";
2
+ import { a as toolResultText, s as ReviewApiError, t as ToolText } from "./agent-client-DfpM8F5N.js";
2
3
  import { t as ReviewInputError } from "./input-error-OLgB_h31.js";
3
- import { n as REVIEW_STATUS_TOOL, t as authoringTools } from "./authoring-tools-D8EkIDjm.js";
4
- import { i as publicTool, n as RECOVERY, r as callPublicTool, t as toolFailure } from "./tool-failure-C1L1vJyz.js";
4
+ import { n as REVIEW_STATUS_TOOL, t as authoringTools } from "./authoring-tools-bm39DRoV.js";
5
+ import { i as publicTool, n as RECOVERY, r as callPublicTool, t as toolFailure } from "./tool-failure-DZaQzi4f.js";
5
6
  import { Server } from "@modelcontextprotocol/sdk/server/index.js";
6
7
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
7
8
  import { CallToolRequestSchema, ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js";
@@ -32,6 +33,7 @@ function mcpAuthoringGuidance(context) {
32
33
  return [
33
34
  "Whiteboard explains code in documents the user reads in Whiteboard Desktop. Call session_get_instructions before creating or editing a Whiteboard and follow it. Read session_capabilities before authoring; session_create opens the new review in Desktop when it is available, so call session_open only for an existing review, and generate software maps only when softwareMapEnabled is true. When the user asks for a Whiteboard or to use Whiteboard (for example to plan work, review a branch or PR stack, or explain a system in Whiteboard), author a Whiteboard with the default topic.",
34
35
  ...context.traceEnabled ? ["For why code exists, what an agent was thinking, or whether an agent solved something before, call session_get_instructions({topic:\"trace-archaeology\"})."] : [],
36
+ "For programmatic MCP calls, use structuredContent when present; otherwise parse JSON from the text content when the result is JSON. An insert through session_edit uses edit.content for the actual document component, never the MCP transport wrapper containing content or structuredContent.",
35
37
  "Never read or write Whiteboard files or SQL."
36
38
  ].join(" ");
37
39
  }
@@ -103,10 +105,15 @@ async function serveReviewMcp(connect, stdin, stdout, stderr = process.stderr, t
103
105
  type: "text",
104
106
  text: RECOVERY
105
107
  }] };
106
- if (request.params.name === REVIEW_STATUS_TOOL.name && offlineStatus) return { content: [{
107
- type: "text",
108
- text: JSON.stringify(await offlineStatus(error instanceof Error ? error.message : String(error)))
109
- }] };
108
+ if (request.params.name === REVIEW_STATUS_TOOL.name && offlineStatus) {
109
+ const result = await offlineStatus(error instanceof Error ? error.message : String(error));
110
+ const response = { content: [{
111
+ type: "text",
112
+ text: JSON.stringify(result)
113
+ }] };
114
+ if (isJsonObject(result)) response.structuredContent = result;
115
+ return response;
116
+ }
110
117
  throw error;
111
118
  }
112
119
  phase = "call";
@@ -115,10 +122,12 @@ async function serveReviewMcp(connect, stdin, stdout, stderr = process.stderr, t
115
122
  const result = await callPublicTool(client, tool, request.params.arguments ?? {}, extra.signal);
116
123
  const text = toolResultText(tool, result);
117
124
  report();
118
- return { content: [{
125
+ const response = { content: [{
119
126
  type: "text",
120
127
  text: listedWhileDown && tool.name === instructionsTool.name ? `${RELOAD_TOOLS}\n\n${text}` : text
121
128
  }] };
129
+ if (!(result instanceof ToolText) && isJsonObject(result)) response.structuredContent = result;
130
+ return response;
122
131
  } catch (error) {
123
132
  report(toolFailure(error, phase));
124
133
  return {
@@ -1,7 +1,7 @@
1
1
  import { c as jsonString, o as jsonObject, u as parseJsonText } from "./src-X9phtB2j.js";
2
- import "./src-Z1x4-Qmb.js";
3
- import { readFileSync } from "node:fs";
2
+ import "./src-DjRaCctk.js";
4
3
  import path from "node:path";
4
+ import { readFileSync } from "node:fs";
5
5
  import { fileURLToPath } from "node:url";
6
6
  //#region ../trace-core/src/package-root.ts
7
7
  /** Finds the package containing a source or bundled module URL. */
@@ -0,0 +1,2 @@
1
+ import { t as openReviewProfile } from "./profile-xzrUFgbT.js";
2
+ export { openReviewProfile };
@@ -1,8 +1,8 @@
1
- import { a as projectOfCheckout, g as CheckoutGroups, o as groupCheckoutsAhead, s as initializeReviewStoreSchema, t as openLocalReviewStore } from "./local-data-DHedfhxw.js";
2
- import "./src-Z1x4-Qmb.js";
3
- import { j as withFileLock } from "./s3-config-CjcLyFV5.js";
4
- import { access, mkdir } from "node:fs/promises";
1
+ import { a as projectOfCheckout, h as CheckoutGroups, o as groupCheckoutsAhead, s as initializeReviewStoreSchema, t as openLocalReviewStore } from "./local-data-BCSeTyic.js";
2
+ import "./src-DjRaCctk.js";
3
+ import { j as withFileLock } from "./s3-config-D7Yt0VhS.js";
5
4
  import path from "node:path";
5
+ import { access, mkdir } from "node:fs/promises";
6
6
  import { DatabaseSync } from "node:sqlite";
7
7
  //#region src/review-api/profile.ts
8
8
  const lockOptions = {
@@ -1,7 +1,7 @@
1
- import { i as readReviewPackageVersion } from "./package-paths-BeYHYjeR.js";
2
- import { a as readReviewServerHealth, l as serverNotReady } from "./server-discovery-DKLIOgg3.js";
3
- import { c as stopBackgroundServer, o as recordedBackgroundServer, r as ensureBackgroundServer } from "./background-server-DHWHzpo5.js";
4
- import { t as ensureRemoteLanguageServer } from "./remote-language-server-Dx13Mg2m.js";
1
+ import { i as readReviewPackageVersion } from "./package-paths-Bg731FHS.js";
2
+ import { a as readReviewServerHealth, l as serverNotReady } from "./server-discovery-aHK_1r3U.js";
3
+ import { c as stopBackgroundServer, o as recordedBackgroundServer, r as ensureBackgroundServer } from "./background-server-B--5p72o.js";
4
+ import { t as ensureRemoteLanguageServer } from "./remote-language-server-CH8sG_CU.js";
5
5
  import path from "node:path";
6
6
  import { spawn } from "node:child_process";
7
7
  import { gt, valid } from "semver";
@@ -1,2 +1,2 @@
1
- import { t as ensureRemoteExtensions } from "./remote-extensions-CsI_BJ_f.js";
1
+ import { t as ensureRemoteExtensions } from "./remote-extensions-CeywZL-Z.js";
2
2
  export { ensureRemoteExtensions };
@@ -1,12 +1,12 @@
1
1
  import { u as parseJsonText } from "./src-X9phtB2j.js";
2
- import { N as devReviewHome } from "./s3-config-CjcLyFV5.js";
3
- import { t as findReviewPackageRoot } from "./package-paths-BeYHYjeR.js";
4
- import "./review-home-paths-CG-sb9Cw.js";
2
+ import { N as devReviewHome } from "./s3-config-D7Yt0VhS.js";
3
+ import { t as findReviewPackageRoot } from "./package-paths-Bg731FHS.js";
4
+ import "./review-home-paths-DcB4xLD-.js";
5
5
  import { createHash } from "node:crypto";
6
6
  import { z } from "zod";
7
+ import path from "node:path";
7
8
  import { createReadStream, createWriteStream } from "node:fs";
8
9
  import { chmod, mkdir, readFile, readdir, rename, rm, writeFile } from "node:fs/promises";
9
- import path from "node:path";
10
10
  import { Readable } from "node:stream";
11
11
  import { execFile } from "node:child_process";
12
12
  import { promisify } from "node:util";
@@ -1,12 +1,12 @@
1
- import "./src-Z1x4-Qmb.js";
2
- import { A as processStartIdentity, j as withFileLock } from "./s3-config-CjcLyFV5.js";
3
- import { t as findReviewPackageRoot } from "./package-paths-BeYHYjeR.js";
4
- import { n as cliSpawn, s as spawnDetached } from "./background-server-DHWHzpo5.js";
5
- import { r as remoteServerPaths, t as ensureRemoteExtensions } from "./remote-extensions-CsI_BJ_f.js";
1
+ import "./src-DjRaCctk.js";
2
+ import { A as processStartIdentity, j as withFileLock } from "./s3-config-D7Yt0VhS.js";
3
+ import { t as findReviewPackageRoot } from "./package-paths-Bg731FHS.js";
4
+ import { n as cliSpawn, s as spawnDetached } from "./background-server-B--5p72o.js";
5
+ import { r as remoteServerPaths, t as ensureRemoteExtensions } from "./remote-extensions-CeywZL-Z.js";
6
6
  import { randomBytes } from "node:crypto";
7
7
  import { z } from "zod";
8
- import { mkdir, readFile, rename, stat, writeFile } from "node:fs/promises";
9
8
  import path from "node:path";
9
+ import { mkdir, readFile, rename, stat, writeFile } from "node:fs/promises";
10
10
  import { setTimeout as setTimeout$1 } from "node:timers/promises";
11
11
  //#region src/remote-language-server.ts
12
12
  const START_TIMEOUT_MS = 15e3;
@@ -1,12 +1,12 @@
1
- import { C as REVIEW_REMOTE_INSTALL_LOCK, w as REVIEW_REMOTE_WRAPPER_MARK } from "./src-DYHVzX3b.js";
2
- import { M as DEV_REVIEW_HOME_ENV } from "./s3-config-CjcLyFV5.js";
3
- import "./review-home-paths-CG-sb9Cw.js";
4
- import { c as stopBackgroundServer, o as recordedBackgroundServer } from "./background-server-DHWHzpo5.js";
5
- import { i as whiteboardRemoteHome } from "./remote-extensions-CsI_BJ_f.js";
6
- import { n as remoteLanguageServerGroups, r as stopProcessGroup } from "./remote-language-server-Dx13Mg2m.js";
1
+ import { C as REVIEW_REMOTE_INSTALL_LOCK, w as REVIEW_REMOTE_WRAPPER_MARK } from "./src-DEOc8bvC.js";
2
+ import { M as DEV_REVIEW_HOME_ENV } from "./s3-config-D7Yt0VhS.js";
3
+ import "./review-home-paths-DcB4xLD-.js";
4
+ import { c as stopBackgroundServer, o as recordedBackgroundServer } from "./background-server-B--5p72o.js";
5
+ import { i as whiteboardRemoteHome } from "./remote-extensions-CeywZL-Z.js";
6
+ import { n as remoteLanguageServerGroups, r as stopProcessGroup } from "./remote-language-server-CH8sG_CU.js";
7
+ import path from "node:path";
7
8
  import { existsSync } from "node:fs";
8
9
  import { lstat, mkdir, readFile, readdir, rm, stat } from "node:fs/promises";
9
- import path from "node:path";
10
10
  import { execFile } from "node:child_process";
11
11
  import { promisify } from "node:util";
12
12
  //#region src/remote-uninstall.ts
@@ -1,7 +1,7 @@
1
1
  import { a as jsonNumber, c as jsonString, i as jsonBoolean, n as isJsonObject } from "./src-X9phtB2j.js";
2
- import { _ as REVIEW_DIFF_LAYOUTS, g as REVIEW_COLOR_THEME_KINDS, v as REVIEW_DIFF_LAYOUT_SCOPES } from "./src-DYHVzX3b.js";
3
- import { t as SELECTION_UNAVAILABLE_REASONS } from "./lens-selection-9byzEgvU.js";
4
- import { n as askAgentIds } from "./thread-state-CO97FUqK.js";
2
+ import { _ as REVIEW_DIFF_LAYOUTS, g as REVIEW_COLOR_THEME_KINDS, v as REVIEW_DIFF_LAYOUT_SCOPES } from "./src-DEOc8bvC.js";
3
+ import { t as SELECTION_UNAVAILABLE_REASONS } from "./lens-selection-EuZdag4Q.js";
4
+ import { n as askAgentIds } from "./thread-state-CbLB0jUA.js";
5
5
  import { z } from "zod";
6
6
  //#region src/telemetry-clean-text.ts
7
7
  const USER_DATA_REGEXES = [
@@ -234,6 +234,7 @@ const SETTING_NAME = [
234
234
  "sharing_enabled",
235
235
  "diffr_config",
236
236
  "theme",
237
+ "diff_theme",
237
238
  "ctrl_tab",
238
239
  "open_files_in",
239
240
  "ready_notification",
@@ -241,6 +242,7 @@ const SETTING_NAME = [
241
242
  "code_font_size",
242
243
  "document_font_size",
243
244
  "reading_font",
245
+ "reading_font_family",
244
246
  "code_font_family",
245
247
  "font_ligatures",
246
248
  "diff_font_size",
@@ -712,6 +714,15 @@ const UI_TELEMETRY_EVENTS = {
712
714
  files: "number"
713
715
  }
714
716
  },
717
+ diff_loaded: {
718
+ event: "review_diff_loaded",
719
+ properties: {
720
+ first_file_ms: "number",
721
+ updates_ms: "number",
722
+ files: "number",
723
+ update_errors: "number"
724
+ }
725
+ },
715
726
  discord_clicked: {
716
727
  event: "review_discord_clicked",
717
728
  properties: { via: DISCORD_VIA }
@@ -1,7 +1,7 @@
1
- import { o as parseReviewVerbResponse } from "./src-DYHVzX3b.js";
2
- import { b as selectReviewInstance, m as healthyReviewInstance, v as reviewInstanceStartHint, y as reviewInstanceUnavailable } from "./server-discovery-DKLIOgg3.js";
3
- import { closeSync, existsSync, fstatSync, mkdtempSync, openSync, readSync } from "node:fs";
1
+ import { o as parseReviewVerbResponse } from "./src-DEOc8bvC.js";
2
+ import { b as selectReviewInstance, m as healthyReviewInstance, v as reviewInstanceStartHint, y as reviewInstanceUnavailable } from "./server-discovery-aHK_1r3U.js";
4
3
  import path from "node:path";
4
+ import { closeSync, existsSync, fstatSync, mkdtempSync, openSync, readSync } from "node:fs";
5
5
  import { homedir, tmpdir } from "node:os";
6
6
  import { execFileSync, spawn } from "node:child_process";
7
7
  //#region src/review-app-launcher.ts
@@ -1,5 +1,5 @@
1
- import "./src-Z1x4-Qmb.js";
2
- import { N as devReviewHome } from "./s3-config-CjcLyFV5.js";
1
+ import "./src-DjRaCctk.js";
2
+ import { N as devReviewHome } from "./s3-config-D7Yt0VhS.js";
3
3
  import crypto from "node:crypto";
4
4
  import path from "node:path";
5
5
  //#region src/review-home-paths.ts
@@ -1,15 +1,15 @@
1
1
  import { c as jsonString, l as jsonValueSchema, o as jsonObject, u as parseJsonText } from "./src-X9phtB2j.js";
2
- import "./src-DYHVzX3b.js";
3
- import "./src-Z1x4-Qmb.js";
4
- import { F as writeFileAtomic, N as devReviewHome, j as withFileLock, k as processIsAlive } from "./s3-config-CjcLyFV5.js";
5
- import { i as readReviewPackageVersion, t as findReviewPackageRoot } from "./package-paths-BeYHYjeR.js";
6
- import "./review-home-paths-CG-sb9Cw.js";
7
- import "./request-origin-CNSLVn7x.js";
2
+ import "./src-DEOc8bvC.js";
3
+ import "./src-DjRaCctk.js";
4
+ import { F as writeFileAtomic, N as devReviewHome, j as withFileLock, k as processIsAlive } from "./s3-config-D7Yt0VhS.js";
5
+ import { i as readReviewPackageVersion, t as findReviewPackageRoot } from "./package-paths-Bg731FHS.js";
6
+ import "./review-home-paths-DcB4xLD-.js";
7
+ import "./request-origin-CargJFvH.js";
8
8
  import { createHmac, randomUUID } from "node:crypto";
9
9
  import { z } from "zod";
10
+ import path from "node:path";
10
11
  import { existsSync, readFileSync, rmSync } from "node:fs";
11
12
  import { readFile, readdir, rm } from "node:fs/promises";
12
- import path from "node:path";
13
13
  import os from "node:os";
14
14
  import { valid } from "semver";
15
15
  //#region src/agent-session-ref.ts
@@ -1,4 +1,4 @@
1
- import { w as runVcs } from "./src-X9ZebeOD.js";
1
+ import { w as runVcs } from "./src-DXnn7RrI.js";
2
2
  import path from "node:path";
3
3
  //#region src/runtime.ts
4
4
  async function resolveReviewRoot(cwd, execFile = execFilePromise) {