vericify 1.2.0 → 1.3.0

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.
@@ -0,0 +1,82 @@
1
+ import { existsSync, mkdirSync } from "node:fs";
2
+ import { dirname, resolve } from "node:path";
3
+ import { createHandshakeResult, emitHandshakeStartedEvent, mergeJsonFile, writeInstructionBlock } from "./common.js";
4
+
5
+ const VERICIFY_MARKER = "<!-- vericify-instruction -->";
6
+
7
+ function copilotInstructionBlock() {
8
+ return `${VERICIFY_MARKER}
9
+ ## Vericify Process Posts
10
+
11
+ When working in this workspace, emit native Vericify process posts at these moments:
12
+
13
+ - **Blocker:** \`vericify post --run-id workspace:current --agent-id vscode-copilot-chat --kind blocker --summary "<what is blocking>"\`
14
+ - **Handoff:** \`vericify post --run-id workspace:current --agent-id vscode-copilot-chat --kind handoff_note --summary "<handoff context>"\`
15
+ - **Completion:** \`vericify post --run-id workspace:current --agent-id vscode-copilot-chat --kind completion --summary "<what changed>"\`
16
+ - **Intentional silence:** \`vericify post --run-id workspace:current --agent-id vscode-copilot-chat --kind stale_ack --summary "<reason>"\`
17
+ <!-- /vericify-instruction -->`;
18
+ }
19
+
20
+ export function vscodeCopilotHandshake(workspaceRoot, options = {}) {
21
+ const result = createHandshakeResult();
22
+ const skippedReasons = [];
23
+ const vscodeDir = resolve(workspaceRoot, ".vscode");
24
+ const githubDir = resolve(workspaceRoot, ".github");
25
+ const mcpPath = resolve(vscodeDir, "mcp.json");
26
+ const instructionPath = resolve(githubDir, "copilot-instructions.md");
27
+
28
+ if (existsSync(vscodeDir)) {
29
+ try {
30
+ const mcpResult = mergeJsonFile(mcpPath, (existing) => {
31
+ const current = existing && typeof existing === "object" ? existing : {};
32
+ return {
33
+ ...current,
34
+ servers: {
35
+ ...(current.servers && typeof current.servers === "object" ? current.servers : {}),
36
+ vericify: current.servers?.vericify ?? {
37
+ _vericify: true,
38
+ type: "stdio",
39
+ command: "vericify-mcp",
40
+ args: ["--workspace-root", "${workspaceFolder}"],
41
+ _note: "vericify-mcp not yet installed. Run: npm install -g vericify-mcp",
42
+ },
43
+ },
44
+ };
45
+ });
46
+ result.mcp_written = Boolean(mcpResult.written);
47
+ result.mcp_path = mcpPath;
48
+ if (mcpResult.skipped) skippedReasons.push(mcpResult.reason);
49
+ } catch (error) {
50
+ skippedReasons.push(`.vscode/mcp.json write failed: ${error instanceof Error ? error.message : String(error)}`);
51
+ }
52
+ } else {
53
+ skippedReasons.push(".vscode directory is missing; MCP placeholder skipped");
54
+ }
55
+
56
+ try {
57
+ mkdirSync(dirname(instructionPath), { recursive: true });
58
+ const instructionResult = writeInstructionBlock(instructionPath, VERICIFY_MARKER, copilotInstructionBlock(), {
59
+ createIfAbsent: true,
60
+ createDirs: true,
61
+ });
62
+ result.instruction_written = Boolean(instructionResult.written);
63
+ result.instruction_path = instructionPath;
64
+ if (instructionResult.skipped) skippedReasons.push(instructionResult.reason);
65
+ } catch (error) {
66
+ skippedReasons.push(`copilot instructions write failed: ${error instanceof Error ? error.message : String(error)}`);
67
+ }
68
+
69
+ const eventResult = emitHandshakeStartedEvent(workspaceRoot, {
70
+ adapterId: "vscode-copilot-chat",
71
+ sessionId: options.sessionId ?? null,
72
+ hooksWritten: false,
73
+ instructionWritten: result.instruction_written,
74
+ mcpWritten: result.mcp_written,
75
+ });
76
+
77
+ return {
78
+ ...result,
79
+ event_emitted: eventResult.event_emitted,
80
+ skipped_reasons: [...skippedReasons, ...eventResult.skipped_reasons].filter(Boolean),
81
+ };
82
+ }
@@ -1,5 +1,6 @@
1
1
  import { existsSync } from "node:fs";
2
2
  import { resolve } from "node:path";
3
+ import { runHandshake } from "./handshake/index.js";
3
4
  import { readAdapterAttachments, upsertAdapterAttachment } from "../store/adapter-attachments.js";
4
5
 
5
6
  const ADAPTER_DEFINITIONS = [
@@ -90,7 +91,27 @@ export function readWorkspaceAdapterAttachments(workspaceRoot) {
90
91
  }
91
92
 
92
93
  export function attachWorkspaceAdapter(workspaceRoot, input) {
93
- return upsertAdapterAttachment(workspaceRoot, input);
94
+ const attachment = upsertAdapterAttachment(workspaceRoot, input);
95
+ let handshake;
96
+ try {
97
+ handshake = runHandshake(workspaceRoot, {
98
+ adapterId: input.adapter_id,
99
+ sessionId: input.session_id,
100
+ captureMode: input.capture_mode,
101
+ });
102
+ } catch (error) {
103
+ handshake = { error: error instanceof Error ? error.message : String(error) };
104
+ }
105
+ const skippedReasons = [
106
+ ...(Array.isArray(handshake?.skipped_reasons) ? handshake.skipped_reasons : []),
107
+ ...(handshake?.reason ? [handshake.reason] : []),
108
+ ...(handshake?.error ? [`Handshake error: ${handshake.error}`] : []),
109
+ ];
110
+ return {
111
+ ...attachment,
112
+ skipped_reasons: skippedReasons,
113
+ handshake,
114
+ };
94
115
  }
95
116
 
96
117
  export function detectWorkspaceAdapters(workspaceRoot, attachments = undefined) {
package/src/api.js CHANGED
@@ -7,6 +7,8 @@ export {
7
7
  loadWorkspaceState,
8
8
  } from "./adapters/index.js";
9
9
  export { buildRunComparison, findRunById } from "./compare/engine.js";
10
+ export { buildCompactDelta } from "./context/delta.js";
11
+ export { buildCompactPacket, buildCompactPacketDetails } from "./context/packet.js";
10
12
  export { projectWorkspaceState } from "./projection/runs.js";
11
13
  export { publishRunArtifact } from "./publish/artifact.js";
12
14
  export { enqueueSyncOutboxItem } from "./sync/outbox.js";
@@ -0,0 +1,97 @@
1
+ import { buildCompactPacketDetails } from "./packet.js";
2
+ import { decodeCompactCursor, isCompactCursor } from "./id.js";
3
+
4
+ function oneLine(value) {
5
+ return String(value ?? "")
6
+ .replace(/\s+/g, " ")
7
+ .trim();
8
+ }
9
+
10
+ function summarizeChangeSummary(current, previous) {
11
+ const rows = [];
12
+ if (!previous) return rows;
13
+
14
+ if (previous.run !== current.run) {
15
+ rows.push(`run switched from ${previous.run ?? "unknown"} to ${current.run}.`);
16
+ }
17
+ if (previous.latest_checkpoint_id !== current.latest_checkpoint_id) {
18
+ rows.push(
19
+ `latest checkpoint moved from ${previous.latest_checkpoint_id ?? "none"} to ${current.latest_checkpoint_id ?? "none"}.`
20
+ );
21
+ }
22
+ if (previous.last_activity_id !== current.last_activity_id) {
23
+ rows.push(`latest activity is now ${current.last_activity_id ?? "unknown"}.`);
24
+ }
25
+ if (previous.last_meaningful_update_at !== current.last_meaningful_update_at) {
26
+ rows.push(`last meaningful update changed to ${current.last_meaningful_update_at ?? "unknown"}.`);
27
+ }
28
+ if (previous.packet_digest !== current.packet_digest) {
29
+ rows.push("packet content changed.");
30
+ }
31
+
32
+ return [...new Set(rows)].slice(0, 3);
33
+ }
34
+
35
+ function buildFallbackChangeSummary(current, since) {
36
+ return [
37
+ `No compact cursor matched ${since}.`,
38
+ `Current checkpoint is ${current.latest_checkpoint_id ?? "unknown"}.`,
39
+ ];
40
+ }
41
+
42
+ function determineChanged(currentCursor, since, selectedRun) {
43
+ if (isCompactCursor(since)) {
44
+ const previous = decodeCompactCursor(since);
45
+ if (!previous) {
46
+ return { changed: true, previous: null };
47
+ }
48
+
49
+ return {
50
+ changed:
51
+ previous.run !== currentCursor.run ||
52
+ previous.status !== currentCursor.status ||
53
+ previous.latest_checkpoint_id !== currentCursor.latest_checkpoint_id ||
54
+ previous.last_meaningful_update_at !== currentCursor.last_meaningful_update_at ||
55
+ previous.last_activity_id !== currentCursor.last_activity_id ||
56
+ previous.packet_digest !== currentCursor.packet_digest,
57
+ previous,
58
+ };
59
+ }
60
+
61
+ if (selectedRun?.run?.run_id === "workspace:current") {
62
+ throw new Error(
63
+ "workspace:current resume requires a vcx_... cursor; run vericify context and reuse the emitted id."
64
+ );
65
+ }
66
+
67
+ const latestCheckpointId = currentCursor.latest_checkpoint_id;
68
+ const changed = since !== latestCheckpointId;
69
+ return { changed, previous: null };
70
+ }
71
+
72
+ export function buildCompactDelta(projected, options = {}) {
73
+ const since = String(options.since ?? "").trim();
74
+ if (!since) {
75
+ throw new Error("Compact delta requires --since.");
76
+ }
77
+
78
+ const details = buildCompactPacketDetails(projected, options);
79
+ const currentCursor = details.cursor.payload;
80
+ const { changed, previous } = determineChanged(currentCursor, since, details.selectedRun);
81
+ const delta = {
82
+ since,
83
+ changed,
84
+ ...details.packet,
85
+ };
86
+
87
+ if (changed) {
88
+ const change_summary = isCompactCursor(since) && previous
89
+ ? summarizeChangeSummary(currentCursor, previous)
90
+ : buildFallbackChangeSummary(currentCursor, since);
91
+ if (change_summary.length) {
92
+ delta.change_summary = change_summary;
93
+ }
94
+ }
95
+
96
+ return delta;
97
+ }
@@ -0,0 +1,61 @@
1
+ import { hashText } from "../core/util.js";
2
+
3
+ const VCX_PREFIX = "vcx_";
4
+
5
+ function encodeBase64Url(value) {
6
+ return Buffer.from(value, "utf8").toString("base64url");
7
+ }
8
+
9
+ function decodeBase64Url(value) {
10
+ return Buffer.from(value, "base64url").toString("utf8");
11
+ }
12
+
13
+ export function isCompactCursor(value) {
14
+ return String(value ?? "").startsWith(VCX_PREFIX);
15
+ }
16
+
17
+ export function decodeCompactCursor(value) {
18
+ if (!isCompactCursor(value)) return null;
19
+ try {
20
+ const raw = decodeBase64Url(String(value).slice(VCX_PREFIX.length));
21
+ const parsed = JSON.parse(raw);
22
+ return parsed && typeof parsed === "object" ? parsed : null;
23
+ } catch {
24
+ return null;
25
+ }
26
+ }
27
+
28
+ export function buildCompactCursorPayload({
29
+ run,
30
+ status,
31
+ latest_checkpoint_id,
32
+ last_meaningful_update_at,
33
+ last_activity_id,
34
+ packet_digest,
35
+ }) {
36
+ return {
37
+ version: 1,
38
+ run: String(run ?? ""),
39
+ status: String(status ?? ""),
40
+ latest_checkpoint_id: latest_checkpoint_id ?? null,
41
+ last_meaningful_update_at: last_meaningful_update_at ?? null,
42
+ last_activity_id: last_activity_id ?? null,
43
+ packet_digest: String(packet_digest ?? ""),
44
+ };
45
+ }
46
+
47
+ export function encodeCompactCursor(payload) {
48
+ return `${VCX_PREFIX}${encodeBase64Url(JSON.stringify(payload))}`;
49
+ }
50
+
51
+ export function buildCompactCursor(input) {
52
+ const payload = buildCompactCursorPayload(input);
53
+ return {
54
+ cursor: encodeCompactCursor(payload),
55
+ payload,
56
+ };
57
+ }
58
+
59
+ export function compactPacketDigest(packet) {
60
+ return hashText(JSON.stringify(packet));
61
+ }
@@ -0,0 +1,210 @@
1
+ import { buildRunComparison } from "../compare/engine.js";
2
+ import { firstDefined, unique } from "../core/util.js";
3
+ import { isMeaningfulActivity } from "../projection/live-signal.js";
4
+ import { buildCompactCursor, compactPacketDigest } from "./id.js";
5
+ import { selectCompactRun } from "./select.js";
6
+
7
+ function oneLine(value) {
8
+ return String(value ?? "")
9
+ .replace(/\s+/g, " ")
10
+ .trim();
11
+ }
12
+
13
+ function truncate(value, max = 160) {
14
+ const text = oneLine(value);
15
+ if (text.length <= max) return text;
16
+ return `${text.slice(0, Math.max(0, max - 1)).trimEnd()}…`;
17
+ }
18
+
19
+ function splitFocusText(value) {
20
+ return oneLine(value)
21
+ .split(/(?:\n|,|;)+/)
22
+ .map((part) => part.trim())
23
+ .filter(Boolean);
24
+ }
25
+
26
+ function formatEvent(item) {
27
+ const parts = [];
28
+ if (item?.actor_id) parts.push(oneLine(item.actor_id));
29
+ if (item?.label) parts.push(oneLine(item.label));
30
+ if (item?.summary) parts.push(truncate(item.summary, 120));
31
+ return parts.length ? parts.join(": ") : truncate(item?.activity_id ?? "activity");
32
+ }
33
+
34
+ function latestMeaningfulActivity(runDetail) {
35
+ const meaningful = (runDetail?.activity_items ?? [])
36
+ .filter((item) => isMeaningfulActivity(item))
37
+ .sort((left, right) => String(left.timestamp ?? "").localeCompare(String(right.timestamp ?? "")));
38
+ return meaningful.at(-1) ?? null;
39
+ }
40
+
41
+ function nullIfUndefined(value) {
42
+ return value === undefined ? null : value;
43
+ }
44
+
45
+ const RECOMMENDED_ACTION_SENTENCES = {
46
+ accept_handoff: "Accept the handoff and continue with the incoming work.",
47
+ write_note: "Write the next note or blocker update.",
48
+ resolve_blocker: "Resolve the blocker before continuing.",
49
+ update_state: "Post the current state update.",
50
+ clarify_owner: "Clarify ownership before proceeding.",
51
+ ack_stale: "Acknowledge the stale state and re-check progress.",
52
+ observe_only: "Observe only. No urgent operator action.",
53
+ };
54
+
55
+ function sentenceForRecommendedAction(action) {
56
+ return RECOMMENDED_ACTION_SENTENCES[String(action ?? "").trim()] ?? null;
57
+ }
58
+
59
+ function deriveFocus(runDetail, latestActivity = null) {
60
+ const focus = [];
61
+ const summaryLabels = runDetail?.run_summary?.current_node_labels ?? [];
62
+ focus.push(...summaryLabels.slice(0, 3).map((label) => oneLine(label)));
63
+
64
+ const latestCheckpoint = runDetail?.recent_checkpoints?.at(-1);
65
+ focus.push(...splitFocusText(latestCheckpoint?.task_delta_summary ?? ""));
66
+ if (!focus.length) {
67
+ focus.push(...splitFocusText(latestCheckpoint?.process_summary?.split("\n")?.[0] ?? ""));
68
+ }
69
+
70
+ const meaningfulActivity = latestActivity ?? latestMeaningfulActivity(runDetail);
71
+ if (meaningfulActivity?.summary) {
72
+ focus.push(truncate(meaningfulActivity.summary, 120));
73
+ }
74
+
75
+ if (!focus.length && runDetail?.run?.title) {
76
+ focus.push(oneLine(runDetail.run.title));
77
+ }
78
+
79
+ return unique(focus).slice(0, 3);
80
+ }
81
+
82
+ function deriveBlockers(runDetail) {
83
+ const blockers = [];
84
+ const blockedNodes = (runDetail?.nodes ?? []).filter((node) => node.status === "blocked").map((node) => node.title);
85
+ blockers.push(...blockedNodes.map((title) => oneLine(title)));
86
+
87
+ const signal = runDetail?.live_signal ?? {};
88
+ if (["blocked", "overdue_handoff"].includes(signal.attention_class)) {
89
+ blockers.push(`Attention class: ${signal.attention_class}`);
90
+ }
91
+ if (signal.attention_reason) {
92
+ blockers.push(truncate(signal.attention_reason, 160));
93
+ }
94
+
95
+ return unique(blockers).slice(0, 3);
96
+ }
97
+
98
+ function deriveLastEvents(runDetail) {
99
+ const meaningful = (runDetail?.activity_items ?? []).filter((item) => isMeaningfulActivity(item));
100
+ const source = meaningful.length
101
+ ? meaningful
102
+ : (runDetail?.recent_checkpoints ?? []).map((checkpoint, index) => ({
103
+ activity_id: checkpoint.checkpoint_id ?? `${runDetail?.run?.run_id ?? "run"}:checkpoint:${index + 1}`,
104
+ actor_id: runDetail?.run?.run_id ?? "run",
105
+ label: "checkpoint",
106
+ summary: checkpoint.task_delta_summary ?? checkpoint.process_summary?.split("\n")?.[0] ?? "",
107
+ timestamp: checkpoint.timestamp,
108
+ }));
109
+
110
+ return [...source]
111
+ .sort((left, right) => String(left.timestamp ?? "").localeCompare(String(right.timestamp ?? "")))
112
+ .slice(-5)
113
+ .map((item) => formatEvent(item));
114
+ }
115
+
116
+ function deriveLiveSignalSubset(runDetail) {
117
+ const signal = runDetail?.live_signal ?? {};
118
+ return {
119
+ attention_class: nullIfUndefined(signal.attention_class),
120
+ expected_next_event: nullIfUndefined(signal.expected_next_event),
121
+ recommended_post_kind: nullIfUndefined(signal.recommended_post_kind),
122
+ needs_narrative: nullIfUndefined(signal.needs_narrative),
123
+ should_interrupt_agent: nullIfUndefined(signal.should_interrupt_agent),
124
+ };
125
+ }
126
+
127
+ function deriveRecommendedAction(selectedRun, comparisonRun) {
128
+ const liveSignalAction = oneLine(selectedRun?.live_signal?.recommended_owner_action ?? "");
129
+ const mappedAction = sentenceForRecommendedAction(liveSignalAction);
130
+ if (mappedAction) return mappedAction;
131
+
132
+ if (comparisonRun) {
133
+ const comparison = buildRunComparison(selectedRun, comparisonRun);
134
+ const recommended = comparison.recommended_actions?.[0];
135
+ if (recommended) return oneLine(recommended);
136
+ }
137
+
138
+ return "Observe only. No urgent operator action.";
139
+ }
140
+
141
+ function buildPacketFingerprint(packet) {
142
+ return {
143
+ run: packet.run,
144
+ status: packet.status,
145
+ focus: packet.focus,
146
+ blockers: packet.blockers,
147
+ live_signal: packet.live_signal,
148
+ last_5_events: packet.last_5_events,
149
+ recommended_action: packet.recommended_action,
150
+ };
151
+ }
152
+
153
+ export function buildCompactPacketDetails(projected, options = {}) {
154
+ const selection = selectCompactRun(projected, options);
155
+ const selectedRun = selection.selectedRun;
156
+ const comparisonRun = selection.comparisonRun;
157
+
158
+ if (!selectedRun) {
159
+ throw new Error("Compact packet requires a selected run.");
160
+ }
161
+
162
+ const latestActivity = latestMeaningfulActivity(selectedRun);
163
+ const focus = deriveFocus(selectedRun, latestActivity);
164
+ const blockers = deriveBlockers(selectedRun);
165
+ const last_5_events = deriveLastEvents(selectedRun);
166
+ const live_signal = deriveLiveSignalSubset(selectedRun);
167
+ const recommended_action = deriveRecommendedAction(selectedRun, comparisonRun);
168
+
169
+ const packet = {
170
+ run: selectedRun.run.run_id,
171
+ status: selectedRun.run.status,
172
+ focus,
173
+ blockers,
174
+ live_signal,
175
+ last_5_events,
176
+ recommended_action,
177
+ };
178
+ const fingerprint = buildPacketFingerprint(packet);
179
+ const packet_digest = compactPacketDigest(fingerprint);
180
+ const latestCheckpoint = selectedRun.recent_checkpoints?.at(-1);
181
+ const cursor = buildCompactCursor({
182
+ run: packet.run,
183
+ status: packet.status,
184
+ latest_checkpoint_id: latestCheckpoint?.checkpoint_id ?? null,
185
+ last_meaningful_update_at: firstDefined(
186
+ selectedRun.live_signal?.last_meaningful_update_at,
187
+ latestActivity?.timestamp,
188
+ selectedRun.run.updated_at
189
+ ),
190
+ last_activity_id: firstDefined(latestActivity?.activity_id, latestCheckpoint?.checkpoint_id),
191
+ packet_digest,
192
+ });
193
+
194
+ return {
195
+ packet: {
196
+ id: cursor.cursor,
197
+ ...packet,
198
+ },
199
+ selection,
200
+ selectedRun,
201
+ comparisonRun,
202
+ fingerprint,
203
+ cursor,
204
+ packet_digest,
205
+ };
206
+ }
207
+
208
+ export function buildCompactPacket(projected, options = {}) {
209
+ return buildCompactPacketDetails(projected, options).packet;
210
+ }
@@ -0,0 +1,65 @@
1
+ import { compareRunsByAttention } from "../projection/live-signal.js";
2
+
3
+ const TRACE_PREFIX = "trace:";
4
+
5
+ export function isTraceRun(runDetail) {
6
+ return String(runDetail?.run?.run_id ?? "").startsWith(TRACE_PREFIX);
7
+ }
8
+
9
+ export function sortCompactRuns(runs) {
10
+ return [...(runs ?? [])].sort(compareRunsByAttention);
11
+ }
12
+
13
+ export function findBestComparisonRun(selectedRun, rankedRuns) {
14
+ const selectedRunId = selectedRun?.run?.run_id;
15
+ const neighborIds = Array.isArray(selectedRun?.similarity?.neighbors)
16
+ ? selectedRun.similarity.neighbors.map((neighbor) => neighbor.run_id).filter(Boolean)
17
+ : [];
18
+
19
+ for (const neighborId of neighborIds) {
20
+ const candidate = rankedRuns.find((run) => run.run?.run_id === neighborId);
21
+ if (candidate && candidate.run?.run_id !== selectedRunId && !isTraceRun(candidate)) {
22
+ return candidate;
23
+ }
24
+ }
25
+
26
+ return rankedRuns.find((run) => run.run?.run_id !== selectedRunId && !isTraceRun(run)) ?? null;
27
+ }
28
+
29
+ export function selectCompactRun(projected, options = {}) {
30
+ const runs = Array.isArray(projected?.runs) ? projected.runs : [];
31
+ if (!runs.length) {
32
+ throw new Error("Compact packet requires projected runs.");
33
+ }
34
+
35
+ const rankedRuns = sortCompactRuns(runs);
36
+ const explicitRunId = String(options.runId ?? "").trim();
37
+ const explicitRun = explicitRunId ? rankedRuns.find((run) => run.run?.run_id === explicitRunId) : undefined;
38
+ if (explicitRunId && !explicitRun) {
39
+ throw new Error(`Unknown run id: ${explicitRunId}`);
40
+ }
41
+
42
+ const workspaceRun = rankedRuns.find((run) => run.run?.run_id === "workspace:current");
43
+ let selectedRun = explicitRun ?? workspaceRun ?? rankedRuns.find((run) => !isTraceRun(run)) ?? rankedRuns[0];
44
+ if (!explicitRun && selectedRun && isTraceRun(selectedRun)) {
45
+ selectedRun = rankedRuns.find((run) => !isTraceRun(run)) ?? selectedRun;
46
+ }
47
+
48
+ const hasNonTraceRun = rankedRuns.some((run) => !isTraceRun(run));
49
+ const surfacedRuns = hasNonTraceRun
50
+ ? rankedRuns.filter((run) => !isTraceRun(run) || run.run?.run_id === selectedRun?.run?.run_id)
51
+ : rankedRuns;
52
+ const hiddenRuns = rankedRuns.filter((run) => !surfacedRuns.includes(run));
53
+
54
+ return {
55
+ selectedRun,
56
+ rankedRuns,
57
+ surfacedRuns,
58
+ hiddenRuns,
59
+ hiddenRunCount: hiddenRuns.length,
60
+ surfaceRunCount: surfacedRuns.length,
61
+ comparisonRun: findBestComparisonRun(selectedRun, rankedRuns),
62
+ explicitRunId: explicitRunId || undefined,
63
+ workspaceRun,
64
+ };
65
+ }