@dev.fast/whiteboard 0.2.0 → 0.2.1-preview.20261006.100

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.
@@ -1 +1 @@
1
- {"version":"0.2.0","commit":"2c6b8b5b8fe7c3435cf223f6a12a87e507ccf6f1","dirty":true,"builtAt":"2026-10-05T19:58:31.957Z"}
1
+ {"version":"0.2.1-preview.20261006.100","commit":"d3340667cc36422df9b7df0fea600a50b3adfc4c","dirty":true,"builtAt":"2026-10-06T16:26:56.021Z"}
@@ -4495,7 +4495,7 @@ async function runReviewCli(input) {
4495
4495
  process.once("SIGINT", stop);
4496
4496
  process.once("SIGTERM", stop);
4497
4497
  try {
4498
- const { runHeadlessServer } = await import("./headless-host-BJxf4dUU.js");
4498
+ const { runHeadlessServer } = await import("./headless-host-D266UVka.js");
4499
4499
  await runHeadlessServer({
4500
4500
  stateDir,
4501
4501
  port,
@@ -4546,7 +4546,7 @@ async function runReviewCli(input) {
4546
4546
  if (problem) throw new Error(`Cannot tell whether a Whiteboard Desktop is using ${stateDir}: ${problem.message}`);
4547
4547
  const inUse = () => /* @__PURE__ */ new Error(`A Whiteboard server is using ${stateDir}. Stop it first, then run whiteboard server reset-id again.`);
4548
4548
  if (desktops.instances.some(({ discovery }) => [discovery.appPid, discovery.serverPid].some((pid) => processIsAlive(pid)))) throw inUse();
4549
- const [{ openReviewProfile }, { withHeadlessServerLock }] = await Promise.all([import("./profile-Bh5FIrmf.js"), import("./headless-host-BJxf4dUU.js")]);
4549
+ const [{ openReviewProfile }, { withHeadlessServerLock }] = await Promise.all([import("./profile-Bh5FIrmf.js"), import("./headless-host-D266UVka.js")]);
4550
4550
  const reset = await withHeadlessServerLock(await realpath(stateDir), async () => {
4551
4551
  const local = await openReviewProfile(stateDir, { manageWorkspaces: false });
4552
4552
  try {
package/dist/cli.js CHANGED
@@ -17,7 +17,7 @@ async function runCli(effectivePath) {
17
17
  `);
18
18
  return 1;
19
19
  }
20
- const { runReviewCli } = await import("./cli-runner-D8y2_luU.js");
20
+ const { runReviewCli } = await import("./cli-runner-CsrwxdfZ.js");
21
21
  return runReviewCli({
22
22
  argv,
23
23
  cliPaths: {
@@ -2,7 +2,7 @@ import { p as isObjectValue } from "./src-X9phtB2j.js";
2
2
  import { O as withFileLock, P as writePrivateJsonAtomic } from "./s3-config-CAxhO9u_.js";
3
3
  import "./review-home-paths-6zZH-1c9.js";
4
4
  import { t as openReviewProfile } from "./profile-4ry2f3AM.js";
5
- import { i as createWhiteboardCore, l as createNodeRequestListener, n as installProcessErrorTelemetry, p as GlobalReviewDesktopVerbRelay, t as drainServerCrashReport } from "./process-error-telemetry-v9e5h6D7.js";
5
+ import { i as createWhiteboardCore, l as createNodeRequestListener, n as installProcessErrorTelemetry, p as GlobalReviewDesktopVerbRelay, t as drainServerCrashReport } from "./process-error-telemetry-Dw91I1r5.js";
6
6
  import { i as reviewServerDiscoveryPath, t as headlessServerLockPath } from "./server-discovery-Dbzn3w6Z.js";
7
7
  import { randomBytes, randomUUID } from "node:crypto";
8
8
  import { mkdir, realpath, rm } from "node:fs/promises";
@@ -2081,9 +2081,10 @@ async function resolveReviewStackLayers(subject, reviews, runGitHubApi = default
2081
2081
  if (!stack) return [];
2082
2082
  const currentIndex = stack.pull_requests.findIndex((pr) => pr.number === pullRequestNumber);
2083
2083
  return stack.pull_requests.map((pr, index) => {
2084
- const review = reviews.find((candidate) => {
2084
+ const matches = reviews.filter((candidate) => {
2085
2085
  return (candidate.origin?.pullRequestUrl?.replace(/\/pull\/\d+.*$/, "") ?? candidate.pins?.repositoryId) === repoKey && candidate.origin?.pullRequestNumber === pr.number;
2086
2086
  });
2087
+ const review = matches.find((candidate) => candidate.reviewId === subject.reviewId) ?? matches.findLast((candidate) => !candidate.dismissedAt) ?? matches.at(-1);
2087
2088
  return {
2088
2089
  branch: pr.head.ref,
2089
2090
  pullRequestNumber: pr.number,
@@ -3398,6 +3399,27 @@ function createReviewApi(store, data, open, shared, capabilities = () => ({
3398
3399
  readReview(reviewId);
3399
3400
  return context.json({ threads: store.askHistory.list(reviewId) });
3400
3401
  });
3402
+ app.get("/:id/ask/:threadId", (context) => {
3403
+ const record = store.askHistory.get(context.req.param("threadId"));
3404
+ if (record?.reviewId !== context.req.param("id")) throw new ReviewInputError("This conversation was not found.", 404);
3405
+ const target = record.selection.target;
3406
+ return context.json({
3407
+ id: record.id,
3408
+ agent: record.agent,
3409
+ agentName: askAgents[record.agent].name,
3410
+ status: "idle",
3411
+ readOnly: true,
3412
+ bypass: false,
3413
+ head: record.head,
3414
+ cwd: record.cwd,
3415
+ title: record.title,
3416
+ selection: {
3417
+ title: record.selection.title,
3418
+ quote: target.kind === "text" ? target.quote : void 0
3419
+ },
3420
+ entries: record.entries ?? []
3421
+ });
3422
+ });
3401
3423
  app.post("/:id/ask/:threadId/open", async (context) => {
3402
3424
  const reviewId = context.req.param("id");
3403
3425
  const threadId = context.req.param("threadId");
@@ -14,7 +14,7 @@ import { t as openReviewProfile } from "../profile-4ry2f3AM.js";
14
14
  import { r as reviewTelemetryChannel, t as ReviewTelemetry } from "../review-telemetry-DEFnQsQ2.js";
15
15
  import { o as REVIEW_TELEMETRY_TABS } from "../request-origin-D_4QIdoN.js";
16
16
  import { c as removeLegacyReviewSkills, d as skipCliInstall, i as finishCliInstallUpdate, l as resetCliInstall, n as applyCliInstall, r as declineCliInstall, s as removeCliInstall, t as aliasInstallationToAccount, u as resolveCliInstallStatus } from "../account-alias-DOHN1RSH.js";
17
- import { a as serverJson, c as recordClientError, d as HttpJsonError, f as ReviewServerError, i as createWhiteboardCore, l as createNodeRequestListener, n as installProcessErrorTelemetry, o as captureSanitizedUiTelemetry, p as GlobalReviewDesktopVerbRelay, r as serverCrashReportPath, s as clientErrorsForSession, t as drainServerCrashReport, u as readBoundedRequestJson } from "../process-error-telemetry-v9e5h6D7.js";
17
+ import { a as serverJson, c as recordClientError, d as HttpJsonError, f as ReviewServerError, i as createWhiteboardCore, l as createNodeRequestListener, n as installProcessErrorTelemetry, o as captureSanitizedUiTelemetry, p as GlobalReviewDesktopVerbRelay, r as serverCrashReportPath, s as clientErrorsForSession, t as drainServerCrashReport, u as readBoundedRequestJson } from "../process-error-telemetry-Dw91I1r5.js";
18
18
  import { c as readQuerySchemas } from "../authoring-tools-Cr6Kpjsx.js";
19
19
  import crypto, { createHash, randomBytes, randomUUID } from "node:crypto";
20
20
  import { z } from "zod";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dev.fast/whiteboard",
3
- "version": "0.2.0",
3
+ "version": "0.2.1-preview.20261006.100",
4
4
  "private": false,
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -72,10 +72,10 @@
72
72
  "typescript": "6.0.3",
73
73
  "vitest": "4.1.11",
74
74
  "@dev.fast/json": "0.0.0",
75
- "@dev.fast/trace-core": "0.0.0",
76
75
  "@dev.fast/review-protocol": "0.0.0",
77
- "@dev.fast/trace-protocol": "0.5.0",
78
- "@dev.fast/review-share-protocol": "0.1.2"
76
+ "@dev.fast/review-share-protocol": "0.1.2",
77
+ "@dev.fast/trace-core": "0.0.0",
78
+ "@dev.fast/trace-protocol": "0.5.0"
79
79
  },
80
80
  "engines": {
81
81
  "node": ">=24 <25"
@@ -85,7 +85,7 @@
85
85
  "repo": "devdotfast/review",
86
86
  "docs": "packages/review/instructions/authoring.md"
87
87
  },
88
- "gitHead": "2c6b8b5b8fe7c3435cf223f6a12a87e507ccf6f1",
88
+ "gitHead": "d3340667cc36422df9b7df0fea600a50b3adfc4c",
89
89
  "scripts": {
90
90
  "prebuild": "pnpm run build:workspace-deps",
91
91
  "ensure:diffr": "diffr-fetch --into bin",
@@ -12,11 +12,12 @@ import {
12
12
  AgentSelectionSchema,
13
13
  selectionMarkdown,
14
14
  } from "@review/agent-selection.js";
15
- import type { AskAgentStatus } from "@review/ask/agents.js";
15
+ import { type AskAgentStatus, askAgents } from "@review/ask/agents.js";
16
16
  import { checkoutFiles, mentionableFiles } from "@review/ask/checkout-files.js";
17
17
  import { parseFileRef, resolveFileRefs } from "@review/ask/file-refs.js";
18
18
  import {
19
19
  type AskAgentId,
20
+ type AskThreadState,
20
21
  askAgentIds,
21
22
  askChoiceKinds,
22
23
  askPicksSchema,
@@ -1309,6 +1310,32 @@ export function createReviewApi(
1309
1310
  return context.json({ threads: store.askHistory.list(reviewId) });
1310
1311
  });
1311
1312
 
1313
+ // A saved conversation, without starting its agent.
1314
+ app.get("/:id/ask/:threadId", (context) => {
1315
+ const record = store.askHistory.get(context.req.param("threadId"));
1316
+
1317
+ if (record?.reviewId !== context.req.param("id"))
1318
+ throw new ReviewInputError("This conversation was not found.", 404);
1319
+ const target = record.selection.target;
1320
+
1321
+ return context.json({
1322
+ id: record.id,
1323
+ agent: record.agent,
1324
+ agentName: askAgents[record.agent].name,
1325
+ status: "idle",
1326
+ readOnly: true,
1327
+ bypass: false,
1328
+ head: record.head,
1329
+ cwd: record.cwd,
1330
+ title: record.title,
1331
+ selection: {
1332
+ title: record.selection.title,
1333
+ quote: target.kind === "text" ? target.quote : undefined,
1334
+ },
1335
+ entries: record.entries ?? [],
1336
+ } satisfies AskThreadState);
1337
+ });
1338
+
1312
1339
  // A saved conversation: attach to it if it is still running, else start
1313
1340
  // the agent and load it, at the commit it was asked about.
1314
1341
  app.post("/:id/ask/:threadId/open", async (context) => {
@@ -23,10 +23,10 @@ const GitHubStacksSchema = z.array(
23
23
  export type RunGitHubApi = (host: string, endpoint: string) => Promise<string>;
24
24
 
25
25
  export async function resolveReviewStackLayers(
26
- subject: Pick<ReviewApiSummary, "origin">,
26
+ subject: Pick<ReviewApiSummary, "origin"> & { reviewId?: string },
27
27
  reviews: readonly Pick<
28
28
  ReviewApiSummary,
29
- "reviewId" | "title" | "origin" | "pins"
29
+ "reviewId" | "title" | "origin" | "pins" | "dismissedAt"
30
30
  >[],
31
31
  runGitHubApi: RunGitHubApi = defaultRunGitHubApi,
32
32
  ): Promise<ReviewStackLayer[]> {
@@ -66,7 +66,7 @@ export async function resolveReviewStackLayers(
66
66
  );
67
67
 
68
68
  return stack.pull_requests.map((pr, index) => {
69
- const review = reviews.find((candidate) => {
69
+ const matches = reviews.filter((candidate) => {
70
70
  const repository =
71
71
  candidate.origin?.pullRequestUrl?.replace(/\/pull\/\d+.*$/, "") ??
72
72
  candidate.pins?.repositoryId;
@@ -77,6 +77,11 @@ export async function resolveReviewStackLayers(
77
77
  );
78
78
  });
79
79
 
80
+ const review =
81
+ matches.find((candidate) => candidate.reviewId === subject.reviewId) ??
82
+ matches.findLast((candidate) => !candidate.dismissedAt) ??
83
+ matches.at(-1);
84
+
80
85
  return {
81
86
  branch: pr.head.ref,
82
87
  pullRequestNumber: pr.number,