@dev.fast/whiteboard 0.2.1-preview.20261006.99 → 0.2.1-preview.20261007.102
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{account-alias-DOHN1RSH.js → account-alias-DLE9XZhi.js} +1 -1
- package/dist/agent-cli-DdfuiyOF.js +2 -0
- package/dist/{agent-cli-ChOq0ZuO.js → agent-cli-d0YJQNrR.js} +6 -6
- package/dist/{agent-client-CPQo7iTI.js → agent-client-DoaODH6I.js} +2 -2
- package/dist/{authoring-tools-Cr6Kpjsx.js → authoring-tools-Bvre8hN2.js} +2 -2
- package/dist/build-info.json +1 -1
- package/dist/{cli-hVgHcdsv.js → cli-DtolttUF.js} +1 -1
- package/dist/{cli-runner-CMmd6o_m.js → cli-runner-CSEH_mQN.js} +11 -11
- package/dist/cli.js +2 -2
- package/dist/{client-DFoh50W3.js → client-D01H2KOV.js} +2 -2
- package/dist/{desktop-discovery-DBO-PM2V.js → desktop-discovery-DK2HoBy5.js} +1 -1
- package/dist/{headless-host-CVMDNmLU.js → headless-host-YxYKMcI5.js} +3 -2
- package/dist/{local-data-CxLk75rx.js → local-data-Dy8AngNb.js} +37 -84
- package/dist/{mcp-QeF8vkdf.js → mcp-DGKhDPMs.js} +3 -3
- package/dist/{process-error-telemetry-CkiiadgB.js → process-error-telemetry-DN5BpZnc.js} +345 -13
- package/dist/{profile-4ry2f3AM.js → profile-B6jrb2QN.js} +1 -1
- package/dist/profile-D8XSrJYI.js +2 -0
- package/dist/{request-origin-D_4QIdoN.js → request-origin-BB2TENZ5.js} +1 -1
- package/dist/{review-telemetry-DEFnQsQ2.js → review-telemetry-2UlsdE4D.js} +2 -2
- package/dist/server/desktop-host.js +14 -303
- package/dist/sharing/index.d.ts +6 -10
- package/dist/sharing/index.js +1 -1
- package/dist/{src-DnwdaQ2r.js → src-DHiDJQDy.js} +7 -14
- package/dist/{stored-document-migration-DHeMpLMb.js → stored-document-migration-T9Uj5MkN.js} +1 -1
- package/dist/{tool-failure-C8zB73HV.js → tool-failure-Djeeax6q.js} +1 -1
- package/package.json +7 -8
- package/src/review-api/comparison-coverage.ts +6 -11
- package/src/review-api/http.ts +31 -4
- package/src/review-api/store.ts +0 -5
- package/src/server/desktop-server.ts +2 -13
- package/src/server/diffr-config.ts +170 -128
- package/src/server/headless-host.ts +3 -0
- package/src/server/structural-diff.ts +12 -39
- package/src/server/test-fixtures/diffr-ui-overrides.toml +38 -0
- package/dist/agent-cli-DzWfZr6G.js +0 -2
- package/dist/profile-Bh5FIrmf.js +0 -2
- package/src/server/diffr-languages.ts +0 -97
|
@@ -1,28 +1,25 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { a as diffFileSummaries, b as resolveRevision, i as diff, x as splitGitPatchFiles } from "../src-CmdiBL20.js";
|
|
3
|
-
import { c as jsonString, i as jsonBoolean,
|
|
4
|
-
import { i as SharedReviewStore } from "../client-
|
|
5
|
-
import { c as ReviewBugReportRequestSchema, l as parseReviewBugReportResponse, o as parseZod, s as ReviewBugReportMetaV2Schema, t as parseReviewCliInstallApplyRequest, v as reviewDiffrSummarizerInputSchema
|
|
6
|
-
import {
|
|
7
|
-
import { C as resourceReferences, p as documentSchema } from "../stored-document-migration-DHeMpLMb.js";
|
|
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 { i as SharedReviewStore } from "../client-D01H2KOV.js";
|
|
5
|
+
import { c as ReviewBugReportRequestSchema, l as parseReviewBugReportResponse, o as parseZod, s as ReviewBugReportMetaV2Schema, t as parseReviewCliInstallApplyRequest, v as reviewDiffrSummarizerInputSchema } from "../src-DHiDJQDy.js";
|
|
6
|
+
import { C as resourceReferences, p as documentSchema } from "../stored-document-migration-T9Uj5MkN.js";
|
|
8
7
|
import { t as ReviewInputError } from "../input-error-OLgB_h31.js";
|
|
9
8
|
import { A as devReviewHome, P as writePrivateJsonAtomic } from "../s3-config-CAxhO9u_.js";
|
|
10
9
|
import { U as shellQuote, i as reviewInstanceDiscoveryPath, o as reviewLegacyDiscoveryPath } from "../review-home-paths-6zZH-1c9.js";
|
|
11
10
|
import { i as readReviewPackageVersion, t as findReviewPackageRoot } from "../package-paths-B6-zxvIO.js";
|
|
12
|
-
import { c as reviewInstanceIdentity, t as REVIEW_INSTANCE_ENV } from "../desktop-discovery-
|
|
13
|
-
import { t as openReviewProfile } from "../profile-
|
|
14
|
-
import { r as reviewTelemetryChannel, t as ReviewTelemetry } from "../review-telemetry-
|
|
15
|
-
import { o as REVIEW_TELEMETRY_TABS } from "../request-origin-
|
|
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-
|
|
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-
|
|
18
|
-
import { c as readQuerySchemas } from "../authoring-tools-
|
|
11
|
+
import { c as reviewInstanceIdentity, t as REVIEW_INSTANCE_ENV } from "../desktop-discovery-DK2HoBy5.js";
|
|
12
|
+
import { t as openReviewProfile } from "../profile-B6jrb2QN.js";
|
|
13
|
+
import { r as reviewTelemetryChannel, t as ReviewTelemetry } from "../review-telemetry-2UlsdE4D.js";
|
|
14
|
+
import { o as REVIEW_TELEMETRY_TABS } from "../request-origin-BB2TENZ5.js";
|
|
15
|
+
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-DLE9XZhi.js";
|
|
16
|
+
import { _ as setDiffrConfigValue, a as serverJson, c as recordClientError, d as HttpJsonError, f as ReviewServerError, g as saveDiffrSummarizer, h as readDiffrConfig, i as createWhiteboardCore, l as createNodeRequestListener, m as migrateDiffrConfig, n as installProcessErrorTelemetry, o as captureSanitizedUiTelemetry, p as GlobalReviewDesktopVerbRelay, r as serverCrashReportPath, s as clientErrorsForSession, t as drainServerCrashReport, u as readBoundedRequestJson, v as testDiffrSummarizer } from "../process-error-telemetry-DN5BpZnc.js";
|
|
17
|
+
import { c as readQuerySchemas } from "../authoring-tools-Bvre8hN2.js";
|
|
19
18
|
import crypto, { createHash, randomBytes, randomUUID } from "node:crypto";
|
|
20
19
|
import { z } from "zod";
|
|
21
20
|
import fs, { existsSync } from "node:fs";
|
|
22
|
-
import { chmod, copyFile, cp, mkdir,
|
|
23
|
-
import path
|
|
24
|
-
import { tmpdir } from "node:os";
|
|
25
|
-
import { execFile } from "node:child_process";
|
|
21
|
+
import { chmod, copyFile, cp, mkdir, readFile, realpath, rename, rm, stat, writeFile } from "node:fs/promises";
|
|
22
|
+
import path from "node:path";
|
|
26
23
|
import { promisify } from "node:util";
|
|
27
24
|
import { setTimeout as setTimeout$1 } from "node:timers/promises";
|
|
28
25
|
import { pathToFileURL } from "node:url";
|
|
@@ -30,7 +27,6 @@ import { Writable } from "node:stream";
|
|
|
30
27
|
import { pipeline } from "node:stream/promises";
|
|
31
28
|
import { gzip, gzipSync } from "node:zlib";
|
|
32
29
|
import { createServer } from "node:http";
|
|
33
|
-
import { stringify } from "smol-toml";
|
|
34
30
|
import { Hono } from "hono";
|
|
35
31
|
//#region src/review-bundled-tools.ts
|
|
36
32
|
function reviewToolsRoot(env = process.env) {
|
|
@@ -426,283 +422,6 @@ async function dumpInside(file, directory) {
|
|
|
426
422
|
}
|
|
427
423
|
}
|
|
428
424
|
//#endregion
|
|
429
|
-
//#region src/server/diffr-config.ts
|
|
430
|
-
const execFileAsync = promisify(execFile);
|
|
431
|
-
let writes = Promise.resolve();
|
|
432
|
-
let testing = false;
|
|
433
|
-
function serialized(operation) {
|
|
434
|
-
const next = writes.then(operation, operation);
|
|
435
|
-
writes = next.catch(() => {});
|
|
436
|
-
return next;
|
|
437
|
-
}
|
|
438
|
-
async function diffr(args, rootPath, options = {}) {
|
|
439
|
-
try {
|
|
440
|
-
const { stdout } = await execFileAsync(diffrExecutable(), args, {
|
|
441
|
-
cwd: rootPath,
|
|
442
|
-
maxBuffer: 16777216,
|
|
443
|
-
signal: options.signal ?? AbortSignal.timeout(3e4),
|
|
444
|
-
env: options.env ?? process.env
|
|
445
|
-
});
|
|
446
|
-
return stdout;
|
|
447
|
-
} catch (error) {
|
|
448
|
-
const failure = error;
|
|
449
|
-
if (failure.code === "ENOENT") throw diffrMissingError();
|
|
450
|
-
if (failure.name === "AbortError") throw new Error("diffr timed out or was cancelled.");
|
|
451
|
-
throw new Error("diffr could not complete the operation. Check its configuration and credentials.");
|
|
452
|
-
}
|
|
453
|
-
}
|
|
454
|
-
async function values(rootPath, reveal = false, signal) {
|
|
455
|
-
return json(await diffr([
|
|
456
|
-
"config",
|
|
457
|
-
"show",
|
|
458
|
-
"--json",
|
|
459
|
-
...reveal ? ["--reveal"] : []
|
|
460
|
-
], rootPath, { signal }));
|
|
461
|
-
}
|
|
462
|
-
function json(output) {
|
|
463
|
-
try {
|
|
464
|
-
const parsed = parseJsonText(output);
|
|
465
|
-
if (isJsonObject(parsed)) return parsed;
|
|
466
|
-
} catch {}
|
|
467
|
-
throw new Error("diffr configuration response is malformed.");
|
|
468
|
-
}
|
|
469
|
-
function valueAt(object, key) {
|
|
470
|
-
let value = object;
|
|
471
|
-
for (const segment of key.split(".")) value = isJsonObject(value) ? value[segment] : void 0;
|
|
472
|
-
return value;
|
|
473
|
-
}
|
|
474
|
-
const prefix = "plugins.bundled.summarize";
|
|
475
|
-
function savedProvider(config) {
|
|
476
|
-
const value = valueAt(config, `${prefix}.provider`);
|
|
477
|
-
return isStringValue(value) ? value : "";
|
|
478
|
-
}
|
|
479
|
-
function savedEndpoint(config) {
|
|
480
|
-
const value = valueAt(config, `${prefix}.endpoint`);
|
|
481
|
-
return isStringValue(value) ? value : "";
|
|
482
|
-
}
|
|
483
|
-
/** Whether a saved key belongs to a different destination than the draft's. */
|
|
484
|
-
function movesKey(config, draft) {
|
|
485
|
-
return draft.provider !== savedProvider(config) || draft.endpoint !== savedEndpoint(config);
|
|
486
|
-
}
|
|
487
|
-
function environmentKey(providers, id) {
|
|
488
|
-
return providers.find((provider) => provider.id === id)?.keyVariables.map((name) => process.env[name]).find((key) => !!key);
|
|
489
|
-
}
|
|
490
|
-
function keyOptional(providers, id, endpoint) {
|
|
491
|
-
return endpoint !== "" && !!providers.find((provider) => provider.id === id)?.keylessCustomEndpoint;
|
|
492
|
-
}
|
|
493
|
-
function strings(value) {
|
|
494
|
-
return Array.isArray(value) ? value.filter(isStringValue) : [];
|
|
495
|
-
}
|
|
496
|
-
/**
|
|
497
|
-
* What diffr's schema says about summaries: each provider's defaults, and
|
|
498
|
-
* the default prompt with the link its description gives. Nothing when
|
|
499
|
-
* diffr cannot describe its schema: the rest of Settings still works.
|
|
500
|
-
*/
|
|
501
|
-
async function summaryDefaults(rootPath) {
|
|
502
|
-
let schema;
|
|
503
|
-
try {
|
|
504
|
-
schema = json(await diffr(["config", "schema"], rootPath));
|
|
505
|
-
} catch {
|
|
506
|
-
return {};
|
|
507
|
-
}
|
|
508
|
-
const options = valueAt(schema, "properties.plugins.properties.bundled.properties.summarize.properties");
|
|
509
|
-
if (!isJsonObject(options)) return {};
|
|
510
|
-
const option = (name) => isJsonObject(options[name]) ? options[name] : {};
|
|
511
|
-
const provider = option("provider");
|
|
512
|
-
const titles = strings(provider["x-enum-titles"]);
|
|
513
|
-
const models = valueAt(option("model"), "x-default-by.values");
|
|
514
|
-
const details = valueAt(option("provider_details"), "x-default-by.values");
|
|
515
|
-
const providers = strings(provider.enum).map((id, index) => {
|
|
516
|
-
const detail = isJsonObject(details) ? details[id] : void 0;
|
|
517
|
-
const model = isJsonObject(models) ? models[id] : void 0;
|
|
518
|
-
const endpoint = isJsonObject(detail) ? detail.endpoint : void 0;
|
|
519
|
-
return {
|
|
520
|
-
id,
|
|
521
|
-
title: titles[index] ?? id,
|
|
522
|
-
model: isStringValue(model) ? model : "",
|
|
523
|
-
endpoint: isStringValue(endpoint) ? endpoint : "",
|
|
524
|
-
keyVariables: isJsonObject(detail) ? strings(detail.key_variables) : [],
|
|
525
|
-
keylessCustomEndpoint: isJsonObject(detail) && detail.keyless_custom_endpoint === true
|
|
526
|
-
};
|
|
527
|
-
});
|
|
528
|
-
const prompt = option("system_prompt");
|
|
529
|
-
return {
|
|
530
|
-
providers,
|
|
531
|
-
defaultPrompt: isStringValue(prompt.default) ? prompt.default : void 0
|
|
532
|
-
};
|
|
533
|
-
}
|
|
534
|
-
async function read(rootPath) {
|
|
535
|
-
const config = await values(rootPath);
|
|
536
|
-
const defaults = await summaryDefaults(rootPath);
|
|
537
|
-
const saved = valueAt(config, `${prefix}.api_key`);
|
|
538
|
-
const credentialSource = isStringValue(saved) && saved.length > 0 ? "config" : environmentKey(defaults.providers ?? [], savedProvider(config)) ? "environment" : "missing";
|
|
539
|
-
const plugins = config.plugins;
|
|
540
|
-
if (isJsonObject(plugins)) for (const namespace of Object.values(plugins)) {
|
|
541
|
-
if (!isJsonObject(namespace)) continue;
|
|
542
|
-
for (const plugin of Object.values(namespace)) if (isJsonObject(plugin)) delete plugin.api_key;
|
|
543
|
-
}
|
|
544
|
-
return {
|
|
545
|
-
values: config,
|
|
546
|
-
credentialSource,
|
|
547
|
-
...defaults
|
|
548
|
-
};
|
|
549
|
-
}
|
|
550
|
-
function readDiffrConfig(rootPath) {
|
|
551
|
-
return serialized(() => read(rootPath));
|
|
552
|
-
}
|
|
553
|
-
function diffrConfigValueText(value) {
|
|
554
|
-
return isStringValue(value) ? value : JSON.stringify(value);
|
|
555
|
-
}
|
|
556
|
-
async function writeSettings(entries, rootPath) {
|
|
557
|
-
let changed = false;
|
|
558
|
-
let error;
|
|
559
|
-
let current = await values(rootPath);
|
|
560
|
-
try {
|
|
561
|
-
for (const [key, value] of entries) {
|
|
562
|
-
if (valueAt(current, key) === value) continue;
|
|
563
|
-
await diffr([
|
|
564
|
-
"config",
|
|
565
|
-
"set",
|
|
566
|
-
key,
|
|
567
|
-
diffrConfigValueText(value)
|
|
568
|
-
], rootPath);
|
|
569
|
-
changed = true;
|
|
570
|
-
current = await values(rootPath);
|
|
571
|
-
}
|
|
572
|
-
} catch {
|
|
573
|
-
error = changed ? "Some settings were saved before diffr rejected a change. Check the current values and try again." : "diffr could not save the setting. Check its configuration and the entered value.";
|
|
574
|
-
} finally {
|
|
575
|
-
if (changed) invalidateStructuralComparisons();
|
|
576
|
-
}
|
|
577
|
-
return {
|
|
578
|
-
...await read(rootPath),
|
|
579
|
-
changed,
|
|
580
|
-
error
|
|
581
|
-
};
|
|
582
|
-
}
|
|
583
|
-
function setDiffrConfigValue(key, value, rootPath) {
|
|
584
|
-
if (!/^[A-Za-z0-9_][A-Za-z0-9_-]*(\.[A-Za-z0-9_][A-Za-z0-9_-]*)*$/.test(key)) throw new Error("Invalid diffr config key.");
|
|
585
|
-
return serialized(() => writeSettings([[key, value]], rootPath));
|
|
586
|
-
}
|
|
587
|
-
function saveDiffrSummarizer(input, rootPath) {
|
|
588
|
-
const parsed = reviewDiffrSummarizerInputSchema.safeParse(input);
|
|
589
|
-
if (!parsed.success) throw new Error("Invalid summary settings.");
|
|
590
|
-
const draft = parsed.data;
|
|
591
|
-
return serialized(async () => {
|
|
592
|
-
const current = await read(rootPath);
|
|
593
|
-
const providers = current.providers ?? [];
|
|
594
|
-
const moving = movesKey(current.values, draft);
|
|
595
|
-
const savedKey = current.credentialSource === "config";
|
|
596
|
-
if (draft.enabled && !draft.apiKey && !(savedKey && !moving) && !environmentKey(providers, draft.provider) && !keyOptional(providers, draft.provider, draft.endpoint)) return {
|
|
597
|
-
...current,
|
|
598
|
-
changed: false,
|
|
599
|
-
error: "Add an API key before enabling summaries."
|
|
600
|
-
};
|
|
601
|
-
const entries = [];
|
|
602
|
-
if (!draft.enabled) entries.push([`${prefix}.enabled`, false]);
|
|
603
|
-
if (moving && savedKey) entries.push([`${prefix}.api_key`, ""]);
|
|
604
|
-
entries.push([`${prefix}.provider`, draft.provider]);
|
|
605
|
-
if (draft.endpoint !== savedEndpoint(current.values) || draft.endpoint && draft.provider !== savedProvider(current.values)) entries.push([`${prefix}.endpoint`, draft.endpoint]);
|
|
606
|
-
if (draft.apiKey) entries.push([`${prefix}.api_key`, draft.apiKey]);
|
|
607
|
-
if (draft.systemPrompt.trim() && draft.systemPrompt !== valueAt(current.values, `${prefix}.system_prompt`)) entries.push([`${prefix}.system_prompt`, draft.systemPrompt]);
|
|
608
|
-
entries.push([`${prefix}.model`, draft.model], [`${prefix}.tests`, draft.tests]);
|
|
609
|
-
if (draft.enabled) entries.push([`${prefix}.enabled`, true]);
|
|
610
|
-
return writeSettings(entries, rootPath);
|
|
611
|
-
});
|
|
612
|
-
}
|
|
613
|
-
async function testDiffrSummarizer(input, rootPath, signal) {
|
|
614
|
-
const parsed = reviewDiffrSummarizerInputSchema.safeParse(input);
|
|
615
|
-
if (!parsed.success) throw new Error("Invalid summary settings.");
|
|
616
|
-
if (testing) throw new Error("A summary test is already running.");
|
|
617
|
-
testing = true;
|
|
618
|
-
const timeout = AbortSignal.timeout(6e4);
|
|
619
|
-
const deadline = signal ? AbortSignal.any([signal, timeout]) : timeout;
|
|
620
|
-
let directory;
|
|
621
|
-
try {
|
|
622
|
-
const config = await serialized(() => values(rootPath, true, deadline));
|
|
623
|
-
const saved = valueAt(config, prefix);
|
|
624
|
-
if (!isJsonObject(saved)) throw new Error("The summarizer is not available in this configuration.");
|
|
625
|
-
const { provider, endpoint, model, systemPrompt } = parsed.data;
|
|
626
|
-
const providers = (await summaryDefaults(rootPath)).providers ?? [];
|
|
627
|
-
const variables = providers.find((known) => known.id === provider)?.keyVariables ?? [];
|
|
628
|
-
const apiKey = parsed.data.apiKey || (!movesKey(config, parsed.data) && isStringValue(saved.api_key) ? saved.api_key : "") || environmentKey(providers, provider);
|
|
629
|
-
if (!apiKey && !keyOptional(providers, provider, endpoint)) throw new Error("Add an API key to test summaries.");
|
|
630
|
-
directory = await mkdtemp(join(tmpdir(), "review-summary-"));
|
|
631
|
-
const options = {
|
|
632
|
-
...saved,
|
|
633
|
-
enabled: true,
|
|
634
|
-
provider,
|
|
635
|
-
model,
|
|
636
|
-
...systemPrompt.trim() && { system_prompt: systemPrompt },
|
|
637
|
-
min_lines: 1,
|
|
638
|
-
retries: 0,
|
|
639
|
-
request_timeout_ms: 45e3
|
|
640
|
-
};
|
|
641
|
-
delete options.api_key;
|
|
642
|
-
delete options.provider_details;
|
|
643
|
-
if (endpoint) options.endpoint = endpoint;
|
|
644
|
-
else delete options.endpoint;
|
|
645
|
-
await mkdir(join(directory, "diffr"));
|
|
646
|
-
await writeFile(join(directory, "diffr", "config.toml"), stringify({
|
|
647
|
-
version: 1,
|
|
648
|
-
plugins: {
|
|
649
|
-
order: ["bundled.summarize"],
|
|
650
|
-
bundled: { summarize: options }
|
|
651
|
-
}
|
|
652
|
-
}), { mode: 384 });
|
|
653
|
-
const before = join(directory, "before.rs"), after = join(directory, "after.rs");
|
|
654
|
-
await writeFile(before, "");
|
|
655
|
-
await writeFile(after, SUMMARY_SAMPLE);
|
|
656
|
-
const output = await diffr([
|
|
657
|
-
"--no-index",
|
|
658
|
-
"--format",
|
|
659
|
-
"ndjson",
|
|
660
|
-
"--stream-annotations",
|
|
661
|
-
"--",
|
|
662
|
-
before,
|
|
663
|
-
after
|
|
664
|
-
], directory, {
|
|
665
|
-
env: {
|
|
666
|
-
...process.env,
|
|
667
|
-
...Object.fromEntries(providers.flatMap((known) => known.keyVariables).map((name) => [name, ""])),
|
|
668
|
-
...apiKey && variables[0] && { [variables[0]]: apiKey },
|
|
669
|
-
XDG_CONFIG_HOME: directory
|
|
670
|
-
},
|
|
671
|
-
signal: deadline
|
|
672
|
-
});
|
|
673
|
-
try {
|
|
674
|
-
const events = output.trim().split("\n").map(decodeStructuralDiffEvent);
|
|
675
|
-
const complete = events.at(-1);
|
|
676
|
-
const annotation = events.flatMap((event) => event.type === "annotations" ? event.annotations : []).find((item) => item.label.trim());
|
|
677
|
-
if (complete?.type === "complete" && complete.failed === 0 && !complete.aborted && !events.some((event) => (event.type === "annotations" || event.type === "file") && event.error) && annotation) return apiKey ? annotation.label.split(apiKey).join("[redacted]") : annotation.label;
|
|
678
|
-
} catch {}
|
|
679
|
-
throw new Error("No summary was produced. Check the API key, model, and network connection.");
|
|
680
|
-
} finally {
|
|
681
|
-
testing = false;
|
|
682
|
-
if (directory) await rm(directory, {
|
|
683
|
-
recursive: true,
|
|
684
|
-
force: true
|
|
685
|
-
});
|
|
686
|
-
}
|
|
687
|
-
}
|
|
688
|
-
const SUMMARY_SAMPLE = `pub fn count_values(values: &[i32]) -> (i32, i32) {
|
|
689
|
-
let mut total = 0;
|
|
690
|
-
let mut count = 0;
|
|
691
|
-
for value in values {
|
|
692
|
-
if *value > 0 {
|
|
693
|
-
total += value;
|
|
694
|
-
count += 1;
|
|
695
|
-
}
|
|
696
|
-
}
|
|
697
|
-
let average = if count > 0 {
|
|
698
|
-
total / count
|
|
699
|
-
} else {
|
|
700
|
-
0
|
|
701
|
-
};
|
|
702
|
-
(count, average)
|
|
703
|
-
}
|
|
704
|
-
`;
|
|
705
|
-
//#endregion
|
|
706
425
|
//#region src/review-diff-files.ts
|
|
707
426
|
async function resolveReviewDiffFiles(input) {
|
|
708
427
|
const baseRef = input.baseRef?.trim();
|
|
@@ -1407,15 +1126,6 @@ function createGlobalReviewServer(input) {
|
|
|
1407
1126
|
await withReviewLock(TUTORIAL_LIFECYCLE_LOCK_KEY, deleteTutorialLocked);
|
|
1408
1127
|
return serverJson(200, { ok: true });
|
|
1409
1128
|
});
|
|
1410
|
-
app.post("/diffr-languages/install", async () => {
|
|
1411
|
-
try {
|
|
1412
|
-
await installFullDiffr();
|
|
1413
|
-
invalidateStructuralComparisons();
|
|
1414
|
-
return serverJson(200, { installed: true });
|
|
1415
|
-
} catch (error) {
|
|
1416
|
-
return serverJson(503, { error: error instanceof Error ? error.message : String(error) });
|
|
1417
|
-
}
|
|
1418
|
-
});
|
|
1419
1129
|
app.get("/diffr-config", async () => serverJson(200, await readDiffrConfig()));
|
|
1420
1130
|
app.put("/diffr-config", async (context) => {
|
|
1421
1131
|
const body = await readBoundedRequestJson(context.req.raw);
|
|
@@ -1522,6 +1232,7 @@ function createGlobalReviewServer(input) {
|
|
|
1522
1232
|
return urlForBoundPort();
|
|
1523
1233
|
},
|
|
1524
1234
|
listen: async () => {
|
|
1235
|
+
await migrateDiffrConfig();
|
|
1525
1236
|
scratchpadEnabled = await readScratchpadEnabled();
|
|
1526
1237
|
boundPort = await listen(httpServer, input.port);
|
|
1527
1238
|
discovery.url = urlForBoundPort();
|
package/dist/sharing/index.d.ts
CHANGED
|
@@ -88,16 +88,7 @@ type StructuralDiffEvent = {
|
|
|
88
88
|
} | ({
|
|
89
89
|
type: "file";
|
|
90
90
|
file: StructuralPairing<StructuralFileRef>;
|
|
91
|
-
visibility?: StructuralVisibility;
|
|
92
91
|
} & StructuralOutcome) | {
|
|
93
|
-
type: "annotations";
|
|
94
|
-
file: StructuralPairing<StructuralFileRef>;
|
|
95
|
-
annotations: {
|
|
96
|
-
region_id: number;
|
|
97
|
-
label: string;
|
|
98
|
-
}[];
|
|
99
|
-
error?: StructuralProblem;
|
|
100
|
-
} | {
|
|
101
92
|
type: "complete";
|
|
102
93
|
succeeded: number;
|
|
103
94
|
failed: number;
|
|
@@ -158,7 +149,8 @@ type StructuralDiff = ({
|
|
|
158
149
|
type StructuralSource = {
|
|
159
150
|
text: string;
|
|
160
151
|
syntax?: StructuralSyntaxSpan[];
|
|
161
|
-
|
|
152
|
+
/** The whole file, as one fold. Both sides' roots share a fold_state_id; its visibility is the file's. */
|
|
153
|
+
root: StructuralRegion;
|
|
162
154
|
};
|
|
163
155
|
type StructuralBinaryRef = {
|
|
164
156
|
size: number;
|
|
@@ -183,6 +175,10 @@ type StructuralNode = {
|
|
|
183
175
|
} | {
|
|
184
176
|
kind: "fold";
|
|
185
177
|
children: StructuralRegion[];
|
|
178
|
+
/** Where the fold's content starts. Collapsed rows sit at this column. */
|
|
179
|
+
indent: StructuralPos;
|
|
180
|
+
/** From the opener to the closer, before rounding to lines. Absent without an opener. */
|
|
181
|
+
syntax?: StructuralSourceRange;
|
|
186
182
|
};
|
|
187
183
|
type StructuralSpan = {
|
|
188
184
|
line: number;
|
package/dist/sharing/index.js
CHANGED
|
@@ -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-
|
|
1
|
+
import { a as sharedReviewId, c as verifyShareRepository, i as SharedReviewStore, l as exportShare, n as ShareClient, o as validateShareBundle } from "../client-D01H2KOV.js";
|
|
2
2
|
export { ShareClient, SharedReviewStore, exportShare, sharedReviewId, validateShareBundle, verifyShareRepository };
|
|
@@ -3,7 +3,7 @@ import { _ as ReviewAgentTraceEventSchema, v as ReviewAgentTraceSessionSchema }
|
|
|
3
3
|
import { z } from "zod";
|
|
4
4
|
//#region ../../node_modules/@dev.fast/diffr/dist/contract.js
|
|
5
5
|
/**
|
|
6
|
-
* Mirrors src/protocol/mod.rs and src/pairing.rs. See ../README.md for sync checks.
|
|
6
|
+
* Mirrors crates/diffr-core/src/protocol/mod.rs and crates/diffr-core/src/pairing.rs. See ../README.md for sync checks.
|
|
7
7
|
* Import only zod: Review embeds this file in generated source.
|
|
8
8
|
* Defaults are omitted, not null. Lines are zero-based, columns are UTF-8 bytes,
|
|
9
9
|
* and ranges are half-open.
|
|
@@ -35,18 +35,8 @@ const StructuralDiffEventSchema = z.lazy(() => z.union([
|
|
|
35
35
|
}),
|
|
36
36
|
z.object({
|
|
37
37
|
type: z.literal("file"),
|
|
38
|
-
file: structuralPairingSchema(StructuralFileRefSchema)
|
|
39
|
-
visibility: StructuralVisibilitySchema.optional()
|
|
38
|
+
file: structuralPairingSchema(StructuralFileRefSchema)
|
|
40
39
|
}).and(StructuralOutcomeSchema),
|
|
41
|
-
z.object({
|
|
42
|
-
type: z.literal("annotations"),
|
|
43
|
-
file: structuralPairingSchema(StructuralFileRefSchema),
|
|
44
|
-
annotations: z.array(z.object({
|
|
45
|
-
region_id: structuralU32,
|
|
46
|
-
label: z.string()
|
|
47
|
-
})),
|
|
48
|
-
error: StructuralProblemSchema.optional()
|
|
49
|
-
}),
|
|
50
40
|
z.object({
|
|
51
41
|
type: z.literal("complete"),
|
|
52
42
|
succeeded: structuralU32,
|
|
@@ -120,7 +110,7 @@ const StructuralDiffSchema = z.lazy(() => z.union([z.object({
|
|
|
120
110
|
const StructuralSourceSchema = z.lazy(() => z.object({
|
|
121
111
|
text: z.string(),
|
|
122
112
|
syntax: z.array(StructuralSyntaxSpanSchema).optional(),
|
|
123
|
-
|
|
113
|
+
root: StructuralRegionSchema
|
|
124
114
|
}));
|
|
125
115
|
const StructuralBinaryRefSchema = z.lazy(() => z.object({ size: z.number().int().nonnegative() }));
|
|
126
116
|
const StructuralSyntaxSpanSchema = z.lazy(() => z.object({
|
|
@@ -141,7 +131,9 @@ const StructuralNodeSchema = z.lazy(() => z.union([z.object({
|
|
|
141
131
|
changed: z.array(StructuralSpanSchema).optional()
|
|
142
132
|
}), z.object({
|
|
143
133
|
kind: z.literal("fold"),
|
|
144
|
-
children: z.array(StructuralRegionSchema)
|
|
134
|
+
children: z.array(StructuralRegionSchema),
|
|
135
|
+
indent: StructuralPosSchema,
|
|
136
|
+
syntax: StructuralSourceRangeSchema.optional()
|
|
145
137
|
})]));
|
|
146
138
|
const StructuralSpanSchema = z.lazy(() => z.object({
|
|
147
139
|
line: structuralU32,
|
|
@@ -262,6 +254,7 @@ z.object({
|
|
|
262
254
|
keylessCustomEndpoint: z.boolean()
|
|
263
255
|
})).optional(),
|
|
264
256
|
defaultPrompt: z.string().optional(),
|
|
257
|
+
defaultHiddenTags: z.array(z.string()).optional(),
|
|
265
258
|
changed: z.boolean().optional(),
|
|
266
259
|
error: z.string().optional()
|
|
267
260
|
});
|
package/dist/{stored-document-migration-DHeMpLMb.js → stored-document-migration-T9Uj5MkN.js}
RENAMED
|
@@ -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-
|
|
2
|
+
import "./src-DHiDJQDy.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";
|
|
@@ -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-
|
|
2
|
+
import { n as callAuthoringTool, s as ReviewApiError, t as ToolText } from "./agent-client-DoaODH6I.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.1-preview.
|
|
3
|
+
"version": "0.2.1-preview.20261007.102",
|
|
4
4
|
"private": false,
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -43,6 +43,7 @@
|
|
|
43
43
|
"@agentclientprotocol/claude-agent-acp": "0.84.0",
|
|
44
44
|
"@agentclientprotocol/codex-acp": "2.0.0",
|
|
45
45
|
"@agentclientprotocol/sdk": "1.5.1",
|
|
46
|
+
"@dev.fast/diffr": "0.1.14",
|
|
46
47
|
"@hono/node-server": "2.0.12",
|
|
47
48
|
"@img/sharp-wasm32": "0.35.4",
|
|
48
49
|
"@modelcontextprotocol/sdk": "1.30.0",
|
|
@@ -61,7 +62,6 @@
|
|
|
61
62
|
"zod": "4.6.5"
|
|
62
63
|
},
|
|
63
64
|
"devDependencies": {
|
|
64
|
-
"@dev.fast/diffr": "0.1.10",
|
|
65
65
|
"@dev.fast/local-vcs": "^0.1.0",
|
|
66
66
|
"@types/node": "24.12.2",
|
|
67
67
|
"@types/semver": "7.7.1",
|
|
@@ -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/
|
|
76
|
-
"@dev.fast/review-protocol": "0.0.0",
|
|
75
|
+
"@dev.fast/trace-core": "0.0.0",
|
|
77
76
|
"@dev.fast/trace-protocol": "0.5.0",
|
|
78
|
-
"@dev.fast/
|
|
77
|
+
"@dev.fast/review-share-protocol": "0.1.2",
|
|
78
|
+
"@dev.fast/review-protocol": "0.0.0"
|
|
79
79
|
},
|
|
80
80
|
"engines": {
|
|
81
81
|
"node": ">=24 <25"
|
|
@@ -85,10 +85,9 @@
|
|
|
85
85
|
"repo": "devdotfast/review",
|
|
86
86
|
"docs": "packages/review/instructions/authoring.md"
|
|
87
87
|
},
|
|
88
|
-
"gitHead": "
|
|
88
|
+
"gitHead": "912c9427199c112f1f66e09b6a9ca86cc6797968",
|
|
89
89
|
"scripts": {
|
|
90
90
|
"prebuild": "pnpm run build:workspace-deps",
|
|
91
|
-
"ensure:diffr": "diffr-fetch --into bin",
|
|
92
91
|
"build": "node --max-old-space-size=8192 node_modules/tsdown/dist/run.mjs",
|
|
93
92
|
"build:tutorial-assets": "tsx scripts/build-tutorial-assets.ts",
|
|
94
93
|
"ensure:tutorial-assets": "tsx scripts/ensure-tutorial-assets.ts",
|
|
@@ -103,7 +102,7 @@
|
|
|
103
102
|
"test:node": "vitest run --config vitest.config.ts --project shared-module-graph",
|
|
104
103
|
"test:telemetry-privacy": "vitest run --config vitest.config.ts src/ui-telemetry-events.test.ts src/error-telemetry.test.ts",
|
|
105
104
|
"typecheck": "pnpm -w exec tsc --noEmit -p packages/review",
|
|
106
|
-
"test:integration:diffr": "pnpm run build:workspace-deps &&
|
|
105
|
+
"test:integration:diffr": "pnpm run build:workspace-deps && vitest run --config vitest.integration.config.ts",
|
|
107
106
|
"whiteboard": "tsx src/cli.ts"
|
|
108
107
|
}
|
|
109
108
|
}
|
|
@@ -4,7 +4,6 @@ import {
|
|
|
4
4
|
type StructuralDiff,
|
|
5
5
|
type StructuralRegion,
|
|
6
6
|
type StructuralSource,
|
|
7
|
-
type StructuralVisibility,
|
|
8
7
|
structuralRows,
|
|
9
8
|
} from "@dev.fast/review-protocol";
|
|
10
9
|
import type { AlignmentRow } from "@review/lens-selection.js";
|
|
@@ -217,7 +216,7 @@ export async function comparisonCoverage(
|
|
|
217
216
|
fingerprint,
|
|
218
217
|
changed:
|
|
219
218
|
diff.type === "text" ? diff.structural_changes : emptyCoverage(),
|
|
220
|
-
folded: foldedChanges(
|
|
219
|
+
folded: foldedChanges(diff),
|
|
221
220
|
viewed: emptyCoverage(),
|
|
222
221
|
});
|
|
223
222
|
publish?.({
|
|
@@ -244,21 +243,17 @@ export async function comparisonCoverage(
|
|
|
244
243
|
* the lines, not just the counts, are known. A paired leaf contributes its
|
|
245
244
|
* changed spans' lines; an unpaired leaf, all of its lines.
|
|
246
245
|
*/
|
|
247
|
-
export function foldedChanges(
|
|
248
|
-
visibility: StructuralVisibility | undefined,
|
|
249
|
-
diff: StructuralDiff,
|
|
250
|
-
): Coverage {
|
|
246
|
+
export function foldedChanges(diff: StructuralDiff): Coverage {
|
|
251
247
|
if (diff.type !== "text") return emptyCoverage();
|
|
252
248
|
const all = diff.structural_changes;
|
|
253
249
|
|
|
254
|
-
if (visibility?.collapsed)
|
|
255
|
-
return { base: unionIntervals(all.base), head: unionIntervals(all.head) };
|
|
256
|
-
|
|
257
250
|
const side = (
|
|
258
251
|
changed: readonly LineInterval[],
|
|
259
252
|
source: StructuralSource | undefined,
|
|
260
253
|
other: StructuralSource | undefined,
|
|
261
254
|
): LineInterval[] => {
|
|
255
|
+
if (source?.root.visibility?.collapsed) return unionIntervals(changed);
|
|
256
|
+
|
|
262
257
|
const paired = new Set<number>();
|
|
263
258
|
|
|
264
259
|
const pair = (region: StructuralRegion) => {
|
|
@@ -266,7 +261,7 @@ export function foldedChanges(
|
|
|
266
261
|
else region.children.forEach(pair);
|
|
267
262
|
};
|
|
268
263
|
|
|
269
|
-
other
|
|
264
|
+
if (other) pair(other.root);
|
|
270
265
|
|
|
271
266
|
const hidden: LineInterval[] = [],
|
|
272
267
|
visible: LineInterval[] = [];
|
|
@@ -292,7 +287,7 @@ export function foldedChanges(
|
|
|
292
287
|
]);
|
|
293
288
|
};
|
|
294
289
|
|
|
295
|
-
|
|
290
|
+
if (source) collect(source.root, false);
|
|
296
291
|
|
|
297
292
|
return subtractIntervals(intersectIntervals(changed, hidden), visible);
|
|
298
293
|
};
|
package/src/review-api/http.ts
CHANGED
|
@@ -12,11 +12,12 @@ import {
|
|
|
12
12
|
AgentSelectionSchema,
|
|
13
13
|
selectionMarkdown,
|
|
14
14
|
} from "@review/agent-selection.js";
|
|
15
|
-
import type
|
|
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,
|
|
@@ -1154,15 +1155,15 @@ export function createReviewApi(
|
|
|
1154
1155
|
checkout.live
|
|
1155
1156
|
? "Your working directory is the repository the review describes."
|
|
1156
1157
|
: `Your working directory is a checkout of the review's head commit, ${checkout.head}. Answer from this code, not from other branches.`,
|
|
1157
|
-
"Answer the question.",
|
|
1158
|
+
"Answer the question for a staff engineer: lead with the answer and keep it short. Explain at the level of components and data flow before functions, and check each claim about the code against code you have read.",
|
|
1158
1159
|
"Name files by their path from the checkout root, with a line where it helps, as in `src/app.ts:42`; the reviewer can open them from your answer.",
|
|
1159
1160
|
...(reach?.kind === "mcp"
|
|
1160
1161
|
? [
|
|
1161
|
-
`The whiteboard MCP tools read and change this review: its sessionId is "${reviewId}". Read it with session_get. If the reviewer asks you to change the review, edit it with session_edit; do not write files to do it.`,
|
|
1162
|
+
`The whiteboard MCP tools read and change this review: its sessionId is "${reviewId}". Read it with session_get. If the reviewer asks you to change the review, first read session_get_instructions({}) for its guidelines and each component's fields (the review exists, so skip creating one), then edit it with session_edit; do not write files to do it.`,
|
|
1162
1163
|
]
|
|
1163
1164
|
: reach?.kind === "cli"
|
|
1164
1165
|
? [
|
|
1165
|
-
`Whiteboard's CLI reads and changes this review from your shell: its sessionId is "${reviewId}". Read it with \`${reach.command} api session_get '{"sessionId":"${reviewId}"}'\`. If the reviewer asks you to change the review, edit it with \`${reach.command} api session_edit '<json>'\`; do not write files to do it. \`${reach.command} api tools\` lists each tool's input.`,
|
|
1166
|
+
`Whiteboard's CLI reads and changes this review from your shell: its sessionId is "${reviewId}". Read it with \`${reach.command} api session_get '{"sessionId":"${reviewId}"}'\`. If the reviewer asks you to change the review, first read \`${reach.command} api session_get_instructions '{}'\` for its guidelines and each component's fields (the review exists, so skip creating one), then edit it with \`${reach.command} api session_edit '<json>'\`; do not write files to do it. \`${reach.command} api tools\` lists each tool's input.`,
|
|
1166
1167
|
]
|
|
1167
1168
|
: []),
|
|
1168
1169
|
"",
|
|
@@ -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) => {
|
package/src/review-api/store.ts
CHANGED
|
@@ -1237,11 +1237,6 @@ export class ReviewStore {
|
|
|
1237
1237
|
...(applied?.children && { children: applied.children }),
|
|
1238
1238
|
};
|
|
1239
1239
|
|
|
1240
|
-
if (snapshot.staleSources?.length)
|
|
1241
|
-
warnings.push(
|
|
1242
|
-
"Some authored source ranges changed. Update their references before presenting this review.",
|
|
1243
|
-
);
|
|
1244
|
-
|
|
1245
1240
|
if (warnings.length) result.warnings = warnings;
|
|
1246
1241
|
|
|
1247
1242
|
this.commitCommand(
|