@workbench-ai/workbench 0.0.66 → 0.0.67

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.
@@ -101,13 +101,26 @@ function isKnownWorkbenchDocumentPath(pathname) {
101
101
  if (segments.length === 0) {
102
102
  return true;
103
103
  }
104
+ if (segments.length === 1 && (segments[0] === "manifest" || segments[0] === "files")) {
105
+ return true;
106
+ }
104
107
  if (segments[0] === "evaluations") {
105
- return segments.length === 1;
108
+ if (segments.length === 1 || segments.length === 2) {
109
+ return true;
110
+ }
111
+ if (segments.length < 4 || segments[2] !== "cases") {
112
+ return false;
113
+ }
114
+ return segments.length === 4 ||
115
+ (segments.length === 5 && (segments[4] === "attempts" || segments[4] === "files"));
106
116
  }
107
117
  if (segments[0] !== "candidates") {
108
118
  return false;
109
119
  }
110
- if (segments.length === 1 || segments.length === 2) {
120
+ if (segments.length === 1 || (segments.length === 2 && segments[1] === "lineage")) {
121
+ return true;
122
+ }
123
+ if (segments.length === 2) {
111
124
  return true;
112
125
  }
113
126
  return segments.length === 3 && (segments[2] === "files" || segments[2] === "manifest");
@@ -132,14 +145,9 @@ async function handleApiRequest(request, response, context, url) {
132
145
  }), 200, request.method);
133
146
  return;
134
147
  case "/api/source/files":
135
- sendJson(response, await inspection.sourceFiles({
148
+ sendJson(response, await inspection.sourceFileSurface({
136
149
  fingerprint: readOptionalSearchString(url.searchParams, "fingerprint"),
137
- }), 200, request.method);
138
- return;
139
- case "/api/source/preview":
140
- sendJson(response, await inspection.sourcePreview({
141
- fingerprint: readOptionalSearchString(url.searchParams, "fingerprint"),
142
- path: readSearchString(url.searchParams, "path"),
150
+ path: readOptionalSearchString(url.searchParams, "path"),
143
151
  view: readPreviewMode(url.searchParams),
144
152
  }), 200, request.method);
145
153
  return;
@@ -151,14 +159,9 @@ async function handleApiRequest(request, response, context, url) {
151
159
  return;
152
160
  case "/api/candidate/files": {
153
161
  const candidateId = readSearchString(url.searchParams, "id");
154
- sendJson(response, await inspection.candidateFiles({ id: candidateId }), 200, request.method);
155
- return;
156
- }
157
- case "/api/candidate/preview": {
158
- const candidateId = readSearchString(url.searchParams, "id");
159
- sendJson(response, await inspection.candidatePreview({
162
+ sendJson(response, await inspection.candidateFileSurface({
160
163
  id: candidateId,
161
- path: readSearchString(url.searchParams, "path"),
164
+ path: readOptionalSearchString(url.searchParams, "path"),
162
165
  view: readPreviewMode(url.searchParams),
163
166
  }), 200, request.method);
164
167
  return;
@@ -186,20 +189,10 @@ async function handleApiRequest(request, response, context, url) {
186
189
  case "/api/execution/files": {
187
190
  const execRunId = readSearchString(url.searchParams, "run");
188
191
  const execJobId = readSearchString(url.searchParams, "id");
189
- sendJson(response, await inspection.executionFiles({
192
+ sendJson(response, await inspection.executionFileSurface({
190
193
  runId: execRunId,
191
194
  jobId: execJobId,
192
- }), 200, request.method);
193
- return;
194
- }
195
- case "/api/execution/preview": {
196
- const previewRunId = readSearchString(url.searchParams, "run");
197
- const previewJobId = readSearchString(url.searchParams, "id");
198
- const previewFilePath = readSearchString(url.searchParams, "path");
199
- sendJson(response, await inspection.executionPreview({
200
- runId: previewRunId,
201
- jobId: previewJobId,
202
- path: previewFilePath,
195
+ path: readOptionalSearchString(url.searchParams, "path"),
203
196
  view: readPreviewMode(url.searchParams),
204
197
  }), 200, request.method);
205
198
  return;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAgJA,UAAU,KAAK;IACb,KAAK,EAAE,MAAM,CAAC,cAAc,CAAC;IAC7B,MAAM,EAAE,MAAM,CAAC,cAAc,CAAC;IAC9B,MAAM,EAAE,MAAM,CAAC,cAAc,CAAC;CAC/B;AA8BD,UAAU,iBAAiB;CAAG;AA2M9B,wBAAsB,MAAM,CAC1B,IAAI,EAAE,SAAS,MAAM,EAAE,EACvB,EAAE,GAAE,KAIH,EACD,cAAc,GAAE,iBAAsB,GACrC,OAAO,CAAC,MAAM,CAAC,CA4HjB"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAmJA,UAAU,KAAK;IACb,KAAK,EAAE,MAAM,CAAC,cAAc,CAAC;IAC7B,MAAM,EAAE,MAAM,CAAC,cAAc,CAAC;IAC9B,MAAM,EAAE,MAAM,CAAC,cAAc,CAAC;CAC/B;AA8BD,UAAU,iBAAiB;CAAG;AAsN9B,wBAAsB,MAAM,CAC1B,IAAI,EAAE,SAAS,MAAM,EAAE,EACvB,EAAE,GAAE,KAIH,EACD,cAAc,GAAE,iBAAsB,GACrC,OAAO,CAAC,MAAM,CAAC,CA4HjB"}
package/dist/index.js CHANGED
@@ -5,7 +5,8 @@ import { createRequire } from "node:module";
5
5
  import os from "node:os";
6
6
  import path from "node:path";
7
7
  import { Writable } from "node:stream";
8
- import { createBaselineCandidateJob as createRuntimeBaselineCandidateJob, evaluationScorecardId, evaluationMeanMetrics, executeWorkbenchExecutionJob, engineResolveBindingForSpec, filterOptimizerTraceJobsForCaseIds, filterCandidateSourceFiles, formatWorkbenchCaseSelector, formatWorkbenchSelectionPolicy, workbenchCaseSelectorUsesAllCases, workbenchExecutionPurpose, workbenchRunExecutionFingerprint, createWorkbenchAdapterAuthBundle, createOptimizerTraceInputFiles, DOCKER_SANDBOX_BACKEND, localWorkbenchAdapterAuthStore, materializeWorkbenchRunResult, normalizeSurfaceFiles, isSurfaceSnapshotFile, jsonRecord, planWorkbenchExecutionJobsForPurpose, runWorkbenchExecutionDag, resolveEngineCaseExecutionConfig, resolveWorkbenchResolvedSourceYaml, runtimeResources, validateWorkbenchRunEnvelope, parseWorkbenchAdapterAuthTarget, workbenchEngineCaseIdsForImproveEvaluation, workbenchEngineCaseIdsForSelector, workbenchImproveOptimizeSelector, workbenchImproveSelectionPolicy, workbenchProjectSourceFingerprint, workbenchRuntimeBundleFingerprint, workbenchRuntimeExplicitActiveId, } from "@workbench-ai/workbench-core";
8
+ import { gzipSync } from "node:zlib";
9
+ import { createBaselineCandidateJob as createRuntimeBaselineCandidateJob, evaluationScorecardId, evaluationMeanMetrics, executeWorkbenchExecutionJob, engineResolveBindingForSpec, filterOptimizerTraceJobsForCaseIds, filterCandidateSourceFiles, formatWorkbenchCaseSelector, formatWorkbenchSelectionPolicy, workbenchCaseSelectorUsesAllCases, workbenchExecutionPurpose, workbenchRunExecutionFingerprint, createWorkbenchAdapterAuthBundle, createOptimizerTraceInputFiles, DOCKER_SANDBOX_BACKEND, localWorkbenchAdapterAuthStore, materializeWorkbenchRunResult, normalizeRelativePath, normalizeSurfaceFiles, isSurfaceSnapshotFile, jsonRecord, planWorkbenchExecutionJobsForPurpose, runWorkbenchExecutionDag, resolveEngineCaseExecutionConfig, resolveWorkbenchResolvedSourceYaml, runtimeResources, validateWorkbenchRunEnvelope, parseWorkbenchAdapterAuthTarget, workbenchEngineCaseIdsForImproveEvaluation, workbenchEngineCaseIdsForSelector, workbenchImproveOptimizeSelector, workbenchImproveSelectionPolicy, workbenchProjectSourceFingerprint, workbenchRuntimeCandidateIdentityForExchange, workbenchRuntimeBundleFingerprint, workbenchRuntimeProjectedActiveId, } from "@workbench-ai/workbench-core";
9
10
  import { assertWorkbenchAdapterOperationResultOk, collectWorkbenchAdapterAuthRequirements, normalizeWorkbenchAdapterOperationRequest, readWorkbenchAdapterOperationResult, workbenchAdapterOperationCommand, workbenchAdapterOperationResultPath, withDefaultWorkbenchAdapterAuthProfiles as applyDefaultWorkbenchAdapterAuthProfiles, } from "@workbench-ai/workbench-protocol";
10
11
  import { builtinLocalTraceAdapter, builtinLocalTraceAdapters, sortLocalTraceRefs, } from "@workbench-ai/workbench-built-in-adapters/local-traces";
11
12
  import { commandUsage, REMOTE_WATCH_LIFECYCLE_NOTE, LOCAL_DEV_OPEN_LIFECYCLE_NOTE, rootUsage, } from "./command-model.js";
@@ -34,6 +35,7 @@ class WorkbenchApiRequestError extends Error {
34
35
  }
35
36
  }
36
37
  const API_REQUEST_MAX_ATTEMPTS = 3;
38
+ const API_REQUEST_GZIP_THRESHOLD_BYTES = 1024 * 1024;
37
39
  const DEFAULT_BASE_URL = "https://v2.workbench.ai";
38
40
  const AUTH_COMMAND_HANDLERS = {
39
41
  connect: authConnect,
@@ -764,6 +766,11 @@ async function localRun(argv, io, runtimeOptions) {
764
766
  detail: { budget, samples, strategy: "greedy", optimizeOn: optimizeOnLabel, selectBy: selectByLabel },
765
767
  }),
766
768
  ];
769
+ const runInput = localRunInput({
770
+ benchmarkFingerprint,
771
+ sourceYaml: projectSource.specSource,
772
+ engineResolveFiles,
773
+ });
767
774
  const runningRun = {
768
775
  id: runId,
769
776
  workflow: "improve",
@@ -786,6 +793,7 @@ async function localRun(argv, io, runtimeOptions) {
786
793
  executionFingerprint,
787
794
  activeCandidateId: snapshot.activeId,
788
795
  outputCandidateId: null,
796
+ input: runInput,
789
797
  };
790
798
  snapshot = upsertLocalRun(snapshot, runningRun, events);
791
799
  await saveLocalArchive(workspace, snapshot);
@@ -954,6 +962,7 @@ async function localRun(argv, io, runtimeOptions) {
954
962
  outcome: failedJobCount > 0 ? "error" : "ok",
955
963
  activeCandidateId: snapshot.activeId,
956
964
  outputCandidateId: outputCandidateId ?? snapshot.activeId,
965
+ input: runInput,
957
966
  };
958
967
  events.push(createLocalEvent("run_finished", finishedAt, {
959
968
  runId,
@@ -1245,6 +1254,11 @@ async function localEvaluateCandidate(argv, io, runtimeOptions) {
1245
1254
  candidateId: evaluatedCandidateId,
1246
1255
  detail: { samples, strategy: "direct" },
1247
1256
  });
1257
+ const runInput = localRunInput({
1258
+ benchmarkFingerprint,
1259
+ sourceYaml: projectSource.specSource,
1260
+ engineResolveFiles,
1261
+ });
1248
1262
  const runningRun = {
1249
1263
  id: runId,
1250
1264
  workflow: "eval",
@@ -1265,6 +1279,7 @@ async function localEvaluateCandidate(argv, io, runtimeOptions) {
1265
1279
  executionFingerprint,
1266
1280
  activeCandidateId: activeCandidateIdBeforeEval,
1267
1281
  outputCandidateId: evaluatedCandidateId,
1282
+ input: runInput,
1268
1283
  };
1269
1284
  snapshot = upsertLocalRun(snapshot, runningRun, [runStartedEvent]);
1270
1285
  await saveLocalArchive(workspace, snapshot);
@@ -1350,7 +1365,7 @@ async function localEvaluateCandidate(argv, io, runtimeOptions) {
1350
1365
  durationMs: Math.max(0, Date.parse(finishedAt) - Date.parse(startedAt)),
1351
1366
  },
1352
1367
  });
1353
- snapshot = upsertLocalRun(snapshot, {
1368
+ const finishedRun = {
1354
1369
  id: runId,
1355
1370
  workflow: "eval",
1356
1371
  benchmarkFingerprint,
@@ -1374,7 +1389,9 @@ async function localEvaluateCandidate(argv, io, runtimeOptions) {
1374
1389
  outcome: currentRunFailedJobCount > 0 ? "error" : "ok",
1375
1390
  activeCandidateId,
1376
1391
  outputCandidateId: evaluatedCandidateId,
1377
- }, [runFinishedEvent]);
1392
+ input: runInput,
1393
+ };
1394
+ snapshot = upsertLocalRun(snapshot, finishedRun, [runFinishedEvent]);
1378
1395
  await saveLocalJobs(workspace, currentRunJobs);
1379
1396
  await saveLocalArchive(workspace, snapshot);
1380
1397
  const evaluation = materialized.evaluations[0] ?? null;
@@ -1578,8 +1595,7 @@ function localDevOpenUrl(baseUrl, snapshot, runId) {
1578
1595
  if (!evaluation) {
1579
1596
  return new URL("candidates", baseUrl).toString();
1580
1597
  }
1581
- const params = new URLSearchParams({ evaluation: evaluation.id });
1582
- return new URL(`candidates/${encodeURIComponent(evaluation.candidateId)}?${params.toString()}`, baseUrl).toString();
1598
+ return new URL(`evaluations/${encodeURIComponent(evaluation.id)}`, baseUrl).toString();
1583
1599
  }
1584
1600
  async function readLocalBenchmarkFingerprint(workspace) {
1585
1601
  return localBenchmarkFingerprint(await readLocalProjectSource(workspace));
@@ -1608,6 +1624,13 @@ function authoredBenchmarkSourceFiles(projectSource) {
1608
1624
  executable: false,
1609
1625
  }];
1610
1626
  }
1627
+ function localRunInput(input) {
1628
+ return {
1629
+ benchmarkFingerprint: input.benchmarkFingerprint,
1630
+ sourceYaml: input.sourceYaml,
1631
+ engineResolveFiles: normalizeSurfaceFiles(input.engineResolveFiles),
1632
+ };
1633
+ }
1611
1634
  function shellQuote(value) {
1612
1635
  return `'${value.replace(/'/gu, "'\\''")}'`;
1613
1636
  }
@@ -1873,11 +1896,16 @@ async function localCandidatePreview(argv, io) {
1873
1896
  const inspection = localInspectionFromParsed(parsed);
1874
1897
  const snapshot = await inspection.snapshot();
1875
1898
  const candidateId = readCandidateIdFlag(parsed, snapshot);
1876
- const preview = await inspection.candidatePreview({
1899
+ const requestedPath = requireFlag(parsed, "path");
1900
+ const surface = await inspection.candidateFileSurface({
1877
1901
  id: candidateId,
1878
- path: requireFlag(parsed, "path"),
1902
+ path: requestedPath,
1879
1903
  view: readPreviewMode(parsed),
1880
1904
  });
1905
+ const preview = surface.preview;
1906
+ if (!preview || preview.path !== normalizeRelativePath(requestedPath)) {
1907
+ throw new UsageError(`File not found: ${requestedPath}`);
1908
+ }
1881
1909
  const content = preview.source?.content ?? preview.rendered_html ?? preview.diff ?? "";
1882
1910
  const outputPath = asOptionalString(parsed.flags.output);
1883
1911
  if (outputPath && outputPath !== "-") {
@@ -3034,7 +3062,7 @@ function adapterAuthRecord(value) {
3034
3062
  }
3035
3063
  async function pushBenchmark(argv, io) {
3036
3064
  const parsed = parseArgs(argv);
3037
- rejectUnknownFlags(parsed, new Set(["dir", "visibility", "dry-run", "json"]));
3065
+ rejectUnknownFlags(parsed, new Set(["dir", "visibility", "dry-run", "force", "json"]));
3038
3066
  const dir = resolveSourceDir(parsed);
3039
3067
  const source = await readLocalProjectSource(dir);
3040
3068
  const origin = await readWorkbenchOrigin(dir);
@@ -3042,17 +3070,18 @@ async function pushBenchmark(argv, io) {
3042
3070
  const visibility = readOptionalBenchmarkVisibility(parsed.flags.visibility);
3043
3071
  const createVisibility = visibility ?? "public";
3044
3072
  const dryRun = parsed.flags["dry-run"] === true;
3073
+ const force = parsed.flags.force === true;
3045
3074
  const runtime = await exportLocalRuntimeBundle(dir, {
3046
3075
  currentBenchmarkFingerprint: localBenchmarkFingerprint(source),
3047
3076
  });
3048
3077
  const localRuntimeFingerprint = workbenchRuntimeBundleFingerprint(runtime);
3049
- const state = localProjectState({
3050
- source,
3051
- runtime,
3052
- origin,
3053
- visibility: createVisibility,
3054
- });
3055
3078
  if (!origin) {
3079
+ const state = localProjectState({
3080
+ source,
3081
+ runtime,
3082
+ origin,
3083
+ visibility: createVisibility,
3084
+ });
3056
3085
  if (dryRun) {
3057
3086
  writeOutput({
3058
3087
  ok: true,
@@ -3100,30 +3129,135 @@ async function pushBenchmark(argv, io) {
3100
3129
  if (!projectId) {
3101
3130
  throw new UsageError("Missing remote benchmark. Run workbench push from a source directory.");
3102
3131
  }
3132
+ const remoteProject = force || dryRun
3133
+ ? await readOriginProjectIfPresent({ baseUrl, origin })
3134
+ : null;
3103
3135
  if (dryRun) {
3104
- const remoteProject = await verifyLinkedPushDryRunTarget({
3105
- baseUrl,
3106
- origin,
3107
- projectId,
3136
+ const state = localProjectState({
3137
+ source,
3138
+ runtime,
3139
+ origin: remoteProject
3140
+ ? { ...origin, projectId: remoteProject.id }
3141
+ : origin,
3142
+ visibility: visibility ?? remoteProject?.visibility ?? createVisibility,
3108
3143
  });
3144
+ if (!remoteProject && !force) {
3145
+ throw new WorkbenchApiRequestError(404, `Workbench benchmark not found: ${origin.remote}`, "");
3146
+ }
3147
+ if (!remoteProject && force) {
3148
+ await assertForceCreateAllowed({ baseUrl, origin, sourceName: source.spec.name });
3149
+ }
3150
+ const action = force && !remoteProject ? "create" : force ? "replace" : "update";
3109
3151
  writeOutput({
3110
3152
  ok: true,
3111
3153
  dryRun: true,
3112
- action: "update",
3154
+ action,
3113
3155
  dir,
3114
3156
  baseUrl,
3115
- benchmarkId: projectId,
3157
+ benchmarkId: remoteProject?.id ?? projectId,
3116
3158
  remote: origin.remote,
3117
- benchmark: remoteProjectSummaryForOutput(remoteProject),
3159
+ ...(remoteProject ? { benchmark: remoteProjectSummaryForOutput(remoteProject) } : {}),
3118
3160
  benchmarkName: source.spec.name,
3119
- visibility: visibility ?? "unchanged",
3161
+ visibility: visibility ?? (remoteProject ? remoteProject.visibility ?? "unchanged" : createVisibility),
3120
3162
  sourceFileCount: sourceFileCount(source),
3121
3163
  runtime: runtimeBundleStats(runtime),
3122
3164
  sourceFingerprint: state.source.fingerprint,
3123
3165
  runtimeFingerprint: localRuntimeFingerprint,
3124
- }, parsed, io, () => `Would push ${sourceFileCount(source)} source file(s) and runtime history to ${origin.remote}.`);
3166
+ }, parsed, io, () => force && !remoteProject
3167
+ ? `Would create ${origin.remote} from local project state.`
3168
+ : force
3169
+ ? `Would replace ${origin.remote} with local project state.`
3170
+ : `Would push ${sourceFileCount(source)} source file(s) and runtime history to ${origin.remote}.`);
3171
+ return 0;
3172
+ }
3173
+ if (force) {
3174
+ const targetOrigin = remoteProject
3175
+ ? { ...origin, projectId: remoteProject.id }
3176
+ : null;
3177
+ const state = localProjectState({
3178
+ source,
3179
+ runtime,
3180
+ origin: targetOrigin,
3181
+ visibility: visibility ?? remoteProject?.visibility ?? createVisibility,
3182
+ });
3183
+ if (!remoteProject) {
3184
+ await assertForceCreateAllowed({ baseUrl, origin, sourceName: source.spec.name });
3185
+ const { project, origin: nextOrigin, result } = await createRemoteBenchmarkFromState({
3186
+ baseUrl,
3187
+ dir,
3188
+ state,
3189
+ });
3190
+ writeOutput({
3191
+ ok: true,
3192
+ action: "create",
3193
+ benchmark: project,
3194
+ visibility: project.visibility ?? createVisibility,
3195
+ origin: nextOrigin,
3196
+ source: result.source,
3197
+ runtime: result.runtime.stats,
3198
+ urls: buildWorkbenchResourceUrls({
3199
+ baseUrl,
3200
+ projectId: project.id,
3201
+ ...originRemoteUrlParts(nextOrigin),
3202
+ }),
3203
+ }, parsed, io, (record) => {
3204
+ const value = record;
3205
+ return [
3206
+ `Pushed ${value.origin.remote} (${value.origin.projectId}).`,
3207
+ `Open benchmark: ${value.urls.benchmark}`,
3208
+ ].join("\n");
3209
+ });
3210
+ return 0;
3211
+ }
3212
+ const response = await apiRequest(projectApiPath(remoteProject.id, "/state?force=true"), {
3213
+ method: "PUT",
3214
+ body: state,
3215
+ }, baseUrl);
3216
+ assertAcceptedForceReplacement({
3217
+ expected: state,
3218
+ actual: response.state,
3219
+ });
3220
+ const responseProject = remoteProjectSummaryFromState(response.state);
3221
+ const publishedProject = await applyRequestedProjectVisibility({
3222
+ baseUrl,
3223
+ projectId: responseProject.id,
3224
+ responseProject,
3225
+ visibility,
3226
+ });
3227
+ const applied = await acceptPushedProjectStateToLocal({
3228
+ dir,
3229
+ baseUrl,
3230
+ state: response.state,
3231
+ });
3232
+ writeOutput({
3233
+ ok: true,
3234
+ action: "replace",
3235
+ changed: true,
3236
+ benchmark: publishedProject,
3237
+ visibility: visibility ?? publishedProject.visibility ?? "unchanged",
3238
+ origin: applied.origin,
3239
+ source: response.source,
3240
+ runtime: response.runtime.stats,
3241
+ urls: buildWorkbenchResourceUrls({
3242
+ baseUrl,
3243
+ projectId: publishedProject.id ?? responseProject.id,
3244
+ ...originRemoteUrlParts(applied.origin),
3245
+ }),
3246
+ }, parsed, io, (record) => {
3247
+ const value = record;
3248
+ return [
3249
+ `Replaced ${value.origin.remote} (${value.origin.projectId}).`,
3250
+ `Open benchmark: ${value.urls.benchmark}`,
3251
+ ].join("\n");
3252
+ });
3125
3253
  return 0;
3126
3254
  }
3255
+ const state = localProjectState({
3256
+ source,
3257
+ runtime,
3258
+ origin,
3259
+ visibility: createVisibility,
3260
+ });
3127
3261
  const response = await apiRequest(projectApiPath(projectId, "/state"), {
3128
3262
  method: "PUT",
3129
3263
  body: state,
@@ -3163,18 +3297,62 @@ async function pushBenchmark(argv, io) {
3163
3297
  });
3164
3298
  return 0;
3165
3299
  }
3166
- async function verifyLinkedPushDryRunTarget(args) {
3167
- const response = await apiRequest(projectApiPath(args.projectId), {}, args.baseUrl);
3168
- const expected = parseOriginRemote(args.origin);
3169
- const actualOwner = response.benchmark.ownerUsername;
3170
- const actualProject = response.benchmark.name;
3171
- if (actualOwner !== expected.owner || actualProject !== expected.project) {
3172
- const actualRemote = actualOwner && actualProject
3173
- ? `${actualOwner}/${actualProject}`
3174
- : "unknown";
3175
- throw new UsageError(`Workbench origin points to ${args.origin.remote}, but ${args.projectId} resolved to ${actualRemote}.`);
3300
+ async function readOriginProjectIfPresent(args) {
3301
+ try {
3302
+ const response = await apiRequest(publicProjectApiPath(parseOriginRemote(args.origin)), {}, args.baseUrl);
3303
+ return response.benchmark;
3304
+ }
3305
+ catch (error) {
3306
+ if (error instanceof WorkbenchApiRequestError && error.status === 404) {
3307
+ return null;
3308
+ }
3309
+ throw error;
3176
3310
  }
3177
- return response.benchmark;
3311
+ }
3312
+ async function assertForceCreateAllowed(args) {
3313
+ const target = parseOriginRemote(args.origin);
3314
+ if (args.sourceName !== target.project) {
3315
+ throw new UsageError(`Local benchmark name ${args.sourceName} does not match force push target ${args.origin.remote}.`);
3316
+ }
3317
+ const expectedOwner = target.owner;
3318
+ const profileStatus = await readWorkbenchProfileStatus(await loadConfig(), args.baseUrl);
3319
+ const actualOwner = profileStatus.profile?.username ?? null;
3320
+ if (!profileStatus.authenticated || actualOwner !== expectedOwner) {
3321
+ throw new WorkbenchApiRequestError(404, `Workbench benchmark not found or not writable: ${args.origin.remote}`, "");
3322
+ }
3323
+ }
3324
+ function assertAcceptedForceReplacement(args) {
3325
+ const expectedSourceFingerprint = args.expected.source.fingerprint ??
3326
+ workbenchProjectSourceFingerprint(args.expected.source);
3327
+ const actualSourceFingerprint = args.actual.source.fingerprint ??
3328
+ workbenchProjectSourceFingerprint(args.actual.source);
3329
+ if (actualSourceFingerprint !== expectedSourceFingerprint) {
3330
+ throw new Error(`Workbench force push did not replace remote source. Expected ${expectedSourceFingerprint}, received ${actualSourceFingerprint}.`);
3331
+ }
3332
+ const expectedRuntime = runtimeReplacementSignature(args.expected.runtime);
3333
+ const actualRuntime = runtimeReplacementSignature(args.actual.runtime);
3334
+ if (JSON.stringify(actualRuntime) !== JSON.stringify(expectedRuntime)) {
3335
+ throw new Error(`Workbench force push did not replace remote runtime. Remote returned different record identity.`);
3336
+ }
3337
+ }
3338
+ function runtimeReplacementSignature(runtime) {
3339
+ return {
3340
+ activeId: runtime.activeId ?? null,
3341
+ candidates: runtime.candidates
3342
+ .map(workbenchRuntimeCandidateIdentityForExchange)
3343
+ .sort((left, right) => left.id.localeCompare(right.id)),
3344
+ candidateFiles: runtimeFileGroupSignature(runtime.candidateFiles, (group) => group.candidateId),
3345
+ evaluations: runtime.evaluations.map((evaluation) => evaluation.id).sort(),
3346
+ runs: runtime.runs.map((run) => run.id).sort(),
3347
+ jobs: runtime.jobs.map((job) => job.id).sort(),
3348
+ executionFiles: runtimeFileGroupSignature(runtime.executionFiles, (group) => group.jobId),
3349
+ events: runtime.events.map((event) => event.id).sort(),
3350
+ };
3351
+ }
3352
+ function runtimeFileGroupSignature(groups, idForGroup) {
3353
+ return groups
3354
+ .map((group) => `${idForGroup(group)}:${group.files.map((file) => file.path).sort().join("\n")}`)
3355
+ .sort();
3178
3356
  }
3179
3357
  function remoteProjectSummaryForOutput(project) {
3180
3358
  return {
@@ -3951,7 +4129,7 @@ function buildWorkbenchResourceUrls(target, refs = {}) {
3951
4129
  ? evaluationScorecardId(refs.runId, refs.candidateId)
3952
4130
  : null;
3953
4131
  urls.candidateEvaluation = evaluationId
3954
- ? `${benchmark}/candidates/${encodeURIComponent(refs.candidateId)}?evaluation=${encodeURIComponent(evaluationId)}`
4132
+ ? `${benchmark}/evaluations/${encodeURIComponent(evaluationId)}`
3955
4133
  : `${benchmark}/candidates/${encodeURIComponent(refs.candidateId)}`;
3956
4134
  }
3957
4135
  return urls;
@@ -4064,10 +4242,10 @@ function localProjectState(args) {
4064
4242
  };
4065
4243
  }
4066
4244
  function projectStateRuntimeStats(state) {
4067
- const activeId = workbenchRuntimeExplicitActiveId({
4245
+ const activeId = workbenchRuntimeProjectedActiveId({
4068
4246
  candidates: state.runtime.candidates,
4247
+ evaluations: state.runtime.evaluations,
4069
4248
  runs: state.runtime.runs,
4070
- preferredActiveId: state.runtime.activeId ?? null,
4071
4249
  benchmarkFingerprint: projectStateBenchmarkFingerprint(state.source),
4072
4250
  });
4073
4251
  return runtimeBundleStats({
@@ -4397,11 +4575,11 @@ function selectWorkbenchBaseUrl(input = {}) {
4397
4575
  input.configBaseUrl ??
4398
4576
  DEFAULT_BASE_URL);
4399
4577
  }
4400
- async function readWorkbenchProfileStatus(config) {
4578
+ async function readWorkbenchProfileStatus(config, baseUrlOverride) {
4401
4579
  if (!config.accessToken) {
4402
4580
  return { authenticated: false, profile: null };
4403
4581
  }
4404
- const baseUrl = selectWorkbenchBaseUrl({ configBaseUrl: config.baseUrl });
4582
+ const baseUrl = baseUrlOverride ?? selectWorkbenchBaseUrl({ configBaseUrl: config.baseUrl });
4405
4583
  try {
4406
4584
  const response = await fetch(`${baseUrl}/api/workbench/profile`, {
4407
4585
  headers: {
@@ -4432,6 +4610,7 @@ async function apiRequest(apiPath, options = {}, baseUrlOverride) {
4432
4610
  : selectWorkbenchBaseUrl({ configBaseUrl: config.baseUrl });
4433
4611
  const method = options.method ?? "GET";
4434
4612
  const canRetry = method === "GET";
4613
+ const requestBody = encodeJsonRequestBody(options.body);
4435
4614
  let lastError = null;
4436
4615
  for (let attempt = 1; attempt <= API_REQUEST_MAX_ATTEMPTS; attempt += 1) {
4437
4616
  let response;
@@ -4439,12 +4618,12 @@ async function apiRequest(apiPath, options = {}, baseUrlOverride) {
4439
4618
  response = await fetch(`${baseUrl}${apiPath}`, {
4440
4619
  method,
4441
4620
  headers: {
4442
- "content-type": "application/json",
4621
+ ...requestBody.headers,
4443
4622
  ...(config.accessToken
4444
4623
  ? { authorization: `Bearer ${config.accessToken}` }
4445
4624
  : {}),
4446
4625
  },
4447
- body: options.body == null ? undefined : JSON.stringify(options.body),
4626
+ body: requestBody.body,
4448
4627
  });
4449
4628
  }
4450
4629
  catch (error) {
@@ -4470,6 +4649,22 @@ async function apiRequest(apiPath, options = {}, baseUrlOverride) {
4470
4649
  }
4471
4650
  throw lastError instanceof Error ? lastError : new Error(String(lastError ?? "Workbench API request failed."));
4472
4651
  }
4652
+ function encodeJsonRequestBody(body) {
4653
+ if (body == null) {
4654
+ return { headers: { "content-type": "application/json" } };
4655
+ }
4656
+ const text = JSON.stringify(body);
4657
+ if (Buffer.byteLength(text) < API_REQUEST_GZIP_THRESHOLD_BYTES) {
4658
+ return { body: text, headers: { "content-type": "application/json" } };
4659
+ }
4660
+ return {
4661
+ body: gzipSync(text),
4662
+ headers: {
4663
+ "content-encoding": "gzip",
4664
+ "content-type": "application/json",
4665
+ },
4666
+ };
4667
+ }
4473
4668
  function apiRequestRetryDelayMs(attempt) {
4474
4669
  return 250 * attempt;
4475
4670
  }
@@ -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, isSurfaceSnapshotFile, jsonRecord, normalizeRelativePath, readSurfaceFiles, workbenchRuntimeExplicitActiveId, workbenchRuntimeBundleStats, workbenchRuntimeCandidateIdentityForExchange, workbenchSurfaceFilesEqualForExchange, writeSurfaceFiles, } from "@workbench-ai/workbench-core";
3
+ import { buildWorkbenchTraceSessionsFromFiles, candidateRecordWithoutDerivedFields, compactWorkbenchRuntimeJobForExchange, mergeWorkbenchRuntimeCandidateForExchange, sanitizeWorkbenchRuntimeCandidateForExchange, sanitizeWorkbenchRuntimeJobForExchange, selectExecutionOutputFilesForInspection, isSurfaceSnapshotFile, jsonRecord, normalizeRelativePath, readSurfaceFiles, workbenchRuntimeBundleStats, workbenchRuntimeCandidateIdentityForExchange, workbenchRuntimeProjectedActiveId, 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) {
@@ -73,12 +73,17 @@ export async function saveLocalJobs(workspace, jobs) {
73
73
  }
74
74
  export async function exportLocalRuntimeBundle(workspace, options = {}) {
75
75
  const snapshot = await loadLocalArchive(workspace);
76
- const jobs = (await readLocalJobs(workspace)).map(compactWorkbenchRuntimeJobForExchange);
76
+ const archivedJobs = await readLocalJobs(workspace);
77
+ const jobs = archivedJobs.map(compactWorkbenchRuntimeJobForExchange);
78
+ const executionFiles = (await Promise.all(archivedJobs.map(async (job) => ({
79
+ jobId: job.id,
80
+ files: await readLocalExecutionFiles(workspace, job.id),
81
+ })))).filter((group) => group.files.length > 0);
77
82
  const activeId = options.currentBenchmarkFingerprint
78
- ? workbenchRuntimeExplicitActiveId({
83
+ ? workbenchRuntimeProjectedActiveId({
79
84
  candidates: snapshot.candidates,
85
+ evaluations: snapshot.evaluations,
80
86
  runs: snapshot.runs,
81
- preferredActiveId: snapshot.activeId,
82
87
  benchmarkFingerprint: options.currentBenchmarkFingerprint,
83
88
  })
84
89
  : snapshot.activeId;
@@ -93,7 +98,7 @@ export async function exportLocalRuntimeBundle(workspace, options = {}) {
93
98
  evaluations: snapshot.evaluations.map((evaluation) => ({ ...evaluation })),
94
99
  runs: snapshot.runs.map((run) => ({ ...run })),
95
100
  jobs,
96
- executionFiles: [],
101
+ executionFiles,
97
102
  events: snapshot.events.map((event) => ({ ...event })),
98
103
  };
99
104
  }
@@ -163,10 +168,10 @@ export async function importLocalRuntimeBundle(workspace, bundle, currentBenchma
163
168
  changed ||= didChange;
164
169
  }, runtimeJobsEqualForExchange).sort((left, right) => (left.startedAt ?? left.createdAt).localeCompare(right.startedAt ?? right.createdAt) ||
165
170
  left.id.localeCompare(right.id));
166
- const activeId = workbenchRuntimeExplicitActiveId({
171
+ const activeId = workbenchRuntimeProjectedActiveId({
167
172
  candidates,
173
+ evaluations,
168
174
  runs,
169
- preferredActiveId: bundle.activeId ?? null,
170
175
  benchmarkFingerprint: currentBenchmarkFingerprint,
171
176
  });
172
177
  if (activeId !== snapshot.activeId) {
@@ -1 +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"}
1
+ {"version":3,"file":"local-inspection.d.ts","sourceRoot":"","sources":["../src/local-inspection.ts"],"names":[],"mappings":"AAEA,OAAO,EAgBL,KAAK,mBAAmB,EAIzB,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,CA2DrB;AAkBD,wBAAgB,8BAA8B,CAC5C,SAAS,EAAE,MAAM,GAChB,MAAM,OAAO,CAAC,kBAAkB,CAAC,CAiBnC"}