prreviewbuddy 0.95.2 → 0.96.2

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.
@@ -851,6 +851,12 @@ var CLAUDE_AGENT_ID = "claude";
851
851
  * about sign-in to errors that have nothing to do with it, which is worse than staying quiet.
852
852
  */
853
853
  var AUTHENTICATION$2 = /\b(?:auth(?:enticat\w*)?|oauth|credential|session expired|not logged in|log ?in)\b/i;
854
+ /**
855
+ * What Claude Code prints, and exits 1 on, for an argument it does not know: `error: unknown option
856
+ * '--setting-sources'` (seen on 2.1.318 with a made-up flag). Every argument comes from
857
+ * `claudeArgs`, so this means the installed CLI is older than one of them.
858
+ */
859
+ var UNKNOWN_OPTION = /unknown option '--[\w-]+'/i;
854
860
  /** Said to the caller that asked for the stop, and never shown to the reviewer who pressed it. */
855
861
  var STOPPED$2 = "The agent was stopped.";
856
862
  /** How long a stopped agent gets to end on its own terms before it is ended for it. */
@@ -1060,6 +1066,17 @@ var VERSION_TIMEOUT_MS$2 = 1e4;
1060
1066
  * `--strict-mcp-config`, with no `--mcp-config` beside it, is the containment clause of the agent
1061
1067
  * contract as Claude satisfies it. It tells the CLI to load no MCP servers at all, so `prb_review`
1062
1068
  * is not there to be reached no matter which `CLAUDE_CONFIG_DIR` the spawn inherits.
1069
+ *
1070
+ * `--setting-sources user` is the other half. The run stands in a worktree of the change under
1071
+ * review, and by default the CLI also loads that worktree's `.claude/settings.json` and
1072
+ * `settings.local.json`: settings written by the change's author. Their hooks run as the reviewer,
1073
+ * before the model sees anything and outside `--allowedTools`, and their permissions widen the tool
1074
+ * policy. Observed on Claude Code 2.1.318 with a canary `SessionStart` hook: it ran under the
1075
+ * arguments above without this flag and not with it, while the reviewer's own hooks ran both times.
1076
+ * The same flag keeps out the worktree's `CLAUDE.md`, `.claude/agents` and `.claude/skills`, which
1077
+ * reach the model as instructions: a canary of each was absent from the `init` event and unknown to
1078
+ * the model under these arguments (2026-11-08), while the reviewer's own agents and skills remained.
1079
+ * `--bare` would also skip them, but it skips the reviewer's sign-in too.
1063
1080
  */
1064
1081
  function claudeArgs(capability, requestedModel) {
1065
1082
  return [
@@ -1070,6 +1087,8 @@ function claudeArgs(capability, requestedModel) {
1070
1087
  "--allowedTools",
1071
1088
  toolsFor(capability),
1072
1089
  "--strict-mcp-config",
1090
+ "--setting-sources",
1091
+ "user",
1073
1092
  ...requestedModel ? ["--model", requestedModel] : []
1074
1093
  ];
1075
1094
  }
@@ -1109,6 +1128,7 @@ var claudeAgent = {
1109
1128
  * situation nobody is in until they have tried the obvious thing first.
1110
1129
  */
1111
1130
  explainFailure(detail, env = {}) {
1131
+ if (UNKNOWN_OPTION.test(detail)) return "This Claude Code is too old for PR Review Buddy. Update it with `claude update`, then try again.";
1112
1132
  if (!AUTHENTICATION$2.test(detail)) return null;
1113
1133
  return `Claude Code could not sign in. This review was started using ${env.CLAUDE_CONFIG_DIR ? `the Claude profile at \`${env.CLAUDE_CONFIG_DIR}\`` : "your default Claude profile"}, so make sure that one is signed in.`;
1114
1134
  },
@@ -1653,14 +1673,25 @@ var codexMcpRegistration = {
1653
1673
  * `--no-mcp`. Two things were established by running read-only commands rather than by reading
1654
1674
  * documentation, and they are the reason this adapter exists at all:
1655
1675
  *
1656
- * - MCP servers resolve only from `$CODEX_HOME/config.toml`, which `--ignore-user-config` drops
1657
- * while leaving the reviewer's login working. So containment costs no credential.
1676
+ * - Configured MCP servers resolve from `$CODEX_HOME/config.toml`, which `--ignore-user-config`
1677
+ * drops while leaving the reviewer's login working. So containment costs no credential. Plugins
1678
+ * are the exception: a remote plugin's server (Notion, from the plugin cache) still started under
1679
+ * it on codex-cli 0.157.0, and only `--disable plugins` stopped it. Connected apps are the other:
1680
+ * the reviewer's ChatGPT apps arrive as a built-in `codex_apps` server (GitHub with
1681
+ * `merge_pull_request`, Google Drive with `share_file`, a web search) whose calls run on OpenAI's
1682
+ * side, where the read-only sandbox does not reach. `--disable apps` removes all of them.
1658
1683
  * - A repository's own `.codex/config.toml` contributes nothing. This was the open question, and
1659
1684
  * it mattered because a review runs in a worktree cut from the repository under review: a
1660
1685
  * project-level server would have been an untrusted repository injecting a tool into a run that
1661
1686
  * is supposed to have none. A canary server declared in project config does not appear in
1662
1687
  * `codex mcp list` run from that repository.
1663
1688
  *
1689
+ * The reviewed repository's `AGENTS.md` is kept out with `-c project_doc_max_bytes=0`, for the same
1690
+ * reason the Claude adapter keeps out `CLAUDE.md`: the author of the change can edit it in the same
1691
+ * change. `-c web_search="disabled"` removes the web tool, which a read-only review has no use for.
1692
+ * The repository's `.codex/` config and hooks need no flag: with user config ignored the repository
1693
+ * is untrusted, so its project layer is never loaded (verified 2026-11-08 with a canary repository).
1694
+ *
1664
1695
  * `-c mcp_servers={}` is deliberately not used: the override layer is deep-merged, so an empty
1665
1696
  * table iterates zero keys and leaves the configured servers exactly as they were. It looks like
1666
1697
  * containment and is not.
@@ -1682,6 +1713,39 @@ var STOPPED = "The run was stopped.";
1682
1713
  */
1683
1714
  var CARRIED = ["CODEX_HOME", "PATH"];
1684
1715
  var VERSION_TIMEOUT_MS = 1e4;
1716
+ /**
1717
+ * The oldest Codex a review will start on.
1718
+ *
1719
+ * `-c web_search="disabled"` is what keeps a review off the web, and Codex ignores a `-c` key it
1720
+ * does not know (exit 0 on 0.157.0), so on a Codex older than the key the switch would do nothing
1721
+ * and say nothing. This is the oldest release where the switch is known to work: 0.122.0 is also
1722
+ * the first with `--ignore-user-config` and `--ignore-rules` (#18646), so nothing older could run
1723
+ * a review anyway. Established 2026-11-08 from the openai/codex source at each tag:
1724
+ *
1725
+ * - the hosted web tool is built only for `Cached` or `Live` (`create_web_search_tool` returns
1726
+ * `None` for `Disabled` at 0.122.0; `hosted_spec.rs` at 0.134.0), and
1727
+ * - the standalone web search extension, from 0.135.0, is enabled only when the mode is not
1728
+ * `Disabled` (checked at 0.135, 0.140, 0.145, 0.150 and 0.157).
1729
+ *
1730
+ * And on 0.157.0 by the tools the Responses API echoed back: `web.run` and `web__run` without the
1731
+ * switch, neither with it. Older macOS builds could not be run to repeat that: every release before
1732
+ * 0.132.0 is signed with a revoked certificate (0.131.0 is not notarized) and macOS refuses it.
1733
+ */
1734
+ var MINIMUM_CODEX_VERSION = "0.122.0";
1735
+ /**
1736
+ * Why a review will not start on this Codex, or null when it will.
1737
+ *
1738
+ * An unreadable version is refused rather than assumed, and a pre-release of the minimum counts as
1739
+ * older than it: neither is known to have what the release has.
1740
+ */
1741
+ function codexVersionRefusal(version) {
1742
+ const update = "Update it with `codex update`, or with the package manager you installed it from if that command is missing, then try again.";
1743
+ const parsed = version ? /^(\d+)\.(\d+)\.(\d+)(.*)$/.exec(version) : null;
1744
+ if (!parsed) return `PR Review Buddy could not tell which Codex is installed, and needs Codex ${MINIMUM_CODEX_VERSION} or newer. ${update}`;
1745
+ const have = parsed.slice(1, 4).map(Number);
1746
+ const need = MINIMUM_CODEX_VERSION.split(".").map(Number);
1747
+ return (have.map((part, i) => part - need[i]).find((diff) => diff !== 0) ?? (parsed[4].startsWith("-") ? -1 : 0)) < 0 ? `Codex ${version} is too old for PR Review Buddy, which needs Codex ${MINIMUM_CODEX_VERSION} or newer. ${update}` : null;
1748
+ }
1685
1749
  var DEFAULT_TIMEOUT_MS = { "read-code": 12e5 };
1686
1750
  function codexArgs(_capability, requestedModel) {
1687
1751
  return [
@@ -1692,6 +1756,14 @@ function codexArgs(_capability, requestedModel) {
1692
1756
  "read-only",
1693
1757
  "--ignore-user-config",
1694
1758
  "--ignore-rules",
1759
+ "--disable",
1760
+ "plugins",
1761
+ "--disable",
1762
+ "apps",
1763
+ "-c",
1764
+ "project_doc_max_bytes=0",
1765
+ "-c",
1766
+ "web_search=\"disabled\"",
1695
1767
  "--ephemeral",
1696
1768
  ...requestedModel ? ["-m", requestedModel] : []
1697
1769
  ];
@@ -1822,6 +1894,14 @@ var AUTHENTICATION = /not (?:logged|signed) in|unauthori[sz]ed|401|authenticat|c
1822
1894
  * authentication failure, so no Retry is offered.
1823
1895
  */
1824
1896
  var REJECTED_KEY = /incorrect api key|invalid[_ ]api[_ ]key/i;
1897
+ /**
1898
+ * A Codex older than a switch a review passes: an unknown feature name (`Unknown feature flag:
1899
+ * <name>` on 0.157.0) or, older still, an option it has never heard of. Fixed by updating, so it is
1900
+ * said as that. `codex update` exists on 0.157.0, but the Codex reading this is older by definition
1901
+ * and may predate it, so the package manager is named as the fallback. Neither refusal reads as a sign-in failure, so it stays an execution failure and
1902
+ * Retry is there after the update.
1903
+ */
1904
+ var TOO_OLD = /unknown feature flag|unexpected argument '--[\w-]+'/i;
1825
1905
  function isAuthentication(detail) {
1826
1906
  return AUTHENTICATION.test(detail) || REJECTED_KEY.test(detail);
1827
1907
  }
@@ -1845,6 +1925,7 @@ var codexAgent = {
1845
1925
  mcpRegistration: codexMcpRegistration,
1846
1926
  /** Which tool, which profile, and the one command that changes it. See `agent.ts` for the budget. */
1847
1927
  explainFailure(detail, env = {}) {
1928
+ if (TOO_OLD.test(detail)) return "This Codex is too old for PR Review Buddy. Update it with `codex update`, or with the package manager you installed it from if that command is missing, then try again.";
1848
1929
  if (!isAuthentication(detail)) return null;
1849
1930
  const profile = env.CODEX_HOME ? `the Codex profile at \`${env.CODEX_HOME}\`` : "your default Codex profile";
1850
1931
  if (REJECTED_KEY.test(detail)) return `OpenAI refused a request from ${profile} even though Codex reports being signed in, which usually points to Codex's own setup rather than your sign-in. Run \`codex doctor\` to see what it is using, and check \`codex features list\` for an experimental feature to turn off with \`codex features disable <name>\`.`;
@@ -1864,11 +1945,20 @@ var codexAgent = {
1864
1945
  encoding: "utf8",
1865
1946
  env: spawnEnv(env)
1866
1947
  }, (error, stdout) => {
1867
- if (!error) return resolvePromise({
1868
- ok: true,
1869
- version: versionFrom(stdout),
1870
- path: whichOn("codex", spawnEnv(env).PATH)
1871
- });
1948
+ if (!error) {
1949
+ const version = versionFrom(stdout);
1950
+ const refusal = codexVersionRefusal(version);
1951
+ if (refusal) return resolvePromise({
1952
+ ok: false,
1953
+ found: true,
1954
+ reason: refusal
1955
+ });
1956
+ return resolvePromise({
1957
+ ok: true,
1958
+ version,
1959
+ path: whichOn("codex", spawnEnv(env).PATH)
1960
+ });
1961
+ }
1872
1962
  const found = error.code !== "ENOENT";
1873
1963
  resolvePromise({
1874
1964
  ok: false,
@@ -1881,6 +1971,9 @@ var codexAgent = {
1881
1971
  async run(input) {
1882
1972
  guardPrompt(input);
1883
1973
  if (input.signal?.aborted) throw new AgentCancelledError(STOPPED);
1974
+ const installed = await codexAgent.available(input.env);
1975
+ if (!installed.ok) throw new CodexUnavailableError(installed.reason);
1976
+ if (input.signal?.aborted) throw new AgentCancelledError(STOPPED);
1884
1977
  const reader = assemble$2(input.onProgress ?? (() => {}));
1885
1978
  return new Promise((resolvePromise, reject) => {
1886
1979
  const child = spawn("codex", codexArgs(input.capability, input.requestedModel), {
@@ -6556,6 +6649,159 @@ function lifecycleItemLabel(target, lifecycle, activity) {
6556
6649
  return lifecycleAction(target);
6557
6650
  }
6558
6651
  //#endregion
6652
+ //#region ../../packages/review-harness/src/workspace/reviewer_outcome.ts
6653
+ var REVIEWER_OUTCOMES = [
6654
+ {
6655
+ id: "no-concerns",
6656
+ name: "Reviewed: no concerns",
6657
+ baseline: true
6658
+ },
6659
+ {
6660
+ id: "needs-changes",
6661
+ name: "Needs changes",
6662
+ baseline: true
6663
+ },
6664
+ {
6665
+ id: "needs-decision",
6666
+ name: "Needs a decision / answer",
6667
+ baseline: true
6668
+ },
6669
+ {
6670
+ id: "deeper-review",
6671
+ name: "Needs deeper review",
6672
+ baseline: false
6673
+ },
6674
+ {
6675
+ id: "later",
6676
+ name: "Later",
6677
+ baseline: false
6678
+ }
6679
+ ];
6680
+ function parseOutcomeSnapshot(value) {
6681
+ if (typeof value !== "object" || value === null) return null;
6682
+ const raw = value;
6683
+ if (typeof raw.sessionId !== "string" || typeof raw.commitSha !== "string" || typeof raw.analysisRevision !== "string") return null;
6684
+ return {
6685
+ sessionId: raw.sessionId,
6686
+ commitSha: raw.commitSha,
6687
+ analysisRevision: raw.analysisRevision
6688
+ };
6689
+ }
6690
+ function outcomeDefinition(kind) {
6691
+ return REVIEWER_OUTCOMES.find((outcome) => outcome.id === kind);
6692
+ }
6693
+ function isBaselineKind(kind) {
6694
+ return outcomeDefinition(kind)?.baseline === true;
6695
+ }
6696
+ function parseStoredOutcome(value) {
6697
+ if (typeof value !== "object" || value === null) return void 0;
6698
+ const raw = value;
6699
+ if (typeof raw.kind !== "string" || !outcomeDefinition(raw.kind)) return void 0;
6700
+ if (typeof raw.at !== "number" || !Number.isFinite(raw.at)) return void 0;
6701
+ if (typeof raw.commitSha !== "string" || typeof raw.sessionId !== "string") return void 0;
6702
+ return {
6703
+ kind: raw.kind,
6704
+ at: raw.at,
6705
+ commitSha: raw.commitSha,
6706
+ sessionId: raw.sessionId,
6707
+ analysisRevision: typeof raw.analysisRevision === "string" ? raw.analysisRevision : "",
6708
+ ...typeof raw.recordId === "string" ? { recordId: raw.recordId } : {},
6709
+ ...typeof raw.analysisJobId === "string" ? { analysisJobId: raw.analysisJobId } : {},
6710
+ ...Array.isArray(raw.answeredQuestions) ? { answeredQuestions: raw.answeredQuestions.filter((q) => typeof q === "string") } : {}
6711
+ };
6712
+ }
6713
+ function parseOutcomes(value) {
6714
+ if (!Array.isArray(value)) return [];
6715
+ return value.map(parseStoredOutcome).filter((outcome) => outcome !== void 0);
6716
+ }
6717
+ /** Newest last, capped. The baseline lives in its own field, so nothing here has to survive pruning. */
6718
+ function appendOutcome(history, outcome) {
6719
+ return [...history, outcome].slice(-20);
6720
+ }
6721
+ /** The newer of two baselines; a non-baseline outcome never replaces one. */
6722
+ function nextBaseline(current, outcome) {
6723
+ if (!isBaselineKind(outcome.kind)) return current;
6724
+ return !current || outcome.at >= current.at ? outcome : current;
6725
+ }
6726
+ /** The newest baseline across a lineage's records, so a re-analysis that made a new record keeps it. */
6727
+ function lastBaseline(baselines) {
6728
+ let newest;
6729
+ for (const baseline of baselines) if (baseline && (!newest || baseline.at > newest.at)) newest = baseline;
6730
+ return newest;
6731
+ }
6732
+ function sameSha(a, b) {
6733
+ if (!a || !b) return false;
6734
+ const length = Math.min(a.length, b.length);
6735
+ return length >= 7 && a.slice(0, length) === b.slice(0, length);
6736
+ }
6737
+ /** How an outcome relates to the analysis on screen. */
6738
+ function outcomeStanding(outcome, snapshot) {
6739
+ if (!sameSha(outcome.commitSha, snapshot.commitSha)) return "earlier-snapshot";
6740
+ if (outcome.sessionId !== snapshot.sessionId || outcome.analysisRevision !== snapshot.analysisRevision) return "earlier-analysis";
6741
+ return "current";
6742
+ }
6743
+ /**
6744
+ * Verdicts come from the history and from the stored baseline, which is never pruned: twenty
6745
+ * deferrals after a verdict push it out of the history, but not out of the reading.
6746
+ */
6747
+ function readOutcomes(history, snapshot, baseline) {
6748
+ const verdicts = history.filter((outcome) => isBaselineKind(outcome.kind));
6749
+ if (baseline && isBaselineKind(baseline.kind) && !verdicts.some((outcome) => outcome.at === baseline.at && outcome.kind === baseline.kind)) verdicts.push(baseline);
6750
+ verdicts.sort((a, b) => a.at - b.at);
6751
+ const newestVerdict = verdicts.at(-1);
6752
+ if (!snapshot) return newestVerdict ? { previous: {
6753
+ outcome: newestVerdict,
6754
+ standing: "earlier-snapshot"
6755
+ } } : {};
6756
+ const verdict = verdicts.filter((outcome) => outcomeStanding(outcome, snapshot) === "current").at(-1);
6757
+ const deferral = history.filter((outcome) => !isBaselineKind(outcome.kind) && outcomeStanding(outcome, snapshot) === "current" && (!verdict || outcome.at > verdict.at)).at(-1);
6758
+ const reading = {};
6759
+ if (verdict) reading.verdict = verdict;
6760
+ else if (newestVerdict) reading.previous = {
6761
+ outcome: newestVerdict,
6762
+ standing: outcomeStanding(newestVerdict, snapshot)
6763
+ };
6764
+ if (deferral) reading.deferral = deferral;
6765
+ return reading;
6766
+ }
6767
+ /** "Needs changes", or how an older one reads beside a newer analysis. */
6768
+ function outcomeLabel(outcome, standing) {
6769
+ const name = outcomeDefinition(outcome.kind)?.name ?? outcome.kind;
6770
+ if (standing === "current") return name;
6771
+ if (standing === "earlier-analysis") return `Outcome on an earlier analysis: ${name}`;
6772
+ return `${name} on ${outcome.commitSha.slice(0, 7)} (earlier snapshot)`;
6773
+ }
6774
+ //#endregion
6775
+ //#region ../../packages/review-harness/src/workspace/analysis_revision.ts
6776
+ /**
6777
+ * A short fingerprint of the analysis a reviewer is reading: its findings, statuses, notes and text.
6778
+ *
6779
+ * The session id and the commit do not move when a Re-check reconciles findings at the same commit,
6780
+ * so a verdict checked against those two alone would be accepted for findings the reviewer never saw.
6781
+ * The reviewer's own ticks are not part of it: those are the reviewer's acts, not the analysis.
6782
+ */
6783
+ function analysisRevision(workspace) {
6784
+ const result = workspace.session.result;
6785
+ if (!result) return "";
6786
+ return createHash("sha256").update(JSON.stringify(result)).digest("hex").slice(0, 16);
6787
+ }
6788
+ /** The analysed commit, falling back the way `reviewedCommit` does. */
6789
+ function analysedCommit(workspace) {
6790
+ return workspace.session.metadata?.commitSha || workspace.changeSet.data.pullRequest.head.sha || "";
6791
+ }
6792
+ function analysisSnapshot(workspace) {
6793
+ if (!workspace.session.result) return null;
6794
+ return {
6795
+ sessionId: workspace.session.id,
6796
+ commitSha: analysedCommit(workspace),
6797
+ analysisRevision: analysisRevision(workspace)
6798
+ };
6799
+ }
6800
+ /** When the analysis on screen was last produced or reconciled: the upper time bound for its history. */
6801
+ function analysedAt(workspace) {
6802
+ return Math.max(workspace.session.metadata?.generatedAt ?? 0, workspace.lastUpdate?.at ?? 0, workspace.lastRecheck?.at ?? 0, workspace.createdAt);
6803
+ }
6804
+ //#endregion
6559
6805
  //#region ../../packages/review-harness/src/workspace/store.ts
6560
6806
  var WORKSPACES_DIR = join(STORE_ROOT, "workspaces");
6561
6807
  /**
@@ -6750,6 +6996,18 @@ function workspaceRevision(workspace) {
6750
6996
  }
6751
6997
  }
6752
6998
  /**
6999
+ * Moves whenever any review is written or deleted, by this process or another: every write stages a
7000
+ * file in the directory and renames it into place, and a delete unlinks one. One stat, where
7001
+ * `workspaceStamps` is one per review, for a cache to ask whether anything changed at all.
7002
+ */
7003
+ function workspacesRevision() {
7004
+ try {
7005
+ return statSync(WORKSPACES_DIR).mtimeMs;
7006
+ } catch {
7007
+ return 0;
7008
+ }
7009
+ }
7010
+ /**
6753
7011
  * Every stored review as an id and a revision token, without reading one of them.
6754
7012
  *
6755
7013
  * Here rather than in `reviews_revision.ts` because this module owns the layout of `workspaces/`,
@@ -7031,6 +7289,14 @@ function groupByLineage(reviews) {
7031
7289
  }
7032
7290
  return [...groups].map(([key, members]) => {
7033
7291
  const history = [...members].sort((a, b) => b.createdAt - a.createdAt);
7292
+ const [newest] = history;
7293
+ if (newest.outcomeInputs && history.length > 1) {
7294
+ const { outcome: _outcome, deferral: _deferral, ...rest } = newest;
7295
+ history[0] = {
7296
+ ...rest,
7297
+ ...lineageOutcome(newest.outcomeInputs, history.map((review) => review.outcomeInputs))
7298
+ };
7299
+ }
7034
7300
  return {
7035
7301
  key,
7036
7302
  latest: history[0],
@@ -7161,6 +7427,8 @@ function summarise(workspace) {
7161
7427
  })(),
7162
7428
  lastViewedAt: workspace.lastViewedAt,
7163
7429
  completedAt: workspace.completedAt,
7430
+ outcomeInputs: outcomeInputsOf(workspace),
7431
+ ...outcomeSummary(outcomeInputsOf(workspace)),
7164
7432
  pinned: workspace.pinned === true,
7165
7433
  lifecycle: parseLifecycle(workspace.lifecycle),
7166
7434
  paths: files.slice(0, MAX_INDEXED_PATHS).map((file) => file.name),
@@ -7171,6 +7439,42 @@ function summarise(workspace) {
7171
7439
  authorName: workspace.prContext?.pullRequest.author?.name || void 0
7172
7440
  };
7173
7441
  }
7442
+ function outcomeInputsOf(workspace) {
7443
+ const baseline = parseStoredOutcome(workspace.reviewedBaseline);
7444
+ return {
7445
+ history: parseOutcomes(workspace.reviewerOutcomes),
7446
+ snapshot: analysisSnapshot(workspace),
7447
+ ...baseline ? { baseline } : {}
7448
+ };
7449
+ }
7450
+ /**
7451
+ * The newest record's outcomes read with the newest baseline of its whole lineage, as Quick Review
7452
+ * reads them. A fresh analysis makes a record with no baseline of its own, and the verdict on the
7453
+ * one before must still show on the card, as an earlier one, instead of vanishing from it.
7454
+ */
7455
+ function lineageOutcome(newest, lineage) {
7456
+ return outcomeSummary(newest, lastBaseline([newest.baseline, ...lineage.map((inputs) => inputs?.baseline)]));
7457
+ }
7458
+ function outcomeSummary(inputs, baseline = inputs.baseline) {
7459
+ const reading = readOutcomes(inputs.history, inputs.snapshot, baseline);
7460
+ const verdict = reading.verdict ? {
7461
+ outcome: reading.verdict,
7462
+ standing: "current"
7463
+ } : reading.previous;
7464
+ return {
7465
+ ...verdict ? { outcome: {
7466
+ kind: verdict.outcome.kind,
7467
+ standing: verdict.standing,
7468
+ label: outcomeLabel(verdict.outcome, verdict.standing),
7469
+ at: verdict.outcome.at
7470
+ } } : {},
7471
+ ...reading.deferral ? { deferral: {
7472
+ kind: reading.deferral.kind,
7473
+ label: outcomeLabel(reading.deferral, "current"),
7474
+ at: reading.deferral.at
7475
+ } } : {}
7476
+ };
7477
+ }
7174
7478
  /**
7175
7479
  * The commit a review was made from, however the record spells it. Null when it recorded none.
7176
7480
  *
@@ -8386,6 +8690,14 @@ function sanitisePullRequest(value) {
8386
8690
  if (template) pr.fileUrlTemplate = template;
8387
8691
  const author = pullRequestAuthor(value.author);
8388
8692
  if (author) pr.author = author;
8693
+ if (isRecord$1(value.headRepository)) {
8694
+ const owner = label(value.headRepository.owner);
8695
+ const repo = label(value.headRepository.repo);
8696
+ if (owner && repo) pr.headRepository = {
8697
+ owner,
8698
+ repo
8699
+ };
8700
+ }
8389
8701
  return pr;
8390
8702
  }
8391
8703
  /**
@@ -9093,7 +9405,7 @@ function slug(repo) {
9093
9405
  * `baseRefOid` is a diff taken against the wrong *commit*, which is worse because the branch name
9094
9406
  * is right and everything reads as normal. A list nobody asserts is a list somebody tidies.
9095
9407
  */
9096
- var PR_FIELDS = "number,url,title,body,headRefName,headRefOid,baseRefName,baseRefOid,changedFiles,author";
9408
+ var PR_FIELDS = "number,url,title,body,headRefName,headRefOid,baseRefName,baseRefOid,changedFiles,author,headRepository,headRepositoryOwner";
9097
9409
  async function viewRequest(repo, reference, cwd, env) {
9098
9410
  return await ghJson([
9099
9411
  "pr",
@@ -9142,7 +9454,11 @@ function identity(raw) {
9142
9454
  author: author ? {
9143
9455
  name: text$2(author.name),
9144
9456
  handle: text$2(author.login)
9145
- } : null
9457
+ } : null,
9458
+ ...text$2(raw.headRepositoryOwner?.login) && text$2(raw.headRepository?.name) ? { headRepository: {
9459
+ owner: text$2(raw.headRepositoryOwner?.login),
9460
+ repo: text$2(raw.headRepository?.name)
9461
+ } } : {}
9146
9462
  };
9147
9463
  }
9148
9464
  /**
@@ -11337,6 +11653,32 @@ ${diff}`;
11337
11653
  };
11338
11654
  }
11339
11655
  //#endregion
11656
+ //#region ../../packages/review-harness/src/domain/ai_review/behaviour_change.ts
11657
+ /** Both sides present and non-empty, or nothing: half an explanation is not one. */
11658
+ function parseBehaviourChange(value) {
11659
+ if (typeof value !== "object" || value === null || Array.isArray(value)) return void 0;
11660
+ const raw = value;
11661
+ const before = typeof raw.before === "string" ? raw.before.trim() : "";
11662
+ const after = typeof raw.after === "string" ? raw.after.trim() : "";
11663
+ return before && after ? {
11664
+ before,
11665
+ after
11666
+ } : void 0;
11667
+ }
11668
+ function withBehaviourChange(change) {
11669
+ return change ? { behaviourChange: change } : {};
11670
+ }
11671
+ /**
11672
+ * The behaviourChange an update keeps.
11673
+ *
11674
+ * Restated: the new one. New code and none restated: dropped, because the old one describes a head
11675
+ * that no longer exists. A conversation-only pass read no new code, so the previous one still holds.
11676
+ */
11677
+ function mergeBehaviourChange(previous, restated, codeChanged) {
11678
+ if (restated) return restated;
11679
+ return codeChanged ? void 0 : previous;
11680
+ }
11681
+ //#endregion
11340
11682
  //#region ../../packages/review-harness/src/domain/utils/html.ts
11341
11683
  function escapeHtml(value) {
11342
11684
  return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
@@ -12652,6 +12994,10 @@ var CONSEQUENCE_GUIDANCE = `"consequence" on an issue: the same issue told as wh
12652
12994
  - An issue names a concrete consequence. A request for missing requirements with no concrete consequence is a question for the author, not an issue, and is never raised as both.`;
12653
12995
  /** What "orientation" is for, as the first analysis and an update ask for it. */
12654
12996
  var ORIENTATION_GUIDANCE = `"orientation": one sentence, at most about 25 words, naming the behaviour or product area this change touches, for a reviewer about to decide on its issues (e.g. "This change affects receipt extraction and how duplicate receipt details are combined."). Say what it touches, not whether it is safe, and do not summarise the issues.`;
12997
+ /** The behaviourChange shape, written out whole for every prompt that asks for it. */
12998
+ var BEHAVIOUR_CHANGE_FIELD = "\"behaviourChange\"?: { \"before\": string, \"after\": string }";
12999
+ /** What "behaviourChange" is for, as the first analysis and an update ask for it. */
13000
+ var BEHAVIOUR_CHANGE_GUIDANCE = `"behaviourChange": how the behaviour this pull request touches works today and how it works with the whole pull request applied, for a reviewer who knows the product. "before" is the behaviour at the base the pull request targets; "after" is the behaviour at the head you analysed. One to three plain sentences each, leading with what a user or the system experiences, not with the implementation (e.g. before: "A failed payment is shown as an error and the order is cancelled.", after: "A failed payment is retried twice before the order is cancelled, and the customer is emailed after the last attempt."). Describe the whole pull request, not one commit. Omit the field entirely when the code you read does not establish how it works today: never guess "before".`;
12655
13001
  /**
12656
13002
  * How an update keeps a prior finding's consequence and resolution true, beside its status.
12657
13003
  *
@@ -13458,6 +13804,7 @@ var STRUCTURED_OUTPUT_INSTRUCTIONS = `In addition to the review principles above
13458
13804
  },
13459
13805
  "overallRisk": "low" | "medium" | "high",
13460
13806
  "orientation": string,
13807
+ ${BEHAVIOUR_CHANGE_FIELD},
13461
13808
  "sections": [{
13462
13809
  "id": string,
13463
13810
  "order": number,
@@ -13495,6 +13842,7 @@ Field guidance:
13495
13842
  - ${CONSEQUENCE_GUIDANCE}
13496
13843
  - ${QUESTION_FRAMING_GUIDANCE}
13497
13844
  - ${ORIENTATION_GUIDANCE}
13845
+ - ${BEHAVIOUR_CHANGE_GUIDANCE}
13498
13846
  - "existingDiscussion" on an issue: set this only when a thread in the PR DISCUSSION CONTEXT block already raises this same concern, and you have independently confirmed from the diff that it still applies. "threadId" must be copied verbatim from that thread's [T_...] marker — never invent an id, never guess, and omit the field entirely if no thread matches. Set "relation" to "same" when the thread already makes this exact point, or "related" when it discusses this area without covering the finding — a reviewer acts differently on "someone said this already" than on "someone was looking near here". Still write the issue normally; this field only records that the point was raised before, so the reviewer is not shown it as a fresh discovery. Do not use it to pad the review with issues you would otherwise have left out.
13499
13847
  - "confidence" on an issue: how sure you are the issue is real, on the same high/medium/low scale as a question's "importance". This is a separate axis from "severity", which is how much it would matter if real — a critical issue you are unsure about and a nit you are certain of are different things to act on, so do not let one field stand in for the other. Use LOW when you are reasoning from a diff excerpt without the surrounding code, and say what would confirm it in the description. Omit the field rather than guessing a middle value.
13500
13848
  - "questions": each one is a genuine open item the reviewer needs answered before they can sign off, not a rhetorical prompt. "importance" is how much attention it deserves, on the same high/medium/low scale as a section's "reviewAttention" (e.g. a question about a possibly-exposed secret is "high"; a naming preference is "low"). "whyThisMatters" is 1-2 sentences explaining the concrete consequence of the question going unanswered — not a restatement of the question itself.
@@ -13553,6 +13901,7 @@ function parseUnchecked(raw) {
13553
13901
  summary,
13554
13902
  overallRisk: RISKS.includes(obj.overallRisk) ? obj.overallRisk : "medium",
13555
13903
  ...asString$1(obj.orientation).trim() ? { orientation: asString$1(obj.orientation).trim() } : {},
13904
+ ...withBehaviourChange(parseBehaviourChange(obj.behaviourChange)),
13556
13905
  sections: normalizeSections(obj.sections),
13557
13906
  issues: normalizeIssues(obj.issues),
13558
13907
  questions: normalizeQuestions(obj.questions),
@@ -18226,6 +18575,7 @@ var RECONCILIATION_SHAPE = `{
18226
18575
  }],
18227
18576
  "summary": string,
18228
18577
  "orientation": string,
18578
+ ${BEHAVIOUR_CHANGE_FIELD},
18229
18579
  "overallRisk": "low" | "medium" | "high",
18230
18580
  "recommendation": string,
18231
18581
  "nextSteps": string[]
@@ -18333,6 +18683,12 @@ var GRADE_TASK = {
18333
18683
  * written to fix a finding is precisely where a regression gets introduced. A run that only
18334
18684
  * reassessed the list has done half the work, and it is the half that looks complete.
18335
18685
  */
18686
+ /** The earlier before-and-after, quoted so the model can restate it rather than start over. */
18687
+ function previousBehaviourChange(previous) {
18688
+ const change = previous.behaviourChange;
18689
+ if (!change) return "";
18690
+ return ` (the earlier analysis said: before: ${JSON.stringify(change.before)}; after: ${JSON.stringify(change.after)}. Keep what still holds, correct what the new changes alter)`;
18691
+ }
18336
18692
  function buildReconciliationInstructions(input, options = {}) {
18337
18693
  const { previous, totalCommits, changedFiles, possiblyTruncated } = input;
18338
18694
  const reassess = input.reassess ?? /* @__PURE__ */ new Map();
@@ -18366,7 +18722,7 @@ ${REASSESS_TASK}` : ""}
18366
18722
  ${GRADE_TASK.question}`] : [],
18367
18723
  conversationOnly ? `No commits were pushed, so there is no new code to review. Report a problem in "newIssues" only where the conversation points at something in the code the earlier analysis missed and you have confirmed it by reading the code. Do NOT repeat a previous finding here: if it still stands, that is what "still-open" is for.` : `Read the changed code above as a reviewer, not only as an auditor of the numbered list. These commits have not been reviewed by anyone. Report every genuinely NEW problem you find in them in "newIssues", whether or not it relates to anything on the list — code written to fix a finding is exactly where a regression gets introduced, and reassessing the list and stopping there is half the job. Do NOT repeat a previous finding here: if it still stands, that is what "still-open" is for.`,
18368
18724
  `If the new changes introduce an area the earlier analysis had no section for, add it to "newSections". Otherwise return an empty array. Do not restate or restructure the earlier analysis's existing sections.`,
18369
- `Give a fresh "summary", "overallRisk", "recommendation" and "nextSteps" for the pull request AS IT NOW STANDS, taking both the earlier findings that survive and the new changes into account. Unlike a partial review, you have been shown the earlier analysis in full and are expected to revise its verdict where the new changes justify it. ${SUMMARY_GUIDANCE} ${ORIENTATION_GUIDANCE}`
18725
+ `Give a fresh "summary", "overallRisk", "recommendation" and "nextSteps" for the pull request AS IT NOW STANDS, taking both the earlier findings that survive and the new changes into account. Unlike a partial review, you have been shown the earlier analysis in full and are expected to revise its verdict where the new changes justify it. ${SUMMARY_GUIDANCE} ${ORIENTATION_GUIDANCE} ${BEHAVIOUR_CHANGE_GUIDANCE} Restate "behaviourChange" for the whole pull request as it now stands${previousBehaviourChange(previous)}, or omit it when the evidence does not establish it.`
18370
18726
  ].map((task, index) => `${index + 1}. ${task}`).join("\n");
18371
18727
  return `${conversationOnly ? `You are RECONCILING an earlier analysis of this pull request against what has changed in its conversation since that analysis was made.
18372
18728
 
@@ -18540,6 +18896,7 @@ function parseReconciliationResult(raw, priorCount, options = {}) {
18540
18896
  })),
18541
18897
  summary: asString$1(obj.summary).trim(),
18542
18898
  orientation: asString$1(obj.orientation).trim(),
18899
+ ...withBehaviourChange(parseBehaviourChange(obj.behaviourChange)),
18543
18900
  overallRisk: RISKS.includes(obj.overallRisk) ? obj.overallRisk : null,
18544
18901
  recommendation: asString$1(obj.recommendation).trim(),
18545
18902
  nextSteps: asStringArray(obj.nextSteps)
@@ -18817,6 +19174,15 @@ function findingsChanged(counts) {
18817
19174
  }
18818
19175
  //#endregion
18819
19176
  //#region ../../packages/review-harness/src/domain/ai_review/update_reconciliation.ts
19177
+ /**
19178
+ * Folding an update's answer onto the analysis it brings up to date.
19179
+ *
19180
+ * One function for every client that updates a review, so the workspace and the extension cannot
19181
+ * come to read the same evidence differently: what a cited comment may settle, when a resolution is
19182
+ * marked for reassessment instead, which findings a changed comment reopens, what each status is
19183
+ * recorded against, and which threads a new finding may claim. A client differs only in how it
19184
+ * fetched the changes and ran the model.
19185
+ */
18820
19186
  function reconciledAgainstOf(basis) {
18821
19187
  return basis.code ? basis.discussion ? "commits-and-conversation" : "commits" : "conversation";
18822
19188
  }
@@ -18849,6 +19215,7 @@ function applyReconciliation(previous, parsed, context) {
18849
19215
  schemaVersion: 1,
18850
19216
  summary: parsed.summary || previous.summary,
18851
19217
  ...parsed.orientation || previous.orientation ? { orientation: parsed.orientation || previous.orientation } : {},
19218
+ ...withBehaviourChange(mergeBehaviourChange(previous.behaviourChange, parsed.behaviourChange, context.codeChanged)),
18852
19219
  ...motivation ? { motivation } : {},
18853
19220
  overallRisk: parsed.overallRisk ?? previous.overallRisk,
18854
19221
  recommendation: parsed.recommendation || previous.recommendation,
@@ -22175,4 +22542,4 @@ var MCP_TOOL_NAMES = [
22175
22542
  "ask_buddy"
22176
22543
  ];
22177
22544
  //#endregion
22178
- export { statusPillLabel as $, TargetError as $a, lifecycleItemLabel as $i, writeJsonAtomically as $n, findExistingReview as $r, statedWorkspacePort as $t, anythingRunning as A, currentGradeIn as Aa, indexReviewGroups as Ai, BUILD_VERSION as An, deleteCollection as Ao, chatConversationSection as Ar, isRequestId as At, completedLabel as B, statusLookup as Ba, previousKissResult as Bi, checkArchitecture as Bn, modelHelpLines as Br, readServerRecord as Bt, parsePullRequestLink as C, isExplanationAction as Ca, driverGone as Ci, readEvents as Cn, AGENT_RUN_MARKER as Co, ASK_REREAD_CEILING_MS as Cr, findingRefs as Ct, INDEX_SECTIONS as D, analysisRefOf as Da, followedRefName as Di, recordTelemetryUploadConsent as Dn, collectionsStamp as Do, isUnchanged as Dr, endAskRequest as Dt, relativeTime as E, NO_STATUS as Ea, decideKissRecommendation as Ei, deliver$1 as En, cleanName as Eo, storedConversation as Er, reviewTagsIn as Et, collectionFold as F, isGradeFor as Fa, loadWorkspace as Fi, recordRuntimeBestEffort as Fn, STORE_ROOT as Fo, renderMarkdown as Fr, readIndexToken as Ft, markItemLabel as G, extractJsonText as Ga, touchWorkspace as Gi, storeArchitecture as Gn, createWorktree as Gr, stopServer as Gt, homeSection as H, followUpFindingId as Ha, reviewedCommit as Hi, partFiles as Hn, isBusy as Hr, serverEntryExists as Ht, collectionHash as I, isOffered as Ia, matchingWorkspaceIds as Ii, RUNTIME_WORDS as In, CODE_BLOCK_CLASS as Ir, bootstrapUrl as It, queueItemLabel as J, WITHHELD_REASON as Ja, LIFECYCLES as Ji, ownersOf as Jn, readMarker as Jr, DEFAULT_WORKSPACE_PORT as Jt, omittedText as K, objectWithAnyOf as Ka, workspaceRevision as Ki, storedScopes as Kn, holdWorktree as Kr, workspaceUrl as Kt, collectionPlacement as L, promptGrade as La, mutateWorkspace as Li, describeRuntimes as Ln, CODE_COPY_CLASS as Lr, clearServerRecordOwnedBy as Lt, authorOptions as M, findingIn as Ma, kissDecisionsOf as Mi, feedbackUrl as Mn, readCollections as Mo, authorPreview as Mr, markAskRunning as Mt, collectionCountLabel as N, findingStatus as Na, latestKissRun as Ni, waitingHistory as Nn, renameCollection as No, hasAuthorContent as Nr, readAskRequest as Nt, INDEX_SECTION_NAMES as O, assessmentLookup as Oa, groupByLineage as Oi, telemetryUploadConsent as On, createCollection as Oo, agentEnvOf as Or, forgetAskRequest as Ot, collectionEmptyText as P, gradeScale as Pa, lineagePosition as Pi, mapRuntimes as Pn, setMembership as Po, GITHUB_MARKDOWN as Pr, recordAskAccepted as Pt, statusLabel as Q, DetachedHeadError as Qa, lifecycleChip as Qi, isArchitectureId as Qn, describeSubject as Qr, resolveWorkspacePort as Qt, collectionsLine as R, reassessIn as Ra, positionInLineage as Ri, formatDuration as Rn, escapeHtml as Rr, ensureServer as Rt, extractPullRequestLinks as S, explanationOf as Sa, clearClaim as Si, append as Sn, versionFrom as So, existingPaths as Sr, findingRef as St, runBatch as T, ANALYSIS_ONLY as Ta, CLAIM_LEASE_MS as Ti, stamp as Tn, AgentUnavailableError as To, saveRefresh as Tr, reviewTag as Tt, isCollectionId as U, SEVERITIES as Ua, reviewedRepositories as Ui, readArchitecture as Un, withReviewLock as Ur, spawnServer as Ut, countLabel as V, wasReassessed as Va, recentWorkspaces as Vi, loadArchitecture as Vn, ReviewBeingDeletedError as Vr, reviewsUrl as Vt, isFoldKey as W, asString$1 as Wa, summarise as Wi, repositoryKey as Wn, MANAGED_ROOT as Wr, stopChild as Wt, sectionOpen as X, resolveTarget as Xa, activityLabel as Xi, REPOSITORY_SCOPE as Xn, requestJobStop as Xr, MIN_WORKSPACE_PORT as Xt, queueOptionLabel as Y, kissOutcome as Ya, activityAttributes as Yi, QUOTE_LIMITS as Yn, removeWorktree as Yr, MAX_WORKSPACE_PORT as Yt, selectAllLabel as Z, originRemote as Za, isLifecycle as Zi, ancestorsOf as Zn, describeMovement as Zr, parseWorkspacePort as Zt, endRun as _, reviewerDispositions as _a, isLiveJob as _i, prepareReview as _n, agentIdentityOf as _o, retireNotification as _r, modelProvenanceOf as _t, lineageIds as a, sectionOf as aa, forgeResolver as ai, cancellableHere as an, git as ao, createPrState as ar, lineageKeysFor as at, stopRun as b, recommendationsOf as ba, progressSteps as bi, runJob as bn, parkedReason as bo, unreadCount as br, humanizeModelId as bt, discardJob as c, queueRemove as ca, allJobs as ci, recheckConversation as cn, resolveModel as co, listNotifications as cr, compactQueue as ct, startKissJob as d, doneVerb as da, fail as di, findingsChanged as dn, readAgentPreference as do, markReadForReview as dr, RUNNER_KINDS as dt, lifecycleName$1 as ea, checkCodeFreshness as ei, writePortPreference as en, isCommitReview as eo, announceJob as er, deliver as et, isDriving as f, isIssueOutstanding as fa, isTerminal as fi, UpdateAlreadyRunningError as fn, writeAgentPreference as fo, moveIds as fr, RUNNER_MARKER as ft, completeRun as g, resolvedByConversation as ga, PHASES as gi, observeReview as gn, agentFor as go, removeNotification as gr, applyUpdate as gt, beginRun as h, questionsOutstanding as ha, saveJob as hi, failureOf as hn, agentById as ho, readStateOf as hr, spawnRunner as ht, deleteReview as i, parseLifecycle as ia, fetchHead as ii, cancelUpdate as in, shorthandOf as io, createArchitectureNotification as ir, retryableMiss as it, authorOptionLabel as j, findingAssessment as ja, kissDecisionRefusal as ji, PACKAGE_NAME as jn, nameProblemText as jo, UNESTABLISHED_MOTIVATION as jr, isTerminalAsk as jt, PR_FILTER_OPTIONS as k, closesFinding as ka, indexKeptIds as ki, telemetryUploadEnabled as kn, createWithMembers as ko, EXPLAIN_SIMPLY_PROMPT as kr, interruption as kt, kissAgentId as l, queueStamp as la, attemptIdOf as li, updateReview as ln, agentIdOf as lo, markAllRead as lr, queueForReview as lt, RUN_KINDS as m, issuesOutstanding as ma, pruneJobResidue as mi, runningJobs as mn, AGENT_IDS as mo, readNotification as mr, runDetached as mt, MCP_TOOL_NAMES as n, lifecyclePillLabel as na, followedBranch as ni, FETCH_TIMEOUT_MS as nn, AmbiguousRefError as no, subjectOf as nr, missedDelivery as nt, liveJobsFor as o, queueAdd as oa, fillFileUrlTemplate as oi, mergeUpdateInto as on, refExists as oo, createPrUpdate as or, settleCollections as ot, track as p, isQuestionOutstanding as pa, loadJob as pi, liveUpdateFor as pn, CONFIG_PATH as po, pruneNotifications as pr, cliEntry as pt, pinItemLabel as q, describeAuthorship as qa, workspaceStamps as qi, trackedFiles as qn, isWorktreeHeld as qr, writeServerRecord as qt, deleteLineage as r, offersLifecycle as ra, reviewedShaOf as ri, RefreshUnavailableError as rn, shortNameOf as ro, categoryOf as rr, openDestination as rt, wasBlocked as s, queueAddMany as sa, RUN_RECORD_RETENTION_MS as si, parkedUpdateRefusal as sn, resolveAgent as so, eventName as sr, activateQueued as st, MCP_SERVER_INFO_NAME as t, lifecyclePillAction as ta, checkReview as ti, COMMIT_REVIEW_CANNOT_UPDATE as tn, resolveBase as to, branchOf as tr, isDelivered as tt, recordKissRun as u, readQueue as ua, endedAt as ui, askCheckout as un, clearAgentPreference as uo, markRead as ur, settleQueue as ut, heldRuns as v, verdictClosure as va, isTerminalJobPhase as vi, settleReview as vn, detectAgents as vo, toneOf as vr, provenanceDetailLines as vt, unsupportedReason as w, withExplanation as wa, isClaimed as wi, record as wn, AgentCancelledError as wo, prepareConversationWithin as wr, refOf as wt, batchIdentity as x, explanationActionOf as xa, purposeOf as xi, startJob as xn, runnableAgents as xo, withUsageRecorded as xr, findingAtRef as xt, reportRun as y, processDiscussion as ya, phasesFor as yi, reanalyseReview as yn, isAgentId as yo, unreadAround as yr, agentDisplayName as yt, completedFold as z, sourcePhrase as za, presentedKissRunOf as zi, ARCHITECTURE_DIR as zn, renderInlineText as zr, healthyPort as zt };
22545
+ export { statusLabel as $, gradeScale as $a, workspacesRevision as $i, isArchitectureId as $n, setMembership as $o, describeSubject as $r, resolveWorkspacePort as $t, PR_FILTER_OPTIONS as A, doneVerb as Aa, indexKeptIds as Ai, telemetryUploadEnabled as An, readAgentPreference as Ao, EXPLAIN_SIMPLY_PROMPT as Ar, interruption as At, completedFold as B, explanationActionOf as Ba, matchingWorkspaceIds as Bi, ARCHITECTURE_DIR as Bn, runnableAgents as Bo, renderInlineText as Br, healthyPort as Bt, parsePullRequestLink as C, parseLifecycle as Ca, clearClaim as Ci, append as Cn, shorthandOf as Co, existingPaths as Cr, findingRef as Ct, INDEX_SECTIONS as D, queueRemove as Da, decideKissRecommendation as Di, deliver$1 as Dn, resolveModel as Do, storedConversation as Dr, reviewTagsIn as Dt, relativeTime as E, queueAddMany as Ea, CLAIM_LEASE_MS as Ei, stamp as En, resolveAgent as Eo, saveRefresh as Er, reviewTag as Et, collectionEmptyText as F, resolvedByConversation as Fa, lineageKey as Fi, mapRuntimes as Fn, agentFor as Fo, GITHUB_MARKDOWN as Fr, recordAskAccepted as Ft, isFoldKey as G, NO_STATUS as Ga, previousKissResult as Gi, repositoryKey as Gn, cleanName as Go, MANAGED_ROOT as Gr, stopChild as Gt, countLabel as H, isExplanationAction as Ha, outcomeInputsOf as Hi, loadArchitecture as Hn, AGENT_RUN_MARKER as Ho, ReviewBeingDeletedError as Hr, reviewsUrl as Ht, collectionFold as I, reviewerDispositions as Ia, lineageKeyFor as Ii, recordRuntimeBestEffort as In, agentIdentityOf as Io, renderMarkdown as Ir, readIndexToken as It, pinItemLabel as J, closesFinding as Ja, reviewedRepositories as Ji, trackedFiles as Jn, createWithMembers as Jo, isWorktreeHeld as Jr, writeServerRecord as Jt, markItemLabel as K, analysisRefOf as Ka, recentWorkspaces as Ki, storeArchitecture as Kn, collectionsStamp as Ko, createWorktree as Kr, stopServer as Kt, collectionHash as L, verdictClosure as La, lineageOutcome as Li, RUNTIME_WORDS as Ln, detectAgents as Lo, CODE_BLOCK_CLASS as Lr, bootstrapUrl as Lt, authorOptionLabel as M, isQuestionOutstanding as Ma, kissDecisionRefusal as Mi, PACKAGE_NAME as Mn, CONFIG_PATH as Mo, UNESTABLISHED_MOTIVATION as Mr, isTerminalAsk as Mt, authorOptions as N, issuesOutstanding as Na, kissDecisionsOf as Ni, feedbackUrl as Nn, AGENT_IDS as No, authorPreview as Nr, markAskRunning as Nt, INDEX_SECTION_NAMES as O, queueStamp as Oa, followedRefName as Oi, recordTelemetryUploadConsent as On, agentIdOf as Oo, isUnchanged as Or, endAskRequest as Ot, collectionCountLabel as P, questionsOutstanding as Pa, latestKissRun as Pi, waitingHistory as Pn, agentById as Po, hasAuthorContent as Pr, readAskRequest as Pt, selectAllLabel as Q, findingStatus as Qa, workspaceStamps as Qi, ancestorsOf as Qn, renameCollection as Qo, describeMovement as Qr, parseWorkspacePort as Qt, collectionPlacement as R, processDiscussion as Ra, lineagePosition as Ri, describeRuntimes as Rn, isAgentId as Ro, CODE_COPY_CLASS as Rr, clearServerRecordOwnedBy as Rt, extractPullRequestLinks as S, offersLifecycle as Sa, purposeOf as Si, startJob as Sn, shortNameOf as So, withUsageRecorded as Sr, findingAtRef as St, runBatch as T, queueAdd as Ta, isClaimed as Ti, record as Tn, refExists as To, prepareConversationWithin as Tr, refOf as Tt, homeSection as U, withExplanation as Ua, positionInLineage as Ui, partFiles as Un, AgentCancelledError as Uo, isBusy as Ur, serverEntryExists as Ut, completedLabel as V, explanationOf as Va, mutateWorkspace as Vi, checkArchitecture as Vn, versionFrom as Vo, modelHelpLines as Vr, readServerRecord as Vt, isCollectionId as W, ANALYSIS_ONLY as Wa, presentedKissRunOf as Wi, readArchitecture as Wn, AgentUnavailableError as Wo, withReviewLock as Wr, spawnServer as Wt, queueOptionLabel as X, findingAssessment as Xa, touchWorkspace as Xi, QUOTE_LIMITS as Xn, nameProblemText as Xo, removeWorktree as Xr, MAX_WORKSPACE_PORT as Xt, queueItemLabel as Y, currentGradeIn as Ya, summarise as Yi, ownersOf as Yn, deleteCollection as Yo, readMarker as Yr, DEFAULT_WORKSPACE_PORT as Yt, sectionOpen as Z, findingIn as Za, workspaceRevision as Zi, REPOSITORY_SCOPE as Zn, readCollections as Zo, requestJobStop as Zr, MIN_WORKSPACE_PORT as Zt, endRun as _, lifecycleChip as _a, PHASES as _i, observeReview as _n, DetachedHeadError as _o, removeNotification as _r, applyUpdate as _t, lineageIds as a, nextBaseline as aa, fetchHead as ai, cancelUpdate as an, statusLookup as ao, createArchitectureNotification as ar, retryableMiss as at, stopRun as b, lifecyclePillAction as ba, phasesFor as bi, reanalyseReview as bn, resolveBase as bo, unreadAround as br, agentDisplayName as bt, discardJob as c, outcomeStanding as ca, RUN_RECORD_RETENTION_MS as ci, parkedUpdateRefusal as cn, SEVERITIES as co, eventName as cr, activateQueued as ct, startKissJob as d, readOutcomes as da, endedAt as di, askCheckout as dn, objectWithAnyOf as do, markRead as dr, settleQueue as dt, analysedAt as ea, findExistingReview as ei, statedWorkspacePort as en, isGradeFor as eo, writeJsonAtomically as er, withWriteLock as es, statusPillLabel as et, isDriving as f, sameSha as fa, fail as fi, findingsChanged as fn, describeAuthorship as fo, markReadForReview as fr, RUNNER_KINDS as ft, completeRun as g, isLifecycle as ga, saveJob as gi, failureOf as gn, originRemote as go, readStateOf as gr, spawnRunner as gt, beginRun as h, activityLabel as ha, pruneJobResidue as hi, runningJobs as hn, resolveTarget as ho, readNotification as hr, runDetached as ht, deleteReview as i, lastBaseline as ia, reviewedShaOf as ii, RefreshUnavailableError as in, sourcePhrase as io, categoryOf as ir, openDestination as it, anythingRunning as j, isIssueOutstanding as ja, indexReviewGroups as ji, BUILD_VERSION as jn, writeAgentPreference as jo, chatConversationSection as jr, isRequestId as jt, LIST_SECTIONS as k, readQueue as ka, groupByLineage as ki, telemetryUploadConsent as kn, clearAgentPreference as ko, agentEnvOf as kr, forgetAskRequest as kt, kissAgentId as l, parseOutcomeSnapshot as la, allJobs as li, recheckConversation as ln, asString$1 as lo, listNotifications as lr, compactQueue as lt, RUN_KINDS as m, activityAttributes as ma, loadJob as mi, liveUpdateFor as mn, kissOutcome as mo, pruneNotifications as mr, cliEntry as mt, MCP_TOOL_NAMES as n, REVIEWER_OUTCOMES as na, checkReview as ni, COMMIT_REVIEW_CANNOT_UPDATE as nn, promptGrade as no, branchOf as nr, isDelivered as nt, liveJobsFor as o, outcomeDefinition as oa, forgeResolver as oi, cancellableHere as on, wasReassessed as oo, createPrState as or, lineageKeysFor as ot, track as p, LIFECYCLES as pa, isTerminal as pi, UpdateAlreadyRunningError as pn, WITHHELD_REASON as po, moveIds as pr, RUNNER_MARKER as pt, omittedText as q, assessmentLookup as qa, reviewedCommit as qi, storedScopes as qn, createCollection as qo, holdWorktree as qr, workspaceUrl as qt, deleteLineage as r, appendOutcome as ra, followedBranch as ri, FETCH_TIMEOUT_MS as rn, reassessIn as ro, subjectOf as rr, missedDelivery as rt, wasBlocked as s, outcomeLabel as sa, fillFileUrlTemplate as si, mergeUpdateInto as sn, followUpFindingId as so, createPrUpdate as sr, settleCollections as st, MCP_SERVER_INFO_NAME as t, analysisSnapshot as ta, checkCodeFreshness as ti, writePortPreference as tn, isOffered as to, announceJob as tr, STORE_ROOT as ts, deliver as tt, recordKissRun as u, parseOutcomes as ua, attemptIdOf as ui, updateReview as un, extractJsonText as uo, markAllRead as ur, queueForReview as ut, heldRuns as v, lifecycleItemLabel as va, isLiveJob as vi, prepareReview as vn, TargetError as vo, retireNotification as vr, modelProvenanceOf as vt, unsupportedReason as w, sectionOf as wa, driverGone as wi, readEvents as wn, git as wo, ASK_REREAD_CEILING_MS as wr, findingRefs as wt, batchIdentity as x, lifecyclePillLabel as xa, progressSteps as xi, runJob as xn, AmbiguousRefError as xo, unreadCount as xr, humanizeModelId as xt, reportRun as y, lifecycleName$1 as ya, isTerminalJobPhase as yi, settleReview as yn, isCommitReview as yo, toneOf as yr, provenanceDetailLines as yt, collectionsLine as z, recommendationsOf as za, loadWorkspace as zi, formatDuration as zn, parkedReason as zo, escapeHtml as zr, ensureServer as zt };