anpord 0.1.26 → 0.1.28

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.
Files changed (51) hide show
  1. package/dist/api-context.cjs +1 -1
  2. package/dist/api-context.d.cts +1 -1
  3. package/dist/api-context.d.mts +1 -1
  4. package/dist/api-context.mjs +1 -1
  5. package/dist/{api-mocks-CMB0IJFX.cjs → api-mocks-BsUGpsim.cjs} +14 -0
  6. package/dist/{api-mocks-CmytjGmi.d.cts → api-mocks-CBa3MWVP.d.cts} +4 -4
  7. package/dist/{api-mocks-CmytjGmi.d.mts → api-mocks-CBa3MWVP.d.mts} +4 -4
  8. package/dist/{api-mocks-C0uY0UBb.mjs → api-mocks-DH1C7any.mjs} +3 -1
  9. package/dist/api-runtime.cjs +1 -1
  10. package/dist/api-runtime.d.cts +1 -1
  11. package/dist/api-runtime.d.mts +1 -1
  12. package/dist/api-runtime.mjs +1 -1
  13. package/dist/api.cjs +1 -1
  14. package/dist/api.d.cts +1 -1
  15. package/dist/api.d.mts +1 -1
  16. package/dist/api.mjs +1 -1
  17. package/dist/bin.cjs +540 -261
  18. package/dist/bin.mjs +538 -259
  19. package/dist/cli-runtime.cjs +3 -3
  20. package/dist/cli-runtime.mjs +3 -3
  21. package/dist/{compiler-BtVNqh3R.mjs → compiler-DDc7Towh.mjs} +35 -20
  22. package/dist/{compiler-s8dJGvE6.cjs → compiler-DKWUVkxB.cjs} +35 -20
  23. package/dist/{errors-ZfXkocHp.cjs → errors-D1L76YFH.cjs} +127 -106
  24. package/dist/{errors-B6JK0hbj.mjs → errors-DaYv6_WJ.mjs} +110 -101
  25. package/dist/{eval-definition-Dz3dHY1S.d.cts → eval-definition-CuuketNm.d.cts} +12 -12
  26. package/dist/{eval-definition-Dz3dHY1S.d.mts → eval-definition-CuuketNm.d.mts} +12 -12
  27. package/dist/eval.cjs +1 -1
  28. package/dist/eval.d.cts +2 -2
  29. package/dist/eval.d.mts +2 -2
  30. package/dist/eval.mjs +1 -1
  31. package/dist/index.cjs +2 -2
  32. package/dist/index.d.cts +1735 -189
  33. package/dist/index.d.mts +1735 -189
  34. package/dist/index.mjs +2 -2
  35. package/dist/mcp-runtime.cjs +4 -12
  36. package/dist/mcp-runtime.mjs +5 -13
  37. package/dist/{runtime-LjI7Hqj3.cjs → runtime-BcX6BpI5.cjs} +3 -9
  38. package/dist/{runtime-BTq5czA4.mjs → runtime-CjDUik7_.mjs} +3 -9
  39. package/dist/sandbox-env-B8cLIcku.cjs +44 -0
  40. package/dist/sandbox-env-s-smXj2C.mjs +9 -0
  41. package/dist/source.d.cts +1 -1
  42. package/dist/source.d.mts +1 -1
  43. package/dist/types-7YOtajZL.d.cts +462 -0
  44. package/dist/types-CwZvwO-F.d.mts +462 -0
  45. package/dist/validator-runtime.cjs +8 -7
  46. package/dist/validator-runtime.d.cts +1 -1
  47. package/dist/validator-runtime.d.mts +1 -1
  48. package/dist/validator-runtime.mjs +8 -7
  49. package/package.json +1 -1
  50. package/dist/types-BsW7R-jL.d.mts +0 -2004
  51. package/dist/types-D8kjPlPR.d.cts +0 -2004
package/dist/bin.cjs CHANGED
@@ -1,22 +1,23 @@
1
1
  #!/usr/bin/env node
2
2
  const require_eval_judges = require("./eval-judges-CO0ds-HQ.cjs");
3
- const require_api_mocks = require("./api-mocks-CMB0IJFX.cjs");
4
- const require_errors = require("./errors-ZfXkocHp.cjs");
5
- const require_compiler = require("./compiler-s8dJGvE6.cjs");
3
+ const require_api_mocks = require("./api-mocks-BsUGpsim.cjs");
4
+ const require_errors = require("./errors-D1L76YFH.cjs");
5
+ const require_compiler = require("./compiler-DKWUVkxB.cjs");
6
6
  const require_eval_turns = require("./eval-turns-B9dd8Vfb.cjs");
7
+ const require_sandbox_env = require("./sandbox-env-B8cLIcku.cjs");
7
8
  let effect = require("effect");
8
9
  let _effect_platform = require("@effect/platform");
9
10
  let node_fs_promises = require("node:fs/promises");
11
+ let node_path = require("node:path");
10
12
  let _effect_cli = require("@effect/cli");
11
13
  let _effect_platform_node = require("@effect/platform-node");
12
14
  let yaml = require("yaml");
13
- let node_path = require("node:path");
14
15
  let node_fs = require("node:fs");
15
16
  let node_os = require("node:os");
16
17
  let node_child_process = require("node:child_process");
17
18
  let node_crypto = require("node:crypto");
18
19
  //#region package.json
19
- var version$1 = "0.1.26";
20
+ var version$1 = "0.1.28";
20
21
  //#endregion
21
22
  //#region ../template/src/extract.ts
22
23
  /** Reads escapes too, so text the renderer leaves literal is never reported as
@@ -81,7 +82,7 @@ const add = _effect_cli.Command.make("add", {
81
82
  onSome: (wanted) => found.authMethods.find((entry) => entry.id === wanted)
82
83
  });
83
84
  if (method === void 0) return yield* note(`${integration} has no auth method called ${effect.Option.getOrElse(wantedMethod, () => "")}.`);
84
- if (method.kind === "device") return yield* note(`${integration} signs in through a browser. Connect it at https://www.anpord.com/settings/connections.`);
85
+ if (method.kind === "device") return yield* note(`${integration} signs in through a browser. Connect it at ${require_errors.WEB_ORIGIN}/settings/connections.`);
85
86
  const values = {};
86
87
  for (const field of method.fields) values[field.name] = yield* readSecret(field.label);
87
88
  const created = yield* api.connectors.add({ payload: {
@@ -639,6 +640,190 @@ const buildGithubCheck = (outcomes, webUrl) => {
639
640
  };
640
641
  };
641
642
  //#endregion
643
+ //#region src/cli/labelled-row.ts
644
+ const LABEL_WIDTH = 9;
645
+ const labelled = (label, value, paint) => ` ${paint.dim(label.padEnd(LABEL_WIDTH))}${value}`;
646
+ const continued$1 = (value) => ` ${" ".repeat(LABEL_WIDTH)}${value}`;
647
+ //#endregion
648
+ //#region src/cli/paint.ts
649
+ const ESCAPE = String.fromCharCode(27);
650
+ const SHADES = {
651
+ blue: {
652
+ fallback: 111,
653
+ rgb: [
654
+ 122,
655
+ 162,
656
+ 247
657
+ ]
658
+ },
659
+ cyan: {
660
+ fallback: 80,
661
+ rgb: [
662
+ 108,
663
+ 196,
664
+ 207
665
+ ]
666
+ },
667
+ dim: {
668
+ fallback: 243,
669
+ rgb: [
670
+ 118,
671
+ 125,
672
+ 135
673
+ ]
674
+ },
675
+ green: {
676
+ fallback: 114,
677
+ rgb: [
678
+ 108,
679
+ 194,
680
+ 135
681
+ ]
682
+ },
683
+ lavender: {
684
+ fallback: 146,
685
+ rgb: [
686
+ 170,
687
+ 160,
688
+ 204
689
+ ]
690
+ },
691
+ magenta: {
692
+ fallback: 141,
693
+ rgb: [
694
+ 182,
695
+ 156,
696
+ 246
697
+ ]
698
+ },
699
+ red: {
700
+ fallback: 167,
701
+ rgb: [
702
+ 229,
703
+ 104,
704
+ 107
705
+ ]
706
+ },
707
+ rose: {
708
+ fallback: 181,
709
+ rgb: [
710
+ 204,
711
+ 158,
712
+ 172
713
+ ]
714
+ },
715
+ sand: {
716
+ fallback: 180,
717
+ rgb: [
718
+ 200,
719
+ 176,
720
+ 138
721
+ ]
722
+ },
723
+ slate: {
724
+ fallback: 110,
725
+ rgb: [
726
+ 138,
727
+ 164,
728
+ 204
729
+ ]
730
+ },
731
+ teal: {
732
+ fallback: 109,
733
+ rgb: [
734
+ 128,
735
+ 184,
736
+ 178
737
+ ]
738
+ },
739
+ yellow: {
740
+ fallback: 179,
741
+ rgb: [
742
+ 217,
743
+ 165,
744
+ 91
745
+ ]
746
+ }
747
+ };
748
+ const TRUE_COLOUR = /* @__PURE__ */ new Set(["truecolor", "24bit"]);
749
+ const wrap = (code) => (text) => `${ESCAPE}[${code}m${text}${ESCAPE}[0m`;
750
+ const shadeCode = ({ fallback, rgb }, trueColour) => trueColour ? `38;2;${rgb.join(";")}` : `38;5;${fallback}`;
751
+ const unpainted = (text) => text;
752
+ const colouredWith = (trueColour) => Object.fromEntries([["bold", wrap("1")], ...Object.entries(SHADES).map(([name, shade]) => [name, wrap(shadeCode(shade, trueColour))])]);
753
+ const MONOCHROME = Object.fromEntries(["bold", ...Object.keys(SHADES)].map((name) => [name, unpainted]));
754
+ const paletteFor = (colour) => colour ? colouredWith(TRUE_COLOUR.has(process.env.COLORTERM ?? "")) : MONOCHROME;
755
+ //#endregion
756
+ //#region src/cli/text-wrap.ts
757
+ const NARROWEST$1 = 20;
758
+ const LEADING_SPACE = /^\s*/;
759
+ const WORDS = /\s+/;
760
+ const TAB = /\t/g;
761
+ const CARRIAGE = /\r\n?/g;
762
+ const wrapLine = (line, width) => {
763
+ if (line.length <= width) return [line];
764
+ const indent = LEADING_SPACE.exec(line)?.[0] ?? "";
765
+ const lines = [];
766
+ let current = "";
767
+ for (const word of line.trim().split(WORDS)) {
768
+ const next = current === "" ? `${indent}${word}` : `${current} ${word}`;
769
+ if (next.length > width && current !== "") {
770
+ lines.push(current);
771
+ current = `${indent}${word}`;
772
+ } else current = next;
773
+ }
774
+ lines.push(current);
775
+ return lines;
776
+ };
777
+ const wrapText = (text, width) => {
778
+ const kept = [];
779
+ for (const line of text.replace(CARRIAGE, "\n").replace(TAB, " ").split("\n")) {
780
+ const trimmed = line.trimEnd();
781
+ if (trimmed !== "" || kept.length > 0 && kept.at(-1) !== "") kept.push(trimmed);
782
+ }
783
+ while (kept.at(-1) === "") kept.pop();
784
+ return kept.flatMap((line) => line === "" ? [""] : wrapLine(line, Math.max(NARROWEST$1, width)));
785
+ };
786
+ //#endregion
787
+ //#region src/cli/transcript-writer.ts
788
+ const PLAIN = {
789
+ colour: false,
790
+ width: 80
791
+ };
792
+ const NARROWEST = 40;
793
+ const UNKNOWN_WIDTH = 100;
794
+ const MARGIN = " ";
795
+ const INDENT = " ";
796
+ const WHITESPACE$1 = /\s+/g;
797
+ const terminalStyle = (colour) => colour && process.env.NO_COLOR === void 0 ? {
798
+ colour: true,
799
+ width: Math.max(NARROWEST, process.stderr.columns ?? UNKNOWN_WIDTH)
800
+ } : PLAIN;
801
+ const stderrStyle = () => terminalStyle(process.stderr.isTTY === true);
802
+ const flat = (text) => text.replace(WHITESPACE$1, " ").trim();
803
+ const clipped$1 = (text, width) => text.length > width ? `${text.slice(0, Math.max(1, width - 1))}…` : text;
804
+ const writerFor = (style) => {
805
+ const paint = paletteFor(style.colour);
806
+ const room = Math.max(1, style.width - 2 - 2);
807
+ const line = (text) => `${MARGIN}${text}`;
808
+ const nested = (text, depth = 1) => line(`${INDENT.repeat(depth)}${text}`);
809
+ const indented = (text, depth, tone = (row) => row) => wrapText(text, room - 2 * (depth - 1)).map((row) => row === "" ? "" : nested(tone(row), depth));
810
+ const header = ({ caseName, ordinal, variant }) => line(`${paint.bold(caseName)} ${paint.dim(variant)}${ordinal === null ? "" : paint.dim(` · trial ${ordinal}`)}`);
811
+ const heading = (glyph, label, tone) => line(tone(`${glyph} ${paint.bold(label)}`));
812
+ const branch = (text) => nested(`${paint.dim("├")} ${text}`);
813
+ const close = (text) => nested(`${paint.dim("└")} ${text}`);
814
+ return {
815
+ branch,
816
+ close,
817
+ header,
818
+ heading,
819
+ indented,
820
+ line,
821
+ nested,
822
+ paint,
823
+ room
824
+ };
825
+ };
826
+ //#endregion
642
827
  //#region src/cli/eval-report.ts
643
828
  const reportJson = effect.Schema.encodeSync(effect.Schema.parseJson(effect.Schema.Array(SuiteOutcome)));
644
829
  const appendSummary = (text) => effect.Effect.gen(function* () {
@@ -647,7 +832,8 @@ const appendSummary = (text) => effect.Effect.gen(function* () {
647
832
  });
648
833
  const reportStarted = (label, id) => effect.Effect.gen(function* () {
649
834
  const url = batchUrl(yield* require_errors.webUrlConfig, id);
650
- yield* note(`${label}: ${url}`);
835
+ const paint = paletteFor(stderrStyle().colour);
836
+ yield* note(labelled("Watch", `${url} ${paint.dim(label)}`, paint));
651
837
  yield* appendSummary(`[Batch ${id}](${url}) started.`);
652
838
  });
653
839
  const writeReport = (outcomes, path) => effect.Effect.gen(function* () {
@@ -758,20 +944,19 @@ const CONCERN_REASONS = {
758
944
  //#region src/cli/eval-usage.ts
759
945
  const THOUSAND = 1e3;
760
946
  const MILLION = 1e6;
761
- const CENT$1 = .01;
762
- const batchUsage = (batch) => {
947
+ const sumUsage = (readings, totalOf, usd) => {
763
948
  const concerns = /* @__PURE__ */ new Set();
764
949
  let inputTokens = 0;
765
950
  let outputTokens = 0;
766
951
  let totalTokens = 0;
767
- for (const trial of batch.runs.flatMap((run) => run.trials)) {
768
- if (!trial.usage) continue;
769
- inputTokens += trial.usage.inputTokens;
770
- outputTokens += trial.usage.outputTokens;
771
- totalTokens += trial.usage.totalTokens;
952
+ for (const reading of readings) {
953
+ if (!reading.usage) continue;
954
+ inputTokens += reading.usage.inputTokens;
955
+ outputTokens += reading.usage.outputTokens;
956
+ totalTokens += totalOf(reading.usage);
772
957
  for (const concern of usageConcerns({
773
- turns: trial.commands,
774
- usage: trial.usage
958
+ turns: reading.commands,
959
+ usage: reading.usage
775
960
  })) concerns.add(concern);
776
961
  }
777
962
  return {
@@ -779,42 +964,20 @@ const batchUsage = (batch) => {
779
964
  inputTokens,
780
965
  outputTokens,
781
966
  totalTokens,
782
- usd: batch.costs?.estimatedEquivalentUsd ?? null
967
+ usd
783
968
  };
784
969
  };
970
+ const batchUsage = (batch) => sumUsage(batch.runs.flatMap((run) => run.trials), (usage) => usage.totalTokens, batch.costs?.estimatedEquivalentUsd ?? null);
785
971
  const formatTokens = (value) => {
786
972
  if (value >= MILLION) return `${(value / MILLION).toFixed(1)}M`;
787
973
  return value >= THOUSAND ? `${Math.round(value / THOUSAND)}k` : String(value);
788
974
  };
789
- const formatUsd = (value) => value < CENT$1 ? "<$0.01" : `$${value.toFixed(2)}`;
975
+ const formatUsd = (value) => value < .01 ? "<$0.01" : `$${value.toFixed(2)}`;
790
976
  const usageLines = (usage) => {
791
977
  if (usage.totalTokens === 0) return [];
792
978
  const spend = usage.usd === null ? "" : `, ${formatUsd(usage.usd)} est.`;
793
979
  return [`${formatTokens(usage.totalTokens)} tokens (${formatTokens(usage.inputTokens)} in, ${formatTokens(usage.outputTokens)} out)${spend}`, ...usage.concerns.map((concern) => CONCERN_REASONS[concern])];
794
980
  };
795
- const localUsage = (cases, costs) => {
796
- const concerns = /* @__PURE__ */ new Set();
797
- let inputTokens = 0;
798
- let outputTokens = 0;
799
- let totalTokens = 0;
800
- for (const one of cases) {
801
- if (!one.usage) continue;
802
- inputTokens += one.usage.inputTokens;
803
- outputTokens += one.usage.outputTokens;
804
- totalTokens += one.usage.inputTokens + one.usage.outputTokens;
805
- for (const concern of usageConcerns({
806
- turns: one.commands,
807
- usage: one.usage
808
- })) concerns.add(concern);
809
- }
810
- return {
811
- concerns: [...concerns],
812
- inputTokens,
813
- outputTokens,
814
- totalTokens,
815
- usd: costs?.estimatedEquivalentUsd ?? null
816
- };
817
- };
818
981
  const PAYERS = [
819
982
  "model",
820
983
  "user",
@@ -824,7 +987,16 @@ const breakdownLine = (costs) => {
824
987
  const parts = PAYERS.flatMap((component) => costs?.components.filter((part) => part.component === component).map(({ usd }) => `${require_errors.COST_COMPONENT_LABELS[component]} ${usd === null ? "not priced" : formatUsd(usd)}`) ?? []);
825
988
  return parts.length > 1 ? [parts.join(", ")] : [];
826
989
  };
827
- const localUsageLines = (cases, costs = null) => [...usageLines(localUsage(cases, costs)), ...breakdownLine(costs)].map((line) => ` ${line}`);
990
+ const localUsage = (cases, costs = null) => {
991
+ const [spend, ...concerns] = usageLines(sumUsage(cases, (usage) => usage.inputTokens + usage.outputTokens, costs?.estimatedEquivalentUsd ?? null));
992
+ return spend === void 0 ? {
993
+ concerns: [],
994
+ lines: []
995
+ } : {
996
+ concerns,
997
+ lines: [spend, ...breakdownLine(costs)]
998
+ };
999
+ };
828
1000
  //#endregion
829
1001
  //#region ../schema/src/domain/shell-word.ts
830
1002
  const ESCAPED_IN_DOUBLE = /* @__PURE__ */ new Set([
@@ -921,117 +1093,153 @@ const callSubjectOf = (input) => {
921
1093
  return firstText(SUBJECT_KEYS.map((key) => fields[key])) ?? firstText(Object.values(fields));
922
1094
  };
923
1095
  //#endregion
924
- //#region src/cli/paint.ts
925
- const ESCAPE = String.fromCharCode(27);
926
- const tint = (code) => (text) => `${ESCAPE}[${code}m${text}${ESCAPE}[0m`;
927
- const unpainted = (text) => text;
928
- const COLOURED = {
929
- blue: tint(34),
930
- bold: tint(1),
931
- cyan: tint(36),
932
- dim: tint(2),
933
- green: tint(32),
934
- magenta: tint(35),
935
- red: tint(31),
936
- yellow: tint(33)
937
- };
938
- const MONOCHROME = {
939
- blue: unpainted,
940
- bold: unpainted,
941
- cyan: unpainted,
942
- dim: unpainted,
943
- green: unpainted,
944
- magenta: unpainted,
945
- red: unpainted,
946
- yellow: unpainted
947
- };
948
- const paletteFor = (colour) => colour ? COLOURED : MONOCHROME;
949
- //#endregion
950
- //#region src/cli/text-wrap.ts
951
- const NARROWEST$1 = 20;
952
- const LEADING_SPACE = /^\s*/;
953
- const WORDS = /\s+/;
954
- const TAB = /\t/g;
955
- const CARRIAGE = /\r\n?/g;
956
- const wrapLine = (line, width) => {
957
- if (line.length <= width) return [line];
958
- const indent = LEADING_SPACE.exec(line)?.[0] ?? "";
959
- const lines = [];
960
- let current = "";
961
- for (const word of line.trim().split(WORDS)) {
962
- const next = current === "" ? `${indent}${word}` : `${current} ${word}`;
963
- if (next.length > width && current !== "") {
964
- lines.push(current);
965
- current = `${indent}${word}`;
966
- } else current = next;
1096
+ //#region ../schema/src/domain/step-title.ts
1097
+ const describeStep = (entry) => {
1098
+ switch (entry._tag) {
1099
+ case "command": return describeCommand(entry.command);
1100
+ case "fileChange": return describeFiles("wrote", "Wrote", entry.paths);
1101
+ case "toolCall": return {
1102
+ target: callSubjectOf(entry.input),
1103
+ title: `Called ${entry.name}`,
1104
+ verb: "called"
1105
+ };
1106
+ case "message": return {
1107
+ target: null,
1108
+ title: summarizeMessage(entry.text),
1109
+ verb: entry.role === "user" ? "you" : "agent"
1110
+ };
1111
+ default: return entry;
967
1112
  }
968
- lines.push(current);
969
- return lines;
970
1113
  };
971
- const wrapText = (text, width) => {
972
- const kept = [];
973
- for (const line of text.replace(CARRIAGE, "\n").replace(TAB, " ").split("\n")) {
974
- const trimmed = line.trimEnd();
975
- if (trimmed !== "" || kept.length > 0 && kept.at(-1) !== "") kept.push(trimmed);
1114
+ const describeCommand = (command) => {
1115
+ const segments = splitSegments(commandText(command));
1116
+ const [first] = segments;
1117
+ if (first === void 0) return {
1118
+ target: null,
1119
+ title: "Ran a command",
1120
+ verb: "ran"
1121
+ };
1122
+ const written = segments.flatMap(redirectTargetOf);
1123
+ if (written.length > 0) return describeFiles("wrote", "Wrote", written);
1124
+ if (segments.every((words) => READERS.has(words[0] ?? ""))) {
1125
+ const files = segments.flatMap((words) => operandsOf(words).at(-1) ?? []);
1126
+ return files.length > 0 ? describeFiles("read", "Read", files) : describeRun(first);
976
1127
  }
977
- while (kept.at(-1) === "") kept.pop();
978
- return kept.flatMap((line) => line === "" ? [""] : wrapLine(line, Math.max(NARROWEST$1, width)));
979
- };
980
- //#endregion
981
- //#region src/cli/transcript-writer.ts
982
- const PLAIN = {
983
- colour: false,
984
- width: 80
985
- };
986
- const NARROWEST = 40;
987
- const UNKNOWN_WIDTH = 100;
988
- const MARGIN = 2;
989
- const RAIL_WIDTH = 2;
990
- const INDENT = " ";
1128
+ return SEARCHERS.has(first[0] ?? "") ? describeSearch(first) : describeRun(first);
1129
+ };
1130
+ const plainText = (text) => text.replace(MARKUP, "").replace(WHITESPACE, " ").trim();
1131
+ const summarizeMessage = (text) => {
1132
+ const plain = plainText(text);
1133
+ return SENTENCE.exec(plain)?.[1] ?? plain;
1134
+ };
1135
+ const READERS = /* @__PURE__ */ new Set([
1136
+ "bat",
1137
+ "cat",
1138
+ "head",
1139
+ "less",
1140
+ "nl",
1141
+ "sed",
1142
+ "tail"
1143
+ ]);
1144
+ const SEARCHERS = /* @__PURE__ */ new Set([
1145
+ "ag",
1146
+ "fd",
1147
+ "find",
1148
+ "grep",
1149
+ "ls",
1150
+ "rg",
1151
+ "tree"
1152
+ ]);
1153
+ const RUNNERS = /* @__PURE__ */ new Set([
1154
+ "bunx",
1155
+ "npx",
1156
+ "pnpx"
1157
+ ]);
1158
+ const SCRIPT_RUNNERS = /* @__PURE__ */ new Set([
1159
+ "bun",
1160
+ "npm",
1161
+ "pnpm",
1162
+ "yarn"
1163
+ ]);
1164
+ const TOKEN$1 = /'[^']*'|"[^"]*"|&&|\|\||[|;]|[^\s|;&]+/g;
1165
+ const SEPARATORS = /* @__PURE__ */ new Set([
1166
+ "&&",
1167
+ "||",
1168
+ ";"
1169
+ ]);
1170
+ const QUOTES = /^['"]|['"]$/g;
1171
+ const MARKUP = /[*`#>_]+/g;
1172
+ const SENTENCE = /^(.+?[.!?])(?:\s|$)/;
991
1173
  const WHITESPACE = /\s+/g;
992
- const terminalStyle = (colour) => colour && process.env.NO_COLOR === void 0 ? {
993
- colour: true,
994
- width: Math.max(NARROWEST, process.stderr.columns ?? UNKNOWN_WIDTH)
995
- } : PLAIN;
996
- const flat = (text) => text.replace(WHITESPACE, " ").trim();
997
- const clipped$1 = (text, width) => text.length > width ? `${text.slice(0, Math.max(1, width - 1))}…` : text;
998
- const writerFor = (style) => {
999
- const paint = paletteFor(style.colour);
1000
- const room = Math.max(1, style.width - MARGIN - RAIL_WIDTH);
1001
- const rail = paint.dim("│");
1002
- const blank = ` ${rail}`;
1003
- const line = (text) => ` ${rail} ${text}`;
1004
- const indented = (text, depth, tone = (row) => row) => wrapText(text, room - 2 * depth).map((row) => row === "" ? blank : line(`${INDENT.repeat(depth)}${tone(row)}`));
1005
- const header = ({ caseName, ordinal, variant }) => {
1006
- const facts = [variant, ordinal === null ? "" : `trial ${ordinal}`].filter((fact) => fact !== "").map((fact) => ` · ${fact}`).join("");
1007
- return ` ${paint.dim("┌")} ${paint.bold(caseName)}${paint.dim(facts)}`;
1174
+ const splitSegments = (script) => {
1175
+ const segments = [[]];
1176
+ let piped = false;
1177
+ for (const token of script.match(TOKEN$1) ?? []) if (SEPARATORS.has(token)) {
1178
+ segments.push([]);
1179
+ piped = false;
1180
+ } else if (token === "|") piped = true;
1181
+ else if (!piped) segments.at(-1)?.push(token.replace(QUOTES, ""));
1182
+ return segments.filter((words) => words.length > 0);
1183
+ };
1184
+ const REDIRECT = /^>>?/;
1185
+ const DISCARDED = "/dev/null";
1186
+ const targetAt = (words, index) => {
1187
+ const word = words[index] ?? "";
1188
+ if (word === ">" || word === ">>") return words[index + 1];
1189
+ return REDIRECT.test(word) ? word.replace(REDIRECT, "") : void 0;
1190
+ };
1191
+ const redirectTargetOf = (words) => words.flatMap((_, index) => {
1192
+ const target = targetAt(words, index);
1193
+ return target === void 0 || target === "" || target === DISCARDED ? [] : [target];
1194
+ });
1195
+ const operandsOf = (words) => words.slice(1).filter((word) => !word.startsWith("-"));
1196
+ const baseName = (path) => path.split("/").at(-1) ?? path;
1197
+ const describeFiles = (verb, word, paths) => {
1198
+ const files = paths.map(baseName);
1199
+ const [only] = files;
1200
+ return {
1201
+ target: files.join(", "),
1202
+ title: files.length === 1 && only !== void 0 ? `${word} ${only}` : `${word} ${files.length} files`,
1203
+ verb
1008
1204
  };
1009
- const heading = (label, tone) => [blank, line(paint.bold(tone(label)))];
1010
- const close = (text) => ` ${paint.dim("└")} ${text}`;
1205
+ };
1206
+ const describeSearch = (words) => {
1207
+ const [program] = words;
1208
+ const operands = operandsOf(words);
1209
+ if (program === "ls" || program === "tree" || words.includes("--files")) return {
1210
+ target: program === "ls" ? operands[0] ?? null : null,
1211
+ title: "Listed files",
1212
+ verb: "searched"
1213
+ };
1214
+ const [pattern, where = null] = operands;
1011
1215
  return {
1012
- blank,
1013
- close,
1014
- header,
1015
- heading,
1016
- indented,
1017
- line,
1018
- paint,
1019
- room
1216
+ target: where,
1217
+ title: pattern === void 0 ? "Searched files" : `Searched for ${pattern}`,
1218
+ verb: "searched"
1020
1219
  };
1021
1220
  };
1022
- //#endregion
1023
- //#region src/cli/transcript-step.ts
1024
- const GLYPH_WIDTH = 2;
1025
- const stepLine = (step, { line, paint, room }) => {
1026
- const width = room - GLYPH_WIDTH;
1027
- if (step._tag === "command") {
1028
- const failed = step.exitCode !== null && step.exitCode !== 0;
1029
- return line(`${paint.yellow("$")} ${paint.dim(clipped$1(flat(commandText(step.command)), width))}${failed ? paint.red(` exit ${step.exitCode}`) : ""}`);
1221
+ const describeRun = (words) => {
1222
+ const [program = "command", first, second] = words;
1223
+ const operands = operandsOf(words);
1224
+ if (RUNNERS.has(program) && first !== void 0) {
1225
+ const tool = [first, operands[1]].filter(Boolean).join(" ");
1226
+ return {
1227
+ target: tool,
1228
+ title: `Ran ${tool}`,
1229
+ verb: "ran"
1230
+ };
1030
1231
  }
1031
- if (step._tag === "fileChange") return line(`${paint.green("+")} ${paint.dim(clipped$1(`wrote ${step.paths.join(", ")}`, width))}`);
1032
- const subject = callSubjectOf(step.input);
1033
- const detail = subject === null ? "" : ` ${paint.dim(clipped$1(flat(subject), Math.max(1, width - step.name.length - 1)))}`;
1034
- return line(`${paint.blue("●")} ${step.name}${detail}${step.error === void 0 ? "" : paint.red(" failed")}`);
1232
+ if (SCRIPT_RUNNERS.has(program) && first === "run" && second !== void 0) return {
1233
+ target: second,
1234
+ title: `Ran the ${second} script`,
1235
+ verb: "ran"
1236
+ };
1237
+ const tool = program === "git" && first !== void 0 ? `git ${first}` : program;
1238
+ return {
1239
+ target: tool,
1240
+ title: `Ran ${tool}`,
1241
+ verb: "ran"
1242
+ };
1035
1243
  };
1036
1244
  //#endregion
1037
1245
  //#region src/cli/duration.ts
@@ -1047,9 +1255,55 @@ const formatDuration = (ms) => {
1047
1255
  return `${Math.floor(seconds / MINUTE$1)}m${String(seconds % MINUTE$1).padStart(2, "0")}s`;
1048
1256
  };
1049
1257
  //#endregion
1258
+ //#region src/cli/transcript-step.ts
1259
+ const BRANCH_WIDTH = 2;
1260
+ const VERB_WIDTH = 7;
1261
+ const NOTABLE_MS = 1e3;
1262
+ const GAP = " ";
1263
+ const VERB_TONES = {
1264
+ ran: "sand",
1265
+ read: "slate",
1266
+ search: "teal",
1267
+ wrote: "rose"
1268
+ };
1269
+ const unless = (text, tone) => text === "" ? "" : tone(text);
1270
+ const tookBetween = (startedAt, finishedAt) => startedAt === null || startedAt === void 0 || finishedAt === null ? null : Math.max(0, finishedAt - startedAt);
1271
+ const commandParts = (step) => {
1272
+ const title = describeCommand(step.command);
1273
+ return {
1274
+ detail: title.verb === "read" || title.verb === "wrote" ? title.target ?? title.title : flat(commandText(step.command)),
1275
+ failure: step.exitCode !== null && step.exitCode !== 0 ? `exit ${step.exitCode}` : null,
1276
+ tookMs: tookBetween(step.startedAtMillis, step.finishedAtMillis),
1277
+ verb: title.verb === "searched" ? "search" : title.verb
1278
+ };
1279
+ };
1280
+ const partsOf = (step) => {
1281
+ if (step._tag === "command") return commandParts(step);
1282
+ if (step._tag === "fileChange") return {
1283
+ detail: describeStep(step).target ?? "",
1284
+ failure: null,
1285
+ tookMs: null,
1286
+ verb: "wrote"
1287
+ };
1288
+ return {
1289
+ detail: flat(callSubjectOf(step.input) ?? ""),
1290
+ failure: step.error === void 0 ? null : "failed",
1291
+ tookMs: tookBetween(step.startedAtMillis, step.finishedAtMillis),
1292
+ verb: step.name
1293
+ };
1294
+ };
1295
+ const stepLine = (step, { branch, paint, room }) => {
1296
+ const { detail, failure, tookMs, verb } = partsOf(step);
1297
+ const took = tookMs === null || tookMs < NOTABLE_MS ? "" : `${GAP}${formatDuration(tookMs)}`;
1298
+ const failed = failure === null ? "" : `${GAP}✗ ${failure}`;
1299
+ const label = verb.padEnd(VERB_WIDTH);
1300
+ const width = room - BRANCH_WIDTH - label.length - 1 - took.length - failed.length;
1301
+ return branch(`${(failure === null ? paint[VERB_TONES[verb] ?? "lavender"] : paint.red)(label)} ${clipped$1(detail, Math.max(1, width))}${unless(took, paint.dim)}${unless(failed, paint.red)}`);
1302
+ };
1303
+ //#endregion
1050
1304
  //#region src/cli/transcript-turn.ts
1051
- const CENT = .01;
1052
1305
  const openedTurn = (previous, startedAt) => ({
1306
+ answering: false,
1053
1307
  costUsd: null,
1054
1308
  endedAt: null,
1055
1309
  number: (previous?.number ?? 0) + 1,
@@ -1070,7 +1324,7 @@ const repliedTurn = (previous, reply) => {
1070
1324
  const durationOf = (turn) => turn.startedAt === null || turn.endedAt === null ? null : formatDuration(Math.max(0, turn.endedAt - turn.startedAt));
1071
1325
  const costOf = (turn) => {
1072
1326
  if (turn.costUsd === null) return null;
1073
- return turn.costUsd > 0 && turn.costUsd < CENT ? "<$0.01" : `$${turn.costUsd.toFixed(2)}`;
1327
+ return turn.costUsd > 0 && turn.costUsd < .01 ? "<$0.01" : `$${turn.costUsd.toFixed(2)}`;
1074
1328
  };
1075
1329
  const turnFacts = (turn) => [
1076
1330
  `turn ${turn.number}`,
@@ -1087,11 +1341,19 @@ const validationSummary = (validation) => {
1087
1341
  };
1088
1342
  //#endregion
1089
1343
  //#region src/cli/outcome-mark.ts
1090
- const outcomeMark = (status, paint) => {
1091
- if (status === "passed") return paint.green(`✓ ${status}`);
1092
- if (status === "failed") return paint.red(`✗ ${status}`);
1093
- return status === "void" || status === "timed out" ? paint.yellow(`○ ${status}`) : paint.dim(status);
1344
+ const GLYPHS = {
1345
+ failed: "✗",
1346
+ passed: "✓",
1347
+ "timed out": "○",
1348
+ void: "○"
1349
+ };
1350
+ const outcomeTone = (status, paint) => {
1351
+ if (status === "passed") return paint.green;
1352
+ if (status === "failed") return paint.red;
1353
+ return status in GLYPHS ? paint.yellow : paint.dim;
1094
1354
  };
1355
+ const outcomeGlyph = (status, paint) => outcomeTone(status, paint)(GLYPHS[status] ?? "·");
1356
+ const outcomeMark = (status, paint) => outcomeTone(status, paint)(status in GLYPHS ? `${GLYPHS[status]} ${status}` : status);
1095
1357
  //#endregion
1096
1358
  //#region src/cli/transcript-verdict.ts
1097
1359
  const SUMMARY_LINES = 3;
@@ -1109,39 +1371,30 @@ const checksOf = (verdict) => {
1109
1371
  summary: null
1110
1372
  }));
1111
1373
  };
1112
- const markOf = (status, paint) => {
1113
- if (status === "passed") return paint.green("✓");
1114
- if (status === "failed" || status === "error") return paint.red(status === "error" ? "!" : "✗");
1115
- return paint.dim(status === "skipped" ? "–" : "·");
1116
- };
1374
+ const failing = (check) => check.status === "failed" || check.status === "error";
1117
1375
  const checkLines = (check, write) => {
1118
1376
  const timing = check.durationMs === null ? "" : write.paint.dim(` · ${formatDuration(check.durationMs)}`);
1119
- const tone = check.status === "passed" ? write.paint.dim : (row) => row;
1120
- const summary = check.summary === null || check.summary === check.name ? [] : write.indented(check.summary, 3, tone).slice(0, SUMMARY_LINES);
1121
- return [write.line(` ${markOf(check.status, write.paint)} ${check.name}${timing}`), ...summary];
1377
+ const mark = write.paint.red(check.status === "error" ? "!" : "✗");
1378
+ const summary = check.summary === null || check.summary === check.name ? [] : write.indented(check.summary, 2).slice(0, SUMMARY_LINES);
1379
+ return [write.nested(`${mark} ${check.name}${timing}`), ...summary];
1122
1380
  };
1123
1381
  const outcomeOf$1 = (verdict, paint) => {
1124
1382
  const mark = outcomeMark(verdict.status, paint);
1125
1383
  if (verdict.status !== "void" && verdict.status !== "timed out") return mark;
1126
1384
  const why = verdict.failure ?? (verdict.voidFields.length === 0 ? "not scored" : verdict.voidFields.join(", "));
1127
- return `${mark}${paint.yellow(` · ${why}`)}`;
1385
+ return `${mark}${paint.dim(` · ${why}`)}`;
1128
1386
  };
1129
1387
  const verdictLines = (verdict, write) => {
1130
1388
  const checks = checksOf(verdict);
1131
1389
  const passed = checks.filter((check) => check.status === "passed").length;
1132
1390
  const tally = checks.length === 0 ? "" : write.paint.dim(` · ${passed} of ${checks.length} checks passed`);
1133
- return [
1134
- ...checks.length === 0 ? [] : [...write.heading("checks", write.paint.blue), ...checks.flatMap((check) => checkLines(check, write))],
1135
- write.blank,
1136
- write.close(`${outcomeOf$1(verdict, write.paint)}${tally}`)
1137
- ];
1391
+ return [write.line(`${outcomeOf$1(verdict, write.paint)}${tally}`), ...checks.filter(failing).flatMap((check) => checkLines(check, write))];
1138
1392
  };
1139
1393
  //#endregion
1140
1394
  //#region src/cli/transcript.ts
1141
1395
  const EMPTY_TRANSCRIPT = {
1142
1396
  closed: /* @__PURE__ */ new Set(),
1143
1397
  current: null,
1144
- printed: false,
1145
1398
  turns: /* @__PURE__ */ new Map()
1146
1399
  };
1147
1400
  var Draft = class {
@@ -1150,29 +1403,41 @@ var Draft = class {
1150
1403
  lines = [];
1151
1404
  turns;
1152
1405
  write;
1153
- printed;
1154
1406
  constructor(transcript, style) {
1155
1407
  this.closed = new Set(transcript.closed);
1156
1408
  this.current = transcript.current;
1157
- this.printed = transcript.printed;
1158
1409
  this.turns = new Map(transcript.turns);
1159
1410
  this.write = writerFor(style);
1160
1411
  }
1161
1412
  enter(speaker) {
1162
1413
  if (speaker.key !== this.current) {
1163
- if (this.printed || this.lines.length > 0) this.lines.push("");
1164
- this.lines.push(this.write.header(speaker));
1414
+ this.lines.push("", this.write.header(speaker));
1165
1415
  this.current = speaker.key;
1166
1416
  }
1167
1417
  }
1168
- say(label, tone, text) {
1169
- this.lines.push(...this.write.heading(label, tone), ...this.write.indented(text, 1));
1418
+ asUser(key, text, startedAt) {
1419
+ const { heading, indented, paint } = this.write;
1420
+ this.closeTurn(key);
1421
+ this.turns.set(key, openedTurn(this.turns.get(key), startedAt));
1422
+ this.lines.push("", heading("❯", "User", paint.blue), ...indented(text, 1));
1423
+ }
1424
+ asAgent(key) {
1425
+ const turn = this.turns.get(key) ?? openedTurn(void 0, null);
1426
+ if (!turn.answering) {
1427
+ const { heading, paint } = this.write;
1428
+ this.lines.push(heading("◆", "Agent", paint.magenta));
1429
+ }
1430
+ this.turns.set(key, {
1431
+ ...turn,
1432
+ answering: true
1433
+ });
1170
1434
  }
1171
1435
  closeTurn(key) {
1172
1436
  const turn = this.turns.get(key);
1173
1437
  if (turn?.open === true) {
1174
- const { blank, line, paint } = this.write;
1175
- this.lines.push(blank, line(turn.replied ? `${paint.green("✓")} ${paint.dim(turnFacts(turn))}` : paint.dim(`· ${turnFacts(turn)} · no reply`)));
1438
+ const { close, nested, paint } = this.write;
1439
+ const facts = turn.replied ? turnFacts(turn) : `${turnFacts(turn)} · no reply`;
1440
+ this.lines.push(turn.answering ? close(paint.dim(facts)) : nested(paint.dim(facts)));
1176
1441
  this.turns.set(key, {
1177
1442
  ...turn,
1178
1443
  open: false
@@ -1185,7 +1450,6 @@ var Draft = class {
1185
1450
  transcript: {
1186
1451
  closed: this.closed,
1187
1452
  current: this.current,
1188
- printed: this.printed || this.lines.length > 0,
1189
1453
  turns: this.turns
1190
1454
  }
1191
1455
  };
@@ -1193,20 +1457,19 @@ var Draft = class {
1193
1457
  };
1194
1458
  const transcribe = (transcript, spoken, style) => {
1195
1459
  const draft = new Draft(transcript, style);
1196
- const { paint } = draft.write;
1197
1460
  for (const { entry, speaker } of spoken) {
1198
1461
  draft.enter(speaker);
1199
- if (entry._tag !== "message") draft.lines.push(stepLine(entry, draft.write));
1200
- else if (entry.role === "user") {
1201
- draft.closeTurn(speaker.key);
1202
- draft.turns.set(speaker.key, openedTurn(draft.turns.get(speaker.key), entry.finishedAtMillis ?? null));
1203
- draft.say("user", paint.cyan, entry.text);
1204
- } else {
1462
+ if (entry._tag !== "message") {
1463
+ draft.asAgent(speaker.key);
1464
+ draft.lines.push(stepLine(entry, draft.write));
1465
+ } else if (entry.role === "user") draft.asUser(speaker.key, entry.text, entry.finishedAtMillis ?? null);
1466
+ else {
1467
+ draft.asAgent(speaker.key);
1205
1468
  draft.turns.set(speaker.key, repliedTurn(draft.turns.get(speaker.key), {
1206
1469
  costUsd: entry.usage?.costUsd ?? null,
1207
1470
  finishedAtMillis: entry.finishedAtMillis ?? null
1208
1471
  }));
1209
- draft.say("agent", paint.magenta, entry.text);
1472
+ draft.lines.push(...draft.write.indented(entry.text, 1));
1210
1473
  }
1211
1474
  }
1212
1475
  return draft.done();
@@ -1216,7 +1479,7 @@ const settle = (transcript, settled, style) => {
1216
1479
  for (const { speaker, verdict } of settled) if (!draft.closed.has(speaker.key)) {
1217
1480
  draft.enter(speaker);
1218
1481
  draft.closeTurn(speaker.key);
1219
- draft.lines.push(...verdictLines(verdict, draft.write));
1482
+ draft.lines.push("", ...verdictLines(verdict, draft.write));
1220
1483
  draft.closed.add(speaker.key);
1221
1484
  draft.current = null;
1222
1485
  }
@@ -1236,12 +1499,7 @@ const makeTranscriber = (style) => effect.Effect.map(effect.Ref.make(EMPTY_TRANS
1236
1499
  });
1237
1500
  //#endregion
1238
1501
  //#region src/cli/batch-progress.ts
1239
- const DIM = "\x1B[2m";
1240
- const BOLD = "\x1B[1m";
1241
- const GREEN = "\x1B[32m";
1242
- const RED = "\x1B[31m";
1243
- const YELLOW = "\x1B[33m";
1244
- const RESET = "\x1B[0m";
1502
+ const { bold, dim, green, red, yellow } = paletteFor(true);
1245
1503
  const DONE$1 = "●";
1246
1504
  const RUNNING = "◐";
1247
1505
  const FILLED = "▰";
@@ -1249,31 +1507,30 @@ const HOLLOW = "▱";
1249
1507
  const PERCENT = 100;
1250
1508
  const SECONDS = 1e3;
1251
1509
  const MINUTE = 60;
1252
- const paint = (colour, text) => `${colour}${text}${RESET}`;
1253
1510
  const formatElapsed = (ms) => {
1254
1511
  const total = Math.floor(ms / SECONDS);
1255
1512
  const minutes = Math.floor(total / MINUTE);
1256
1513
  return minutes === 0 ? `${total}s` : `${minutes}m${String(total % MINUTE).padStart(2, "0")}s`;
1257
1514
  };
1258
1515
  const formatStatus = (run) => {
1259
- if (run.status === "finished") return paint(GREEN, DONE$1);
1260
- return run.status === "failed" ? paint(RED, DONE$1) : paint(YELLOW, RUNNING);
1516
+ if (run.status === "finished") return green(DONE$1);
1517
+ return run.status === "failed" ? red(DONE$1) : yellow(RUNNING);
1261
1518
  };
1262
1519
  const formatTrialProgress = (run, trials) => {
1263
1520
  const settled = run.trials.filter((trial) => trial.status !== "queued" && trial.status !== "running").length;
1264
- return `${FILLED.repeat(settled)}${paint(DIM, HOLLOW.repeat(Math.max(0, trials - settled)))}`;
1521
+ return `${FILLED.repeat(settled)}${dim(HOLLOW.repeat(Math.max(0, trials - settled)))}`;
1265
1522
  };
1266
1523
  const formatPassRate = (run) => {
1267
1524
  const rate = run.distribution.passRate;
1268
- if (run.distribution.scored === 0) return paint(DIM, "—");
1525
+ if (run.distribution.scored === 0) return dim("—");
1269
1526
  const shown = `${Math.round(rate * PERCENT)}%`;
1270
- return paint(rate === 1 ? GREEN : RED, shown);
1527
+ return (rate === 1 ? green : red)(shown);
1271
1528
  };
1272
1529
  const formatActivity = (run) => {
1273
1530
  const running = run.trials.find((trial) => trial.status === "running");
1274
1531
  if (running === void 0) return [];
1275
1532
  const counts = [`${running.commands} cmd`, ...running.filesChanged.length === 0 ? [] : [`${running.filesChanged.length} files`]].join(", ");
1276
- return [paint(DIM, ` ${counts}`)];
1533
+ return [dim(` ${counts}`)];
1277
1534
  };
1278
1535
  const widest = (batch) => batch.runs.reduce((width, run) => Math.max(width, formatVariant(run.variant).length), 0);
1279
1536
  const byCase = (batch) => Map.groupBy(batch.runs, (run) => run.case.id).values();
@@ -1281,7 +1538,7 @@ const formatBatch = (batch, trials, elapsedMs) => {
1281
1538
  const width = widest(batch);
1282
1539
  const lines = [];
1283
1540
  for (const runs of byCase(batch)) {
1284
- lines.push(` ${BOLD}${runs[0]?.case.name ?? ""}${RESET}`);
1541
+ lines.push(` ${bold(runs[0]?.case.name ?? "")}`);
1285
1542
  for (const run of runs) {
1286
1543
  lines.push(` ${formatStatus(run)} ${formatVariant(run.variant).padEnd(width)} ${formatTrialProgress(run, trials)} ${formatPassRate(run)}`);
1287
1544
  lines.push(...formatActivity(run));
@@ -1290,8 +1547,8 @@ const formatBatch = (batch, trials, elapsedMs) => {
1290
1547
  }
1291
1548
  const usage = batchUsage(batch);
1292
1549
  const [spend, ...concerns] = usageLines(usage);
1293
- lines.push(paint(DIM, spend === void 0 ? ` ${formatElapsed(elapsedMs)} elapsed` : ` ${formatElapsed(elapsedMs)} elapsed · ${spend}`));
1294
- for (const concern of concerns) lines.push(paint(YELLOW, ` ${concern}`));
1550
+ lines.push(dim(spend === void 0 ? ` ${formatElapsed(elapsedMs)} elapsed` : ` ${formatElapsed(elapsedMs)} elapsed · ${spend}`));
1551
+ for (const concern of concerns) lines.push(yellow(` ${concern}`));
1295
1552
  return lines;
1296
1553
  };
1297
1554
  const up = (rows) => `[${rows}A`;
@@ -1374,7 +1631,7 @@ const followBatch = (id, api) => effect.Stream.unwrap(effect.Effect.gen(function
1374
1631
  const TICK = 1e3;
1375
1632
  const FLOOR_POLL = 15e3;
1376
1633
  const running = (batch) => batch.status === "running";
1377
- const settledIn = (batch) => batch.runs.flatMap((run) => run.trials).filter((trial) => trial.status !== "queued" && trial.status !== "running").length;
1634
+ const settledIn = (batch) => batch.runs.flatMap((run) => run.trials).filter((trial) => require_errors.isSettledTrial(trial.status)).length;
1378
1635
  var EvalWaitTimeout = class extends effect.Data.TaggedError("EvalWaitTimeout") {
1379
1636
  get message() {
1380
1637
  return `Timed out after ${this.seconds}s waiting for ${this.batchId}. The batch was not cancelled.`;
@@ -1412,15 +1669,15 @@ const waitForBatch = (id, watcher, timeoutSeconds) => effect.Effect.gen(function
1412
1669
  }), effect.Effect.withSpan("Cli.waitForBatch", { attributes: { batchId: id } }));
1413
1670
  //#endregion
1414
1671
  //#region src/cli/open-browser.ts
1415
- const BROWSER = "ANPORD_BROWSER";
1416
- const opened = (url) => {
1417
- const chosen = process.env[BROWSER];
1672
+ const browserConfig = effect.Config.string("ANPORD_BROWSER").pipe(effect.Config.option, effect.Config.map(effect.Option.getOrUndefined));
1673
+ const opened = (url, chosen) => {
1418
1674
  if (process.platform === "darwin") return chosen === void 0 ? _effect_platform.Command.make("open", url) : _effect_platform.Command.make("open", "-a", chosen, url);
1419
1675
  return process.platform === "win32" ? _effect_platform.Command.make("start", url) : _effect_platform.Command.make(chosen ?? "xdg-open", url);
1420
1676
  };
1421
1677
  const openBrowser = (url) => effect.Effect.gen(function* () {
1422
- if (process.env[BROWSER] === "none") return;
1423
- yield* (yield* _effect_platform.CommandExecutor.CommandExecutor).exitCode(opened(url));
1678
+ const chosen = yield* browserConfig;
1679
+ if (chosen === "none") return;
1680
+ yield* (yield* _effect_platform.CommandExecutor.CommandExecutor).exitCode(opened(url, chosen));
1424
1681
  }).pipe(effect.Effect.ignore);
1425
1682
  //#endregion
1426
1683
  //#region src/cli/batch-outcome.ts
@@ -2743,13 +3000,12 @@ const decodeGeminiLine = (line, at) => {
2743
3000
  //#endregion
2744
3001
  //#region ../eval/src/adapters/harness/opencode-config.ts
2745
3002
  const CONFIG_VARIABLE = "OPENCODE_CONFIG_CONTENT";
2746
- const isObject = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
2747
3003
  const decodeJson$1 = effect.Schema.decodeUnknownOption(effect.Schema.parseJson());
2748
- const parsed = (value) => decodeJson$1(value).pipe(effect.Option.filter(isObject), effect.Option.getOrElse(() => ({})));
3004
+ const parsed = (value) => decodeJson$1(value).pipe(effect.Option.filter(effect.Predicate.isRecord), effect.Option.getOrElse(() => ({})));
2749
3005
  const stringsOf = (value) => Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
2750
3006
  const merged = (base, extra) => Object.fromEntries([...Object.entries(base), ...Object.entries(extra).map(([key, value]) => {
2751
3007
  const current = base[key];
2752
- return [key, isObject(current) && isObject(value) ? merged(current, value) : value];
3008
+ return [key, effect.Predicate.isRecord(current) && effect.Predicate.isRecord(value) ? merged(current, value) : value];
2753
3009
  })]);
2754
3010
  const opencodeConfigContent = (env, systemPromptPath) => {
2755
3011
  const own = parsed(env[CONFIG_VARIABLE]);
@@ -3218,20 +3474,28 @@ const localCache = (store, staging) => ({
3218
3474
  //#endregion
3219
3475
  //#region ../eval/src/adapters/sandbox/local-detached.ts
3220
3476
  const call$1 = providerCall("local");
3477
+ const KILLED = 137;
3221
3478
  const read$1 = (path) => effect.Effect.tryPromise(() => (0, node_fs_promises.readFile)(path, "utf8")).pipe(effect.Effect.orElseSucceed(() => ""));
3222
3479
  const localDetached = (root, path, env) => {
3223
3480
  const runs = (0, node_path.join)(root, ".anpord-runs");
3224
3481
  const fileOf = (id, suffix) => (0, node_path.join)(runs, `${id}.${suffix}`);
3482
+ const exits = /* @__PURE__ */ new Map();
3483
+ const closedWith = /* @__PURE__ */ new Map();
3225
3484
  return {
3226
- progress: (started) => effect.Effect.all([
3227
- read$1(fileOf(started.id, "out")),
3228
- read$1(fileOf(started.id, "err")),
3229
- read$1(fileOf(started.id, "exit"))
3230
- ], { concurrency: "unbounded" }).pipe(effect.Effect.map(([stdout, stderr, status]) => ({
3231
- exitCode: status.trim() === "" ? null : Number(status.trim()),
3232
- stderr,
3233
- stdout
3234
- }))),
3485
+ progress: (started) => effect.Effect.gen(function* () {
3486
+ const closed = closedWith.get(started.id);
3487
+ const status = (yield* read$1(fileOf(started.id, "exit"))).trim();
3488
+ const [stdout, stderr] = yield* effect.Effect.all([read$1(fileOf(started.id, "out")), read$1(fileOf(started.id, "err"))], { concurrency: "unbounded" });
3489
+ return {
3490
+ exitCode: status === "" ? closed ?? null : Number(status),
3491
+ stderr,
3492
+ stdout
3493
+ };
3494
+ }),
3495
+ settled: (started) => {
3496
+ const exit = exits.get(started.id);
3497
+ return exit === void 0 ? effect.Effect.never : effect.Effect.promise(() => exit);
3498
+ },
3235
3499
  start: (command, options) => effect.Effect.gen(function* () {
3236
3500
  const id = `run-${yield* effect.Random.nextIntBetween(0, 1e6)}`;
3237
3501
  const [out, err, exit] = [
@@ -3240,7 +3504,7 @@ const localDetached = (root, path, env) => {
3240
3504
  "exit"
3241
3505
  ].map((suffix) => JSON.stringify(fileOf(id, suffix)));
3242
3506
  yield* call$1(() => (0, node_fs_promises.mkdir)(runs, { recursive: true }));
3243
- yield* effect.Effect.try({
3507
+ const child = yield* effect.Effect.try({
3244
3508
  catch: unavailableFor("local"),
3245
3509
  try: () => (0, node_child_process.spawn)(`{ ${command} ; } > ${out} 2> ${err}; printf %s $? > ${exit}`, {
3246
3510
  cwd: options?.cwd ?? root,
@@ -3252,8 +3516,13 @@ const localDetached = (root, path, env) => {
3252
3516
  },
3253
3517
  shell: "/bin/bash",
3254
3518
  stdio: "ignore"
3255
- }).unref()
3519
+ })
3256
3520
  });
3521
+ exits.set(id, new Promise((resolve) => child.once("close", (code) => {
3522
+ closedWith.set(id, code ?? KILLED);
3523
+ resolve();
3524
+ })));
3525
+ child.unref();
3257
3526
  return {
3258
3527
  id,
3259
3528
  session: id
@@ -3514,6 +3783,9 @@ const stepResultsOf = (script, output) => {
3514
3783
  };
3515
3784
  const withoutMarks = (output) => output.replace(MARK_LINES, "");
3516
3785
  //#endregion
3786
+ //#region ../eval/src/adapters/scorers/scorer-timeout.ts
3787
+ const SCORER_TIMEOUT_MS = 3e5;
3788
+ //#endregion
3517
3789
  //#region ../eval/src/adapters/scorers/shell-pipeline.ts
3518
3790
  const isUnguardedPipeline = (command) => {
3519
3791
  let quote = null;
@@ -3693,10 +3965,7 @@ const executeValidation = (input) => effect.Effect.gen(function* () {
3693
3965
  //#region ../eval/src/domain/answer-file.ts
3694
3966
  const ANSWER_PATH = (home) => `${home}/.anpord-answer.txt`;
3695
3967
  const TRANSCRIPT_PATH = (home) => `${home}/.anpord-transcript.txt`;
3696
- const ANSWER_ENV = "ANPORD_ANSWER_FILE";
3697
- const TRANSCRIPT_ENV = "ANPORD_TRANSCRIPT_FILE";
3698
3968
  const TURNS_PATH = (home) => `${home}/.anpord-turns.json`;
3699
- const TURNS_ENV = "ANPORD_TURNS_FILE";
3700
3969
  //#endregion
3701
3970
  //#region ../eval/src/domain/journal.ts
3702
3971
  const commandsIn = (events) => events.filter((event) => event._tag === "Command").length;
@@ -3726,9 +3995,9 @@ const writeAnswer = (sandbox, events, turns) => effect.Effect.all([
3726
3995
  sandbox.writeFile(TURNS_PATH(sandbox.home), JSON.stringify(turns))
3727
3996
  ], { discard: true });
3728
3997
  const answerEnv = (sandbox) => ({
3729
- [ANSWER_ENV]: ANSWER_PATH(sandbox.home),
3730
- [TRANSCRIPT_ENV]: TRANSCRIPT_PATH(sandbox.home),
3731
- [TURNS_ENV]: TURNS_PATH(sandbox.home)
3998
+ [require_sandbox_env.ANSWER_ENV]: ANSWER_PATH(sandbox.home),
3999
+ [require_sandbox_env.TRANSCRIPT_ENV]: TRANSCRIPT_PATH(sandbox.home),
4000
+ [require_sandbox_env.TURNS_ENV]: TURNS_PATH(sandbox.home)
3732
4001
  });
3733
4002
  const resultStatus = (passed) => passed ? "passed" : "failed";
3734
4003
  const processError = (execution) => {
@@ -3795,7 +4064,7 @@ const scoreCommand = (request) => effect.Effect.gen(function* () {
3795
4064
  ...request.env,
3796
4065
  ...answerEnv(request.sandbox)
3797
4066
  },
3798
- timeoutMs: 3e5
4067
+ timeoutMs: SCORER_TIMEOUT_MS
3799
4068
  },
3800
4069
  records: [record],
3801
4070
  observe: request.onValidation,
@@ -3905,11 +4174,11 @@ const scoreValidator = (request) => effect.Effect.gen(function* () {
3905
4174
  command: `node ${shellQuote(path)}`,
3906
4175
  options: {
3907
4176
  cwd: request.workspace,
3908
- timeoutMs: 3e5,
4177
+ timeoutMs: SCORER_TIMEOUT_MS,
3909
4178
  env: {
3910
4179
  ...request.env,
3911
4180
  ...answerEnv(request.sandbox),
3912
- ANPORD_PREPARE_VALUE: JSON.stringify(request.prepared ?? {})
4181
+ [require_sandbox_env.PREPARE_VALUE_ENV]: JSON.stringify(request.prepared ?? {})
3913
4182
  }
3914
4183
  },
3915
4184
  records,
@@ -4893,7 +5162,6 @@ const runProfileInstall = (input) => {
4893
5162
  };
4894
5163
  //#endregion
4895
5164
  //#region ../eval/src/domain/prepare-output.ts
4896
- const MARKER = "ANPORD_PREPARE_RESULT=";
4897
5165
  const PREPARED_LIMIT = 16e3;
4898
5166
  const CARRIED_LIMIT = PREPARED_LIMIT * 2;
4899
5167
  const LINE_BREAK = /[\r\n]/;
@@ -4907,9 +5175,9 @@ const NOTHING_STREAMED = {
4907
5175
  stdout: ""
4908
5176
  };
4909
5177
  const readPrepareValue = (output) => {
4910
- const line = output.split("\n").findLast((entry) => entry.startsWith(MARKER));
5178
+ const line = output.split("\n").findLast((entry) => entry.startsWith(require_sandbox_env.PREPARE_RESULT_MARKER));
4911
5179
  if (line === void 0) return effect.Either.left("it printed no result, so its output was cut off");
4912
- const encoded = line.slice(22);
5180
+ const encoded = line.slice(require_sandbox_env.PREPARE_RESULT_MARKER.length);
4913
5181
  if (encoded.length > PREPARED_LIMIT) return effect.Either.left(`it returned ${encoded.length} characters and the limit is ${PREPARED_LIMIT}. Return a summary and keep large data in files`);
4914
5182
  return decodeJson(encoded).pipe(effect.Either.mapLeft(() => "its result was not valid JSON"), effect.Either.flatMap((value) => effect.Either.mapLeft(decodeObject(value), () => "it returned something other than an object")));
4915
5183
  };
@@ -4923,14 +5191,14 @@ const completeLines = (text) => {
4923
5191
  const shownPrepareOutput = (carried, arrived, known) => {
4924
5192
  const stdout = completeLines(carried.stdout + arrived.stdout);
4925
5193
  const stderr = completeLines(carried.stderr + arrived.stderr);
4926
- return [require_api_mocks.redactSecrets([...stdout.lines, ...stderr.lines].filter((line) => !line.includes(MARKER)).join("\n"), known).slice(-400).trim(), {
5194
+ return [require_api_mocks.redactSecrets([...stdout.lines, ...stderr.lines].filter((line) => !line.includes(require_sandbox_env.PREPARE_RESULT_MARKER)).join("\n"), known).slice(-400).trim(), {
4927
5195
  stderr: stderr.carry,
4928
5196
  stdout: stdout.carry
4929
5197
  }];
4930
5198
  };
4931
5199
  //#endregion
4932
5200
  //#region ../eval/src/services/polled-command.ts
4933
- const FIRST_CHECK_MS = 5e3;
5201
+ const FIRST_CHECK_MS = 500;
4934
5202
  const SLOWEST_CHECK_MS = 3e4;
4935
5203
  const WIDENING = 1.5;
4936
5204
  const CHECK_RETRY = effect.Schedule.exponential("200 millis").pipe(effect.Schedule.compose(effect.Schedule.recurs(3)));
@@ -4950,7 +5218,7 @@ const pollUntilDone = (input) => effect.Effect.gen(function* () {
4950
5218
  }, {
4951
5219
  body: () => effect.Effect.gen(function* () {
4952
5220
  const millis = yield* effect.Ref.getAndUpdate(gap, (current) => Math.min(Math.round(current * WIDENING), SLOWEST_CHECK_MS));
4953
- yield* input.suspender.waitFor(effect.Duration.millis(millis));
5221
+ yield* effect.Effect.raceFirst(input.suspender.waitFor(effect.Duration.millis(millis)), input.resumable.settled(started));
4954
5222
  const progress = yield* check;
4955
5223
  yield* report(progress);
4956
5224
  return {
@@ -4972,9 +5240,8 @@ const pollUntilDone = (input) => effect.Effect.gen(function* () {
4972
5240
  });
4973
5241
  //#endregion
4974
5242
  //#region ../eval/src/services/long-command.ts
4975
- const DEFAULT_TIMEOUT_MS = 12e4;
4976
5243
  const runLongCommand = (sandbox, command, options) => effect.Effect.gen(function* () {
4977
- const timeoutMs = options?.timeoutMs ?? DEFAULT_TIMEOUT_MS;
5244
+ const timeoutMs = options?.timeoutMs ?? 12e4;
4978
5245
  const exec = {
4979
5246
  cwd: options?.cwd,
4980
5247
  env: options?.env,
@@ -5016,7 +5283,7 @@ const runPrepare = (input) => effect.Effect.scoped(effect.Effect.gen(function* (
5016
5283
  mounted: effect.Option.isSome(cache)
5017
5284
  }));
5018
5285
  const env = {
5019
- ...restored ? { ANPORD_CACHE_RESTORED: "1" } : {},
5286
+ ...restored ? { [require_sandbox_env.CACHE_RESTORED_ENV]: "1" } : {},
5020
5287
  ...input.forwarded
5021
5288
  };
5022
5289
  const carried = yield* effect.Ref.make(NOTHING_STREAMED);
@@ -5099,7 +5366,7 @@ const prepareWorkspace = (input) => effect.Effect.gen(function* () {
5099
5366
  sandbox: input.sandbox,
5100
5367
  version: input.harnessVersion
5101
5368
  });
5102
- yield* runCommand(input.sandbox, `mkdir -p ${input.workspace}`, { timeoutMs: 6e4 });
5369
+ yield* runCommand(input.sandbox, `mkdir -p ${input.workspace}`, { timeoutMs: MKDIR_TIMEOUT_MS });
5103
5370
  yield* materialiseStage(input, "home");
5104
5371
  yield* materialise(input);
5105
5372
  yield* materialiseStage(input, "workspace");
@@ -5734,7 +6001,7 @@ const runVariant = (request, variant, transcript, options) => effect.Effect.gen(
5734
6001
  return yield* effect.Effect.forEach(request.cases.flatMap((subject) => ordinals.map((ordinal) => [subject, ordinal])), ([subject, ordinal]) => attempt(subject, ordinal), { concurrency: 1 });
5735
6002
  }).pipe(effect.Effect.provide(options.credentials === void 0 || options.credentials.size === 0 ? EvalLocalLive : evalLocalWith(credentialResolverFrom(options.credentials))), effect.Effect.scoped);
5736
6003
  const runLocally = (request, options = {}) => effect.Effect.gen(function* () {
5737
- const transcript = yield* makeTranscriber(terminalStyle(process.stderr.isTTY === true));
6004
+ const transcript = yield* makeTranscriber(stderrStyle());
5738
6005
  return (yield* effect.Effect.forEach(request.variants, (variant) => runVariant(request, variant, transcript, options), { concurrency: 1 })).flat();
5739
6006
  }).pipe(effect.Effect.withConfigProvider(localConfig));
5740
6007
  //#endregion
@@ -5869,32 +6136,44 @@ const runStoredCase = (caseId, variants, options, save) => effect.Effect.gen(fun
5869
6136
  //#endregion
5870
6137
  //#region src/cli/local-report.ts
5871
6138
  const STATUSES = [
5872
- "passed",
5873
6139
  "failed",
6140
+ "timed out",
5874
6141
  "void",
5875
- "timed out"
6142
+ "passed"
5876
6143
  ];
5877
- const trialName = (one, numbered) => `${one.name} on ${one.variant}${numbered ? `, trial ${one.ordinal}` : ""}`;
5878
- const tallyOf = (cases) => STATUSES.flatMap((status) => {
5879
- const count = cases.filter((one) => one.status === status).length;
5880
- return count === 0 ? [] : [`${count} ${status}`];
5881
- }).join(", ");
5882
- const summaryLines = (label, cases, costs, link, paint = paletteFor(false)) => {
6144
+ const trialName = (one, numbered) => numbered ? `${one.name} #${one.ordinal}` : one.name;
6145
+ const tallyOf = (cases, paint) => {
6146
+ return `${STATUSES.flatMap((status) => {
6147
+ const count = cases.filter((one) => one.status === status).length;
6148
+ return count === 0 ? [] : [outcomeTone(status, paint)(`${count} ${status}`)];
6149
+ }).join(paint.dim(" | "))} ${paint.dim(`(${cases.length})`)}`;
6150
+ };
6151
+ const trialLines = (cases, paint) => {
5883
6152
  const numbered = cases.some((one) => one.ordinal > 1);
5884
- const statusWidth = Math.max(...cases.map((one) => one.status.length), 0);
5885
- const nameWidth = Math.max(...cases.map((one) => trialName(one, numbered).length), 0);
5886
- const reasonIndent = " ".repeat(statusWidth + 6);
5887
- return [
5888
- `${paint.bold(label)}: ${[`${cases.length} ${cases.length === 1 ? "trial" : "trials"} on this machine`, tallyOf(cases)].filter((part) => part !== "").join(", ")}`,
5889
- ...cases.flatMap((one) => [` ${outcomeMark(one.status, paint)}${" ".repeat(statusWidth - one.status.length)} ${trialName(one, numbered).padEnd(nameWidth)} ${paint.dim(formatDuration(one.durationMs))}`, ...one.reason === null ? [] : [`${reasonIndent}${one.reason}`]]),
5890
- ...localUsageLines(cases, costs),
5891
- ...effect.Option.match(link, {
5892
- onNone: () => [],
5893
- onSome: (url) => [` Results: ${url}`]
5894
- })
6153
+ const nameWidth = Math.max(...cases.map((one) => trialName(one, numbered).length));
6154
+ const variantWidth = Math.max(...cases.map((one) => one.variant.length));
6155
+ return cases.flatMap((one) => [` ${outcomeGlyph(one.status, paint)} ${trialName(one, numbered).padEnd(nameWidth)} ${paint.dim(one.variant.padEnd(variantWidth))} ${paint.dim(formatDuration(one.durationMs))}`, ...one.reason === null ? [] : [` ${outcomeTone(one.status, paint)(one.status)} ${paint.dim(`· ${one.reason}`)}`]]);
6156
+ };
6157
+ const usageRows = (cases, costs, paint) => {
6158
+ const { concerns, lines } = localUsage(cases, costs);
6159
+ const [first, ...rest] = lines;
6160
+ return first === void 0 ? [] : [
6161
+ labelled("Usage", first, paint),
6162
+ ...rest.map(continued$1),
6163
+ ...concerns.map((concern) => continued$1(paint.yellow(`⚠ ${concern}`)))
5895
6164
  ];
5896
6165
  };
5897
- const reportLocal = (label, cases, costs, link) => note(["", ...summaryLines(label, cases, costs, link, paletteFor(terminalStyle(process.stderr.isTTY === true).colour))].join("\n"));
6166
+ const summaryLines = (label, cases, costs, link, paint = paletteFor(false)) => [
6167
+ ...cases.length === 0 ? [] : [...trialLines(cases, paint), ""],
6168
+ labelled("Suite", label, paint),
6169
+ labelled("Trials", cases.length === 0 ? "none ran" : tallyOf(cases, paint), paint),
6170
+ ...usageRows(cases, costs, paint),
6171
+ ...effect.Option.match(link, {
6172
+ onNone: () => [],
6173
+ onSome: (url) => [labelled("Results", url, paint)]
6174
+ })
6175
+ ];
6176
+ const reportLocal = (label, cases, costs, link) => note(["", ...summaryLines(label, cases, costs, link, paletteFor(stderrStyle().colour))].join("\n"));
5898
6177
  const localProblems = (label, cases) => {
5899
6178
  const missed = cases.filter((one) => one.status !== "passed").length;
5900
6179
  return missed === 0 ? [] : [`${label}: ${missed} of ${cases.length} trials did not pass.`];
@@ -6036,7 +6315,7 @@ const whoami = _effect_cli.Command.make("whoami", { asJson: asJson$2 }, ({ asJso
6036
6315
  if (wantsJson) return yield* json(found);
6037
6316
  return yield* effect.Effect.forEach(whoamiLines(found), row);
6038
6317
  })).pipe(_effect_cli.Command.withDescription("Show the organization and key ANPORD_API_KEY acts for"));
6039
- const announceOrganization = require_errors.AnpordApi.pipe(effect.Effect.flatMap((api) => api.auth.whoami({ payload: {} })), effect.Effect.flatMap(({ organization }) => note(`Runs land in ${organization.name} (${organization.slug}).`)), effect.Effect.ignore);
6318
+ const announceOrganization = require_errors.AnpordApi.pipe(effect.Effect.flatMap((api) => api.auth.whoami({ payload: {} })), effect.Effect.flatMap(({ organization }) => note(labelled("Org", `${organization.name} (${organization.slug})`, paletteFor(stderrStyle().colour)))), effect.Effect.ignore);
6040
6319
  //#endregion
6041
6320
  //#region src/cli/suite-local.ts
6042
6321
  var LocalTimeout = class extends effect.Data.TaggedError("LocalTimeout") {
@@ -6246,7 +6525,7 @@ const commandsWith = (prompts) => [
6246
6525
  const commands = commandsWith(false);
6247
6526
  //#endregion
6248
6527
  //#region src/cli/failure.ts
6249
- const MISSING_KEY = "Set ANPORD_API_KEY to an API key from https://www.anpord.com/settings/keys";
6528
+ const MISSING_KEY = `Set ANPORD_API_KEY to an API key from ${require_errors.WEB_ORIGIN}/settings/keys`;
6250
6529
  const EXIT_GATE_FAILED = 2;
6251
6530
  const tagOf = (error) => typeof error === "object" && error !== null && "_tag" in error ? error._tag : void 0;
6252
6531
  const describe = (error) => {