@workbench-ai/workbench 0.0.49 → 0.0.51

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.
@@ -2,16 +2,15 @@ import { promises as fs } from "node:fs";
2
2
  import { createHash, randomUUID } from "node:crypto";
3
3
  import { spawn } from "node:child_process";
4
4
  import path from "node:path";
5
- import { BENCHMARK_SPEC_FILE, buildWorkbenchProjectSourceFiles, engineResolveInvocationForSpec, normalizeSurfaceFiles, parseWorkbenchSourceFiles, resolveWorkbenchResolvedSourceYaml, serializeWorkbenchResolvedSourceYaml, validateWorkbenchResolvedSourceYaml, } from "@workbench-ai/workbench-core";
5
+ import { BENCHMARK_SPEC_FILE, CANDIDATE_SPEC_FILE, buildWorkbenchProjectSourceFiles, engineResolveInvocationForSpec, normalizeSurfaceFiles, parseWorkbenchSourceFiles, resolveWorkbenchResolvedSourceYaml, serializeWorkbenchResolvedSourceYaml, validateWorkbenchResolvedSourceYaml, } from "@workbench-ai/workbench-core";
6
6
  import { assertWorkbenchAdapterOperationSupport, assertWorkbenchAdapterOperationResultOk, collectWorkbenchAdapterInvocations, readWorkbenchAdapterOperationResult, workbenchAdapterOperationCommand, workbenchAdapterOperationResultPath, } from "@workbench-ai/workbench-protocol";
7
7
  import { readSnapshotFiles, WorkspaceSnapshotError, } from "./workspace-snapshot.js";
8
8
  import { defaultAdapterManifests, composeRuntimeDockerfileWithAdapters, resolveDefaultWorkbenchAdapter, resolveProjectAdapterSource, resolveWorkbenchAdaptersForProject, } from "./adapter-project.js";
9
9
  import { createAdapterCommandEnv } from "./adapter-command-env.js";
10
10
  import YAML from "yaml";
11
11
  export const WORKBENCH_BENCHMARK_FILE = BENCHMARK_SPEC_FILE;
12
- export const WORKBENCH_SUBJECTS_DIR = "subjects";
13
- export const WORKBENCH_OPTIMIZERS_DIR = "optimizers";
14
- export const WORKBENCH_SUBJECT_FILE = "subject.yaml";
12
+ export const WORKBENCH_CANDIDATES_DIR = "candidates";
13
+ export const WORKBENCH_CANDIDATE_FILE = CANDIDATE_SPEC_FILE;
15
14
  function rootAdapterInvocations(spec) {
16
15
  return [
17
16
  engineResolveInvocationForSpec(spec),
@@ -22,25 +21,20 @@ function rootAdapterInvocations(spec) {
22
21
  }
23
22
  export async function readLocalProjectSource(source, options = {}) {
24
23
  const paths = await resolveLocalProjectSourcePaths(source, options);
25
- const { dir, benchmarkPath, subjectSpecPath, subjectDir, optimizerPath, } = paths;
24
+ const { dir, benchmarkPath, candidateSpecPath, candidateDir, } = paths;
26
25
  const benchmarkSource = await readRequiredTextFile(benchmarkPath, WORKBENCH_BENCHMARK_FILE);
27
- const subjectSource = await readRequiredTextFile(subjectSpecPath, "subject YAML");
28
- const optimizerSource = optimizerPath
29
- ? await readRequiredTextFile(optimizerPath, "optimizer YAML")
30
- : undefined;
26
+ const candidateSource = await readRequiredTextFile(candidateSpecPath, "candidate YAML");
31
27
  const normalizedSources = await normalizeSourceYamlForExecution({
32
28
  dir,
33
29
  benchmarkPath,
34
30
  benchmarkSource,
35
- subjectSpecPath,
36
- subjectSource,
37
- optimizerPath,
38
- optimizerSource,
31
+ candidateSpecPath,
32
+ candidateSource,
39
33
  });
40
34
  const resolvedSource = parseWorkbenchSourceFiles({
41
35
  benchmarkSource: normalizedSources.benchmarkSource,
42
- subjectSource: normalizedSources.subjectSource,
43
- optimizerSource: normalizedSources.optimizerSource,
36
+ candidateSource: normalizedSources.candidateSource,
37
+ runId: options.runId,
44
38
  });
45
39
  const specSource = serializeWorkbenchResolvedSourceYaml(resolvedSource);
46
40
  const validation = validateWorkbenchResolvedSourceYaml(specSource);
@@ -60,10 +54,10 @@ export async function readLocalProjectSource(source, options = {}) {
60
54
  ];
61
55
  const composedDockerfile = await composeRuntimeDockerfileWithAdapters(dockerfile, adapters);
62
56
  const adapterFiles = adapterSourceFiles(adapters);
63
- const absoluteSubjectFilesPath = resolveProjectPath(dir, spec.subject.files.path);
64
- const subjectFilesPath = absoluteSubjectFilesPath;
65
- const subjectFiles = await directoryExists(absoluteSubjectFilesPath)
66
- ? normalizeSurfaceFiles(await readSnapshotFiles(absoluteSubjectFilesPath))
57
+ const absoluteCandidateFilesPath = resolveProjectPath(dir, spec.candidate.files.path);
58
+ const candidateFilesPath = absoluteCandidateFilesPath;
59
+ const candidateFiles = await directoryExists(absoluteCandidateFilesPath)
60
+ ? normalizeSurfaceFiles(await readSnapshotFiles(absoluteCandidateFilesPath))
67
61
  : [];
68
62
  const rawEngineResolveFiles = engineResolveFilesFromBundles(normalizedSources.engineCases);
69
63
  const engineResolveFiles = toHostedFiles(rawEngineResolveFiles);
@@ -79,19 +73,20 @@ export async function readLocalProjectSource(source, options = {}) {
79
73
  spec,
80
74
  benchmarkPath,
81
75
  benchmarkSource,
82
- subjectName: path.basename(subjectDir),
83
- subjectDir,
84
- subjectFilesPath,
85
- subjectSpecPath,
86
- subjectSource,
87
- ...(optimizerSource !== undefined && optimizerPath ? { optimizerPath, optimizerSource } : {}),
76
+ candidateName: path.basename(candidateDir),
77
+ candidateDir,
78
+ candidateFilesPath,
79
+ candidateSpecPath,
80
+ candidateSource,
81
+ candidateRunId: spec.candidate.selectedRunId,
82
+ candidateRunIds: Object.keys(spec.candidate.runs).sort(),
88
83
  benchmarkAdapterSources: [...resolvedSource.benchmark.adapters],
89
84
  benchmarkAdapterIds,
90
85
  dockerfilePath,
91
86
  dockerfile,
92
87
  runtimeDockerfile: composedDockerfile,
93
88
  dockerfileFiles: toHostedFiles(dockerfileSourceFiles(dockerfileSources)),
94
- subjectFiles: toHostedFiles(subjectFiles),
89
+ candidateFiles: toHostedFiles(candidateFiles),
95
90
  engineResolveFiles,
96
91
  adapters,
97
92
  adapterFiles: toHostedFiles(adapterFiles),
@@ -105,13 +100,10 @@ export async function readLocalProjectSource(source, options = {}) {
105
100
  sourceFiles: buildWorkbenchProjectSourceFiles({
106
101
  specFiles: [
107
102
  textSourceFile(toRootRelativePath(dir, benchmarkPath), benchmarkSource),
108
- textSourceFile(toRootRelativePath(dir, subjectSpecPath), subjectSource),
109
- ...(optimizerSource !== undefined && optimizerPath
110
- ? [textSourceFile(toRootRelativePath(dir, optimizerPath), optimizerSource)]
111
- : []),
103
+ textSourceFile(toRootRelativePath(dir, candidateSpecPath), candidateSource),
112
104
  ],
113
- subjectFilesPath: spec.subject.files.path,
114
- subjectFiles,
105
+ candidateFilesPath: spec.candidate.files.path,
106
+ candidateFiles: candidateFiles,
115
107
  engineResolveFilesPath: normalizedSources.engineResolveFingerprintPath,
116
108
  engineResolveFiles: rawEngineResolveFiles,
117
109
  adapterFiles,
@@ -120,16 +112,13 @@ export async function readLocalProjectSource(source, options = {}) {
120
112
  };
121
113
  }
122
114
  export async function readLocalAuthoredProjectSource(source, options = {}) {
123
- const { dir, benchmarkPath, subjectSpecPath, subjectDir, optimizerPath, } = await resolveLocalProjectSourcePaths(source, options);
115
+ const { dir, benchmarkPath, candidateSpecPath, candidateDir, } = await resolveLocalProjectSourcePaths(source, options);
124
116
  const benchmarkSource = await readRequiredTextFile(benchmarkPath, WORKBENCH_BENCHMARK_FILE);
125
- const subjectSource = await readRequiredTextFile(subjectSpecPath, "subject YAML");
126
- const optimizerSource = optimizerPath
127
- ? await readRequiredTextFile(optimizerPath, "optimizer YAML")
128
- : undefined;
117
+ const candidateSource = await readRequiredTextFile(candidateSpecPath, "candidate YAML");
129
118
  const resolvedSource = parseWorkbenchSourceFiles({
130
119
  benchmarkSource,
131
- subjectSource,
132
- optimizerSource,
120
+ candidateSource,
121
+ runId: options.runId,
133
122
  });
134
123
  const specSource = serializeWorkbenchResolvedSourceYaml(resolvedSource);
135
124
  return {
@@ -138,16 +127,12 @@ export async function readLocalAuthoredProjectSource(source, options = {}) {
138
127
  specSource,
139
128
  benchmarkPath,
140
129
  benchmarkSource,
141
- subjectDir,
142
- subjectSpecPath,
143
- subjectSource,
144
- ...(optimizerPath && optimizerSource !== undefined ? { optimizerPath, optimizerSource } : {}),
130
+ candidateDir,
131
+ candidateSpecPath,
132
+ candidateSource,
145
133
  sourceFiles: [
146
134
  textSourceFile(toRootRelativePath(dir, benchmarkPath), benchmarkSource),
147
- textSourceFile(toRootRelativePath(dir, subjectSpecPath), subjectSource),
148
- ...(optimizerPath && optimizerSource !== undefined
149
- ? [textSourceFile(toRootRelativePath(dir, optimizerPath), optimizerSource)]
150
- : []),
135
+ textSourceFile(toRootRelativePath(dir, candidateSpecPath), candidateSource),
151
136
  ],
152
137
  };
153
138
  }
@@ -156,105 +141,67 @@ async function resolveLocalProjectSourcePaths(source, options) {
156
141
  const stat = await fs.stat(resolved).catch(() => null);
157
142
  if (stat?.isFile()) {
158
143
  const sourceRecord = await readYamlRecordFile(resolved);
159
- if (isSubjectSourceRecord(sourceRecord)) {
160
- const subjectDir = path.dirname(resolved);
161
- const dir = projectRootForSubjectDir(subjectDir);
144
+ if (isCandidateSourceRecord(sourceRecord)) {
145
+ const candidateDir = path.dirname(resolved);
146
+ const dir = projectRootForCandidateDir(candidateDir);
162
147
  return {
163
148
  dir,
164
149
  benchmarkPath: path.join(dir, WORKBENCH_BENCHMARK_FILE),
165
- subjectDir,
166
- subjectSpecPath: resolved,
167
- optimizerPath: await resolveOptimizerPath(dir, options.optimizerPath, path.basename(subjectDir)),
150
+ candidateDir,
151
+ candidateSpecPath: resolved,
168
152
  };
169
153
  }
170
154
  if (isBenchmarkSourceRecord(sourceRecord)) {
171
155
  const dir = path.dirname(resolved);
172
- const subjectPaths = await resolveSubjectPaths(dir);
156
+ const candidatePaths = await resolveCandidatePaths(dir);
173
157
  return {
174
158
  dir,
175
159
  benchmarkPath: resolved,
176
- ...subjectPaths,
177
- optimizerPath: await resolveOptimizerPath(dir, options.optimizerPath, path.basename(subjectPaths.subjectDir)),
160
+ ...candidatePaths,
178
161
  };
179
162
  }
180
- if (isOptimizerSourceRecord(sourceRecord)) {
181
- throw new WorkspaceSnapshotError(`Optimizer source must be passed with --optimizer; pass a source directory or subject YAML as SOURCE: ${resolved}`);
182
- }
183
163
  throw new WorkspaceSnapshotError(`Unsupported Workbench YAML source: ${resolved}`);
184
164
  }
185
165
  const dir = resolved;
186
- const directorySubject = await subjectPathsForSubjectDirectory(dir);
187
- if (directorySubject) {
188
- return {
189
- ...directorySubject,
190
- optimizerPath: await resolveOptimizerPath(directorySubject.dir, options.optimizerPath, path.basename(directorySubject.subjectDir)),
191
- };
166
+ const directoryCandidate = await candidatePathsForCandidateDirectory(dir);
167
+ if (directoryCandidate) {
168
+ return directoryCandidate;
192
169
  }
193
- const subjectPaths = await resolveSubjectPathsWithOptimizer(dir, options.optimizerPath);
170
+ const candidatePaths = await resolveCandidatePaths(dir);
194
171
  return {
195
172
  dir,
196
173
  benchmarkPath: path.join(dir, WORKBENCH_BENCHMARK_FILE),
197
- ...subjectPaths,
198
- };
199
- }
200
- async function resolveSubjectPathsWithOptimizer(dir, explicitOptimizerPath) {
201
- const subjectPaths = await resolveSubjectPaths(dir);
202
- return {
203
- ...subjectPaths,
204
- optimizerPath: await resolveOptimizerPath(dir, explicitOptimizerPath, path.basename(subjectPaths.subjectDir)),
174
+ ...candidatePaths,
205
175
  };
206
176
  }
207
- async function resolveSubjectPaths(dir) {
208
- const subjectsDir = path.join(dir, WORKBENCH_SUBJECTS_DIR);
209
- const subjects = await listSubjectManifestFiles(subjectsDir);
210
- if (subjects.length === 1) {
211
- const subjectSpecPath = subjects[0];
212
- const subjectDir = path.dirname(subjectSpecPath);
177
+ async function resolveCandidatePaths(dir) {
178
+ const candidatesDir = path.join(dir, WORKBENCH_CANDIDATES_DIR);
179
+ const candidates = await listCandidateManifestFiles(candidatesDir);
180
+ if (candidates.length === 1) {
181
+ const candidateSpecPath = candidates[0];
182
+ const candidateDir = path.dirname(candidateSpecPath);
213
183
  return {
214
- subjectDir,
215
- subjectSpecPath,
184
+ candidateDir,
185
+ candidateSpecPath,
216
186
  };
217
187
  }
218
- if (subjects.length > 1) {
219
- throw new WorkspaceSnapshotError(`Multiple subject directories found under ${subjectsDir}; pass subjects/NAME or subjects/NAME/subject.yaml explicitly.`);
220
- }
221
- throw new WorkspaceSnapshotError(`No subject directories found under ${subjectsDir}; create subjects/NAME/subject.yaml with files.path.`);
222
- }
223
- async function resolveOptimizerPath(dir, explicit, subjectName) {
224
- if (explicit) {
225
- return path.resolve(dir, explicit);
226
- }
227
- if (subjectName) {
228
- const named = path.join(dir, WORKBENCH_OPTIMIZERS_DIR, `${subjectName}.yaml`);
229
- if (await fileExists(named)) {
230
- return named;
231
- }
232
- }
233
- const optimizersDir = path.join(dir, WORKBENCH_OPTIMIZERS_DIR);
234
- const optimizers = await listYamlFiles(optimizersDir);
235
- if (optimizers.length === 1) {
236
- return optimizers[0];
188
+ if (candidates.length > 1) {
189
+ throw new WorkspaceSnapshotError(`Multiple candidate directories found under ${candidatesDir}; pass candidates/NAME or candidates/NAME/candidate.yaml explicitly.`);
237
190
  }
238
- return undefined;
191
+ throw new WorkspaceSnapshotError(`No candidate directories found under ${candidatesDir}; create candidates/NAME/candidate.yaml with files.path.`);
239
192
  }
240
193
  async function normalizeSourceYamlForExecution(args) {
241
194
  const benchmark = parseYamlRecord(args.benchmarkSource, args.benchmarkPath);
242
- const subject = parseYamlRecord(args.subjectSource, args.subjectSpecPath);
243
- const optimizer = args.optimizerSource === undefined || args.optimizerPath === undefined
244
- ? undefined
245
- : parseYamlRecord(args.optimizerSource, args.optimizerPath);
195
+ const candidate = parseYamlRecord(args.candidateSource, args.candidateSpecPath);
246
196
  const benchmarkDir = path.dirname(args.benchmarkPath);
247
- const subjectDir = path.dirname(args.subjectSpecPath);
197
+ const candidateDir = path.dirname(args.candidateSpecPath);
248
198
  normalizeAdapterSourcePaths(args.dir, benchmark, benchmarkDir);
249
- normalizeAdapterSourcePaths(args.dir, subject, subjectDir);
250
- if (optimizer && args.optimizerPath) {
251
- normalizeAdapterSourcePaths(args.dir, optimizer, path.dirname(args.optimizerPath));
252
- }
199
+ normalizeAdapterSourcePaths(args.dir, candidate, candidateDir);
253
200
  const engine = yamlRecord(benchmark.engine);
254
201
  if (!engine || typeof engine.use !== "string" || !engine.use.trim()) {
255
202
  throw new WorkspaceSnapshotError("benchmark.yaml engine must declare an adapter invocation with use.");
256
203
  }
257
- normalizeEngineForExecution(args.dir, benchmarkDir, subjectDir, benchmark, subject);
204
+ normalizeEngineForExecution(args.dir, benchmarkDir, candidateDir, benchmark, candidate);
258
205
  const authoredEngineResolve = engineResolveInvocationFromRecord(engine);
259
206
  const engineResolve = await resolveEngineResolveAdapter({
260
207
  root: args.dir,
@@ -267,21 +214,18 @@ async function normalizeSourceYamlForExecution(args) {
267
214
  applyEngineResolveEnvironment(benchmark, engineResolve.environment);
268
215
  return {
269
216
  benchmarkSource: YAML.stringify(benchmark).trimEnd() + "\n",
270
- subjectSource: YAML.stringify(subject).trimEnd() + "\n",
217
+ candidateSource: YAML.stringify(candidate).trimEnd() + "\n",
271
218
  engineResolveFingerprintPath,
272
219
  engineResolve: authoredEngineResolve,
273
220
  ...(engineResolve.environment ? { engineResolveEnvironment: engineResolve.environment } : {}),
274
221
  engineCases: engineResolve.engineCases,
275
- ...(optimizer
276
- ? { optimizerSource: YAML.stringify(optimizer).trimEnd() + "\n" }
277
- : {}),
278
222
  };
279
223
  }
280
- function normalizeEngineForExecution(root, benchmarkDir, subjectDir, benchmark, subject) {
281
- const subjectFiles = yamlRecord(subject.files);
282
- if (subjectFiles && typeof subjectFiles.path === "string") {
283
- subjectFiles.path = toRootRelativePath(root, resolveYamlReference(subjectDir, subjectFiles.path));
284
- subject.files = subjectFiles;
224
+ function normalizeEngineForExecution(root, benchmarkDir, candidateDir, benchmark, candidate) {
225
+ const candidateFiles = yamlRecord(candidate.files);
226
+ if (candidateFiles && typeof candidateFiles.path === "string") {
227
+ candidateFiles.path = toRootRelativePath(root, resolveYamlReference(candidateDir, candidateFiles.path));
228
+ candidate.files = candidateFiles;
285
229
  }
286
230
  const engine = yamlRecord(benchmark.engine);
287
231
  const engineConfig = yamlRecord(engine?.with) ?? {};
@@ -569,34 +513,31 @@ function isPathAdapterSource(source) {
569
513
  function isBenchmarkSourceRecord(record) {
570
514
  return record.engine !== undefined;
571
515
  }
572
- function isSubjectSourceRecord(record) {
573
- return record.run !== undefined;
574
- }
575
- function isOptimizerSourceRecord(record) {
576
- return record.edits !== undefined && record.improve !== undefined;
516
+ function isCandidateSourceRecord(record) {
517
+ return record.runs !== undefined;
577
518
  }
578
519
  async function readYamlRecordFile(filePath) {
579
520
  return parseYamlRecord(await readRequiredTextFile(filePath, path.basename(filePath)), filePath);
580
521
  }
581
- async function subjectPathsForSubjectDirectory(sourceDir) {
582
- const subjectSpecPath = path.join(sourceDir, WORKBENCH_SUBJECT_FILE);
583
- if (!(await fileExists(subjectSpecPath))) {
522
+ async function candidatePathsForCandidateDirectory(sourceDir) {
523
+ const candidateSpecPath = path.join(sourceDir, WORKBENCH_CANDIDATE_FILE);
524
+ if (!(await fileExists(candidateSpecPath))) {
584
525
  return null;
585
526
  }
586
- const dir = projectRootForSubjectDir(sourceDir);
527
+ const dir = projectRootForCandidateDir(sourceDir);
587
528
  return {
588
529
  dir,
589
530
  benchmarkPath: path.join(dir, WORKBENCH_BENCHMARK_FILE),
590
- subjectDir: sourceDir,
591
- subjectSpecPath,
531
+ candidateDir: sourceDir,
532
+ candidateSpecPath,
592
533
  };
593
534
  }
594
- function projectRootForSubjectDir(subjectDir) {
595
- const parent = path.basename(path.dirname(subjectDir));
596
- if (parent !== WORKBENCH_SUBJECTS_DIR) {
597
- throw new WorkspaceSnapshotError(`Subject directory must be under ${WORKBENCH_SUBJECTS_DIR}/NAME: ${subjectDir}`);
535
+ function projectRootForCandidateDir(candidateDir) {
536
+ const parent = path.basename(path.dirname(candidateDir));
537
+ if (parent !== WORKBENCH_CANDIDATES_DIR) {
538
+ throw new WorkspaceSnapshotError(`Candidate directory must be under ${WORKBENCH_CANDIDATES_DIR}/NAME: ${candidateDir}`);
598
539
  }
599
- return path.dirname(path.dirname(subjectDir));
540
+ return path.dirname(path.dirname(candidateDir));
600
541
  }
601
542
  function parseYamlRecord(source, label) {
602
543
  const parsed = YAML.parse(source);
@@ -632,7 +573,7 @@ async function fileExists(filePath) {
632
573
  async function directoryExists(filePath) {
633
574
  return await fs.stat(filePath).then((stat) => stat.isDirectory(), () => false);
634
575
  }
635
- async function listSubjectManifestFiles(dir) {
576
+ async function listCandidateManifestFiles(dir) {
636
577
  const entries = await fs.readdir(dir, { withFileTypes: true }).catch((error) => {
637
578
  if (error.code === "ENOENT") {
638
579
  return [];
@@ -642,25 +583,13 @@ async function listSubjectManifestFiles(dir) {
642
583
  const manifests = await Promise.all(entries
643
584
  .filter((entry) => entry.isDirectory())
644
585
  .map(async (entry) => {
645
- const manifestPath = path.join(dir, entry.name, WORKBENCH_SUBJECT_FILE);
586
+ const manifestPath = path.join(dir, entry.name, WORKBENCH_CANDIDATE_FILE);
646
587
  return await fileExists(manifestPath) ? manifestPath : null;
647
588
  }));
648
589
  return manifests
649
590
  .filter((entry) => Boolean(entry))
650
591
  .sort((left, right) => left.localeCompare(right));
651
592
  }
652
- async function listYamlFiles(dir) {
653
- const entries = await fs.readdir(dir, { withFileTypes: true }).catch((error) => {
654
- if (error.code === "ENOENT") {
655
- return [];
656
- }
657
- throw error;
658
- });
659
- return entries
660
- .filter((entry) => entry.isFile() && /\.ya?ml$/iu.test(entry.name))
661
- .map((entry) => path.join(dir, entry.name))
662
- .sort((left, right) => left.localeCompare(right));
663
- }
664
593
  async function readRequiredTextFile(filePath, label) {
665
594
  return await fs.readFile(filePath, "utf8").catch((error) => {
666
595
  if (error.code === "ENOENT") {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@workbench-ai/workbench",
3
- "version": "0.0.49",
3
+ "version": "0.0.51",
4
4
  "type": "module",
5
5
  "repository": {
6
6
  "type": "git",
@@ -21,9 +21,9 @@
21
21
  ],
22
22
  "dependencies": {
23
23
  "yaml": "^2.8.2",
24
- "@workbench-ai/workbench-built-in-adapters": "0.0.49",
25
- "@workbench-ai/workbench-protocol": "0.0.49",
26
- "@workbench-ai/workbench-core": "0.0.49"
24
+ "@workbench-ai/workbench-core": "0.0.51",
25
+ "@workbench-ai/workbench-protocol": "0.0.51",
26
+ "@workbench-ai/workbench-built-in-adapters": "0.0.51"
27
27
  },
28
28
  "devDependencies": {
29
29
  "@tailwindcss/postcss": "^4.2.2",