@dev.fast/whiteboard 0.2.5-preview.20261015.120 → 0.2.5-preview.20261016.121

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 (45) hide show
  1. package/dist/{account-alias-pAq80fex.js → account-alias-CwNaMOwU.js} +3 -3
  2. package/dist/agent-cli-NWQPsL0W.js +2 -0
  3. package/dist/{agent-cli-CACGYhRh.js → agent-cli-S0bt9I7V.js} +6 -6
  4. package/dist/{agent-client-D6xrEdTj.js → agent-client-DjXQy6CL.js} +3 -3
  5. package/dist/{authoring-tools-8UhtCkjL.js → authoring-tools-CjFAGHBa.js} +4 -3
  6. package/dist/{background-server-KKSjffhS.js → background-server-CVL7QpsL.js} +1 -1
  7. package/dist/{background-server-D7maQkJV.js → background-server-ONy-xKJ1.js} +2 -2
  8. package/dist/build-info.json +1 -1
  9. package/dist/{cli-CGWs67Pz.js → cli-Cko4zfif.js} +1 -1
  10. package/dist/{cli-runner-D3XvcVCa.js → cli-runner-BhBnAC_R.js} +17 -17
  11. package/dist/cli.js +2 -2
  12. package/dist/{client-tIkQ_x_x.js → client-CLqKkzOc.js} +2 -2
  13. package/dist/{fuzzy-match-CufFp9IR.js → fuzzy-match-DFPKdSLR.js} +1 -1
  14. package/dist/{headless-host-COLvXm9A.js → headless-host-BNF-TR3x.js} +1 -1
  15. package/dist/{headless-host-CDh2qQbQ.js → headless-host-mg9sQG6H.js} +12 -12
  16. package/dist/{local-data-Bhs4VnzC.js → local-data-DN-A5p0K.js} +5 -4
  17. package/dist/{mcp-DqmNumCl.js → mcp-BY4ExeMQ.js} +3 -3
  18. package/dist/profile-BBw-jMW3.js +2 -0
  19. package/dist/{profile-CbAW3qDm.js → profile-Dm6irjbg.js} +1 -1
  20. package/dist/{remote-attach-DK4FaWbL.js → remote-attach-DJxW9402.js} +3 -3
  21. package/dist/{remote-language-server-BIP6hw3K.js → remote-language-server-CGsCaqro.js} +1 -1
  22. package/dist/{remote-uninstall-CLf9aKCN.js → remote-uninstall-DYmNOetF.js} +3 -3
  23. package/dist/{request-origin-2d7P4ugC.js → request-origin-D1RNVSeK.js} +1 -1
  24. package/dist/{review-app-launcher-C7PUhYox.js → review-app-launcher-i1hmxOZA.js} +2 -2
  25. package/dist/{review-telemetry-kTLRwBj_.js → review-telemetry-DnQNwgR1.js} +2 -2
  26. package/dist/server/desktop-host.js +15 -12
  27. package/dist/{server-discovery-IdX3Wc8g.js → server-discovery-DVqrcIDT.js} +1 -1
  28. package/dist/sharing/index.d.ts +7 -4
  29. package/dist/sharing/index.js +1 -1
  30. package/dist/{src-C_iUYopM.js → src-DZG1xAqW.js} +1 -0
  31. package/dist/{stored-document-migration-DVJJPBGd.js → stored-document-migration-DLBhkSfp.js} +46 -6
  32. package/dist/{thread-state-CS-v-hc0.js → thread-state-CaQ2lGdj.js} +1 -1
  33. package/dist/{tool-failure-A9dVocGs.js → tool-failure-CVJrfi5s.js} +1 -1
  34. package/package.json +4 -4
  35. package/src/review-api/blocks/definition.ts +3 -1
  36. package/src/review-api/blocks/index.ts +19 -0
  37. package/src/review-api/blocks/sequence.ts +80 -2
  38. package/src/review-api/component-reference.ts +2 -1
  39. package/src/review-api/document-text.ts +7 -2
  40. package/src/review-api/document.ts +13 -9
  41. package/src/review-api/store.ts +14 -5
  42. package/src/server/review-gateway-hosts.ts +8 -1
  43. package/src/server/review-gateway-list.ts +1 -0
  44. package/dist/agent-cli-Bs2PzjQx.js +0 -2
  45. package/dist/profile-DY_QhMQ9.js +0 -2
@@ -1,10 +1,10 @@
1
1
  import { n as isJsonObject } from "./src-X9phtB2j.js";
2
- import { w as ReviewCliInstallStampSchema } from "./src-C_iUYopM.js";
2
+ import { w as ReviewCliInstallStampSchema } from "./src-DZG1xAqW.js";
3
3
  import { C as disableTraceMachine, E as traceMachineStatus, G as listTraceRepositoryRoots, K as traceRepositoryStatus, Q as removeAgentTraceHook, S as configureTraceMachine, T as traceMachineEnabled, U as disableAllTraceRepositories, W as enableTraceRepository, X as describeTraceHookOwners, Y as AGENT_TRACE_HOOK_AGENTS, Z as installHarnessHooks, dt as StoreClient, n as reviewDesktopStateDir, nt as traceScope, ot as readStoreAuth } from "./review-home-paths-Lc02WFuX.js";
4
4
  import { A as withFileLock, F as writeFileAtomicAsync, I as writePrivateJsonAtomic, M as devReviewHome } from "./s3-config-DDWWMh5x.js";
5
5
  import { i as readReviewPackageVersion } from "./package-paths-jMg6JxDP.js";
6
- import { f as isDirectory, p as isFile } from "./thread-state-CS-v-hc0.js";
7
- import { s as findExecutable } from "./fuzzy-match-CufFp9IR.js";
6
+ import { f as isDirectory, p as isFile } from "./thread-state-CaQ2lGdj.js";
7
+ import { s as findExecutable } from "./fuzzy-match-DFPKdSLR.js";
8
8
  import { createHash } from "node:crypto";
9
9
  import { constants } from "node:fs";
10
10
  import { access, chmod, lstat, mkdir, readFile, rm, stat, writeFile } from "node:fs/promises";
@@ -0,0 +1,2 @@
1
+ import { n as runReviewAgentCli } from "./agent-cli-S0bt9I7V.js";
2
+ export { runReviewAgentCli };
@@ -1,11 +1,11 @@
1
1
  import { n as isJsonObject, u as parseJsonText } from "./src-X9phtB2j.js";
2
2
  import { T as traceMachineEnabled } from "./review-home-paths-Lc02WFuX.js";
3
- import { a as toolResultText, i as connectReviewInstance, s as ReviewApiError } from "./agent-client-D6xrEdTj.js";
3
+ import { a as toolResultText, i as connectReviewInstance, s as ReviewApiError } from "./agent-client-DjXQy6CL.js";
4
4
  import { M as devReviewHome } from "./s3-config-DDWWMh5x.js";
5
- import { b as selectReviewInstance, u as REVIEW_INSTANCE_ENV } from "./server-discovery-IdX3Wc8g.js";
6
- import { n as reviewSessionAgent } from "./review-telemetry-kTLRwBj_.js";
7
- import { n as REVIEW_VIA_HEADER, t as REVIEW_AGENT_HEADER } from "./request-origin-2d7P4ugC.js";
8
- import { i as publicTool, n as RECOVERY, r as callPublicTool, t as toolFailure } from "./tool-failure-A9dVocGs.js";
5
+ import { b as selectReviewInstance, u as REVIEW_INSTANCE_ENV } from "./server-discovery-DVqrcIDT.js";
6
+ import { n as reviewSessionAgent } from "./review-telemetry-DnQNwgR1.js";
7
+ import { n as REVIEW_VIA_HEADER, t as REVIEW_AGENT_HEADER } from "./request-origin-D1RNVSeK.js";
8
+ import { i as publicTool, n as RECOVERY, r as callPublicTool, t as toolFailure } from "./tool-failure-CVJrfi5s.js";
9
9
  //#region src/review-api/agent-cli.ts
10
10
  const reviewAgentCliHelp = "whiteboard api tools (one line per tool)\nwhiteboard api tools <tool-name> (its description and input schema)\nwhiteboard api <tool-name> '<json>'\nwhiteboard api <tool-name> - (read JSON from stdin)\nwhiteboard mcp (stdio MCP adapter; Whiteboard Desktop or whiteboard server start must be running)\nSelect headless state with DEV_REVIEW_SERVER_DIR or whiteboard --state-dir <path> api/mcp.\n";
11
11
  async function runReviewAgentCli(input) {
@@ -20,7 +20,7 @@ async function runReviewAgentCli(input) {
20
20
  if (extra.length || mode === "mcp" && name) throw new Error("Unexpected arguments. Use whiteboard api --help.");
21
21
  const headers = Object.fromEntries([[REVIEW_VIA_HEADER, mode === "mcp" ? "mcp" : "api"], [REVIEW_AGENT_HEADER, reviewSessionAgent(env)]]);
22
22
  if (mode === "mcp") {
23
- const { serveReviewMcp } = await import("./mcp-DqmNumCl.js");
23
+ const { serveReviewMcp } = await import("./mcp-BY4ExeMQ.js");
24
24
  await serveReviewMcp(async (key, agentKind) => {
25
25
  const connected = await connectReviewInstance(key ? {
26
26
  ...env,
@@ -1,7 +1,7 @@
1
1
  import { d as isBooleanValue, f as isNumberValue, m as isStringValue, u as parseJsonText } from "./src-X9phtB2j.js";
2
- import "./src-C_iUYopM.js";
2
+ import "./src-DZG1xAqW.js";
3
3
  import { t as ReviewInputError } from "./input-error-OLgB_h31.js";
4
- import { b as selectReviewInstance, c as reviewServerStateDir, d as ReviewInstanceUnavailableError, i as readReviewServerDiscovery, l as serverNotReady, m as healthyReviewInstance, s as reviewServerIsHealthy, y as reviewInstanceUnavailable } from "./server-discovery-IdX3Wc8g.js";
4
+ import { b as selectReviewInstance, c as reviewServerStateDir, d as ReviewInstanceUnavailableError, i as readReviewServerDiscovery, l as serverNotReady, m as healthyReviewInstance, s as reviewServerIsHealthy, y as reviewInstanceUnavailable } from "./server-discovery-DVqrcIDT.js";
5
5
  //#region ../review-protocol/src/review-api-client.ts
6
6
  const defaultRequest = (url, init) => fetch(url, init);
7
7
  /** A non-2xx reply; the status tells a caller whether retrying can help. */
@@ -249,7 +249,7 @@ async function connectReviewInstance(env = process.env, headers = {}, options =
249
249
  const selection = await selectReviewInstance({ env });
250
250
  const discovery = healthyReviewInstance(selection);
251
251
  if (!discovery) {
252
- const { ensureServerWithoutDesktop } = await import("./background-server-KKSjffhS.js");
252
+ const { ensureServerWithoutDesktop } = await import("./background-server-CVL7QpsL.js");
253
253
  const server = await ensureServerWithoutDesktop({
254
254
  selection,
255
255
  env,
@@ -1,5 +1,5 @@
1
- import { c as activityEndSchema, i as commandSchema, l as activityUpdateSchema, n as uploadSchema, s as activityBeginSchema } from "./local-data-Bhs4VnzC.js";
2
- import { S as publishedEditSchema, y as pathTargetSchema } from "./stored-document-migration-DVJJPBGd.js";
1
+ import { c as activityEndSchema, i as commandSchema, l as activityUpdateSchema, n as uploadSchema, s as activityBeginSchema } from "./local-data-DN-A5p0K.js";
2
+ import { S as publishedEditSchema, y as pathTargetSchema } from "./stored-document-migration-DLBhkSfp.js";
3
3
  import { t as ReviewInputError } from "./input-error-OLgB_h31.js";
4
4
  import { t as findReviewPackageRoot } from "./package-paths-jMg6JxDP.js";
5
5
  import { z } from "zod";
@@ -85,7 +85,8 @@ const COMPONENT_REFERENCE = [
85
85
  "- trace_quote {traceId, eventId, text}",
86
86
  "- software_map {mapVersionId, focusElementId?}",
87
87
  "- sequence {title, actors: {key: label}, steps: step[], pins?}",
88
- "- step {from, to (actor keys), label, style?: call|return|async, and exactly one of source: anchor, explanation, or code: {text, language?}; pins?}",
88
+ "- step {from, to (actor keys), label, style?: call|return|async, and exactly one of source: anchor or anchor[] (at most 4), explanation, or code: {text, language?}; pins?}",
89
+ "A step's source is the line where its arrow leaves `from` (the call, return or emit). Add an additional range only when that line can't be read without it: the type or schema of what it passes, or the code that builds its argument. Never the callee's body; that is the next step.",
89
90
  "- flow_diagram {title, nodes: flow_node[] (1 to 100), edges: flow_edge[], description?, direction?: right|down, pins?}",
90
91
  "- flow_node {key, label, description?, kind?: process|decision|terminal, attachments?: {label, sources: anchor[], pins?}[], link?: {from or to, label?, style?}}",
91
92
  "- flow_edge {from, to (node keys), label?, style?: solid|dashed}",
@@ -1,2 +1,2 @@
1
- import { a as headlessServerOwner, c as stopBackgroundServer, i as ensureServerWithoutDesktop, r as ensureBackgroundServer } from "./background-server-D7maQkJV.js";
1
+ import { a as headlessServerOwner, c as stopBackgroundServer, i as ensureServerWithoutDesktop, r as ensureBackgroundServer } from "./background-server-ONy-xKJ1.js";
2
2
  export { ensureBackgroundServer, ensureServerWithoutDesktop, headlessServerOwner, stopBackgroundServer };
@@ -1,8 +1,8 @@
1
1
  import "./review-home-paths-Lc02WFuX.js";
2
2
  import { D as liveLockOwner, O as processIsAlive } from "./s3-config-DDWWMh5x.js";
3
3
  import { t as findReviewPackageRoot } from "./package-paths-jMg6JxDP.js";
4
- import { a as readReviewServerHealth, c as reviewServerStateDir, i as readReviewServerDiscovery, n as desktopOwnsHome, p as desktopUsesHome, r as headlessServerLockPath } from "./server-discovery-IdX3Wc8g.js";
5
- import { t as desktopApplicationInstalled } from "./review-app-launcher-C7PUhYox.js";
4
+ import { a as readReviewServerHealth, c as reviewServerStateDir, i as readReviewServerDiscovery, n as desktopOwnsHome, p as desktopUsesHome, r as headlessServerLockPath } from "./server-discovery-DVqrcIDT.js";
5
+ import { t as desktopApplicationInstalled } from "./review-app-launcher-i1hmxOZA.js";
6
6
  import { mkdir, open, readFile, realpath, stat } from "node:fs/promises";
7
7
  import path from "node:path";
8
8
  import { spawn } from "node:child_process";
@@ -1 +1 @@
1
- {"version":"0.2.5-preview.20261015.120","commit":"99b613b6ce358d31fd4cc2b59822a939f7f113cf","dirty":true,"builtAt":"2026-10-15T23:32:28.351Z"}
1
+ {"version":"0.2.5-preview.20261016.121","commit":"3c1b0477310222ab5a70d7c52b66f65dc62ffc5d","dirty":true,"builtAt":"2026-10-16T14:28:51.746Z"}
@@ -1,4 +1,4 @@
1
- import { r as connectReviewApi } from "./agent-client-D6xrEdTj.js";
1
+ import { r as connectReviewApi } from "./agent-client-DjXQy6CL.js";
2
2
  import { randomUUID } from "node:crypto";
3
3
  import { z } from "zod";
4
4
  //#region src/sharing/cli.ts
@@ -1,18 +1,18 @@
1
1
  import { C as setLocalVcsCommandObserver, E as __toESM, T as __require, w as __commonJSMin } from "./src-BMPuxWv4.js";
2
2
  import { c as jsonString, n as isJsonObject, u as parseJsonText } from "./src-X9phtB2j.js";
3
- import { _ as REVIEW_REMOTE_AGENT_IDS, p as REVIEW_CLI_INSTALL_TARGET_LABELS, v as REVIEW_REMOTE_ATTACH_BEGIN, y as REVIEW_REMOTE_ATTACH_END } from "./src-C_iUYopM.js";
3
+ import { _ as REVIEW_REMOTE_AGENT_IDS, p as REVIEW_CLI_INSTALL_TARGET_LABELS, v as REVIEW_REMOTE_ATTACH_BEGIN, y as REVIEW_REMOTE_ATTACH_END } from "./src-DZG1xAqW.js";
4
4
  import { $ as resolveTraceCommand, A as runTraceDeny, E as traceMachineStatus, F as describeSelection, H as resolveTraceRepositoryTarget, J as HOSTED_CAPTURE_SCOPE_DESCRIPTION, K as traceRepositoryStatus, L as loadS3TraceStorage, M as runTraceSessions, N as runTraceStoreDelete, P as runTraceStoreInfo, T as traceMachineEnabled, V as requireTraceConsent, _ as runTraceRepair, b as runTraceGitHook, c as registerTraceCommands, ct as runStoreLogout, d as runTraceList$1, dt as StoreClient, f as runTracePull$1, ft as withStoreAuthorization, g as runTraceInstallMachine, gt as jsonRequestedInArgv, h as runTraceEnable, ht as humanStream, j as runTraceOnboard, k as runTraceAllow, l as resolveTraceReadStorage, lt as runStoreWhoami, m as runTraceDisable, mt as failWithJsonError, nt as traceScope, ot as readStoreAuth, p as runTraceShow, pt as emitJsonEvent, q as readTraceUserConfig, rt as DEFAULT_STORE_ORIGIN, st as runStoreLogin, tt as traceHomeDir, u as runTraceBlame, ut as StoreApiError, v as runTraceStatus, w as readLegacyCaptureSettings, x as runTraceHook, y as runTraceSync, z as selectTraceStorage } from "./review-home-paths-Lc02WFuX.js";
5
- import { o as ReviewApiClient, s as ReviewApiError } from "./agent-client-D6xrEdTj.js";
5
+ import { o as ReviewApiClient, s as ReviewApiError } from "./agent-client-DjXQy6CL.js";
6
6
  import { t as resolveReviewRoot } from "./runtime-CKiWhGrf.js";
7
7
  import { C as s3ProfileSchema, E as writeTraceConfigFile, M as devReviewHome, N as normalizeStoreOrigin, S as readTraceConfigFile, T as sameS3Profile, _ as emptyTraceConfig, c as resolveS3Setup, g as currentStore, h as TraceConfigurationError, i as isS3MockMode, n as clearTraceEnvCache, o as readTraceEnvFile, p as traceSettingsPath, w as s3Store } from "./s3-config-DDWWMh5x.js";
8
8
  import { t as errorMessage } from "./error-message-OtiDonty.js";
9
9
  import { i as readReviewPackageVersion, r as readBuildInfo, t as findReviewPackageRoot } from "./package-paths-jMg6JxDP.js";
10
- import { a as readReviewServerHealth, b as selectReviewInstance, c as reviewServerStateDir, d as ReviewInstanceUnavailableError, f as clearDefaultReviewInstance, g as requireHealthyReviewDesktop, h as readDefaultReviewInstance, i as readReviewServerDiscovery, l as serverNotReady, m as healthyReviewInstance, p as desktopUsesHome, t as DESKTOP_TAKEOVER_SIGNAL, u as REVIEW_INSTANCE_ENV, x as writeDefaultReviewInstance, y as reviewInstanceUnavailable } from "./server-discovery-IdX3Wc8g.js";
11
- import { t as ReviewTelemetry } from "./review-telemetry-kTLRwBj_.js";
12
- import { t as fuzzyRank } from "./fuzzy-match-CufFp9IR.js";
13
- import { _ as isInstallTarget, a as isOwnedShim, f as windowsInstallerCommand, g as ALL_INSTALL_TARGETS, h as detectConnectAgents, m as connectAgent, o as pathShimPath, p as scanLegacySkills, t as aliasInstallationToAccount, v as connectPrompts } from "./account-alias-pAq80fex.js";
14
- import { n as focusReviewDesktop, r as runReviewAppLaunch } from "./review-app-launcher-C7PUhYox.js";
15
- import { t as reviewAgentCliHelp } from "./agent-cli-CACGYhRh.js";
10
+ import { a as readReviewServerHealth, b as selectReviewInstance, c as reviewServerStateDir, d as ReviewInstanceUnavailableError, f as clearDefaultReviewInstance, g as requireHealthyReviewDesktop, h as readDefaultReviewInstance, i as readReviewServerDiscovery, l as serverNotReady, m as healthyReviewInstance, p as desktopUsesHome, t as DESKTOP_TAKEOVER_SIGNAL, u as REVIEW_INSTANCE_ENV, x as writeDefaultReviewInstance, y as reviewInstanceUnavailable } from "./server-discovery-DVqrcIDT.js";
11
+ import { t as ReviewTelemetry } from "./review-telemetry-DnQNwgR1.js";
12
+ import { t as fuzzyRank } from "./fuzzy-match-DFPKdSLR.js";
13
+ import { _ as isInstallTarget, a as isOwnedShim, f as windowsInstallerCommand, g as ALL_INSTALL_TARGETS, h as detectConnectAgents, m as connectAgent, o as pathShimPath, p as scanLegacySkills, t as aliasInstallationToAccount, v as connectPrompts } from "./account-alias-CwNaMOwU.js";
14
+ import { n as focusReviewDesktop, r as runReviewAppLaunch } from "./review-app-launcher-i1hmxOZA.js";
15
+ import { t as reviewAgentCliHelp } from "./agent-cli-S0bt9I7V.js";
16
16
  import { existsSync, mkdirSync, renameSync, writeFileSync } from "node:fs";
17
17
  import { realpath } from "node:fs/promises";
18
18
  import path from "node:path";
@@ -4297,7 +4297,7 @@ async function runReviewCli(input) {
4297
4297
  if (!Number.isInteger(port) || port < 0 || port > 65535) throw new ReviewCliUsageError("--port must be an integer between 0 and 65535.");
4298
4298
  const stateDir = reviewServerStateDir(authoringEnv(options.stateDir));
4299
4299
  if (options.detach) {
4300
- const { ensureBackgroundServer } = await import("./background-server-KKSjffhS.js");
4300
+ const { ensureBackgroundServer } = await import("./background-server-CVL7QpsL.js");
4301
4301
  const { discovery, started } = await ensureBackgroundServer({
4302
4302
  stateDir,
4303
4303
  env,
@@ -4319,7 +4319,7 @@ async function runReviewCli(input) {
4319
4319
  process.once("SIGINT", stop);
4320
4320
  process.once("SIGTERM", stop);
4321
4321
  process.once(DESKTOP_TAKEOVER_SIGNAL, takeover);
4322
- const { HeadlessServerBusyError, runHeadlessServer } = await import("./headless-host-COLvXm9A.js");
4322
+ const { HeadlessServerBusyError, runHeadlessServer } = await import("./headless-host-BNF-TR3x.js");
4323
4323
  try {
4324
4324
  await runHeadlessServer({
4325
4325
  stateDir,
@@ -4356,7 +4356,7 @@ async function runReviewCli(input) {
4356
4356
  configureJsonOutput(serverCommand.command("stop").description("Stop a background server started by --detach, the CLI, or Desktop").option("--state-dir <path>", "directory selected when starting the server"), "plain").action(async (_options, command) => {
4357
4357
  const options = command.optsWithGlobals();
4358
4358
  const stateDir = reviewServerStateDir(authoringEnv(options.stateDir));
4359
- const { headlessServerOwner, stopBackgroundServer } = await import("./background-server-KKSjffhS.js");
4359
+ const { headlessServerOwner, stopBackgroundServer } = await import("./background-server-CVL7QpsL.js");
4360
4360
  const owner = await headlessServerOwner(stateDir);
4361
4361
  const discovery = await readReviewServerDiscovery(stateDir);
4362
4362
  if (owner === void 0) {
@@ -4384,7 +4384,7 @@ async function runReviewCli(input) {
4384
4384
  if (!existsSync(path.join(stateDir, "review-api.db"))) throw new Error(`No saved reviews in ${stateDir}, so there is no server id to reset. Check --state-dir.`);
4385
4385
  const inUse = `A Whiteboard server is using ${stateDir}. Stop it first, then run whiteboard server reset-id again.`;
4386
4386
  if (await desktopUsesHome(stateDir, env)) return refuse(inUse, options.json);
4387
- const [{ openReviewProfile }, { withHeadlessServerLock }] = await Promise.all([import("./profile-DY_QhMQ9.js"), import("./headless-host-COLvXm9A.js")]);
4387
+ const [{ openReviewProfile }, { withHeadlessServerLock }] = await Promise.all([import("./profile-BBw-jMW3.js"), import("./headless-host-BNF-TR3x.js")]);
4388
4388
  const reset = await withHeadlessServerLock(await realpath(stateDir), async () => {
4389
4389
  const local = await openReviewProfile(stateDir, { manageWorkspaces: false });
4390
4390
  try {
@@ -4406,7 +4406,7 @@ async function runReviewCli(input) {
4406
4406
  configureJsonOutput(remote.command("attach").description("Start a background server if none is healthy and report how to reach it").option("--state-dir <path>", "directory for saved reviews and server discovery").option("--groups <groups>", "comma-separated optional extension groups the Desktop has enabled, such as go").option("--replace", "stop a running server of another version that the CLI or Desktop started, and start this one"), "plain").action(async (_options, command) => {
4407
4407
  const options = command.optsWithGlobals();
4408
4408
  const stateDir = reviewServerStateDir(authoringEnv(options.stateDir));
4409
- const { remoteAttach } = await import("./remote-attach-DK4FaWbL.js");
4409
+ const { remoteAttach } = await import("./remote-attach-DJxW9402.js");
4410
4410
  let attach;
4411
4411
  try {
4412
4412
  attach = await remoteAttach({
@@ -4448,7 +4448,7 @@ async function runReviewCli(input) {
4448
4448
  configureJsonOutput(remote.command("uninstall").description("Remove what Whiteboard Desktop installed on this host, stopping a server it started").addOption(new Option("--keep-reviews", "leave the saved reviews in place").conflicts("deleteReviews")).addOption(new Option("--delete-reviews", "also delete the saved reviews")).option("--state-dir <path>", "directory for saved reviews and server discovery"), "plain").action(async (_options, command) => {
4449
4449
  const options = command.optsWithGlobals();
4450
4450
  const stateDir = reviewServerStateDir(authoringEnv(options.stateDir));
4451
- const { remoteUninstall } = await import("./remote-uninstall-CLf9aKCN.js");
4451
+ const { remoteUninstall } = await import("./remote-uninstall-DYmNOetF.js");
4452
4452
  let deleteReviews = options.deleteReviews ? true : options.keepReviews ? false : void 0;
4453
4453
  if (deleteReviews === void 0 && !options.json && input.stdin?.isTTY) {
4454
4454
  const prompt = createInterface$1({
@@ -4670,14 +4670,14 @@ async function runReviewCli(input) {
4670
4670
  command.showHelpAfterError(false).error("Markdown review migration is no longer supported. Ask your agent to migrate your old Whiteboard reviews.");
4671
4671
  });
4672
4672
  configureJsonOutput(configureJsonOutput(program.command("share").description("Upload an immutable review snapshot and return its share link"), "plain").option("--review <id>", "review ID").option("--version <number>", "Saved version to share").option("--preview", "Open the share link in Whiteboard Preview by default").option("--request-id <uuid>", "Reuse this ID when retrying the same immutable upload").action(async (options) => {
4673
- const { runShareCli } = await import("./cli-CGWs67Pz.js");
4673
+ const { runShareCli } = await import("./cli-Cko4zfif.js");
4674
4674
  state.exitCode = await runShareCli({
4675
4675
  ...input,
4676
4676
  ...options,
4677
4677
  env: authoringEnv()
4678
4678
  });
4679
4679
  }).command("revoke <share-id>").description("Revoke future downloads of a share"), "plain").action(async (shareId, options) => {
4680
- const { runShareCli } = await import("./cli-CGWs67Pz.js");
4680
+ const { runShareCli } = await import("./cli-Cko4zfif.js");
4681
4681
  state.exitCode = await runShareCli({
4682
4682
  ...input,
4683
4683
  ...options,
@@ -4758,7 +4758,7 @@ async function runReviewCli(input) {
4758
4758
  return commands;
4759
4759
  } });
4760
4760
  for (const [name, description] of [["api", "Call a JSON review authoring tool on the running server"], ["mcp", "Serve the JSON review authoring tools over stdio MCP"]]) configureOutput(program.command(name).description(description).argument("[args...]", "tool name and JSON input").allowUnknownOption().allowExcessArguments().helpOption(false).passThroughOptions().addHelpText("after", `\n${reviewAgentCliHelp}`), "plain").action(async (args) => {
4761
- const { runReviewAgentCli } = await import("./agent-cli-Bs2PzjQx.js");
4761
+ const { runReviewAgentCli } = await import("./agent-cli-NWQPsL0W.js");
4762
4762
  state.exitCode = await runReviewAgentCli({
4763
4763
  ...input,
4764
4764
  env: authoringEnv(),
package/dist/cli.js CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  import { a as jsonNumber, c as jsonString, o as jsonObject, u as parseJsonText } from "./src-X9phtB2j.js";
3
- import "./src-C_iUYopM.js";
3
+ import "./src-DZG1xAqW.js";
4
4
  import { existsSync, readFileSync, readdirSync, realpathSync } from "node:fs";
5
5
  import path from "node:path";
6
6
  import os from "node:os";
@@ -17,7 +17,7 @@ async function runCli(effectivePath) {
17
17
  `);
18
18
  return 1;
19
19
  }
20
- const { runReviewCli } = await import("./cli-runner-D3XvcVCa.js");
20
+ const { runReviewCli } = await import("./cli-runner-BhBnAC_R.js");
21
21
  return runReviewCli({
22
22
  argv,
23
23
  cliPaths: {
@@ -1,7 +1,7 @@
1
1
  import { f as gitAt } from "./src-BMPuxWv4.js";
2
2
  import { n as isJsonObject, u as parseJsonText } from "./src-X9phtB2j.js";
3
- import "./src-C_iUYopM.js";
4
- import { B as markdownNodes, C as resourceReference, D as sourceReferences, N as lensSchema, P as lensSelections, V as parseMarkdown, _ as hasCodeReferences, c as traceSchema, h as elements, k as checkReferences, l as textIncludesQuote, n as migrateStoredDocument, o as normalizedSoftwareElementSchema, p as documentSchema, q as sourceAnchors, r as migrateStoredLenses, s as normalizedSoftwareRelationshipSchema, t as liftFileLenses, u as anchorPins, w as resourceReferences, x as pinsSchema } from "./stored-document-migration-DVJJPBGd.js";
3
+ import "./src-DZG1xAqW.js";
4
+ import { C as resourceReference, D as sourceReferences, F as lensSchema, H as markdownNodes, I as lensSelections, U as parseMarkdown, Y as sourceAnchors, _ as hasCodeReferences, c as traceSchema, h as elements, k as checkReferences, l as textIncludesQuote, n as migrateStoredDocument, o as normalizedSoftwareElementSchema, p as documentSchema, r as migrateStoredLenses, s as normalizedSoftwareRelationshipSchema, t as liftFileLenses, u as anchorPins, w as resourceReferences, x as pinsSchema } from "./stored-document-migration-DLBhkSfp.js";
5
5
  import { t as ReviewInputError } from "./input-error-OLgB_h31.js";
6
6
  import { createHash, randomUUID } from "node:crypto";
7
7
  import { z } from "zod";
@@ -1,6 +1,6 @@
1
1
  import "./review-home-paths-Lc02WFuX.js";
2
2
  import { M as devReviewHome } from "./s3-config-DDWWMh5x.js";
3
- import { n as askAgentIds } from "./thread-state-CS-v-hc0.js";
3
+ import { n as askAgentIds } from "./thread-state-CaQ2lGdj.js";
4
4
  import { createRequire } from "node:module";
5
5
  import { constants } from "node:fs";
6
6
  import { access, chmod, mkdir, readFile, rename, stat, writeFile } from "node:fs/promises";
@@ -1,2 +1,2 @@
1
- import { i as withHeadlessServerLock, n as runHeadlessServer, r as takeOverServerHome, t as HeadlessServerBusyError } from "./headless-host-CDh2qQbQ.js";
1
+ import { i as withHeadlessServerLock, n as runHeadlessServer, r as takeOverServerHome, t as HeadlessServerBusyError } from "./headless-host-mg9sQG6H.js";
2
2
  export { HeadlessServerBusyError, runHeadlessServer, takeOverServerHome, withHeadlessServerLock };
@@ -1,21 +1,21 @@
1
1
  import { d as git, r as defaultBranch } from "./src-BMPuxWv4.js";
2
2
  import { c as jsonString, m as isStringValue, n as isJsonObject, p as isObjectValue, u as parseJsonText } from "./src-X9phtB2j.js";
3
- import { a as sharedReviewId, c as verifyShareRepository, d as StreamLimitError, f as readBoundedStream, l as exportShare, m as shareIdSchema, n as ShareClient, p as parseShareLink, r as SharePreflightError, s as readShareRepository, t as ShareAuthError, u as validateShareSources } from "./client-tIkQ_x_x.js";
4
- import { a as inspectSnapshot, d as diffrExecutable, f as diffrMissingError, i as commandSchema, r as SCRATCHPAD_ID, u as invalidateStructuralComparisons } from "./local-data-Bhs4VnzC.js";
5
- import { A as reviewDiffrSummarizerInputSchema, D as ReviewOnboardingRunRequestSchema, a as parseReviewVerbRequest, g as REVIEW_HOST_HEADER, i as parseReviewDesktopVerbResult, j as decodeStructuralDiffEvent } from "./src-C_iUYopM.js";
3
+ import { a as sharedReviewId, c as verifyShareRepository, d as StreamLimitError, f as readBoundedStream, l as exportShare, m as shareIdSchema, n as ShareClient, p as parseShareLink, r as SharePreflightError, s as readShareRepository, t as ShareAuthError, u as validateShareSources } from "./client-CLqKkzOc.js";
4
+ import { a as inspectSnapshot, d as diffrExecutable, f as diffrMissingError, i as commandSchema, r as SCRATCHPAD_ID, u as invalidateStructuralComparisons } from "./local-data-DN-A5p0K.js";
5
+ import { A as reviewDiffrSummarizerInputSchema, D as ReviewOnboardingRunRequestSchema, a as parseReviewVerbRequest, g as REVIEW_HOST_HEADER, i as parseReviewDesktopVerbResult, j as decodeStructuralDiffEvent } from "./src-DZG1xAqW.js";
6
6
  import { B as TraceStorageDeniedError, D as listReviewTraceSessions, I as isTraceStorageConfigured, O as loadReviewAgentTrace, R as resolveTraceStorage, T as traceMachineEnabled, at as openUrlInBrowser, et as shellQuote, it as clearStoreAuth, ot as readStoreAuth, rt as DEFAULT_STORE_ORIGIN, st as runStoreLogin, z as selectTraceStorage } from "./review-home-paths-Lc02WFuX.js";
7
- import { $ as subtractIntervals, A as UNCATEGORIZED_LENS_ID, E as selectionReferences, G as resolveDiffSelection, H as anchorSelection, K as selectionKey, L as fileLineRangeSchema, P as lensSelections, Q as scopedCoverage, U as comparisonKey, W as formatAnchor, X as emptyCoverage, Y as coverageSources, et as unionIntervals, h as elements, u as anchorPins } from "./stored-document-migration-DVJJPBGd.js";
7
+ import { E as selectionReferences, G as comparisonKey, I as lensSelections, J as selectionKey, K as formatAnchor, M as UNCATEGORIZED_LENS_ID, Q as emptyCoverage, W as anchorSelection, Z as coverageSources, et as scopedCoverage, h as elements, j as stepSources, nt as unionIntervals, q as resolveDiffSelection, tt as subtractIntervals, u as anchorPins, z as fileLineRangeSchema } from "./stored-document-migration-DLBhkSfp.js";
8
8
  import { t as ReviewInputError } from "./input-error-OLgB_h31.js";
9
9
  import { A as withFileLock, I as writePrivateJsonAtomic, M as devReviewHome, h as TraceConfigurationError, i as isS3MockMode } from "./s3-config-DDWWMh5x.js";
10
10
  import { t as errorMessage } from "./error-message-OtiDonty.js";
11
11
  import { i as readReviewPackageVersion, n as readBuildCommit, t as findReviewPackageRoot } from "./package-paths-jMg6JxDP.js";
12
- import { i as readReviewServerDiscovery, n as desktopOwnsHome, o as reviewServerDiscoveryPath, p as desktopUsesHome, r as headlessServerLockPath, t as DESKTOP_TAKEOVER_SIGNAL } from "./server-discovery-IdX3Wc8g.js";
13
- import { c as askPicksSchema, d as selectionMarkdown, l as askQuestionSchema, n as askAgentIds, r as askChoiceKinds, s as askPermissionsSchema, t as applyAskChange, u as AgentSelectionSchema } from "./thread-state-CS-v-hc0.js";
14
- import { t as openReviewProfile } from "./profile-CbAW3qDm.js";
15
- import { a as REVIEW_APP_SESSION_ID_HEADER, c as sanitizeUiTelemetryEvent, i as BUNDLE_FRAME_PATTERN, l as cleanTelemetryText, r as reviewRequestOrigin, s as isReportableCleanedMessage } from "./request-origin-2d7P4ugC.js";
16
- import { a as defaultAskPermissions, c as launchAskAgent, i as askAgents, n as askAgentSettings, o as detectAskAgents, r as askAgentTakesMcp, t as fuzzyRank } from "./fuzzy-match-CufFp9IR.js";
17
- import { a as scratchpadAvailable, c as readQuerySchemas, i as renderInstructions, o as inspectQuerySchema, r as instructionsQuerySchema, s as queryAnchor, t as authoringTools } from "./authoring-tools-8UhtCkjL.js";
18
- import { a as headlessServerOwner, c as stopBackgroundServer } from "./background-server-D7maQkJV.js";
12
+ import { i as readReviewServerDiscovery, n as desktopOwnsHome, o as reviewServerDiscoveryPath, p as desktopUsesHome, r as headlessServerLockPath, t as DESKTOP_TAKEOVER_SIGNAL } from "./server-discovery-DVqrcIDT.js";
13
+ import { c as askPicksSchema, d as selectionMarkdown, l as askQuestionSchema, n as askAgentIds, r as askChoiceKinds, s as askPermissionsSchema, t as applyAskChange, u as AgentSelectionSchema } from "./thread-state-CaQ2lGdj.js";
14
+ import { t as openReviewProfile } from "./profile-Dm6irjbg.js";
15
+ import { a as REVIEW_APP_SESSION_ID_HEADER, c as sanitizeUiTelemetryEvent, i as BUNDLE_FRAME_PATTERN, l as cleanTelemetryText, r as reviewRequestOrigin, s as isReportableCleanedMessage } from "./request-origin-D1RNVSeK.js";
16
+ import { a as defaultAskPermissions, c as launchAskAgent, i as askAgents, n as askAgentSettings, o as detectAskAgents, r as askAgentTakesMcp, t as fuzzyRank } from "./fuzzy-match-DFPKdSLR.js";
17
+ import { a as scratchpadAvailable, c as readQuerySchemas, i as renderInstructions, o as inspectQuerySchema, r as instructionsQuerySchema, s as queryAnchor, t as authoringTools } from "./authoring-tools-CjFAGHBa.js";
18
+ import { a as headlessServerOwner, c as stopBackgroundServer } from "./background-server-ONy-xKJ1.js";
19
19
  import crypto$1, { createHash, randomBytes, randomUUID } from "node:crypto";
20
20
  import { z } from "zod";
21
21
  import { existsSync, mkdirSync, writeFileSync } from "node:fs";
@@ -2959,7 +2959,7 @@ function documentText(snapshot, targetId, full = false) {
2959
2959
  break;
2960
2960
  case "step":
2961
2961
  detail(`${element.from} → ${element.to}: ${element.label} (${element.style})`);
2962
- if (element.source) detail(element.source);
2962
+ for (const source of stepSources(element)) detail(source);
2963
2963
  if (detailed && element.explanation) detail(element.explanation);
2964
2964
  if (detailed && element.code) code(depth + 1, element.code);
2965
2965
  break;
@@ -1,12 +1,12 @@
1
1
  import { _ as parseGitRemote, a as diff, b as resolveRepoContextSync, c as diffFileSummariesWorkingTree, d as git, g as listTrackedFilesAtCommit, h as listCommitRange, i as detectLocalVcs, l as diffTrees, m as jjRevisionIsConflicted, n as createBlobBatchReader, p as gitCommonDir, s as diffFileSummariesTrees, t as LocalVcsToolsMissingError, u as diffWorkingTree, v as readFileAtCommit, x as resolveRevision, y as resolveRepoContext } from "./src-BMPuxWv4.js";
2
2
  import { c as jsonString, o as jsonObject, u as parseJsonText } from "./src-X9phtB2j.js";
3
- import { M as structuralChangeCounts, j as decodeStructuralDiffEvent, k as SCRATCHPAD_REVIEW_ID } from "./src-C_iUYopM.js";
3
+ import { M as structuralChangeCounts, j as decodeStructuralDiffEvent, k as SCRATCHPAD_REVIEW_ID } from "./src-DZG1xAqW.js";
4
4
  import { s as safeStorageSegment } from "./review-home-paths-Lc02WFuX.js";
5
- import { $ as subtractIntervals, C as resourceReference, D as sourceReferences, F as SourceRangeError, I as checkSourcePath, J as coverageSchema, L as fileLineRangeSchema, M as lensEditSchema, O as summarizeEdit, P as lensSelections, R as requireVisibleSource, T as reviewTargetSchema, X as emptyCoverage, Z as intersectIntervals, a as defineSoftwareMap, b as pinnedRepositoryIds, c as traceSchema, d as applyEdit, et as unionIntervals, f as assignFreshIds, g as explicitPins, h as elements, i as SoftwareModelValidationError, j as applyLensEdit, k as checkReferences, l as textIncludesQuote, m as editSchema, n as migrateStoredDocument, p as documentSchema, q as sourceAnchors, r as migrateStoredLenses, t as liftFileLenses, tt as updateCoverage, u as anchorPins, v as isUnit, w as resourceReferences, x as pinsSchema, z as sliceSourceRange } from "./stored-document-migration-DVJJPBGd.js";
5
+ import { $ as intersectIntervals, A as documentAdvice, B as requireVisibleSource, C as resourceReference, D as sourceReferences, I as lensSelections, L as SourceRangeError, N as applyLensEdit, O as summarizeEdit, P as lensEditSchema, Q as emptyCoverage, R as checkSourcePath, T as reviewTargetSchema, V as sliceSourceRange, X as coverageSchema, Y as sourceAnchors, a as defineSoftwareMap, b as pinnedRepositoryIds, c as traceSchema, d as applyEdit, f as assignFreshIds, g as explicitPins, h as elements, i as SoftwareModelValidationError, k as checkReferences, l as textIncludesQuote, m as editSchema, n as migrateStoredDocument, nt as unionIntervals, p as documentSchema, r as migrateStoredLenses, rt as updateCoverage, t as liftFileLenses, tt as subtractIntervals, u as anchorPins, v as isUnit, w as resourceReferences, x as pinsSchema, z as fileLineRangeSchema } from "./stored-document-migration-DLBhkSfp.js";
6
6
  import { t as ReviewInputError } from "./input-error-OLgB_h31.js";
7
7
  import { A as withFileLock, I as writePrivateJsonAtomic, O as processIsAlive } from "./s3-config-DDWWMh5x.js";
8
8
  import { t as errorMessage } from "./error-message-OtiDonty.js";
9
- import { a as askHistoryEntrySchema, i as askEntrySchema, m as isMissingFileError, o as askOfferSchema, s as askPermissionsSchema } from "./thread-state-CS-v-hc0.js";
9
+ import { a as askHistoryEntrySchema, i as askEntrySchema, m as isMissingFileError, o as askOfferSchema, s as askPermissionsSchema } from "./thread-state-CaQ2lGdj.js";
10
10
  import { createRequire } from "node:module";
11
11
  import crypto, { createHash, randomUUID } from "node:crypto";
12
12
  import { z } from "zod";
@@ -2703,7 +2703,8 @@ var ReviewStore = class {
2703
2703
  if (snapshot.target?.kind === "worktree" && op.type !== "restore" && !resolvedTarget) snapshot = await this.projectLiveSource(snapshot);
2704
2704
  checkReferences(snapshot.document);
2705
2705
  if (snapshot.pins && (!previous || JSON.stringify(previous.pins) !== JSON.stringify(snapshot.pins))) await this.providers.validatePins(snapshot.pins);
2706
- const warnings = await this.validateExternal(snapshot, previous, op.type === "set_target");
2706
+ const advised = new Set(documentAdvice(previous?.document ?? []));
2707
+ const warnings = [...documentAdvice(snapshot.document).filter((advice) => !advised.has(advice)), ...await this.validateExternal(snapshot, previous, op.type === "set_target")];
2707
2708
  snapshot.version = previous ? previous.version + 1 : 0;
2708
2709
  snapshot.createdAt = (/* @__PURE__ */ new Date()).toISOString();
2709
2710
  const result = {
@@ -1,7 +1,7 @@
1
- import { a as toolResultText, s as ReviewApiError } from "./agent-client-D6xrEdTj.js";
1
+ import { a as toolResultText, s as ReviewApiError } from "./agent-client-DjXQy6CL.js";
2
2
  import { t as ReviewInputError } from "./input-error-OLgB_h31.js";
3
- import { n as REVIEW_STATUS_TOOL, t as authoringTools } from "./authoring-tools-8UhtCkjL.js";
4
- import { i as publicTool, n as RECOVERY, r as callPublicTool, t as toolFailure } from "./tool-failure-A9dVocGs.js";
3
+ import { n as REVIEW_STATUS_TOOL, t as authoringTools } from "./authoring-tools-CjFAGHBa.js";
4
+ import { i as publicTool, n as RECOVERY, r as callPublicTool, t as toolFailure } from "./tool-failure-CVJrfi5s.js";
5
5
  import { Server } from "@modelcontextprotocol/sdk/server/index.js";
6
6
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
7
7
  import { CallToolRequestSchema, ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js";
@@ -0,0 +1,2 @@
1
+ import { t as openReviewProfile } from "./profile-Dm6irjbg.js";
2
+ export { openReviewProfile };
@@ -1,4 +1,4 @@
1
- import { o as initializeReviewStoreSchema, t as openLocalReviewStore } from "./local-data-Bhs4VnzC.js";
1
+ import { o as initializeReviewStoreSchema, t as openLocalReviewStore } from "./local-data-DN-A5p0K.js";
2
2
  import "./review-home-paths-Lc02WFuX.js";
3
3
  import { A as withFileLock } from "./s3-config-DDWWMh5x.js";
4
4
  import { access, mkdir } from "node:fs/promises";
@@ -1,7 +1,7 @@
1
1
  import { i as readReviewPackageVersion } from "./package-paths-jMg6JxDP.js";
2
- import { a as readReviewServerHealth, l as serverNotReady } from "./server-discovery-IdX3Wc8g.js";
3
- import { c as stopBackgroundServer, o as recordedBackgroundServer, r as ensureBackgroundServer } from "./background-server-D7maQkJV.js";
4
- import { t as ensureRemoteLanguageServer } from "./remote-language-server-BIP6hw3K.js";
2
+ import { a as readReviewServerHealth, l as serverNotReady } from "./server-discovery-DVqrcIDT.js";
3
+ import { c as stopBackgroundServer, o as recordedBackgroundServer, r as ensureBackgroundServer } from "./background-server-ONy-xKJ1.js";
4
+ import { t as ensureRemoteLanguageServer } from "./remote-language-server-CGsCaqro.js";
5
5
  import path from "node:path";
6
6
  import { spawn } from "node:child_process";
7
7
  import { gt, valid } from "semver";
@@ -1,7 +1,7 @@
1
1
  import "./review-home-paths-Lc02WFuX.js";
2
2
  import { A as withFileLock, k as processStartIdentity } from "./s3-config-DDWWMh5x.js";
3
3
  import { t as findReviewPackageRoot } from "./package-paths-jMg6JxDP.js";
4
- import { n as cliSpawn, s as spawnDetached } from "./background-server-D7maQkJV.js";
4
+ import { n as cliSpawn, s as spawnDetached } from "./background-server-ONy-xKJ1.js";
5
5
  import { r as remoteServerPaths, t as ensureRemoteExtensions } from "./remote-extensions-BCAbQZTv.js";
6
6
  import { randomBytes } from "node:crypto";
7
7
  import { z } from "zod";
@@ -1,9 +1,9 @@
1
- import { b as REVIEW_REMOTE_INSTALL_LOCK, x as REVIEW_REMOTE_WRAPPER_MARK } from "./src-C_iUYopM.js";
1
+ import { b as REVIEW_REMOTE_INSTALL_LOCK, x as REVIEW_REMOTE_WRAPPER_MARK } from "./src-DZG1xAqW.js";
2
2
  import "./review-home-paths-Lc02WFuX.js";
3
3
  import { j as DEV_REVIEW_HOME_ENV } from "./s3-config-DDWWMh5x.js";
4
- import { c as stopBackgroundServer, o as recordedBackgroundServer } from "./background-server-D7maQkJV.js";
4
+ import { c as stopBackgroundServer, o as recordedBackgroundServer } from "./background-server-ONy-xKJ1.js";
5
5
  import { i as whiteboardRemoteHome } from "./remote-extensions-BCAbQZTv.js";
6
- import { n as remoteLanguageServerGroups, r as stopProcessGroup } from "./remote-language-server-BIP6hw3K.js";
6
+ import { n as remoteLanguageServerGroups, r as stopProcessGroup } from "./remote-language-server-CGsCaqro.js";
7
7
  import { existsSync } from "node:fs";
8
8
  import { lstat, mkdir, readFile, readdir, rm, stat } from "node:fs/promises";
9
9
  import path from "node:path";
@@ -1,5 +1,5 @@
1
1
  import { a as jsonNumber, c as jsonString, i as jsonBoolean, n as isJsonObject } from "./src-X9phtB2j.js";
2
- import { S as REVIEW_THEME_CHOICES, h as REVIEW_DIFF_LAYOUT_SCOPES, m as REVIEW_DIFF_LAYOUTS } from "./src-C_iUYopM.js";
2
+ import { S as REVIEW_THEME_CHOICES, h as REVIEW_DIFF_LAYOUT_SCOPES, m as REVIEW_DIFF_LAYOUTS } from "./src-DZG1xAqW.js";
3
3
  import { z } from "zod";
4
4
  //#region src/telemetry-clean-text.ts
5
5
  const USER_DATA_REGEXES = [
@@ -1,5 +1,5 @@
1
- import { o as parseReviewVerbResponse } from "./src-C_iUYopM.js";
2
- import { b as selectReviewInstance, m as healthyReviewInstance, v as reviewInstanceStartHint, y as reviewInstanceUnavailable } from "./server-discovery-IdX3Wc8g.js";
1
+ import { o as parseReviewVerbResponse } from "./src-DZG1xAqW.js";
2
+ import { b as selectReviewInstance, m as healthyReviewInstance, v as reviewInstanceStartHint, y as reviewInstanceUnavailable } from "./server-discovery-DVqrcIDT.js";
3
3
  import { closeSync, existsSync, fstatSync, mkdtempSync, openSync, readSync } from "node:fs";
4
4
  import path from "node:path";
5
5
  import { homedir, tmpdir } from "node:os";
@@ -1,9 +1,9 @@
1
1
  import { c as jsonString, l as jsonValueSchema, o as jsonObject, u as parseJsonText } from "./src-X9phtB2j.js";
2
- import "./src-C_iUYopM.js";
2
+ import "./src-DZG1xAqW.js";
3
3
  import "./review-home-paths-Lc02WFuX.js";
4
4
  import { A as withFileLock, M as devReviewHome, O as processIsAlive, P as writeFileAtomic } from "./s3-config-DDWWMh5x.js";
5
5
  import { i as readReviewPackageVersion, t as findReviewPackageRoot } from "./package-paths-jMg6JxDP.js";
6
- import "./request-origin-2d7P4ugC.js";
6
+ import "./request-origin-D1RNVSeK.js";
7
7
  import { createHmac, randomUUID } from "node:crypto";
8
8
  import { z } from "zod";
9
9
  import { existsSync, readFileSync, rmSync } from "node:fs";
@@ -1,21 +1,21 @@
1
1
  #!/usr/bin/env node
2
2
  import { S as splitGitPatchFiles, a as diff, o as diffFileSummaries } from "../src-BMPuxWv4.js";
3
3
  import { c as jsonString, i as jsonBoolean, n as isJsonObject, p as isObjectValue, s as jsonProperty, u as parseJsonText } from "../src-X9phtB2j.js";
4
- import { d as StreamLimitError, f as readBoundedStream, i as SharedReviewStore } from "../client-tIkQ_x_x.js";
5
- import { A as reviewDiffrSummarizerInputSchema, E as ReviewGatewayHostSchema, T as ReviewConnectRequestSchema, c as ReviewBugReportMetaV2Schema, d as REVIEW_CLIENT_HEADER, f as REVIEW_CLIENT_REMOTE, g as REVIEW_HOST_HEADER, l as ReviewBugReportRequestSchema, r as parseReviewDesktopVerbFrame, s as parseZod, t as parseReviewCliInstallApplyRequest, u as parseReviewBugReportResponse } from "../src-C_iUYopM.js";
4
+ import { d as StreamLimitError, f as readBoundedStream, i as SharedReviewStore } from "../client-CLqKkzOc.js";
5
+ import { A as reviewDiffrSummarizerInputSchema, E as ReviewGatewayHostSchema, T as ReviewConnectRequestSchema, c as ReviewBugReportMetaV2Schema, d as REVIEW_CLIENT_HEADER, f as REVIEW_CLIENT_REMOTE, g as REVIEW_HOST_HEADER, l as ReviewBugReportRequestSchema, r as parseReviewDesktopVerbFrame, s as parseZod, t as parseReviewCliInstallApplyRequest, u as parseReviewBugReportResponse } from "../src-DZG1xAqW.js";
6
6
  import { i as reviewInstanceDiscoveryPath, o as reviewLegacyDiscoveryPath } from "../review-home-paths-Lc02WFuX.js";
7
- import { w as resourceReferences } from "../stored-document-migration-DVJJPBGd.js";
7
+ import { w as resourceReferences } from "../stored-document-migration-DLBhkSfp.js";
8
8
  import { t as ReviewInputError } from "../input-error-OLgB_h31.js";
9
9
  import { I as writePrivateJsonAtomic, M as devReviewHome } from "../s3-config-DDWWMh5x.js";
10
10
  import { t as errorMessage } from "../error-message-OtiDonty.js";
11
11
  import { i as readReviewPackageVersion, t as findReviewPackageRoot } from "../package-paths-jMg6JxDP.js";
12
- import { _ as reviewInstanceIdentity, u as REVIEW_INSTANCE_ENV } from "../server-discovery-IdX3Wc8g.js";
13
- import { t as openReviewProfile } from "../profile-CbAW3qDm.js";
14
- import { r as reviewTelemetryChannel, t as ReviewTelemetry } from "../review-telemetry-kTLRwBj_.js";
15
- import { o as REVIEW_TELEMETRY_TABS } from "../request-origin-2d7P4ugC.js";
16
- import { c as removeLegacyReviewSkills, d as skipCliInstall, h as detectConnectAgents, i as finishCliInstallUpdate, l as resetCliInstall, m as connectAgent, n as applyCliInstall, o as pathShimPath, r as declineCliInstall, s as removeCliInstall, t as aliasInstallationToAccount, u as resolveCliInstallStatus } from "../account-alias-pAq80fex.js";
17
- import { C as saveDiffrSummarizer, E as cliAskTools, S as readDiffrConfig, T as testDiffrSummarizer, _ as DEFAULT_MAX_REQUEST_BYTES, a as drainServerCrashReport, b as GlobalReviewDesktopVerbRelay, c as answerErrorsAsJson, d as legacyModeSchema, f as captureSanitizedUiTelemetry, g as readBoundedRequestJson, h as createNodeRequestListener, l as createWhiteboardCore, m as recordClientError, o as installProcessErrorTelemetry, p as clientErrorsForSession, r as takeOverServerHome, s as serverCrashReportPath, u as serverJson, v as HttpJsonError, w as setDiffrConfigValue, x as migrateDiffrConfig, y as ReviewServerError } from "../headless-host-CDh2qQbQ.js";
18
- import { c as readQuerySchemas } from "../authoring-tools-8UhtCkjL.js";
12
+ import { _ as reviewInstanceIdentity, u as REVIEW_INSTANCE_ENV } from "../server-discovery-DVqrcIDT.js";
13
+ import { t as openReviewProfile } from "../profile-Dm6irjbg.js";
14
+ import { r as reviewTelemetryChannel, t as ReviewTelemetry } from "../review-telemetry-DnQNwgR1.js";
15
+ import { o as REVIEW_TELEMETRY_TABS } from "../request-origin-D1RNVSeK.js";
16
+ import { c as removeLegacyReviewSkills, d as skipCliInstall, h as detectConnectAgents, i as finishCliInstallUpdate, l as resetCliInstall, m as connectAgent, n as applyCliInstall, o as pathShimPath, r as declineCliInstall, s as removeCliInstall, t as aliasInstallationToAccount, u as resolveCliInstallStatus } from "../account-alias-CwNaMOwU.js";
17
+ import { C as saveDiffrSummarizer, E as cliAskTools, S as readDiffrConfig, T as testDiffrSummarizer, _ as DEFAULT_MAX_REQUEST_BYTES, a as drainServerCrashReport, b as GlobalReviewDesktopVerbRelay, c as answerErrorsAsJson, d as legacyModeSchema, f as captureSanitizedUiTelemetry, g as readBoundedRequestJson, h as createNodeRequestListener, l as createWhiteboardCore, m as recordClientError, o as installProcessErrorTelemetry, p as clientErrorsForSession, r as takeOverServerHome, s as serverCrashReportPath, u as serverJson, v as HttpJsonError, w as setDiffrConfigValue, x as migrateDiffrConfig, y as ReviewServerError } from "../headless-host-mg9sQG6H.js";
18
+ import { c as readQuerySchemas } from "../authoring-tools-CjFAGHBa.js";
19
19
  import crypto, { createHash, randomBytes } from "node:crypto";
20
20
  import { z } from "zod";
21
21
  import fs, { existsSync, readFileSync } from "node:fs";
@@ -1111,6 +1111,7 @@ function createGatewayHosts(input) {
1111
1111
  previous.delete(given.alias);
1112
1112
  current.languageFeatures = given.languageFeatures;
1113
1113
  current.languageFeaturesDetail = given.languageFeaturesDetail;
1114
+ current.languageFeaturesPending = given.languageFeaturesPending;
1114
1115
  current.languageGroups = given.languageGroups;
1115
1116
  next.push(current);
1116
1117
  } else {
@@ -1145,9 +1146,10 @@ function createGatewayHosts(input) {
1145
1146
  }
1146
1147
  };
1147
1148
  }
1148
- const languageOf = ({ languageFeatures, languageFeaturesDetail, languageGroups }) => ({
1149
+ const languageOf = ({ languageFeatures, languageFeaturesDetail, languageFeaturesPending, languageGroups }) => ({
1149
1150
  ...languageFeatures !== void 0 && { languageFeatures },
1150
1151
  ...languageFeaturesDetail !== void 0 && { languageFeaturesDetail },
1152
+ ...languageFeaturesPending && { languageFeaturesPending },
1151
1153
  ...languageGroups !== void 0 && { languageGroups }
1152
1154
  });
1153
1155
  //#endregion
@@ -1171,7 +1173,8 @@ function decorate(entry, state, hostState) {
1171
1173
  hostState,
1172
1174
  available: {
1173
1175
  sourceWindows: features,
1174
- ...reason && { reason }
1176
+ ...reason && { reason },
1177
+ ...online && state.languageFeaturesPending && { pending: true }
1175
1178
  }
1176
1179
  };
1177
1180
  }
@@ -1,5 +1,5 @@
1
1
  import { a as jsonNumber, n as isJsonObject, s as jsonProperty, u as parseJsonText } from "./src-X9phtB2j.js";
2
- import { n as parseReviewDesktopDiscovery } from "./src-C_iUYopM.js";
2
+ import { n as parseReviewDesktopDiscovery } from "./src-DZG1xAqW.js";
3
3
  import { a as reviewInstancesDir, o as reviewLegacyDiscoveryPath, r as reviewDevInstanceKey, t as reviewDefaultInstancePath } from "./review-home-paths-Lc02WFuX.js";
4
4
  import { M as devReviewHome, O as processIsAlive, j as DEV_REVIEW_HOME_ENV } from "./s3-config-DDWWMh5x.js";
5
5
  import { z } from "zod";
@@ -274,6 +274,7 @@ interface ReviewApiSummary {
274
274
  sourceWindows: boolean;
275
275
  /** Why they are off: the host's sentence, or its language features detail. */
276
276
  reason?: string;
277
+ pending?: true;
277
278
  };
278
279
  }
279
280
  interface ReviewSourceEntry {
@@ -319,6 +320,7 @@ declare const ReviewGatewayHostSchema: z.ZodObject<{
319
320
  }, z.core.$strict>>;
320
321
  languageFeatures: z.ZodOptional<z.ZodBoolean>;
321
322
  languageFeaturesDetail: z.ZodOptional<z.ZodString>;
323
+ languageFeaturesPending: z.ZodOptional<z.ZodLiteral<true>>;
322
324
  languageGroups: z.ZodOptional<z.ZodArray<z.ZodObject<{
323
325
  group: z.ZodString;
324
326
  installed: z.ZodBoolean;
@@ -334,6 +336,7 @@ interface ReviewGatewayHostState {
334
336
  installCommand?: string;
335
337
  languageFeatures?: boolean;
336
338
  languageFeaturesDetail?: string;
339
+ languageFeaturesPending?: true;
337
340
  languageGroups?: ReviewGatewayHost["languageGroups"];
338
341
  declined?: true;
339
342
  }
@@ -452,7 +455,7 @@ declare const stepSchema: z.ZodObject<{
452
455
  return: "return";
453
456
  async: "async";
454
457
  }>>;
455
- source: any;
458
+ source: z.ZodOptional<z.ZodUnion<readonly [any, z.ZodArray<any>]>>;
456
459
  pins: any;
457
460
  explanation: z.ZodOptional<z.ZodString>;
458
461
  code: z.ZodOptional<z.ZodObject<{
@@ -518,7 +521,7 @@ declare const leafSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
518
521
  return: "return";
519
522
  async: "async";
520
523
  }>>;
521
- source: any;
524
+ source: z.ZodOptional<z.ZodUnion<readonly [any, z.ZodArray<any>]>>;
522
525
  pins: any;
523
526
  explanation: z.ZodOptional<z.ZodString>;
524
527
  code: z.ZodOptional<z.ZodObject<{
@@ -759,7 +762,7 @@ declare const editSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
759
762
  return: "return";
760
763
  async: "async";
761
764
  }>>;
762
- source: any;
765
+ source: z.ZodOptional<z.ZodUnion<readonly [any, z.ZodArray<any>]>>;
763
766
  pins: any;
764
767
  explanation: z.ZodOptional<z.ZodString>;
765
768
  code: z.ZodOptional<z.ZodObject<{
@@ -944,7 +947,7 @@ declare const editSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
944
947
  return: "return";
945
948
  async: "async";
946
949
  }>>;
947
- source: any;
950
+ source: z.ZodOptional<z.ZodUnion<readonly [any, z.ZodArray<any>]>>;
948
951
  pins: any;
949
952
  explanation: z.ZodOptional<z.ZodString>;
950
953
  code: z.ZodOptional<z.ZodObject<{
@@ -1,2 +1,2 @@
1
- import { a as sharedReviewId, c as verifyShareRepository, i as SharedReviewStore, l as exportShare, n as ShareClient, o as validateShareBundle } from "../client-tIkQ_x_x.js";
1
+ import { a as sharedReviewId, c as verifyShareRepository, i as SharedReviewStore, l as exportShare, n as ShareClient, o as validateShareBundle } from "../client-CLqKkzOc.js";
2
2
  export { ShareClient, SharedReviewStore, exportShare, sharedReviewId, validateShareBundle, verifyShareRepository };
@@ -321,6 +321,7 @@ const ReviewGatewayHostSchema = z.strictObject({
321
321
  ]) }).optional(),
322
322
  languageFeatures: z.boolean().optional(),
323
323
  languageFeaturesDetail: stringAllowEmpty.optional(),
324
+ languageFeaturesPending: z.literal(true).optional(),
324
325
  languageGroups: z.array(z.strictObject({
325
326
  group: requiredString$1,
326
327
  installed: z.boolean(),
@@ -1,5 +1,5 @@
1
1
  import { f as isNumberValue, l as jsonValueSchema, m as isStringValue, n as isJsonObject } from "./src-X9phtB2j.js";
2
- import "./src-C_iUYopM.js";
2
+ import "./src-DZG1xAqW.js";
3
3
  import { t as ReviewInputError } from "./input-error-OLgB_h31.js";
4
4
  import { z } from "zod";
5
5
  import { fromMarkdown } from "mdast-util-from-markdown";
@@ -1393,6 +1393,8 @@ const section = {
1393
1393
  };
1394
1394
  //#endregion
1395
1395
  //#region src/review-api/blocks/sequence.ts
1396
+ /** A call that needs more lines than this is rare; a function body is not. */
1397
+ const callSiteLines = 12;
1396
1398
  const stepSchema = z.strictObject({
1397
1399
  ...identity,
1398
1400
  type: z.literal("step").default("step"),
@@ -1404,7 +1406,9 @@ const stepSchema = z.strictObject({
1404
1406
  "return",
1405
1407
  "async"
1406
1408
  ]).default("call"),
1407
- source: anchorSchema.optional(),
1409
+ /** The call site this arrow stands for, then any ranges directly about
1410
+ * that call, each shown as its own card. */
1411
+ source: z.union([anchorSchema, z.array(anchorSchema).min(1).max(4)]).optional(),
1408
1412
  pins: elementPins,
1409
1413
  explanation: label$1.optional(),
1410
1414
  code: z.strictObject(codeFields).optional()
@@ -1413,6 +1417,10 @@ const stepSchema = z.strictObject({
1413
1417
  s.explanation,
1414
1418
  s.code
1415
1419
  ].filter((v) => v !== void 0).length === 1, "A step needs exactly one of source, explanation, or code.");
1420
+ /** A step's ranges in order; a single anchor is a list of one. */
1421
+ function stepSources(step) {
1422
+ return step.source === void 0 ? [] : [step.source].flat();
1423
+ }
1416
1424
  const sequence = {
1417
1425
  type: "sequence",
1418
1426
  schema: defineBlock("sequence", {
@@ -1422,12 +1430,36 @@ const sequence = {
1422
1430
  pins: elementPins
1423
1431
  }),
1424
1432
  check(block) {
1433
+ const pins = (step) => JSON.stringify(step.pins ?? block.pins);
1425
1434
  for (const step of block.steps) {
1426
1435
  requireKey(block.actors, step.from);
1427
1436
  requireKey(block.actors, step.to);
1437
+ const own = stepSources(step);
1438
+ for (const [at, range] of own.entries()) {
1439
+ if (own.some((other, index) => index !== at && covers(range, other))) throw new ReviewInputError(`Range ${range} repeats another range of its step.`);
1440
+ for (const [index, other] of block.steps.entries()) if (at > 0 && other !== step && pins(other) === pins(step) && stepSources(other).some((theirs) => covers(range, theirs))) throw new ReviewInputError(`Range ${range} is step ${index + 1}'s code; link the step instead.`);
1441
+ }
1428
1442
  }
1443
+ },
1444
+ advise(block) {
1445
+ return block.steps.flatMap((step, index) => stepSources(step).flatMap((range) => {
1446
+ const lines = oneSideLines(parseAnchor(range));
1447
+ return lines && lines > callSiteLines ? [`Step ${index + 1} of "${block.title}" pins ${lines} lines at ${range}. A step's source is its call site; pin the call, not the function body.`] : [];
1448
+ }));
1429
1449
  }
1430
1450
  };
1451
+ /** Lines in a one-sided selection; a selection across sides has no count. */
1452
+ function oneSideLines({ start, end }) {
1453
+ return start.side === end.side ? end.line - start.line + 1 : void 0;
1454
+ }
1455
+ /** Whether `outer` equals or contains `inner`, in the same file. Across
1456
+ * sides, only the same anchor counts. */
1457
+ function covers(outer, inner) {
1458
+ if (outer === inner) return true;
1459
+ const a = parseAnchor(outer);
1460
+ const b = parseAnchor(inner);
1461
+ return a.file === b.file && oneSideLines(a) !== void 0 && oneSideLines(b) !== void 0 && a.start.side === b.start.side && a.start.line <= b.start.line && a.end.line >= b.end.line;
1462
+ }
1431
1463
  //#endregion
1432
1464
  //#region src/review-api/blocks/software_map.ts
1433
1465
  const software_map = {
@@ -1500,6 +1532,14 @@ function checkReferences(document) {
1500
1532
  };
1501
1533
  document.forEach(visit);
1502
1534
  }
1535
+ function adviseBlock(type, block) {
1536
+ return blocks[type].advise?.(block) ?? [];
1537
+ }
1538
+ /** Every block's warnings about allowed content that is usually a mistake. */
1539
+ function documentAdvice(document) {
1540
+ const visit = (block) => [...adviseBlock(block.type, block), ..."children" in block ? block.children.flatMap(visit) : []];
1541
+ return document.flatMap(visit);
1542
+ }
1503
1543
  //#endregion
1504
1544
  //#region src/review-api/document.ts
1505
1545
  const text = z.string();
@@ -1649,12 +1689,12 @@ function documentReferences(document, { tolerant = false, markdown = true } = {}
1649
1689
  label: element.caption,
1650
1690
  peek: true
1651
1691
  }));
1652
- if (element.type === "sequence") return element.steps.flatMap((step) => step.source ? select(step.source, step.pins ?? element.pins).map((source) => ({
1653
- id: step.id,
1692
+ if (element.type === "sequence") return element.steps.flatMap((step) => stepSources(step).flatMap((anchor, index) => select(anchor, step.pins ?? element.pins).map((source) => ({
1693
+ id: index ? `${step.id}:source:${index}` : step.id,
1654
1694
  source,
1655
1695
  label: step.label,
1656
1696
  peek: true
1657
- })) : []);
1697
+ }))));
1658
1698
  if (element.type === "flow_diagram") return element.nodes.flatMap((node) => node.attachments.flatMap((attachment, index) => attachment.sources.flatMap((anchor, sourceIndex) => select(anchor, attachment.pins ?? element.pins).map((source) => ({
1659
1699
  id: `${element.id}:${node.key}:${index}:${sourceIndex}`,
1660
1700
  source,
@@ -2646,4 +2686,4 @@ function liftFileLenses(document) {
2646
2686
  };
2647
2687
  }
2648
2688
  //#endregion
2649
- export { subtractIntervals as $, UNCATEGORIZED_LENS_ID as A, markdownNodes as B, resourceReference as C, sourceReferences as D, selectionReferences as E, SourceRangeError as F, resolveDiffSelection as G, anchorSelection as H, checkSourcePath as I, coverageSchema as J, selectionKey as K, fileLineRangeSchema as L, lensEditSchema as M, lensSchema as N, summarizeEdit as O, lensSelections as P, scopedCoverage as Q, requireVisibleSource as R, publishedEditSchema as S, reviewTargetSchema as T, comparisonKey as U, parseMarkdown as V, formatAnchor as W, emptyCoverage as X, coverageSources as Y, intersectIntervals as Z, hasCodeReferences as _, defineSoftwareMap as a, pinnedRepositoryIds as b, traceSchema as c, applyEdit as d, unionIntervals as et, assignFreshIds as f, explicitPins as g, elements as h, SoftwareModelValidationError as i, applyLensEdit as j, checkReferences as k, textIncludesQuote as l, editSchema as m, migrateStoredDocument as n, normalizedSoftwareElementSchema as o, documentSchema as p, sourceAnchors as q, migrateStoredLenses as r, normalizedSoftwareRelationshipSchema as s, liftFileLenses as t, updateCoverage as tt, anchorPins as u, isUnit as v, resourceReferences as w, pinsSchema as x, pathTargetSchema as y, sliceSourceRange as z };
2689
+ export { intersectIntervals as $, documentAdvice as A, requireVisibleSource as B, resourceReference as C, sourceReferences as D, selectionReferences as E, lensSchema as F, comparisonKey as G, markdownNodes as H, lensSelections as I, selectionKey as J, formatAnchor as K, SourceRangeError as L, UNCATEGORIZED_LENS_ID as M, applyLensEdit as N, summarizeEdit as O, lensEditSchema as P, emptyCoverage as Q, checkSourcePath as R, publishedEditSchema as S, reviewTargetSchema as T, parseMarkdown as U, sliceSourceRange as V, anchorSelection as W, coverageSchema as X, sourceAnchors as Y, coverageSources as Z, hasCodeReferences as _, defineSoftwareMap as a, pinnedRepositoryIds as b, traceSchema as c, applyEdit as d, scopedCoverage as et, assignFreshIds as f, explicitPins as g, elements as h, SoftwareModelValidationError as i, stepSources as j, checkReferences as k, textIncludesQuote as l, editSchema as m, migrateStoredDocument as n, unionIntervals as nt, normalizedSoftwareElementSchema as o, documentSchema as p, resolveDiffSelection as q, migrateStoredLenses as r, updateCoverage as rt, normalizedSoftwareRelationshipSchema as s, liftFileLenses as t, subtractIntervals as tt, anchorPins as u, isUnit as v, resourceReferences as w, pinsSchema as x, pathTargetSchema as y, fileLineRangeSchema as z };
@@ -1,4 +1,4 @@
1
- import { C as ReviewApiSelectionSourceSchema, O as ReviewSelectedDiffSchema } from "./src-C_iUYopM.js";
1
+ import { C as ReviewApiSelectionSourceSchema, O as ReviewSelectedDiffSchema } from "./src-DZG1xAqW.js";
2
2
  import { z } from "zod";
3
3
  import { stat } from "node:fs/promises";
4
4
  //#region src/fs-utils.ts
@@ -1,5 +1,5 @@
1
1
  import { m as isStringValue, n as isJsonObject } from "./src-X9phtB2j.js";
2
- import { n as callAuthoringTool, s as ReviewApiError, t as ToolText } from "./agent-client-D6xrEdTj.js";
2
+ import { n as callAuthoringTool, s as ReviewApiError, t as ToolText } from "./agent-client-DjXQy6CL.js";
3
3
  import { t as ReviewInputError } from "./input-error-OLgB_h31.js";
4
4
  //#region src/review-api/public-tools.ts
5
5
  function publicDescription(text) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dev.fast/whiteboard",
3
- "version": "0.2.5-preview.20261015.120",
3
+ "version": "0.2.5-preview.20261016.121",
4
4
  "private": false,
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -74,10 +74,10 @@
74
74
  "tsx": "4.21.0",
75
75
  "typescript": "6.0.3",
76
76
  "vitest": "4.1.11",
77
- "@dev.fast/review-protocol": "0.0.0",
78
77
  "@dev.fast/json": "0.0.0",
79
- "@dev.fast/review-share-protocol": "0.1.2",
78
+ "@dev.fast/review-protocol": "0.0.0",
80
79
  "@dev.fast/trace-core": "0.0.0",
80
+ "@dev.fast/review-share-protocol": "0.1.2",
81
81
  "@dev.fast/trace-protocol": "0.5.0"
82
82
  },
83
83
  "engines": {
@@ -88,7 +88,7 @@
88
88
  "repo": "devdotfast/review",
89
89
  "docs": "packages/review/instructions/authoring.md"
90
90
  },
91
- "gitHead": "99b613b6ce358d31fd4cc2b59822a939f7f113cf",
91
+ "gitHead": "3c1b0477310222ab5a70d7c52b66f65dc62ffc5d",
92
92
  "scripts": {
93
93
  "prebuild": "pnpm run build:workspace-deps",
94
94
  "build": "node --max-old-space-size=8192 node_modules/tsdown/dist/run.mjs",
@@ -17,12 +17,14 @@ export const elementPins = sourcePinsSchema
17
17
 
18
18
  /**
19
19
  * One block kind: the strict schema the store parses with, and the rules a
20
- * field schema cannot express. `check` throws ReviewInputError.
20
+ * field schema cannot express. `check` throws ReviewInputError; `advise`
21
+ * returns warnings for content that is allowed but usually a mistake.
21
22
  */
22
23
  export interface BlockDefinition<Content extends { type: string }> {
23
24
  type: Content["type"];
24
25
  schema: z.ZodType<Content>;
25
26
  check?(block: Content): void;
27
+ advise?(block: Content): string[];
26
28
  }
27
29
 
28
30
  /** The strict schema for a block's own fields; id and type are added here. */
@@ -86,3 +86,22 @@ export function checkReferences(document: Block[]): void {
86
86
 
87
87
  document.forEach(visit);
88
88
  }
89
+
90
+ function adviseBlock<K extends BlockType>(
91
+ type: K,
92
+ block: Extract<Block, { type: K }>,
93
+ ): string[] {
94
+ const definition: Definitions[K] = blocks[type];
95
+
96
+ return definition.advise?.(block) ?? [];
97
+ }
98
+
99
+ /** Every block's warnings about allowed content that is usually a mistake. */
100
+ export function documentAdvice(document: Block[]): string[] {
101
+ const visit = (block: Block): string[] => [
102
+ ...adviseBlock(block.type, block),
103
+ ...("children" in block ? block.children.flatMap(visit) : []),
104
+ ];
105
+
106
+ return document.flatMap(visit);
107
+ }
@@ -1,4 +1,10 @@
1
- import { anchorSchema } from "@review/lens-selection.js";
1
+ import {
2
+ type Anchor,
3
+ type DiffSelection,
4
+ anchorSchema,
5
+ parseAnchor,
6
+ } from "@review/lens-selection.js";
7
+ import { ReviewInputError } from "@review/review-api/input-error.js";
2
8
  import { z } from "zod";
3
9
 
4
10
  import { codeFields } from "./code.js";
@@ -12,6 +18,9 @@ import {
12
18
  text,
13
19
  } from "./definition.js";
14
20
 
21
+ /** A call that needs more lines than this is rare; a function body is not. */
22
+ const callSiteLines = 12;
23
+
15
24
  export const stepSchema = z
16
25
  .strictObject({
17
26
  ...identity,
@@ -20,7 +29,11 @@ export const stepSchema = z
20
29
  to: label,
21
30
  label,
22
31
  style: z.enum(["call", "return", "async"]).default("call"),
23
- source: anchorSchema.optional(),
32
+ /** The call site this arrow stands for, then any ranges directly about
33
+ * that call, each shown as its own card. */
34
+ source: z
35
+ .union([anchorSchema, z.array(anchorSchema).min(1).max(4)])
36
+ .optional(),
24
37
  pins: elementPins,
25
38
  explanation: label.optional(),
26
39
  code: z.strictObject(codeFields).optional(),
@@ -34,6 +47,11 @@ export const stepSchema = z
34
47
 
35
48
  export type Step = z.infer<typeof stepSchema>;
36
49
 
50
+ /** A step's ranges in order; a single anchor is a list of one. */
51
+ export function stepSources(step: Pick<Step, "source">): Anchor[] {
52
+ return step.source === undefined ? [] : [step.source].flat();
53
+ }
54
+
37
55
  /** Kept for callers that parsed sequences directly. */
38
56
  export const sequenceSchema = defineBlock("sequence", {
39
57
  title: label,
@@ -48,9 +66,69 @@ export const sequence = {
48
66
  type: "sequence",
49
67
  schema: sequenceSchema,
50
68
  check(block: SequenceBlock) {
69
+ const pins = (step: Step) => JSON.stringify(step.pins ?? block.pins);
70
+
51
71
  for (const step of block.steps) {
52
72
  requireKey(block.actors, step.from);
53
73
  requireKey(block.actors, step.to);
74
+
75
+ const own = stepSources(step);
76
+
77
+ for (const [at, range] of own.entries()) {
78
+ if (own.some((other, index) => index !== at && covers(range, other)))
79
+ throw new ReviewInputError(
80
+ `Range ${range} repeats another range of its step.`,
81
+ );
82
+
83
+ // A range after the call site that holds another step's range is
84
+ // that step's code: the callee is the next step, not more of this
85
+ // one. The call site itself may repeat, as a call and its return do.
86
+ for (const [index, other] of block.steps.entries())
87
+ if (
88
+ at > 0 &&
89
+ other !== step &&
90
+ pins(other) === pins(step) &&
91
+ stepSources(other).some((theirs) => covers(range, theirs))
92
+ )
93
+ throw new ReviewInputError(
94
+ `Range ${range} is step ${index + 1}'s code; link the step instead.`,
95
+ );
96
+ }
54
97
  }
55
98
  },
99
+ advise(block: SequenceBlock) {
100
+ return block.steps.flatMap((step, index) =>
101
+ stepSources(step).flatMap((range) => {
102
+ const lines = oneSideLines(parseAnchor(range)!);
103
+
104
+ return lines && lines > callSiteLines
105
+ ? [
106
+ `Step ${index + 1} of "${block.title}" pins ${lines} lines at ${range}. A step's source is its call site; pin the call, not the function body.`,
107
+ ]
108
+ : [];
109
+ }),
110
+ );
111
+ },
56
112
  } satisfies BlockDefinition<SequenceBlock>;
113
+
114
+ /** Lines in a one-sided selection; a selection across sides has no count. */
115
+ function oneSideLines({ start, end }: DiffSelection): number | undefined {
116
+ return start.side === end.side ? end.line - start.line + 1 : undefined;
117
+ }
118
+
119
+ /** Whether `outer` equals or contains `inner`, in the same file. Across
120
+ * sides, only the same anchor counts. */
121
+ function covers(outer: Anchor, inner: Anchor): boolean {
122
+ if (outer === inner) return true;
123
+ const a = parseAnchor(outer)!;
124
+ const b = parseAnchor(inner)!;
125
+
126
+ return (
127
+ a.file === b.file &&
128
+ oneSideLines(a) !== undefined &&
129
+ oneSideLines(b) !== undefined &&
130
+ a.start.side === b.start.side &&
131
+ a.start.line <= b.start.line &&
132
+ a.end.line >= b.end.line
133
+ );
134
+ }
@@ -15,7 +15,8 @@ export const COMPONENT_REFERENCE = [
15
15
  "- trace_quote {traceId, eventId, text}",
16
16
  "- software_map {mapVersionId, focusElementId?}",
17
17
  "- sequence {title, actors: {key: label}, steps: step[], pins?}",
18
- "- step {from, to (actor keys), label, style?: call|return|async, and exactly one of source: anchor, explanation, or code: {text, language?}; pins?}",
18
+ "- step {from, to (actor keys), label, style?: call|return|async, and exactly one of source: anchor or anchor[] (at most 4), explanation, or code: {text, language?}; pins?}",
19
+ "A step's source is the line where its arrow leaves `from` (the call, return or emit). Add an additional range only when that line can't be read without it: the type or schema of what it passes, or the code that builds its argument. Never the callee's body; that is the next step.",
19
20
  "- flow_diagram {title, nodes: flow_node[] (1 to 100), edges: flow_edge[], description?, direction?: right|down, pins?}",
20
21
  "- flow_node {key, label, description?, kind?: process|decision|terminal, attachments?: {label, sources: anchor[], pins?}[], link?: {from or to, label?, style?}}",
21
22
  "- flow_edge {from, to (node keys), label?, style?: solid|dashed}",
@@ -1,4 +1,9 @@
1
- import { type Element, ReviewInputError, elements } from "./document.js";
1
+ import {
2
+ type Element,
3
+ ReviewInputError,
4
+ elements,
5
+ stepSources,
6
+ } from "./document.js";
2
7
  import type { Snapshot } from "./store.js";
3
8
 
4
9
  /** A reading view of saved content, not another document format to maintain. */
@@ -79,7 +84,7 @@ export function documentText(
79
84
  `${element.from} → ${element.to}: ${element.label} (${element.style})`,
80
85
  );
81
86
 
82
- if (element.source) detail(element.source);
87
+ for (const source of stepSources(element)) detail(source);
83
88
 
84
89
  if (detailed && element.explanation) detail(element.explanation);
85
90
 
@@ -31,7 +31,7 @@ import {
31
31
  blockSchema,
32
32
  blocks,
33
33
  } from "./blocks/index.js";
34
- import { type Step, stepSchema } from "./blocks/sequence.js";
34
+ import { type Step, stepSchema, stepSources } from "./blocks/sequence.js";
35
35
  import type { Lens } from "./diff-lenses.js";
36
36
  import { ReviewInputError } from "./input-error.js";
37
37
 
@@ -45,6 +45,7 @@ export {
45
45
  blockSchema,
46
46
  blocks,
47
47
  checkReferences,
48
+ documentAdvice,
48
49
  } from "./blocks/index.js";
49
50
 
50
51
  export { type Frame, frameSchema } from "./blocks/call_stack_diff.js";
@@ -67,6 +68,7 @@ export {
67
68
  type Step,
68
69
  sequenceSchema,
69
70
  stepSchema,
71
+ stepSources,
70
72
  } from "./blocks/sequence.js";
71
73
 
72
74
  const text = z.string();
@@ -321,16 +323,18 @@ function documentReferences(
321
323
  // A step, frame and operation render the range as a peek, so a
322
324
  // whitespace-only range is an authoring mistake for each of them; prose
323
325
  // links and context sources only need the range to exist.
326
+ // Each of a step's ranges is its own reference, so a stale one marks only
327
+ // itself. The first keeps the step's id, as a single source always had.
324
328
  if (element.type === "sequence")
325
329
  return element.steps.flatMap((step) =>
326
- step.source
327
- ? select(step.source, step.pins ?? element.pins).map((source) => ({
328
- id: step.id!,
329
- source,
330
- label: step.label,
331
- peek: true,
332
- }))
333
- : [],
330
+ stepSources(step).flatMap((anchor, index) =>
331
+ select(anchor, step.pins ?? element.pins).map((source) => ({
332
+ id: index ? `${step.id}:source:${index}` : step.id!,
333
+ source,
334
+ label: step.label,
335
+ peek: true,
336
+ })),
337
+ ),
334
338
  );
335
339
 
336
340
  if (element.type === "flow_diagram")
@@ -44,6 +44,7 @@ import {
44
44
  applyEdit,
45
45
  assignFreshIds,
46
46
  checkReferences,
47
+ documentAdvice,
47
48
  documentSchema,
48
49
  editSchema,
49
50
  elements,
@@ -1452,11 +1453,19 @@ export class ReviewStore {
1452
1453
  )
1453
1454
  await this.providers.validatePins(snapshot.pins);
1454
1455
 
1455
- const warnings = await this.validateExternal(
1456
- snapshot,
1457
- previous,
1458
- op.type === "set_target",
1459
- );
1456
+ // Advice is about what this command wrote; earlier advice stays said.
1457
+ const advised = new Set(documentAdvice(previous?.document ?? []));
1458
+
1459
+ const warnings = [
1460
+ ...documentAdvice(snapshot.document).filter(
1461
+ (advice) => !advised.has(advice),
1462
+ ),
1463
+ ...(await this.validateExternal(
1464
+ snapshot,
1465
+ previous,
1466
+ op.type === "set_target",
1467
+ )),
1468
+ ];
1460
1469
 
1461
1470
  snapshot.version = previous ? previous.version + 1 : 0;
1462
1471
  snapshot.createdAt = new Date().toISOString();
@@ -55,6 +55,7 @@ interface Host extends GatewayRemote {
55
55
  problem?: ReviewGatewayHost["problem"];
56
56
  languageFeatures?: boolean;
57
57
  languageFeaturesDetail?: string;
58
+ languageFeaturesPending?: true;
58
59
  languageGroups?: ReviewGatewayHost["languageGroups"];
59
60
  installing?: ReviewGatewayHost["installing"];
60
61
  declined?: true;
@@ -316,6 +317,7 @@ export function createGatewayHosts(input: {
316
317
  previous.delete(given.alias);
317
318
  current.languageFeatures = given.languageFeatures;
318
319
  current.languageFeaturesDetail = given.languageFeaturesDetail;
320
+ current.languageFeaturesPending = given.languageFeaturesPending;
319
321
  current.languageGroups = given.languageGroups;
320
322
  next.push(current);
321
323
  } else {
@@ -362,12 +364,17 @@ export type GatewayHosts = ReturnType<typeof createGatewayHosts>;
362
364
  const languageOf = ({
363
365
  languageFeatures,
364
366
  languageFeaturesDetail,
367
+ languageFeaturesPending,
365
368
  languageGroups,
366
369
  }: Pick<
367
370
  ReviewGatewayHost,
368
- "languageFeatures" | "languageFeaturesDetail" | "languageGroups"
371
+ | "languageFeatures"
372
+ | "languageFeaturesDetail"
373
+ | "languageFeaturesPending"
374
+ | "languageGroups"
369
375
  >) => ({
370
376
  ...(languageFeatures !== undefined && { languageFeatures }),
371
377
  ...(languageFeaturesDetail !== undefined && { languageFeaturesDetail }),
378
+ ...(languageFeaturesPending && { languageFeaturesPending }),
372
379
  ...(languageGroups !== undefined && { languageGroups }),
373
380
  });
@@ -43,6 +43,7 @@ function decorate(
43
43
  available: {
44
44
  sourceWindows: features,
45
45
  ...(reason && { reason }),
46
+ ...(online && state.languageFeaturesPending && { pending: true }),
46
47
  },
47
48
  };
48
49
  }
@@ -1,2 +0,0 @@
1
- import { n as runReviewAgentCli } from "./agent-cli-CACGYhRh.js";
2
- export { runReviewAgentCli };
@@ -1,2 +0,0 @@
1
- import { t as openReviewProfile } from "./profile-CbAW3qDm.js";
2
- export { openReviewProfile };