@workbench-ai/workbench 0.0.64 → 0.0.66

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,4 @@
1
- import { type CandidateRecord, type EvaluationScorecard, type HostedWorkbenchJob, type RunSummary, type RuntimeEvent, type SurfaceSnapshotFile, type WorkbenchRuntimeBundle, type WorkbenchRuntimeBundleStats, type WorkbenchRuntimeImportResult, type WorkbenchExecutionTrace, type WorkbenchTraceSession } from "@workbench-ai/workbench-core";
1
+ import { type CandidateRecord, type EvaluationScorecard, type RemoteWorkbenchJob, type RunSummary, type RuntimeEvent, type SurfaceSnapshotFile, type WorkbenchRuntimeBundle, type WorkbenchRuntimeBundleStats, type WorkbenchRuntimeImportResult, type WorkbenchExecutionTrace, type WorkbenchTraceSession } from "@workbench-ai/workbench-core";
2
2
  export interface LocalArchiveSnapshot {
3
3
  activeId: string | null;
4
4
  candidates: CandidateRecord[];
@@ -14,7 +14,7 @@ export interface LocalArchiveIndex {
14
14
  runs: RunSummary[];
15
15
  events: RuntimeEvent[];
16
16
  }
17
- export type LocalArchivedJob = HostedWorkbenchJob & {
17
+ export type LocalArchivedJob = RemoteWorkbenchJob & {
18
18
  trace?: WorkbenchExecutionTrace;
19
19
  traceSessions?: WorkbenchTraceSession[];
20
20
  };
@@ -22,13 +22,13 @@ export declare function localRuntimeDir(workspace: string): string;
22
22
  export declare function loadLocalArchive(workspace: string): Promise<LocalArchiveSnapshot>;
23
23
  export declare function loadLocalArchiveIndex(workspace: string): Promise<LocalArchiveIndex>;
24
24
  export declare function saveLocalArchive(workspace: string, snapshot: LocalArchiveSnapshot): Promise<void>;
25
- export declare function saveLocalJobs(workspace: string, jobs: readonly HostedWorkbenchJob[]): Promise<void>;
25
+ export declare function saveLocalJobs(workspace: string, jobs: readonly RemoteWorkbenchJob[]): Promise<void>;
26
26
  export declare function exportLocalRuntimeBundle(workspace: string, options?: {
27
27
  currentBenchmarkFingerprint?: string;
28
28
  }): Promise<WorkbenchRuntimeBundle>;
29
29
  export declare function importLocalRuntimeBundle(workspace: string, bundle: WorkbenchRuntimeBundle, currentBenchmarkFingerprint: string): Promise<WorkbenchRuntimeImportResult>;
30
30
  export declare function runtimeBundleStats(bundle: WorkbenchRuntimeBundle): WorkbenchRuntimeBundleStats;
31
- export declare function sanitizeRuntimeJobForExchange(job: HostedWorkbenchJob): HostedWorkbenchJob;
31
+ export declare function sanitizeRuntimeJobForExchange(job: RemoteWorkbenchJob): RemoteWorkbenchJob;
32
32
  export declare function readLocalExecutionFiles(workspace: string, jobId: string): Promise<SurfaceSnapshotFile[]>;
33
33
  export declare function readLocalCandidateRecord(workspace: string, candidateId: string): Promise<CandidateRecord>;
34
34
  export declare function readLocalCandidateFilesForId(workspace: string, candidateId: string): Promise<SurfaceSnapshotFile[]>;
@@ -1 +1 @@
1
- {"version":3,"file":"local-archive.d.ts","sourceRoot":"","sources":["../src/local-archive.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"local-archive.d.ts","sourceRoot":"","sources":["../src/local-archive.ts"],"names":[],"mappings":"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"}
@@ -1,6 +1,6 @@
1
1
  import { promises as fs } from "node:fs";
2
2
  import path from "node:path";
3
- import { buildWorkbenchTraceSessionsFromFiles, candidateRecordWithoutDerivedFields, compactWorkbenchRuntimeJobForExchange, mergeWorkbenchRuntimeCandidateForExchange, sanitizeWorkbenchRuntimeCandidateForExchange, sanitizeWorkbenchRuntimeJobForExchange, selectExecutionOutputFilesForInspection, workbenchRuntimeExplicitActiveId, workbenchRuntimeBundleStats, workbenchRuntimeCandidateIdentityForExchange, workbenchSurfaceFilesEqualForExchange, } from "@workbench-ai/workbench-core";
3
+ import { buildWorkbenchTraceSessionsFromFiles, candidateRecordWithoutDerivedFields, compactWorkbenchRuntimeJobForExchange, mergeWorkbenchRuntimeCandidateForExchange, sanitizeWorkbenchRuntimeCandidateForExchange, sanitizeWorkbenchRuntimeJobForExchange, selectExecutionOutputFilesForInspection, isSurfaceSnapshotFile, jsonRecord, normalizeRelativePath, readSurfaceFiles, workbenchRuntimeExplicitActiveId, workbenchRuntimeBundleStats, workbenchRuntimeCandidateIdentityForExchange, workbenchSurfaceFilesEqualForExchange, writeSurfaceFiles, } from "@workbench-ai/workbench-core";
4
4
  const RUNTIME_DIR = ".workbench/runtime";
5
5
  const CANDIDATE_RECORDS_DIR = "candidates";
6
6
  export function localRuntimeDir(workspace) {
@@ -682,14 +682,6 @@ function completedJobOutputFiles(job) {
682
682
  }
683
683
  return output.files.filter(isSurfaceSnapshotFile).map((file) => ({ ...file }));
684
684
  }
685
- function isSurfaceSnapshotFile(value) {
686
- const record = jsonRecord(value);
687
- return (typeof record.path === "string" &&
688
- (record.kind === "text" || record.kind === "binary") &&
689
- (record.encoding === "utf8" || record.encoding === "base64") &&
690
- typeof record.content === "string" &&
691
- typeof record.executable === "boolean");
692
- }
693
685
  function readExecutionPurpose(job) {
694
686
  const input = jsonRecord(job.input);
695
687
  return stringValue(jsonRecord(input.execution).purpose);
@@ -751,15 +743,9 @@ function traceEvent(args) {
751
743
  }
752
744
  function traceUsageSummary(value) {
753
745
  const record = jsonRecord(value);
754
- const usage = Object.keys(jsonRecord(record.total)).length > 0
755
- ? jsonRecord(record.total)
756
- : Object.keys(jsonRecord(record.improver)).length > 0
757
- ? jsonRecord(record.improver)
758
- : Object.keys(jsonRecord(record.runner)).length > 0
759
- ? jsonRecord(record.runner)
760
- : Object.keys(jsonRecord(record.engine)).length > 0
761
- ? jsonRecord(record.engine)
762
- : record;
746
+ const usage = ["total", "improver", "runner", "engine"]
747
+ .map((key) => jsonRecord(record[key]))
748
+ .find((entry) => Object.keys(entry).length > 0) ?? record;
763
749
  if (Object.keys(usage).length === 0) {
764
750
  return null;
765
751
  }
@@ -779,11 +765,6 @@ function traceUsageSummary(value) {
779
765
  pricing_source: stringValue(usage.pricingSource) ?? stringValue(usage.pricing_source),
780
766
  };
781
767
  }
782
- function jsonRecord(value) {
783
- return value && typeof value === "object" && !Array.isArray(value)
784
- ? value
785
- : {};
786
- }
787
768
  function stringValue(value) {
788
769
  return typeof value === "string" && value.length > 0 ? value : null;
789
770
  }
@@ -850,70 +831,3 @@ async function writeJson(filePath, value) {
850
831
  await fs.mkdir(path.dirname(filePath), { recursive: true });
851
832
  await fs.writeFile(filePath, `${JSON.stringify(value, null, 2)}\n`);
852
833
  }
853
- async function writeSurfaceFiles(root, files) {
854
- await fs.mkdir(root, { recursive: true });
855
- for (const file of files) {
856
- const target = path.join(root, normalizeRelativePath(file.path));
857
- await fs.mkdir(path.dirname(target), { recursive: true });
858
- const body = file.encoding === "base64" ? Buffer.from(file.content, "base64") : Buffer.from(file.content, "utf8");
859
- await fs.writeFile(target, body);
860
- if (file.executable) {
861
- await fs.chmod(target, 0o755).catch(() => undefined);
862
- }
863
- }
864
- }
865
- async function readSurfaceFiles(root) {
866
- const decoder = new TextDecoder("utf-8", { fatal: true });
867
- const files = [];
868
- async function walk(directory) {
869
- const entries = await fs.readdir(directory, { withFileTypes: true }).catch(() => []);
870
- for (const entry of entries) {
871
- const absolutePath = path.join(directory, entry.name);
872
- if (entry.isDirectory()) {
873
- await walk(absolutePath);
874
- continue;
875
- }
876
- if (!entry.isFile()) {
877
- continue;
878
- }
879
- const body = await fs.readFile(absolutePath);
880
- const relativePath = normalizeRelativePath(path.relative(root, absolutePath).replace(/\\/gu, "/"));
881
- const stats = await fs.stat(absolutePath);
882
- const content = encodeContent(body, decoder);
883
- files.push({
884
- path: relativePath,
885
- kind: content.encoding === "base64" ? "binary" : "text",
886
- encoding: content.encoding,
887
- content: content.content,
888
- executable: (stats.mode & 0o111) !== 0,
889
- });
890
- }
891
- }
892
- await walk(root);
893
- return files.sort((left, right) => left.path.localeCompare(right.path));
894
- }
895
- function encodeContent(body, decoder) {
896
- try {
897
- return {
898
- encoding: "utf8",
899
- content: decoder.decode(body),
900
- };
901
- }
902
- catch {
903
- return {
904
- encoding: "base64",
905
- content: body.toString("base64"),
906
- };
907
- }
908
- }
909
- function normalizeRelativePath(filePath) {
910
- const normalized = filePath.replace(/\\/gu, "/").replace(/^\/+/u, "");
911
- if (!normalized || normalized.includes("\0")) {
912
- throw new Error("File paths must be non-empty relative paths.");
913
- }
914
- const parts = normalized.split("/");
915
- if (parts.some((part) => part === ".." || part === "." || part === "")) {
916
- throw new Error(`Unsafe relative file path: ${filePath}`);
917
- }
918
- return normalized;
919
- }
@@ -0,0 +1,9 @@
1
+ import { type WorkbenchInspection } from "@workbench-ai/workbench-core";
2
+ import { type LocalProjectSource } from "./project-source.js";
3
+ export interface LocalWorkbenchInspectionOptions {
4
+ workspace: string;
5
+ readProjectSource?: () => Promise<LocalProjectSource>;
6
+ }
7
+ export declare function createLocalWorkbenchInspection(options: LocalWorkbenchInspectionOptions): WorkbenchInspection;
8
+ export declare function createLocalProjectSourceReader(workspace: string): () => Promise<LocalProjectSource>;
9
+ //# sourceMappingURL=local-inspection.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"local-inspection.d.ts","sourceRoot":"","sources":["../src/local-inspection.ts"],"names":[],"mappings":"AAEA,OAAO,EAaL,KAAK,mBAAmB,EAGzB,MAAM,8BAA8B,CAAC;AAetC,OAAO,EAGL,KAAK,kBAAkB,EAExB,MAAM,qBAAqB,CAAC;AAgB7B,MAAM,WAAW,+BAA+B;IAC9C,SAAS,EAAE,MAAM,CAAC;IAClB,iBAAiB,CAAC,EAAE,MAAM,OAAO,CAAC,kBAAkB,CAAC,CAAC;CACvD;AAED,wBAAgB,8BAA8B,CAC5C,OAAO,EAAE,+BAA+B,GACvC,mBAAmB,CAqCrB;AAED,wBAAgB,8BAA8B,CAC5C,SAAS,EAAE,MAAM,GAChB,MAAM,OAAO,CAAC,kBAAkB,CAAC,CAiBnC"}
@@ -0,0 +1,317 @@
1
+ import path from "node:path";
2
+ import { WorkbenchInspectionError, candidateRecordWithoutDerivedFields, candidateSummaryFromRecord, createWorkbenchInspection, loadAuthoredWorkbenchSourceDocument, traceSessionLabel, } from "@workbench-ai/workbench-core";
3
+ import { localBenchmarkFingerprint } from "./benchmark-fingerprint.js";
4
+ import { loadLocalArchiveIndex, readLocalCandidateFilesForId, readLocalCandidateRecord, readLocalEvaluationRecord, readLocalExecutionFiles, readLocalJobInRun, readLocalRunJobs, readLocalRunRecord, } from "./local-archive.js";
5
+ import { readLocalAuthoredProjectSource, readLocalProjectSource, WORKBENCH_BENCHMARK_FILE, } from "./project-source.js";
6
+ const PROJECT_SOURCE_CACHE_TTL_MS = 1000;
7
+ export function createLocalWorkbenchInspection(options) {
8
+ const context = {
9
+ workspace: path.resolve(options.workspace),
10
+ readProjectSource: options.readProjectSource ??
11
+ createLocalProjectSourceReader(options.workspace),
12
+ };
13
+ const backend = {
14
+ projectId: "local",
15
+ snapshot: () => localBenchmarkSnapshot(context),
16
+ spec: (input) => localSpecDocument(context, input.fingerprint),
17
+ sourceFiles: (input) => localBenchmarkMountedFiles(context, input.fingerprint),
18
+ candidate: (input) => readCandidateForInspection(context.workspace, input.id),
19
+ candidateFiles: async (input) => {
20
+ const candidate = await readCandidateForInspection(context.workspace, input.id);
21
+ return {
22
+ files: await readCandidateFilesForInspection(context.workspace, input.id),
23
+ changedPaths: candidate.fileChanges,
24
+ };
25
+ },
26
+ evaluation: (input) => readEvaluationForInspection(context.workspace, input.id),
27
+ run: async (input) => {
28
+ const run = await readRunForInspection(context.workspace, input.id);
29
+ return {
30
+ run,
31
+ ...(input.includeJobs
32
+ ? { jobs: await readLocalRunJobs(context.workspace, input.id) }
33
+ : {}),
34
+ };
35
+ },
36
+ jobInRun: (input) => readExecutionJobForRun(context.workspace, input.runId, input.jobId),
37
+ executionFiles: (input) => readExecutionFilesForRun(context.workspace, input.runId, input.jobId),
38
+ traceForJob: readLocalAggregateTrace,
39
+ traceSessionsForJob: readLocalTraceSessions,
40
+ };
41
+ return createWorkbenchInspection(backend);
42
+ }
43
+ export function createLocalProjectSourceReader(workspace) {
44
+ const resolvedWorkspace = path.resolve(workspace);
45
+ let cached = null;
46
+ return () => {
47
+ const now = Date.now();
48
+ if (cached && now - cached.loadedAt < PROJECT_SOURCE_CACHE_TTL_MS) {
49
+ return cached.promise;
50
+ }
51
+ const promise = readLocalProjectSource(resolvedWorkspace);
52
+ cached = { loadedAt: now, promise };
53
+ promise.catch(() => {
54
+ if (cached?.promise === promise) {
55
+ cached = null;
56
+ }
57
+ });
58
+ return promise;
59
+ };
60
+ }
61
+ async function localBenchmarkSnapshot(context) {
62
+ const { workspace } = context;
63
+ const snapshot = await loadLocalArchiveIndex(workspace);
64
+ const candidates = snapshot.candidates.filter(isInspectableCandidateRecord);
65
+ const summaries = candidates.map(candidateSummaryFromRecord);
66
+ const activeId = snapshot.activeId && candidates.some((candidate) => candidate.id === snapshot.activeId)
67
+ ? snapshot.activeId
68
+ : null;
69
+ const currentBenchmarkFingerprint = await readCurrentBenchmarkFingerprint(context);
70
+ return {
71
+ workspaceRoot: path.resolve(workspace),
72
+ activeId,
73
+ currentBenchmarkFingerprint,
74
+ summaries,
75
+ evaluations: snapshot.evaluations.map(evaluationSummary),
76
+ runs: snapshot.runs.map(publicLocalRunSummary),
77
+ };
78
+ }
79
+ async function localSpecDocument(context, benchmarkFingerprint) {
80
+ const { workspace } = context;
81
+ const projectSource = await context.readProjectSource().catch(() => null);
82
+ const authoredSource = projectSource
83
+ ? null
84
+ : await readLocalAuthoredProjectSource(workspace).catch(() => null);
85
+ const requestedFingerprint = normalizeOptionalFingerprint(benchmarkFingerprint);
86
+ const currentFingerprint = projectSource
87
+ ? localBenchmarkFingerprint(projectSource)
88
+ : null;
89
+ if (requestedFingerprint &&
90
+ currentFingerprint &&
91
+ requestedFingerprint !== currentFingerprint) {
92
+ const snapshot = await loadLocalArchiveIndex(workspace);
93
+ const document = localHistoricalBenchmarkDocument(snapshot, requestedFingerprint);
94
+ if (document) {
95
+ return document;
96
+ }
97
+ throw new WorkbenchInspectionError(`Benchmark version not found: ${requestedFingerprint}`, { status: 404 });
98
+ }
99
+ const sourceYaml = projectSource?.specSource ?? authoredSource?.specSource ?? "";
100
+ const cases = projectSource
101
+ ? caseSummaryFilesFromEngineCases(projectSource.engineCases, projectSource.engineResolveFiles)
102
+ : [];
103
+ return loadAuthoredWorkbenchSourceDocument({
104
+ sourceYaml,
105
+ path: WORKBENCH_BENCHMARK_FILE,
106
+ sourceFiles: projectSource?.sourceFiles ?? authoredSource?.sourceFiles,
107
+ cases,
108
+ });
109
+ }
110
+ async function localBenchmarkMountedFiles(context, benchmarkFingerprint) {
111
+ const requestedFingerprint = normalizeOptionalFingerprint(benchmarkFingerprint);
112
+ const { workspace } = context;
113
+ const projectSource = await context.readProjectSource();
114
+ const currentFingerprint = localBenchmarkFingerprint(projectSource);
115
+ if (requestedFingerprint &&
116
+ currentFingerprint &&
117
+ requestedFingerprint !== currentFingerprint) {
118
+ const snapshot = await loadLocalArchiveIndex(workspace);
119
+ return localHistoricalBenchmarkFiles(snapshot, requestedFingerprint);
120
+ }
121
+ return inspectableEngineCaseFiles(projectSource.engineCases);
122
+ }
123
+ function publicLocalRunSummary(run) {
124
+ const { executionFingerprint: _executionFingerprint, ...summary } = run;
125
+ return summary;
126
+ }
127
+ async function readCurrentBenchmarkFingerprint(context) {
128
+ return await context.readProjectSource()
129
+ .then(localBenchmarkFingerprint)
130
+ .catch(() => null);
131
+ }
132
+ function caseSummaryFilesFromEngineCases(engineCases, files) {
133
+ const existingCaseIds = new Set(files.flatMap((file) => {
134
+ const normalized = file.path.replace(/\\/gu, "/").replace(/^\/+/u, "");
135
+ const slash = normalized.indexOf("/");
136
+ return slash > 0 ? [normalized.slice(0, slash)] : [];
137
+ }));
138
+ return [
139
+ ...files.map((file) => ({ ...file })),
140
+ ...engineCases
141
+ .filter((engineCase) => !existingCaseIds.has(engineCase.id))
142
+ .map((engineCase) => ({
143
+ path: `${engineCase.id}/.workbench-case.json`,
144
+ encoding: "utf8",
145
+ content: `${JSON.stringify({ id: engineCase.id })}\n`,
146
+ executable: false,
147
+ })),
148
+ ];
149
+ }
150
+ function localHistoricalBenchmarkDocument(snapshot, benchmarkFingerprint) {
151
+ const candidate = snapshot.candidates.find((entry) => entry.benchmarkFingerprint === benchmarkFingerprint && readBenchmarkSourceMetadata(entry));
152
+ const source = candidate ? readBenchmarkSourceMetadata(candidate) : null;
153
+ if (!source?.sourceYaml) {
154
+ return null;
155
+ }
156
+ return loadAuthoredWorkbenchSourceDocument({
157
+ sourceYaml: source.sourceYaml,
158
+ path: WORKBENCH_BENCHMARK_FILE,
159
+ sourceFiles: source.files,
160
+ cases: localHistoricalBenchmarkFiles(snapshot, benchmarkFingerprint),
161
+ });
162
+ }
163
+ function localHistoricalBenchmarkFiles(_snapshot, _benchmarkFingerprint) {
164
+ return [];
165
+ }
166
+ function inspectableEngineCaseFiles(engineCases) {
167
+ return engineCases.flatMap((bundle) => engineCaseFiles(bundle).map((file) => ({
168
+ ...file,
169
+ path: `${bundle.id}/${file.path}`,
170
+ }))).sort((left, right) => left.path.localeCompare(right.path));
171
+ }
172
+ function engineCaseFiles(bundle) {
173
+ const buckets = bundle.files;
174
+ return buckets.source?.length
175
+ ? buckets.source
176
+ : [...(buckets.public ?? []), ...(buckets.private ?? [])];
177
+ }
178
+ function isInspectableCandidateRecord(candidate) {
179
+ return Boolean(candidate.eval || asRecord(asRecord(candidate.meta)?.source));
180
+ }
181
+ function evaluationSummary(evaluation) {
182
+ const { evaluation: _evaluation, ...summary } = evaluation;
183
+ return summary;
184
+ }
185
+ async function readCandidateForInspection(workspace, candidateId) {
186
+ const candidate = await readArchiveRecord("Candidate", candidateId, () => readLocalCandidateRecord(workspace, candidateId));
187
+ return candidateRecordWithoutDerivedFields(candidate);
188
+ }
189
+ async function readEvaluationForInspection(workspace, evaluationId) {
190
+ return await readArchiveRecord("Evaluation", evaluationId, () => readLocalEvaluationRecord(workspace, evaluationId));
191
+ }
192
+ async function readRunForInspection(workspace, runId) {
193
+ return await readArchiveRecord("Run", runId, () => readLocalRunRecord(workspace, runId));
194
+ }
195
+ async function readCandidateFilesForInspection(workspace, candidateId) {
196
+ return await readArchiveRecord("Candidate", candidateId, () => readLocalCandidateFilesForId(workspace, candidateId));
197
+ }
198
+ function readBenchmarkSourceMetadata(candidate) {
199
+ const benchmark = asRecord(asRecord(candidate.meta)?.benchmark);
200
+ const files = Array.isArray(benchmark?.files)
201
+ ? benchmark.files
202
+ .map(readSurfaceSnapshotFile)
203
+ .filter((file) => file !== null)
204
+ : [];
205
+ const sourceYaml = files.find((file) => file.path === WORKBENCH_BENCHMARK_FILE)?.content ?? null;
206
+ if (!sourceYaml) {
207
+ return null;
208
+ }
209
+ return { sourceYaml, files };
210
+ }
211
+ function readSurfaceSnapshotFile(value) {
212
+ const record = asRecord(value);
213
+ if (!record) {
214
+ return null;
215
+ }
216
+ const filePath = typeof record?.path === "string" ? record.path : "";
217
+ const content = typeof record?.content === "string" ? record.content : null;
218
+ if (!filePath || content === null) {
219
+ return null;
220
+ }
221
+ return {
222
+ path: filePath,
223
+ kind: record.kind === "binary" ? "binary" : "text",
224
+ encoding: record.encoding === "base64" ? "base64" : "utf8",
225
+ content,
226
+ executable: record.executable === true,
227
+ };
228
+ }
229
+ async function readArchiveRecord(kind, id, read) {
230
+ try {
231
+ return await read();
232
+ }
233
+ catch (error) {
234
+ if (error instanceof Error && error.message === `${kind} not found: ${id}`) {
235
+ throw new WorkbenchInspectionError(error.message, { status: 404 });
236
+ }
237
+ throw error;
238
+ }
239
+ }
240
+ async function readExecutionFilesForRun(workspace, runId, jobId) {
241
+ await readExecutionJobForRun(workspace, runId, jobId);
242
+ return await readLocalExecutionFiles(workspace, jobId);
243
+ }
244
+ async function readExecutionJobForRun(workspace, runId, jobId) {
245
+ const job = await readLocalJobInRun(workspace, runId, jobId);
246
+ if (!job) {
247
+ throw new WorkbenchInspectionError(`Execution job not found: ${jobId}`, { status: 404 });
248
+ }
249
+ return job;
250
+ }
251
+ function readLocalAggregateTrace(job) {
252
+ const trace = job.trace;
253
+ if (!trace || typeof trace !== "object" || Array.isArray(trace)) {
254
+ return { trace_id: job.id, spans: [], events: [], summaries: [] };
255
+ }
256
+ const record = trace;
257
+ return {
258
+ trace_id: typeof record.trace_id === "string" ? record.trace_id : job.id,
259
+ spans: Array.isArray(record.spans) ? record.spans : [],
260
+ events: Array.isArray(record.events) ? record.events : [],
261
+ summaries: Array.isArray(record.summaries) ? record.summaries : [],
262
+ };
263
+ }
264
+ function readLocalTraceSessions(job, role) {
265
+ if (!Array.isArray(job.traceSessions)) {
266
+ return [];
267
+ }
268
+ return job.traceSessions.map((session) => ({
269
+ id: typeof session.id === "string" && session.id.length > 0
270
+ ? session.id
271
+ : `${job.id}:trace`,
272
+ jobId: typeof session.jobId === "string" && session.jobId.length > 0
273
+ ? session.jobId
274
+ : job.id,
275
+ role: session.role === "improver" || session.role === "runner" || session.role === "engine"
276
+ ? session.role
277
+ : role,
278
+ kind: typeof session.kind === "string" && session.kind.length > 0 ? session.kind : "trace",
279
+ label: traceSessionDisplayLabel(session, role),
280
+ sourcePath: typeof session.sourcePath === "string" ? session.sourcePath : null,
281
+ trace: sanitizeLocalTrace(session.trace, session.id),
282
+ ...(session.metadata && typeof session.metadata === "object" && !Array.isArray(session.metadata)
283
+ ? { metadata: session.metadata }
284
+ : {}),
285
+ }));
286
+ }
287
+ function traceSessionDisplayLabel(session, fallbackRole) {
288
+ const role = session.role === "improver" || session.role === "runner" || session.role === "engine"
289
+ ? session.role
290
+ : fallbackRole;
291
+ return typeof session.label === "string" && session.label.length > 0
292
+ ? session.label
293
+ : typeof session.sourcePath === "string" && session.sourcePath.length > 0
294
+ ? traceSessionLabel(session.sourcePath, role)
295
+ : "Trace";
296
+ }
297
+ function sanitizeLocalTrace(trace, fallbackId) {
298
+ if (!trace || typeof trace !== "object" || Array.isArray(trace)) {
299
+ return { trace_id: fallbackId, spans: [], events: [], summaries: [] };
300
+ }
301
+ const record = trace;
302
+ return {
303
+ trace_id: typeof record.trace_id === "string" ? record.trace_id : fallbackId,
304
+ spans: Array.isArray(record.spans) ? record.spans : [],
305
+ events: Array.isArray(record.events) ? record.events : [],
306
+ summaries: Array.isArray(record.summaries) ? record.summaries : [],
307
+ };
308
+ }
309
+ function asRecord(value) {
310
+ return value && typeof value === "object" && !Array.isArray(value)
311
+ ? value
312
+ : null;
313
+ }
314
+ function normalizeOptionalFingerprint(value) {
315
+ const normalized = value?.trim();
316
+ return normalized ? normalized : null;
317
+ }
@@ -5,7 +5,7 @@ import { type ResolvedWorkbenchAdapter } from "./adapter-project.js";
5
5
  export declare const WORKBENCH_BENCHMARK_FILE = "benchmark.yaml";
6
6
  export declare const WORKBENCH_CANDIDATES_DIR = "candidates";
7
7
  export declare const WORKBENCH_CANDIDATE_FILE = "candidate.yaml";
8
- export type HostedFile = WorkspaceSnapshotFile;
8
+ export type RemoteFile = WorkspaceSnapshotFile;
9
9
  export interface LocalProjectSource {
10
10
  dir: string;
11
11
  specPath: string;
@@ -25,11 +25,11 @@ export interface LocalProjectSource {
25
25
  dockerfilePath: string;
26
26
  dockerfile: string;
27
27
  runtimeDockerfile: string;
28
- dockerfileFiles: HostedFile[];
29
- candidateFiles: HostedFile[];
30
- engineResolveFiles: HostedFile[];
28
+ dockerfileFiles: RemoteFile[];
29
+ candidateFiles: RemoteFile[];
30
+ engineResolveFiles: RemoteFile[];
31
31
  adapters: ResolvedWorkbenchAdapter[];
32
- adapterFiles: HostedFile[];
32
+ adapterFiles: RemoteFile[];
33
33
  caseIds: string[];
34
34
  engineCases: WorkbenchEngineCase[];
35
35
  engineResolve: LocalEngineResolveInvocation;
@@ -58,6 +58,6 @@ interface LocalProjectSourceOptions {
58
58
  }
59
59
  export declare function readLocalProjectSource(source: string, options?: LocalProjectSourceOptions): Promise<LocalProjectSource>;
60
60
  export declare function readLocalAuthoredProjectSource(source: string, options?: LocalProjectSourceOptions): Promise<LocalAuthoredProjectSource>;
61
- export declare function hostedEngineResolveFiles(source: LocalProjectSource): HostedFile[];
61
+ export declare function remoteEngineResolveFiles(source: LocalProjectSource): RemoteFile[];
62
62
  export {};
63
63
  //# sourceMappingURL=project-source.d.ts.map
@@ -60,7 +60,7 @@ export async function readLocalProjectSource(source, options = {}) {
60
60
  ? normalizeSurfaceFiles(await readSnapshotFiles(absoluteCandidateFilesPath))
61
61
  : [];
62
62
  const rawEngineResolveFiles = engineResolveFilesFromBundles(normalizedSources.engineCases);
63
- const engineResolveFiles = toHostedFiles(rawEngineResolveFiles);
63
+ const engineResolveFiles = toRemoteFiles(rawEngineResolveFiles);
64
64
  const engineCases = normalizedSources.engineCases;
65
65
  if (engineCases.length === 0) {
66
66
  throw new WorkspaceSnapshotError(`Engine resolver ${normalizedSources.engineResolve.use} did not emit any cases.`);
@@ -85,11 +85,11 @@ export async function readLocalProjectSource(source, options = {}) {
85
85
  dockerfilePath,
86
86
  dockerfile,
87
87
  runtimeDockerfile: composedDockerfile,
88
- dockerfileFiles: toHostedFiles(dockerfileSourceFiles(dockerfileSources)),
89
- candidateFiles: toHostedFiles(candidateFiles),
88
+ dockerfileFiles: toRemoteFiles(dockerfileSourceFiles(dockerfileSources)),
89
+ candidateFiles: toRemoteFiles(candidateFiles),
90
90
  engineResolveFiles,
91
91
  adapters,
92
- adapterFiles: toHostedFiles(adapterFiles),
92
+ adapterFiles: toRemoteFiles(adapterFiles),
93
93
  caseIds,
94
94
  engineCases,
95
95
  engineResolve: normalizedSources.engineResolve,
@@ -136,7 +136,7 @@ export async function readLocalAuthoredProjectSource(source, options = {}) {
136
136
  ],
137
137
  };
138
138
  }
139
- export function hostedEngineResolveFiles(source) {
139
+ export function remoteEngineResolveFiles(source) {
140
140
  return [
141
141
  ...source.engineResolveFiles,
142
142
  {
@@ -672,7 +672,7 @@ function adapterSourceSnapshotPath(adapter, filePath) {
672
672
  ? `${adapter.source}/${filePath}`
673
673
  : `adapters/${adapter.manifest.id}/${filePath}`;
674
674
  }
675
- function toHostedFiles(files) {
675
+ function toRemoteFiles(files) {
676
676
  return files.map((file) => ({
677
677
  path: file.path,
678
678
  content: file.content,
package/package.json CHANGED
@@ -1,6 +1,11 @@
1
1
  {
2
2
  "name": "@workbench-ai/workbench",
3
- "version": "0.0.64",
3
+ "version": "0.0.66",
4
+ "repository": {
5
+ "type": "git",
6
+ "url": "git+https://github.com/workbench-ai/workbench.git",
7
+ "directory": "packages/cli"
8
+ },
4
9
  "type": "module",
5
10
  "publishConfig": {
6
11
  "registry": "https://registry.npmjs.org/",
@@ -16,9 +21,9 @@
16
21
  ],
17
22
  "dependencies": {
18
23
  "yaml": "^2.8.2",
19
- "@workbench-ai/workbench-built-in-adapters": "0.0.64",
20
- "@workbench-ai/workbench-protocol": "0.0.64",
21
- "@workbench-ai/workbench-core": "0.0.64"
24
+ "@workbench-ai/workbench-built-in-adapters": "0.0.66",
25
+ "@workbench-ai/workbench-protocol": "0.0.66",
26
+ "@workbench-ai/workbench-core": "0.0.66"
22
27
  },
23
28
  "devDependencies": {
24
29
  "@tailwindcss/postcss": "^4.2.2",