@workbench-ai/workbench 0.0.76 → 0.0.78

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 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAgEA,MAAM,WAAW,KAAK;IACpB,MAAM,EAAE,MAAM,CAAC,cAAc,CAAC;IAC9B,MAAM,EAAE,MAAM,CAAC,cAAc,CAAC;CAC/B;AAuTD,wBAAsB,MAAM,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,EAAE,EAAE,GAAE,KAGzD,GAAG,OAAO,CAAC,MAAM,CAAC,CAoMlB"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAiEA,MAAM,WAAW,KAAK;IACpB,MAAM,EAAE,MAAM,CAAC,cAAc,CAAC;IAC9B,MAAM,EAAE,MAAM,CAAC,cAAc,CAAC;CAC/B;AAuTD,wBAAsB,MAAM,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,EAAE,EAAE,GAAE,KAGzD,GAAG,OAAO,CAAC,MAAM,CAAC,CAoMlB"}
package/dist/index.js CHANGED
@@ -404,7 +404,7 @@ export async function runCli(argv, io = {
404
404
  if (command === "diff") {
405
405
  const range = optionalPositional(parsed, 1) ?? await defaultDiffRange(core);
406
406
  const diffs = await diffWorkbenchVersions(range, core);
407
- return output(diffs, parsed, io, () => diffs.map((entry) => `${entry.status}\t${entry.path}`).join("\n") || "No diff.");
407
+ return output(diffs, parsed, io, () => formatDiff(diffs));
408
408
  }
409
409
  if (command === "show") {
410
410
  return await handleShow(parsed, io);
@@ -1047,8 +1047,7 @@ function formatFanOut(fanout) {
1047
1047
  if (fanout.linkedAgents.length === 0) {
1048
1048
  return "fanout: completed";
1049
1049
  }
1050
- const suffix = fanout.additionalAgents ? ` and ${fanout.additionalAgents} more` : "";
1051
- return `fanned out to: ${fanout.linkedAgents.join(", ")}${suffix}`;
1050
+ return "fanout: completed";
1052
1051
  }
1053
1052
  async function latestInstallVersion(record) {
1054
1053
  const handle = normalizedOwnerSkillHandle(record.handle);
@@ -1413,7 +1412,7 @@ function cloudExecutionNextCommand(runs, successCommand) {
1413
1412
  if (!first) {
1414
1413
  return "workbench log --runs";
1415
1414
  }
1416
- if (first.status === "running" || first.status === "failed" || first.status === "canceled") {
1415
+ if (first.status === "queued" || first.status === "running" || first.status === "failed" || first.status === "canceled") {
1417
1416
  return `workbench show ${displayRef(first.id)}`;
1418
1417
  }
1419
1418
  return successCommand;
@@ -1802,7 +1801,7 @@ async function apiRequest(apiPath, options = {}, baseUrlOverride) {
1802
1801
  : selectWorkbenchBaseUrl({ configBaseUrl: config.baseUrl });
1803
1802
  const token = await workbenchCloudToken({ baseUrl });
1804
1803
  const method = options.method ?? "GET";
1805
- const canRetry = method === "GET";
1804
+ const canRetry = isIdempotentApiRequestMethod(method);
1806
1805
  const requestBody = encodeJsonRequestBody(options.body);
1807
1806
  let lastError = null;
1808
1807
  for (let attempt = 1; attempt <= API_REQUEST_MAX_ATTEMPTS; attempt += 1) {
@@ -1977,6 +1976,9 @@ function isTransientFetchError(error) {
1977
1976
  function isTransientApiRequestError(error) {
1978
1977
  return error instanceof WorkbenchApiRequestError && (error.status === 429 || error.status >= 500);
1979
1978
  }
1979
+ function isIdempotentApiRequestMethod(method) {
1980
+ return method === "GET" || method === "PUT" || method === "DELETE";
1981
+ }
1980
1982
  function errorMessage(error) {
1981
1983
  return error instanceof Error ? error.message : String(error);
1982
1984
  }
@@ -2764,7 +2766,7 @@ async function statusWithCausalNext(status, auth, core, machine) {
2764
2766
  const lastRun = snapshot?.runs
2765
2767
  .slice()
2766
2768
  .sort((left, right) => right.createdAt.localeCompare(left.createdAt))[0];
2767
- if ((lastRun?.status === "running" || lastRun?.status === "failed" || lastRun?.status === "canceled") && lastRun.id) {
2769
+ if ((lastRun?.status === "queued" || lastRun?.status === "running" || lastRun?.status === "failed" || lastRun?.status === "canceled") && lastRun.id) {
2768
2770
  return { ...status, next: `workbench show ${displayRef(lastRun.id)}` };
2769
2771
  }
2770
2772
  const failedRemote = status.remotes.find((remote) => remote.sync.status === "error");
@@ -2803,6 +2805,15 @@ async function statusWithCausalNext(status, auth, core, machine) {
2803
2805
  currentVersionId !== undefined &&
2804
2806
  remote.publication.versionId !== currentVersionId);
2805
2807
  if (canPublish && stalePublishedCloudRemote) {
2808
+ const publishedVersionId = stalePublishedCloudRemote.publication.versionId;
2809
+ if (snapshot && publishedVersionId && currentVersionId) {
2810
+ if (versionHasAncestor(snapshot, currentVersionId, publishedVersionId)) {
2811
+ return { ...status, next: "workbench publish" };
2812
+ }
2813
+ if (versionHasAncestor(snapshot, publishedVersionId, currentVersionId)) {
2814
+ return { ...status, next: `workbench switch ${displayRef(publishedVersionId)}` };
2815
+ }
2816
+ }
2806
2817
  return { ...status, next: "workbench publish" };
2807
2818
  }
2808
2819
  const publishedCloudRemote = status.remotes.find((remote) => remote.kind === "workbench-cloud" &&
@@ -2816,6 +2827,43 @@ async function statusWithCausalNext(status, auth, core, machine) {
2816
2827
  next: null,
2817
2828
  };
2818
2829
  }
2830
+ function versionHasAncestor(snapshot, versionId, ancestorId) {
2831
+ if (versionId === ancestorId) {
2832
+ return true;
2833
+ }
2834
+ const parentsByChild = new Map();
2835
+ for (const edge of snapshot.lineage) {
2836
+ const parents = parentsByChild.get(edge.childId) ?? [];
2837
+ parents.push(edge.parentId);
2838
+ parentsByChild.set(edge.childId, parents);
2839
+ }
2840
+ for (const version of snapshot.versions) {
2841
+ if (version.parentIds.length === 0) {
2842
+ continue;
2843
+ }
2844
+ const parents = parentsByChild.get(version.id) ?? [];
2845
+ for (const parentId of version.parentIds) {
2846
+ if (!parents.includes(parentId)) {
2847
+ parents.push(parentId);
2848
+ }
2849
+ }
2850
+ parentsByChild.set(version.id, parents);
2851
+ }
2852
+ const seen = new Set();
2853
+ const stack = [...(parentsByChild.get(versionId) ?? [])];
2854
+ while (stack.length > 0) {
2855
+ const next = stack.pop();
2856
+ if (next === ancestorId) {
2857
+ return true;
2858
+ }
2859
+ if (seen.has(next)) {
2860
+ continue;
2861
+ }
2862
+ seen.add(next);
2863
+ stack.push(...(parentsByChild.get(next) ?? []));
2864
+ }
2865
+ return false;
2866
+ }
2819
2867
  function displayRef(id) {
2820
2868
  const version = /^v_([0-9a-f]{8,})$/iu.exec(id);
2821
2869
  if (version?.[1]) {
@@ -3315,7 +3363,8 @@ function formatJob(job) {
3315
3363
  }
3316
3364
  function formatComparison(comparison) {
3317
3365
  const lines = ["version\tskill\tagent\tstatus\tscore\tcost\tlatency\trun"];
3318
- for (const cell of comparison.cells) {
3366
+ const evidenceCells = comparison.cells.filter((cell) => cell.runId || cell.status);
3367
+ for (const cell of evidenceCells) {
3319
3368
  lines.push([
3320
3369
  displayRef(cell.versionId),
3321
3370
  cell.skillName,
@@ -3327,7 +3376,73 @@ function formatComparison(comparison) {
3327
3376
  cell.runId ? displayRef(cell.runId) : "n/a",
3328
3377
  ].join("\t"));
3329
3378
  }
3330
- return lines.join("\n");
3379
+ return lines.length === 1 ? "No comparable runs." : lines.join("\n");
3380
+ }
3381
+ function formatDiff(entries) {
3382
+ if (entries.length === 0) {
3383
+ return "No diff.";
3384
+ }
3385
+ return entries.map(formatDiffEntry).join("\n");
3386
+ }
3387
+ function formatDiffEntry(entry) {
3388
+ const before = entry.before ?? "";
3389
+ const after = entry.after ?? "";
3390
+ if (entry.status === "modified" || entry.status === "added" || entry.status === "removed") {
3391
+ return [
3392
+ `diff --workbench ${entry.path}`,
3393
+ `--- ${entry.status === "added" ? "/dev/null" : `a/${entry.path}`}`,
3394
+ `+++ ${entry.status === "removed" ? "/dev/null" : `b/${entry.path}`}`,
3395
+ ...unifiedLineDiff(before, after),
3396
+ ].join("\n");
3397
+ }
3398
+ return `${entry.status}\t${entry.path}`;
3399
+ }
3400
+ function unifiedLineDiff(before, after) {
3401
+ const beforeLines = splitDiffLines(before);
3402
+ const afterLines = splitDiffLines(after);
3403
+ const table = longestCommonSubsequenceTable(beforeLines, afterLines);
3404
+ const lines = [];
3405
+ let left = 0;
3406
+ let right = 0;
3407
+ while (left < beforeLines.length && right < afterLines.length) {
3408
+ if (beforeLines[left] === afterLines[right]) {
3409
+ lines.push(` ${beforeLines[left]}`);
3410
+ left += 1;
3411
+ right += 1;
3412
+ }
3413
+ else if (table[left + 1][right] >= table[left][right + 1]) {
3414
+ lines.push(`-${beforeLines[left]}`);
3415
+ left += 1;
3416
+ }
3417
+ else {
3418
+ lines.push(`+${afterLines[right]}`);
3419
+ right += 1;
3420
+ }
3421
+ }
3422
+ while (left < beforeLines.length) {
3423
+ lines.push(`-${beforeLines[left]}`);
3424
+ left += 1;
3425
+ }
3426
+ while (right < afterLines.length) {
3427
+ lines.push(`+${afterLines[right]}`);
3428
+ right += 1;
3429
+ }
3430
+ return lines.length > 0 ? lines : [" "];
3431
+ }
3432
+ function splitDiffLines(value) {
3433
+ const withoutFinalNewline = value.endsWith("\n") ? value.slice(0, -1) : value;
3434
+ return withoutFinalNewline ? withoutFinalNewline.split(/\r?\n/u) : [];
3435
+ }
3436
+ function longestCommonSubsequenceTable(left, right) {
3437
+ const table = Array.from({ length: left.length + 1 }, () => Array.from({ length: right.length + 1 }, () => 0));
3438
+ for (let i = left.length - 1; i >= 0; i -= 1) {
3439
+ for (let j = right.length - 1; j >= 0; j -= 1) {
3440
+ table[i][j] = left[i] === right[j]
3441
+ ? table[i + 1][j + 1] + 1
3442
+ : Math.max(table[i + 1][j], table[i][j + 1]);
3443
+ }
3444
+ }
3445
+ return table;
3331
3446
  }
3332
3447
  function shortObjectId(id) {
3333
3448
  return id.length > 8 ? id.slice(0, 8) : id;
package/dist/workbench.js CHANGED
@@ -1,4 +1,12 @@
1
1
  #!/usr/bin/env node
2
2
  import { runCli } from "./index.js";
3
+ for (const stream of [process.stdout, process.stderr]) {
4
+ stream.on("error", (error) => {
5
+ if (error.code === "EPIPE") {
6
+ process.exit(0);
7
+ }
8
+ throw error;
9
+ });
10
+ }
3
11
  const exitCode = await runCli(process.argv.slice(2));
4
12
  process.exitCode = exitCode;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@workbench-ai/workbench",
3
- "version": "0.0.76",
3
+ "version": "0.0.78",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/workbench-ai/workbench.git",
@@ -22,10 +22,10 @@
22
22
  "dependencies": {
23
23
  "skills": "1.5.11",
24
24
  "yaml": "^2.8.2",
25
- "@workbench-ai/workbench-built-in-adapters": "0.0.76",
26
- "@workbench-ai/workbench-core": "0.0.76",
27
- "@workbench-ai/workbench-contract": "0.0.76",
28
- "@workbench-ai/workbench-protocol": "0.0.76"
25
+ "@workbench-ai/workbench-built-in-adapters": "0.0.78",
26
+ "@workbench-ai/workbench-protocol": "0.0.78",
27
+ "@workbench-ai/workbench-contract": "0.0.78",
28
+ "@workbench-ai/workbench-core": "0.0.78"
29
29
  },
30
30
  "devDependencies": {
31
31
  "@tailwindcss/postcss": "^4.2.2",
@@ -36,7 +36,7 @@
36
36
  "react-dom": "^19.2.0",
37
37
  "typescript": "^5.9.2",
38
38
  "vitest": "^3.2.4",
39
- "@workbench-ai/workbench-ui": "0.0.76"
39
+ "@workbench-ai/workbench-ui": "0.0.78"
40
40
  },
41
41
  "scripts": {
42
42
  "build": "rm -rf dist && tsc -p tsconfig.json && chmod 755 dist/workbench.js && node ./scripts/build-dev-open-assets.mjs",