severally-mcp 2.4.0 → 2.4.2
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.
- package/README.ja.md +44 -19
- package/README.md +44 -18
- package/package.json +3 -1
- package/plugins/severally/.claude-plugin/plugin.json +1 -1
- package/plugins/severally/.codex-plugin/plugin.json +1 -1
- package/plugins/severally/dist/severally-mcp.mjs +585 -255
- package/plugins/severally/skills/_template/reference.md.tmpl +2 -2
- package/plugins/severally/skills/antigravity/severally/reference.md +2 -2
- package/plugins/severally/skills/claude/severally/reference.md +2 -2
- package/plugins/severally/skills/codex/severally/reference.md +2 -2
- package/plugins/severally/skills/opencode/severally/reference.md +2 -2
- package/scripts/build.mjs +57 -54
- package/scripts/doctor.mjs +15 -4
- package/scripts/install.mjs +73 -9
- package/scripts/live-check.mjs +11 -5
- package/scripts/live-mcp-check.mjs +10 -4
- package/src/adapters/antigravity-sandbox.mjs +24 -6
- package/src/adapters/antigravity.mjs +80 -17
- package/src/adapters/claude-code.mjs +49 -8
- package/src/adapters/codex.mjs +56 -6
- package/src/adapters/opencode-sandbox.mjs +20 -2
- package/src/adapters/opencode.mjs +115 -19
- package/src/brief.mjs +84 -7
- package/src/consultants.mjs +184 -25
- package/src/cross-spawn-escape.d.ts +7 -0
- package/src/export.mjs +50 -6
- package/src/expose-paths.mjs +149 -7
- package/src/failures.mjs +8 -1
- package/src/jobs.mjs +578 -132
- package/src/jsonc.mjs +11 -3
- package/src/parse-result.mjs +91 -14
- package/src/platform.mjs +52 -2
- package/src/policy.mjs +151 -28
- package/src/redact.mjs +22 -3
- package/src/result-schema.mjs +13 -0
- package/src/run.mjs +158 -12
- package/src/schema.mjs +84 -11
- package/src/server.mjs +49 -14
- package/src/status.mjs +16 -4
- package/src/store.mjs +287 -6
|
@@ -62,7 +62,7 @@ function stripJsonc(text2) {
|
|
|
62
62
|
}
|
|
63
63
|
};
|
|
64
64
|
while (i < text2.length) {
|
|
65
|
-
const c = text2
|
|
65
|
+
const c = text2.charAt(i);
|
|
66
66
|
const next = text2[i + 1];
|
|
67
67
|
if (inString) {
|
|
68
68
|
out.push(c);
|
|
@@ -110,6 +110,7 @@ function parseJsonc(text2) {
|
|
|
110
110
|
}
|
|
111
111
|
var init_jsonc = __esm({
|
|
112
112
|
"src/jsonc.mjs"() {
|
|
113
|
+
"use strict";
|
|
113
114
|
}
|
|
114
115
|
});
|
|
115
116
|
|
|
@@ -634,7 +635,10 @@ function findCommand(command, { cwd = process.cwd(), env = process.env } = {}) {
|
|
|
634
635
|
if (typeof command !== "string" || !command) return null;
|
|
635
636
|
cwd = path.resolve(cwd);
|
|
636
637
|
const windows = process.platform === "win32";
|
|
637
|
-
const value = (name) =>
|
|
638
|
+
const value = (name) => {
|
|
639
|
+
const key = windows ? Object.keys(env).find((k) => k.toUpperCase() === name) : name;
|
|
640
|
+
return key === void 0 ? void 0 : env[key];
|
|
641
|
+
};
|
|
638
642
|
const explicit = command.includes("/") || windows && command.includes("\\");
|
|
639
643
|
const dirs = explicit ? [cwd] : [...windows ? [cwd] : [], ...(value("PATH") ?? "").split(path.delimiter).filter(Boolean)];
|
|
640
644
|
for (const dir of dirs) {
|
|
@@ -654,7 +658,8 @@ function findCommand(command, { cwd = process.cwd(), env = process.env } = {}) {
|
|
|
654
658
|
return null;
|
|
655
659
|
}
|
|
656
660
|
function execCommandSync(command, args, options = {}) {
|
|
657
|
-
const
|
|
661
|
+
const encoding = "utf8";
|
|
662
|
+
const opts = { windowsHide: true, encoding, ...options };
|
|
658
663
|
const batch = batchInvocation(command, args, opts);
|
|
659
664
|
const result = batch ? nativeSpawnSync(batch.command, batch.args, batch.options) : import_cross_spawn.default.sync(command, args, opts);
|
|
660
665
|
if (result.error) throw result.error;
|
|
@@ -666,6 +671,7 @@ function execCommandSync(command, args, options = {}) {
|
|
|
666
671
|
var import_which, import_cross_spawn, import_escape;
|
|
667
672
|
var init_platform = __esm({
|
|
668
673
|
"src/platform.mjs"() {
|
|
674
|
+
"use strict";
|
|
669
675
|
import_which = __toESM(require_which(), 1);
|
|
670
676
|
import_cross_spawn = __toESM(require_cross_spawn(), 1);
|
|
671
677
|
import_escape = __toESM(require_escape(), 1);
|
|
@@ -691,7 +697,10 @@ function modelVersion(model) {
|
|
|
691
697
|
const at = words.findIndex(isDigitWord);
|
|
692
698
|
if (at === -1) return null;
|
|
693
699
|
const digits = [];
|
|
694
|
-
for (
|
|
700
|
+
for (const w of words.slice(at)) {
|
|
701
|
+
if (!isDigitWord(w)) break;
|
|
702
|
+
digits.push(Number(w.replace(/^v/, "")));
|
|
703
|
+
}
|
|
695
704
|
return digits;
|
|
696
705
|
}
|
|
697
706
|
function vendorOf(model, explicit = null) {
|
|
@@ -715,7 +724,9 @@ function byTier(cands, tier) {
|
|
|
715
724
|
if (!tier.family(cands)) return cands;
|
|
716
725
|
const rank = (c) => {
|
|
717
726
|
const words = modelWords(c.model);
|
|
718
|
-
|
|
727
|
+
const last = words.at(-1);
|
|
728
|
+
if (tier.last) return last === void 0 ? -1 : tier.words.indexOf(last);
|
|
729
|
+
return tier.words.findIndex((w) => words.includes(w));
|
|
719
730
|
};
|
|
720
731
|
const ranks = cands.map(rank);
|
|
721
732
|
if (ranks.includes(-1) || new Set(ranks).size === 1) return cands;
|
|
@@ -723,11 +734,18 @@ function byTier(cands, tier) {
|
|
|
723
734
|
return cands.filter((_, i) => ranks[i] === best);
|
|
724
735
|
}
|
|
725
736
|
function byVersion(cands) {
|
|
726
|
-
const
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
737
|
+
const [first] = cands;
|
|
738
|
+
if (!first) return cands;
|
|
739
|
+
const stem = modelStem(first.model).join("-");
|
|
740
|
+
if (cands.some((c) => modelStem(c.model).join("-") !== stem)) return cands;
|
|
741
|
+
const versioned = [];
|
|
742
|
+
for (const c of cands) {
|
|
743
|
+
const version2 = modelVersion(c.model);
|
|
744
|
+
if (version2 === null) return cands;
|
|
745
|
+
versioned.push({ c, version: version2 });
|
|
746
|
+
}
|
|
747
|
+
const best = versioned.map((x) => x.version).reduce((a, b) => compareVersions(a, b) >= 0 ? a : b);
|
|
748
|
+
return versioned.filter((x) => compareVersions(x.version, best) === 0).map((x) => x.c);
|
|
731
749
|
}
|
|
732
750
|
function rankCandidates(cands, ranking = []) {
|
|
733
751
|
let left = byRanking(cands, ranking);
|
|
@@ -736,7 +754,7 @@ function rankCandidates(cands, ranking = []) {
|
|
|
736
754
|
left = byTier(left, tier);
|
|
737
755
|
}
|
|
738
756
|
if (left.length > 1) left = byVersion(left);
|
|
739
|
-
return left.length === 1 ? left[0] : null;
|
|
757
|
+
return left.length === 1 ? left[0] ?? null : null;
|
|
740
758
|
}
|
|
741
759
|
function resolveConsultant(input2, consultants, ranking = []) {
|
|
742
760
|
const key = normalizeName(input2);
|
|
@@ -747,8 +765,9 @@ function resolveConsultant(input2, consultants, ranking = []) {
|
|
|
747
765
|
if (CLI_ALIASES[key]) cands = consultants.filter((c) => c.cli === CLI_ALIASES[key]);
|
|
748
766
|
else if (VENDOR_WORDS[key]) cands = consultants.filter((c) => c.vendor === VENDOR_WORDS[key]);
|
|
749
767
|
else cands = consultants.filter((c) => matchesWords(c.model, input2));
|
|
750
|
-
|
|
751
|
-
if (
|
|
768
|
+
const [only] = cands;
|
|
769
|
+
if (!only) return { none: true };
|
|
770
|
+
if (cands.length === 1) return { consultant: only };
|
|
752
771
|
const winner = rankCandidates(cands, ranking);
|
|
753
772
|
return winner ? { consultant: winner } : { ambiguous: cands };
|
|
754
773
|
}
|
|
@@ -773,7 +792,8 @@ function buildConsultants(config2) {
|
|
|
773
792
|
for (const key of Object.keys(cfg)) {
|
|
774
793
|
if (!TOP_KEYS.includes(key)) fault(`unknown top-level key "${key}" (allowed: ${TOP_KEYS.join(", ")})`);
|
|
775
794
|
}
|
|
776
|
-
|
|
795
|
+
const networkModes = ["on", "off"];
|
|
796
|
+
if (isObject(cfg.server) && cfg.server.network !== void 0 && !networkModes.includes(cfg.server.network)) {
|
|
777
797
|
fault('server.network must be "on" or "off"');
|
|
778
798
|
}
|
|
779
799
|
let ranking = [];
|
|
@@ -782,11 +802,12 @@ function buildConsultants(config2) {
|
|
|
782
802
|
ranking = cfg.ranking.map((r) => r.trim());
|
|
783
803
|
else fault("ranking must be an array of non-empty strings");
|
|
784
804
|
}
|
|
805
|
+
const cliIds = CLI_IDS;
|
|
785
806
|
if (cfg.clis !== void 0) {
|
|
786
807
|
if (!isObject(cfg.clis)) fault("clis must be an object keyed by CLI");
|
|
787
808
|
else {
|
|
788
809
|
for (const [id2, def] of Object.entries(cfg.clis)) {
|
|
789
|
-
if (!
|
|
810
|
+
if (!cliIds.includes(id2)) {
|
|
790
811
|
problems.push(`clis.${id2}: unknown CLI (known: ${CLI_IDS.join(", ")})`);
|
|
791
812
|
continue;
|
|
792
813
|
}
|
|
@@ -840,7 +861,7 @@ function buildConsultants(config2) {
|
|
|
840
861
|
}
|
|
841
862
|
for (const k of Object.keys(def))
|
|
842
863
|
if (!CONSULTANT_KEYS.includes(k)) problems.push(`${at}.${k}: unknown key (allowed: ${CONSULTANT_KEYS.join(", ")})`);
|
|
843
|
-
if (!
|
|
864
|
+
if (!cliIds.includes(def.cli)) problems.push(`${at}.cli: must be one of ${CLI_IDS.join(", ")}`);
|
|
844
865
|
if (typeof def.model !== "string" || def.model.trim() === "") problems.push(`${at}.model: required`);
|
|
845
866
|
for (const k of ["effort", "vendor", "note"]) {
|
|
846
867
|
if (def[k] !== void 0 && typeof def[k] !== "string") problems.push(`${at}.${k}: must be a string`);
|
|
@@ -858,15 +879,19 @@ function buildConsultants(config2) {
|
|
|
858
879
|
broken[name] = problems.slice(before);
|
|
859
880
|
continue;
|
|
860
881
|
}
|
|
861
|
-
const
|
|
862
|
-
|
|
882
|
+
const valid = (
|
|
883
|
+
/** @type {ConsultantDef} */
|
|
884
|
+
def
|
|
885
|
+
);
|
|
886
|
+
const pair = `${valid.cli}
|
|
887
|
+
${valid.model.trim()}`;
|
|
863
888
|
if (seen.has(pair)) {
|
|
864
889
|
problems.push(`${at}: same cli and model as consultants.${seen.get(pair)}; asking one model twice adds no independence`);
|
|
865
890
|
broken[name] = problems.slice(before);
|
|
866
891
|
continue;
|
|
867
892
|
}
|
|
868
893
|
seen.set(pair, name);
|
|
869
|
-
consultants.push(make(name,
|
|
894
|
+
consultants.push(make(name, valid));
|
|
870
895
|
}
|
|
871
896
|
}
|
|
872
897
|
return { consultants: Object.freeze(consultants), ranking: Object.freeze(ranking), problems, global: global2, broken, brokenClis };
|
|
@@ -880,7 +905,7 @@ function make(name, def) {
|
|
|
880
905
|
effort: typeof def.effort === "string" && def.effort.trim() !== "" ? def.effort.trim() : null,
|
|
881
906
|
timeoutMs: typeof def.timeout_ms === "number" ? clamp(def.timeout_ms, 1e3, 72e5) : null,
|
|
882
907
|
maxBudgetUsd: typeof def.max_budget_usd === "number" ? clamp(def.max_budget_usd, 0.05, 20) : null,
|
|
883
|
-
vendor: vendorOf(model, def.vendor
|
|
908
|
+
vendor: vendorOf(model, def.vendor),
|
|
884
909
|
enabled: def.enabled !== false,
|
|
885
910
|
note: typeof def.note === "string" && def.note.trim() !== "" ? def.note.trim() : null
|
|
886
911
|
});
|
|
@@ -888,6 +913,7 @@ function make(name, def) {
|
|
|
888
913
|
var CLI_IDS, DEFAULT_MODELS, CLI_VENDORS, table, CLI_ALIASES, VENDOR_WORDS, STEM_VENDORS, sameStem, TIERS, RESERVED_NAMES, isDigitWord, containsRun, compareVersions, TOP_KEYS, CLI_KEYS, CONSULTANT_KEYS, isObject, clamp;
|
|
889
914
|
var init_consultants = __esm({
|
|
890
915
|
"src/consultants.mjs"() {
|
|
916
|
+
"use strict";
|
|
891
917
|
CLI_IDS = Object.freeze(["codex", "claude-code", "antigravity", "opencode"]);
|
|
892
918
|
DEFAULT_MODELS = Object.freeze({
|
|
893
919
|
codex: "gpt-6-astra",
|
|
@@ -960,7 +986,7 @@ function isInstalled(command) {
|
|
|
960
986
|
}
|
|
961
987
|
function resolveCaller(raw) {
|
|
962
988
|
const key = normalizeName(raw);
|
|
963
|
-
return typeof key === "string"
|
|
989
|
+
return typeof key === "string" ? CALLER_ALIASES[key] ?? null : null;
|
|
964
990
|
}
|
|
965
991
|
function detectCaller(env = process.env) {
|
|
966
992
|
if (env.CLAUDECODE === "1" || env.CLAUDE_CODE_ENTRYPOINT) return "claude-code";
|
|
@@ -1020,6 +1046,11 @@ function isProtectedPath(abs) {
|
|
|
1020
1046
|
function consultantByName(name) {
|
|
1021
1047
|
return POLICY.consultants.find((c) => c.name === name);
|
|
1022
1048
|
}
|
|
1049
|
+
function consultantNamed(name) {
|
|
1050
|
+
const c = consultantByName(name);
|
|
1051
|
+
if (!c) throw new Error(`no consultant named "${name}"`);
|
|
1052
|
+
return c;
|
|
1053
|
+
}
|
|
1023
1054
|
function consultantAvailable(c) {
|
|
1024
1055
|
return c.enabled && POLICY.clis[c.cli].available;
|
|
1025
1056
|
}
|
|
@@ -1051,17 +1082,20 @@ function limitsSummary() {
|
|
|
1051
1082
|
max_concurrent_jobs: POLICY.maxConcurrent,
|
|
1052
1083
|
max_wait_ms: POLICY.maxWaitMs,
|
|
1053
1084
|
input_char_budget: POLICY.input.totalCharsMax,
|
|
1054
|
-
// Stated so it is true of all
|
|
1055
|
-
//
|
|
1056
|
-
//
|
|
1057
|
-
//
|
|
1058
|
-
//
|
|
1059
|
-
|
|
1085
|
+
// Stated so it is true of all four CLIs. Reading is allowed for every one
|
|
1086
|
+
// of them -- what keeps a consultation brief-only is that the child starts
|
|
1087
|
+
// in an empty working directory and is never told where the repository
|
|
1088
|
+
// is, not that it cannot read. Only Claude Code drops its whole-disk read
|
|
1089
|
+
// when files are exposed (claude-code.mjs). Codex is additionally
|
|
1090
|
+
// sandboxed read-only rather than execution-free, so do not advertise
|
|
1091
|
+
// "no execution".
|
|
1092
|
+
consultant_permissions: "web search/browse and reading local files allowed; the consultant starts in an empty working directory and is not told where your repository is. context.expose_paths copies the files or directories it names read-only into that working directory, but that is not a read boundary: apart from the protected credential paths, Codex, Antigravity and OpenCode can read any file your account can, and so can Claude Code when nothing is exposed; file edits, network access, MCP tools and further consultations denied; the Codex consultant additionally has a read-only shell"
|
|
1060
1093
|
};
|
|
1061
1094
|
}
|
|
1062
1095
|
var CONFIG_HOME, CONFIG_PATH, configError, CONFIG, BUILT, FROM, LOCAL, usable, cfgCli, cfgServer, num, limit, str, CALLER_ALIASES, CALLER_INPUTS, MODES, VERDICTS, STANCES, binFor, disabledNote, isEnabled, CODEX_BIN, CLAUDE_BIN, AGY_BIN, OPENCODE_BIN, extras, POLICY, PROTECTED_HOME_PATHS, PROTECTED_SEVERALLY_PATHS, PROTECTED_FILE_NAMES, escapeRe, PATHS_CASE_INSENSITIVE, artifactKinds;
|
|
1063
1096
|
var init_policy = __esm({
|
|
1064
1097
|
"src/policy.mjs"() {
|
|
1098
|
+
"use strict";
|
|
1065
1099
|
init_jsonc();
|
|
1066
1100
|
init_platform();
|
|
1067
1101
|
init_consultants();
|
|
@@ -1072,7 +1106,8 @@ var init_policy = __esm({
|
|
|
1072
1106
|
try {
|
|
1073
1107
|
return parseJsonc(fs2.readFileSync(CONFIG_PATH, "utf8"));
|
|
1074
1108
|
} catch (err) {
|
|
1075
|
-
|
|
1109
|
+
const code = err instanceof Error && "code" in err ? err.code : void 0;
|
|
1110
|
+
if (code !== "ENOENT") configError = `${CONFIG_PATH}: ${err instanceof Error ? err.message : String(err)}`;
|
|
1076
1111
|
return {};
|
|
1077
1112
|
}
|
|
1078
1113
|
})();
|
|
@@ -1083,17 +1118,22 @@ var init_policy = __esm({
|
|
|
1083
1118
|
}
|
|
1084
1119
|
FROM = configError ? buildConsultants({}) : BUILT;
|
|
1085
1120
|
LOCAL = configError ? [] : BUILT.problems.filter((p) => !BUILT.global.includes(p));
|
|
1086
|
-
usable = configError ? {} : CONFIG;
|
|
1121
|
+
usable = configError || !isObject(CONFIG) ? {} : CONFIG;
|
|
1087
1122
|
cfgCli = (id2) => {
|
|
1088
|
-
|
|
1123
|
+
const broken = Object.hasOwn(BUILT.brokenClis, id2) ? BUILT.brokenClis[id2] : void 0;
|
|
1124
|
+
if (broken && !configError) {
|
|
1089
1125
|
return {
|
|
1090
1126
|
enabled: false,
|
|
1091
|
-
note: `turned off: the config file has a mistake in clis.${id2}: ${
|
|
1127
|
+
note: `turned off: the config file has a mistake in clis.${id2}: ${broken.join("; ")}`
|
|
1092
1128
|
};
|
|
1093
1129
|
}
|
|
1094
|
-
|
|
1130
|
+
const def = isObject(usable.clis) ? usable.clis[id2] : void 0;
|
|
1131
|
+
return isObject(def) ? (
|
|
1132
|
+
/** @type {CliSection} */
|
|
1133
|
+
def
|
|
1134
|
+
) : {};
|
|
1095
1135
|
};
|
|
1096
|
-
cfgServer = usable.server
|
|
1136
|
+
cfgServer = isObject(usable.server) ? usable.server : {};
|
|
1097
1137
|
num = (name, dflt, min, max) => {
|
|
1098
1138
|
const raw = process.env[name];
|
|
1099
1139
|
if (raw === void 0 || raw === "") return dflt;
|
|
@@ -1131,7 +1171,8 @@ var init_policy = __esm({
|
|
|
1131
1171
|
CALLER_INPUTS = Object.freeze(Object.keys(CALLER_ALIASES));
|
|
1132
1172
|
MODES = ["explore", "review", "debate"];
|
|
1133
1173
|
VERDICTS = ["unverified", "confirmed", "not_applicable", "unverifiable"];
|
|
1134
|
-
STANCES =
|
|
1174
|
+
STANCES = /** @type {const} */
|
|
1175
|
+
["proceed", "do_not_proceed", "alternative", "undetermined"];
|
|
1135
1176
|
binFor = (id2, envName, dflt) => {
|
|
1136
1177
|
const fromEnv = process.env[envName];
|
|
1137
1178
|
if (fromEnv !== void 0 && fromEnv !== "") return expandHome(fromEnv);
|
|
@@ -1151,7 +1192,10 @@ var init_policy = __esm({
|
|
|
1151
1192
|
CLAUDE_BIN = binFor("claude-code", "SEVERALLY_CLAUDE_BIN", "claude");
|
|
1152
1193
|
AGY_BIN = binFor("antigravity", "SEVERALLY_AGY_BIN", "agy");
|
|
1153
1194
|
OPENCODE_BIN = binFor("opencode", "SEVERALLY_OPENCODE_BIN", "opencode");
|
|
1154
|
-
extras = (id2) => ({
|
|
1195
|
+
extras = (id2) => ({
|
|
1196
|
+
extraArgs: cfgCli(id2).args ?? [],
|
|
1197
|
+
extraEnv: cfgCli(id2).env ?? {}
|
|
1198
|
+
});
|
|
1155
1199
|
POLICY = Object.freeze({
|
|
1156
1200
|
home: str("SEVERALLY_HOME", path2.join(os.homedir(), ".severally")),
|
|
1157
1201
|
// Per CLI: how to launch it and whether it can run here. What runs on it is
|
|
@@ -32610,12 +32654,12 @@ var init_ajvProvider_CEoC_sr = __esm({
|
|
|
32610
32654
|
},
|
|
32611
32655
|
code(cxt) {
|
|
32612
32656
|
const { gen, data, schema, parentSchema, it } = cxt;
|
|
32613
|
-
const { oneOf } = parentSchema;
|
|
32657
|
+
const { oneOf: oneOf2 } = parentSchema;
|
|
32614
32658
|
if (!it.opts.discriminator) throw new Error("discriminator: requires discriminator option");
|
|
32615
32659
|
const tagName = schema.propertyName;
|
|
32616
32660
|
if (typeof tagName != "string") throw new Error("discriminator: requires propertyName");
|
|
32617
32661
|
if (schema.mapping) throw new Error("discriminator: mapping is not supported");
|
|
32618
|
-
if (!
|
|
32662
|
+
if (!oneOf2) throw new Error("discriminator: requires oneOf keyword");
|
|
32619
32663
|
const valid = gen.let("valid", false);
|
|
32620
32664
|
const tag = gen.const("tag", (0, codegen_1._)`${data}${(0, codegen_1.getProperty)(tagName)}`);
|
|
32621
32665
|
gen.if((0, codegen_1._)`typeof ${tag} == "string"`, () => validateMapping(), () => cxt.error(false, {
|
|
@@ -32653,8 +32697,8 @@ var init_ajvProvider_CEoC_sr = __esm({
|
|
|
32653
32697
|
const oneOfMapping = {};
|
|
32654
32698
|
const topRequired = hasRequired(parentSchema);
|
|
32655
32699
|
let tagRequired = true;
|
|
32656
|
-
for (let i = 0; i <
|
|
32657
|
-
let sch =
|
|
32700
|
+
for (let i = 0; i < oneOf2.length; i++) {
|
|
32701
|
+
let sch = oneOf2[i];
|
|
32658
32702
|
if ((sch === null || sch === void 0 ? void 0 : sch.$ref) && !(0, util_1.schemaHasRulesButRef)(sch, it.self.RULES)) {
|
|
32659
32703
|
const ref = sch.$ref;
|
|
32660
32704
|
sch = compile_1.resolveRef.call(it.self, it.schemaEnv.root, it.baseId, ref);
|
|
@@ -35561,11 +35605,11 @@ var init_mcp_DIH4cS6P = __esm({
|
|
|
35561
35605
|
* @param errorMessage - The error message.
|
|
35562
35606
|
* @returns The tool error result.
|
|
35563
35607
|
*/
|
|
35564
|
-
createToolError(
|
|
35608
|
+
createToolError(errorMessage3) {
|
|
35565
35609
|
return {
|
|
35566
35610
|
content: [{
|
|
35567
35611
|
type: "text",
|
|
35568
|
-
text:
|
|
35612
|
+
text: errorMessage3
|
|
35569
35613
|
}],
|
|
35570
35614
|
isError: true
|
|
35571
35615
|
};
|
|
@@ -36508,8 +36552,8 @@ var init_stdio = __esm({
|
|
|
36508
36552
|
import path3 from "node:path";
|
|
36509
36553
|
function charCount(req) {
|
|
36510
36554
|
let n = req.question.length + (req.objective?.length ?? 0);
|
|
36511
|
-
for (const c of req.constraints) n += c.length;
|
|
36512
|
-
for (const c of req.success_criteria) n += c.length;
|
|
36555
|
+
for (const c of req.constraints ?? []) n += c.length;
|
|
36556
|
+
for (const c of req.success_criteria ?? []) n += c.length;
|
|
36513
36557
|
for (const f of req.context.facts) n += f.length;
|
|
36514
36558
|
for (const c of req.context.counterpoints) n += c.length;
|
|
36515
36559
|
if (req.context.proposal) n += req.context.proposal.length;
|
|
@@ -36522,19 +36566,27 @@ function parseRequest(raw, { isFollowup = false, parentMode = null, groupFollowu
|
|
|
36522
36566
|
const issues = parsed.error.issues.map((i) => `${i.path.join(".") || "(root)"}: ${i.message}`);
|
|
36523
36567
|
throw new RequestError(`request failed validation: ${issues.join("; ")}`, "invalid_request", issues);
|
|
36524
36568
|
}
|
|
36525
|
-
const req =
|
|
36526
|
-
|
|
36527
|
-
|
|
36569
|
+
const req = (
|
|
36570
|
+
/** @type {DraftRequest} */
|
|
36571
|
+
parsed.data
|
|
36572
|
+
);
|
|
36573
|
+
const hoist = (k) => {
|
|
36574
|
+
const inner = req.context[k];
|
|
36575
|
+
if (inner === void 0) return;
|
|
36528
36576
|
if (req[k] !== void 0) {
|
|
36529
36577
|
throw new RequestError(`pass ${k} once: at the top of the request or inside context, not both`, "invalid_request");
|
|
36530
36578
|
}
|
|
36531
|
-
req[k] =
|
|
36579
|
+
req[k] = inner;
|
|
36532
36580
|
delete req.context[k];
|
|
36533
|
-
}
|
|
36581
|
+
};
|
|
36582
|
+
hoist("constraints");
|
|
36583
|
+
hoist("success_criteria");
|
|
36534
36584
|
req.network ??= "on";
|
|
36535
36585
|
req.success_criteria ??= [];
|
|
36536
36586
|
req.constraints ??= [];
|
|
36537
|
-
|
|
36587
|
+
req.context.facts ??= [];
|
|
36588
|
+
req.context.counterpoints ??= [];
|
|
36589
|
+
req.context.artifacts ??= [];
|
|
36538
36590
|
const hasSingle = req.target !== void 0 && req.target !== null;
|
|
36539
36591
|
const hasMany = Array.isArray(req.targets) && req.targets.length > 0;
|
|
36540
36592
|
if (hasSingle && hasMany) {
|
|
@@ -36605,7 +36657,8 @@ function parseRequest(raw, { isFollowup = false, parentMode = null, groupFollowu
|
|
|
36605
36657
|
);
|
|
36606
36658
|
}
|
|
36607
36659
|
req.targets = unique;
|
|
36608
|
-
req.target =
|
|
36660
|
+
req.target = /** @type {string} */
|
|
36661
|
+
unique[0];
|
|
36609
36662
|
req.fanout = unique.length > 1;
|
|
36610
36663
|
req.caller = resolveCaller(req.caller ?? "") ?? null;
|
|
36611
36664
|
req.caller_model = req.caller_model ?? null;
|
|
@@ -36656,6 +36709,7 @@ function parseRequest(raw, { isFollowup = false, parentMode = null, groupFollowu
|
|
|
36656
36709
|
var L, trimmed, artifactSchema, exposedPathSchema, gitRevision, contextSchema, targetSpec, INITIATORS, NETWORK_MODES, requestSchema, RequestError;
|
|
36657
36710
|
var init_schema = __esm({
|
|
36658
36711
|
"src/schema.mjs"() {
|
|
36712
|
+
"use strict";
|
|
36659
36713
|
init_zod();
|
|
36660
36714
|
init_policy();
|
|
36661
36715
|
init_consultants();
|
|
@@ -36740,6 +36794,11 @@ var init_schema = __esm({
|
|
|
36740
36794
|
followup_to: external_exports.string().trim().max(80).nullish()
|
|
36741
36795
|
}).strict();
|
|
36742
36796
|
RequestError = class extends Error {
|
|
36797
|
+
/**
|
|
36798
|
+
* @param {string} message
|
|
36799
|
+
* @param {string} [code]
|
|
36800
|
+
* @param {unknown[]} [details]
|
|
36801
|
+
*/
|
|
36743
36802
|
constructor(message, code = "invalid_request", details = void 0) {
|
|
36744
36803
|
super(message);
|
|
36745
36804
|
this.name = "RequestError";
|
|
@@ -36754,6 +36813,7 @@ var init_schema = __esm({
|
|
|
36754
36813
|
var s, enumOf, obj, arr, CONSULT_RESULT_SCHEMA, RESULT_KEYS;
|
|
36755
36814
|
var init_result_schema = __esm({
|
|
36756
36815
|
"src/result-schema.mjs"() {
|
|
36816
|
+
"use strict";
|
|
36757
36817
|
s = (desc) => ({ type: "string", description: desc });
|
|
36758
36818
|
enumOf = (values, desc) => ({ type: "string", enum: values, description: desc });
|
|
36759
36819
|
obj = (properties, description) => ({
|
|
@@ -36844,14 +36904,14 @@ var init_result_schema = __esm({
|
|
|
36844
36904
|
function looksLikeCode(name, value) {
|
|
36845
36905
|
if (CODE_WORDS.has(value.toLowerCase()) || value.startsWith("${") || /^\$[A-Z_][A-Z0-9_]*$/.test(value) || /^(?:process\.env\.|os\.environ\b)/.test(value))
|
|
36846
36906
|
return true;
|
|
36847
|
-
if (value.length >= 8 && /^PASSW(?:OR)?D$/i.test(name.split("_").at(-1))) return false;
|
|
36907
|
+
if (value.length >= 8 && /^PASSW(?:OR)?D$/i.test(name.split("_").at(-1) ?? "")) return false;
|
|
36848
36908
|
return /^[A-Za-z_][\w$]*(?:\.[A-Za-z_$][\w$]*)+$/.test(value) || /^[A-Za-z_][\w$.]*\(/.test(value) || value.startsWith("&");
|
|
36849
36909
|
}
|
|
36850
36910
|
function redact(value) {
|
|
36851
36911
|
if (value === null || value === void 0) return value;
|
|
36852
36912
|
if (typeof value === "string") {
|
|
36853
36913
|
let out = value;
|
|
36854
|
-
for (const [re, rep] of PATTERNS) out = out.replace(re, rep);
|
|
36914
|
+
for (const [re, rep] of PATTERNS) out = typeof rep === "string" ? out.replace(re, rep) : out.replace(re, rep);
|
|
36855
36915
|
return out;
|
|
36856
36916
|
}
|
|
36857
36917
|
if (Array.isArray(value)) return value.map(redact);
|
|
@@ -36865,6 +36925,7 @@ function redact(value) {
|
|
|
36865
36925
|
var CODE_WORDS, PATTERNS;
|
|
36866
36926
|
var init_redact = __esm({
|
|
36867
36927
|
"src/redact.mjs"() {
|
|
36928
|
+
"use strict";
|
|
36868
36929
|
CODE_WORDS = /* @__PURE__ */ new Set([
|
|
36869
36930
|
"true",
|
|
36870
36931
|
"false",
|
|
@@ -36960,7 +37021,7 @@ function statEntry(source) {
|
|
|
36960
37021
|
try {
|
|
36961
37022
|
stat = fs3.lstatSync(source);
|
|
36962
37023
|
} catch (err) {
|
|
36963
|
-
throw new ExposeError(`expose_paths entry "${source}" cannot be read: ${err
|
|
37024
|
+
throw new ExposeError(`expose_paths entry "${source}" cannot be read: ${messageOf(err)}`, "not_found");
|
|
36964
37025
|
}
|
|
36965
37026
|
if (stat.isSymbolicLink()) {
|
|
36966
37027
|
throw new ExposeError(`expose_paths entry "${source}" is a symlink; name the path it points at instead`, "invalid_type");
|
|
@@ -36975,7 +37036,7 @@ function walkDir(root, dir, onFile, totals, skipped) {
|
|
|
36975
37036
|
try {
|
|
36976
37037
|
items = fs3.readdirSync(dir, { withFileTypes: true }).sort((a, b) => a.name < b.name ? -1 : a.name > b.name ? 1 : 0);
|
|
36977
37038
|
} catch (err) {
|
|
36978
|
-
throw new ExposeError(`expose_paths entry "${dir}" cannot be read: ${err
|
|
37039
|
+
throw new ExposeError(`expose_paths entry "${dir}" cannot be read: ${messageOf(err)}`, "not_found");
|
|
36979
37040
|
}
|
|
36980
37041
|
for (const item of items) {
|
|
36981
37042
|
const child = path4.join(dir, item.name);
|
|
@@ -37002,7 +37063,11 @@ function walkDir(root, dir, onFile, totals, skipped) {
|
|
|
37002
37063
|
function byPathParts(a, b) {
|
|
37003
37064
|
const x = a.split("/");
|
|
37004
37065
|
const y = b.split("/");
|
|
37005
|
-
for (
|
|
37066
|
+
for (const [i, part] of x.entries()) {
|
|
37067
|
+
const other = y[i];
|
|
37068
|
+
if (other === void 0) break;
|
|
37069
|
+
if (part !== other) return part < other ? -1 : 1;
|
|
37070
|
+
}
|
|
37006
37071
|
return x.length - y.length;
|
|
37007
37072
|
}
|
|
37008
37073
|
function walkGit(dir, exposedAs, onFile, totals, skipped) {
|
|
@@ -37213,7 +37278,7 @@ function reexposeChange(workdir, entry, manifest) {
|
|
|
37213
37278
|
const changes = [];
|
|
37214
37279
|
for (const c of entry.changes ?? []) {
|
|
37215
37280
|
const m = /^(.*) \(([^()]+)\)$/.exec(c);
|
|
37216
|
-
const p = m
|
|
37281
|
+
const p = m?.[1] ?? c;
|
|
37217
37282
|
if (p.includes("[REDACTED")) {
|
|
37218
37283
|
manifest.skipped.push({ source: path4.join(entry.source, p), reason: "masked", exposed_as: `workspace/repo/${p}` });
|
|
37219
37284
|
continue;
|
|
@@ -37268,10 +37333,7 @@ function git(cwd, args) {
|
|
|
37268
37333
|
env: { ...env, GIT_OPTIONAL_LOCKS: "0" }
|
|
37269
37334
|
});
|
|
37270
37335
|
if (r.error || r.status !== 0) {
|
|
37271
|
-
const why = (
|
|
37272
|
-
/** @type {NodeJS.ErrnoException} */
|
|
37273
|
-
r.error?.code === "ENOENT" ? "git is not on PATH" : String(r.stderr ?? r.error?.message ?? "").trim()
|
|
37274
|
-
);
|
|
37336
|
+
const why = r.error && "code" in r.error && r.error.code === "ENOENT" ? "git is not on PATH" : String(r.stderr ?? r.error?.message ?? "").trim();
|
|
37275
37337
|
throw new ExposeError(`context.expose_diff: git ${args[0]} failed in "${cwd}": ${why}`, "not_found");
|
|
37276
37338
|
}
|
|
37277
37339
|
return r.stdout;
|
|
@@ -37283,7 +37345,11 @@ function exposeDiff(workdir, { repo, base = "HEAD" }, manifest, maxChars) {
|
|
|
37283
37345
|
git(top, ["diff", "--name-status", "-z", "--no-renames", "--no-relative", "--no-ext-diff", "--no-textconv", base, "--"])
|
|
37284
37346
|
);
|
|
37285
37347
|
const changes = [];
|
|
37286
|
-
for (let i = 0; i + 1 < tokens.length; i += 2)
|
|
37348
|
+
for (let i = 0; i + 1 < tokens.length; i += 2) {
|
|
37349
|
+
const status2 = tokens[i];
|
|
37350
|
+
const file2 = tokens[i + 1];
|
|
37351
|
+
if (status2 !== void 0 && file2 !== void 0) changes.push({ status: status2, path: file2 });
|
|
37352
|
+
}
|
|
37287
37353
|
for (const p of nulSplit(git(top, ["ls-files", "--others", "--exclude-standard", "-z"])))
|
|
37288
37354
|
changes.push({ status: "untracked", path: p });
|
|
37289
37355
|
const before = { ...manifest.totals };
|
|
@@ -37336,18 +37402,24 @@ function exposeDiff(workdir, { repo, base = "HEAD" }, manifest, maxChars) {
|
|
|
37336
37402
|
}
|
|
37337
37403
|
return text2;
|
|
37338
37404
|
}
|
|
37339
|
-
var ExposeError, nulSplit;
|
|
37405
|
+
var ExposeError, messageOf, nulSplit;
|
|
37340
37406
|
var init_expose_paths = __esm({
|
|
37341
37407
|
"src/expose-paths.mjs"() {
|
|
37408
|
+
"use strict";
|
|
37342
37409
|
init_policy();
|
|
37343
37410
|
init_redact();
|
|
37344
37411
|
ExposeError = class extends Error {
|
|
37412
|
+
/**
|
|
37413
|
+
* @param {string} message
|
|
37414
|
+
* @param {string} code
|
|
37415
|
+
*/
|
|
37345
37416
|
constructor(message, code) {
|
|
37346
37417
|
super(message);
|
|
37347
37418
|
this.name = "ExposeError";
|
|
37348
37419
|
this.code = code;
|
|
37349
37420
|
}
|
|
37350
37421
|
};
|
|
37422
|
+
messageOf = (err) => err instanceof Error ? err.message : String(err);
|
|
37351
37423
|
nulSplit = (buf) => buf.toString("utf8").split("\0").filter(Boolean);
|
|
37352
37424
|
}
|
|
37353
37425
|
});
|
|
@@ -37458,7 +37530,7 @@ function renderExposeManifest(manifest) {
|
|
|
37458
37530
|
if (skipped.length) skipped.unshift("");
|
|
37459
37531
|
return [
|
|
37460
37532
|
"The lead exposed these paths; they are copied under ./workspace in your working directory and you may read",
|
|
37461
|
-
"them directly. They are the only part of the lead's repository you
|
|
37533
|
+
"them directly. They are the only part of the lead's repository the lead gave you -- if you need something that is",
|
|
37462
37534
|
"not here, record it under `unknowns` rather than guessing. Text files were passed through the same",
|
|
37463
37535
|
"credential masking as this brief; binary files were copied unchanged.",
|
|
37464
37536
|
"",
|
|
@@ -37518,8 +37590,9 @@ function renderBrief(req, chain = { round: 1, priorRounds: [] }, exposeManifest
|
|
|
37518
37590
|
const blocks = chain.priorRounds.map(renderRound);
|
|
37519
37591
|
let kept = 0;
|
|
37520
37592
|
let used = 0;
|
|
37521
|
-
for (
|
|
37522
|
-
used
|
|
37593
|
+
for (const block of [...blocks].reverse()) {
|
|
37594
|
+
if (kept > 0 && used + block.length > HISTORY_CHARS) break;
|
|
37595
|
+
used += block.length;
|
|
37523
37596
|
kept += 1;
|
|
37524
37597
|
}
|
|
37525
37598
|
const left = chain.priorRounds.slice(0, blocks.length - kept).map((p) => p.round);
|
|
@@ -37543,6 +37616,7 @@ function renderBrief(req, chain = { round: 1, priorRounds: [] }, exposeManifest
|
|
|
37543
37616
|
var MODE_BRIEFS, GUARDRAILS_HEAD, GUARDRAILS_RULES, WEB_ON, WEB_OFF, WORKDIR_EMPTY, WORKDIR_EXPOSED, GUARDRAILS_TAIL, EXPOSE_LISTING_MAX, clip, HISTORY_CHARS;
|
|
37544
37617
|
var init_brief = __esm({
|
|
37545
37618
|
"src/brief.mjs"() {
|
|
37619
|
+
"use strict";
|
|
37546
37620
|
init_policy();
|
|
37547
37621
|
init_result_schema();
|
|
37548
37622
|
init_redact();
|
|
@@ -37581,7 +37655,7 @@ var init_brief = __esm({
|
|
|
37581
37655
|
WEB_ON = "- You may search and browse the web freely; cite what you actually opened in `references`.";
|
|
37582
37656
|
WEB_OFF = "- Web search and browsing are switched off for this consultation. Answer from the brief, and record what you would have looked up under `unknowns`.";
|
|
37583
37657
|
WORKDIR_EMPTY = "- You are running in an empty working directory and do not have the lead's repository; everything you are meant to have is in the brief below. If something is missing, record it under `unknowns` instead of guessing or substituting an assumption.";
|
|
37584
|
-
WORKDIR_EXPOSED = '- Your working directory is not empty: the lead exposed specific paths, listed under "Files available to you" below and copied read-only under ./workspace. Those are the only part of the lead\'s repository you
|
|
37658
|
+
WORKDIR_EXPOSED = '- Your working directory is not empty: the lead exposed specific paths, listed under "Files available to you" below and copied read-only under ./workspace. Those are the only part of the lead\'s repository the lead gave you. If something you need is missing, record it under `unknowns` instead of guessing or substituting an assumption.';
|
|
37585
37659
|
GUARDRAILS_TAIL = [
|
|
37586
37660
|
"- Never include credentials, API keys, tokens, or environment variable values in your answer.",
|
|
37587
37661
|
"- Do not output your internal reasoning trace. Report conclusions with their grounds and evidence.",
|
|
@@ -37611,15 +37685,15 @@ function clampText(v, max) {
|
|
|
37611
37685
|
}
|
|
37612
37686
|
function level(v, fallback = null) {
|
|
37613
37687
|
const t = typeof v === "string" ? v.trim().toLowerCase() : "";
|
|
37614
|
-
return LEVELS
|
|
37688
|
+
return oneOf(LEVELS, t) ? t : fallback;
|
|
37615
37689
|
}
|
|
37616
37690
|
function basis(v) {
|
|
37617
37691
|
const t = typeof v === "string" ? v.trim().toLowerCase() : "";
|
|
37618
|
-
return BASIS
|
|
37692
|
+
return oneOf(BASIS, t) ? t : null;
|
|
37619
37693
|
}
|
|
37620
37694
|
function stance(v) {
|
|
37621
37695
|
const t = typeof v === "string" ? v.trim().toLowerCase() : "";
|
|
37622
|
-
return STANCES
|
|
37696
|
+
return oneOf(STANCES, t) ? t : null;
|
|
37623
37697
|
}
|
|
37624
37698
|
function extractJson(text2) {
|
|
37625
37699
|
if (typeof text2 !== "string") return null;
|
|
@@ -37627,9 +37701,7 @@ function extractJson(text2) {
|
|
|
37627
37701
|
if (!trimmed2) return null;
|
|
37628
37702
|
const candidates = [];
|
|
37629
37703
|
if (trimmed2.startsWith("{")) candidates.push(trimmed2);
|
|
37630
|
-
const
|
|
37631
|
-
let m;
|
|
37632
|
-
while ((m = fence2.exec(trimmed2)) !== null) candidates.push(m[1].trim());
|
|
37704
|
+
for (const [, body = ""] of trimmed2.matchAll(/```(?:json)?\s*([\s\S]*?)```/gi)) candidates.push(body.trim());
|
|
37633
37705
|
const start = trimmed2.indexOf("{");
|
|
37634
37706
|
if (start !== -1) {
|
|
37635
37707
|
let depth = 0;
|
|
@@ -37679,7 +37751,10 @@ function mapList(raw, fields, { requiredField, idPrefix = null }) {
|
|
|
37679
37751
|
item[key] = kind === "level" ? level(entry[key]) : clampText(entry[key], O.itemTextMax);
|
|
37680
37752
|
}
|
|
37681
37753
|
if (!item[requiredField]) continue;
|
|
37682
|
-
out.push(
|
|
37754
|
+
out.push(
|
|
37755
|
+
/** @type {T} */
|
|
37756
|
+
item
|
|
37757
|
+
);
|
|
37683
37758
|
}
|
|
37684
37759
|
return out;
|
|
37685
37760
|
}
|
|
@@ -37758,10 +37833,14 @@ function normalizeResult(raw) {
|
|
|
37758
37833
|
).filter((r) => r.title || r.url)
|
|
37759
37834
|
};
|
|
37760
37835
|
const listTruncated = Object.fromEntries(
|
|
37761
|
-
["findings", "alternatives", "unknowns", "decision_changers", "next_checks", "remaining_disagreements"].
|
|
37836
|
+
["findings", "alternatives", "unknowns", "decision_changers", "next_checks", "remaining_disagreements"].flatMap((k) => {
|
|
37837
|
+
const v = src[k];
|
|
37838
|
+
return Array.isArray(v) && v.length > O.listMax ? [[k, v.length]] : [];
|
|
37839
|
+
})
|
|
37762
37840
|
);
|
|
37841
|
+
const fields = result;
|
|
37763
37842
|
const missing = RESULT_KEYS.filter((k) => {
|
|
37764
|
-
const v =
|
|
37843
|
+
const v = fields[k];
|
|
37765
37844
|
return v === null || v === void 0 || Array.isArray(v) && v.length === 0;
|
|
37766
37845
|
});
|
|
37767
37846
|
const groundless = result.findings.filter((f) => !f.grounds).length;
|
|
@@ -37775,13 +37854,18 @@ function normalizeResult(raw) {
|
|
|
37775
37854
|
}
|
|
37776
37855
|
};
|
|
37777
37856
|
}
|
|
37778
|
-
var OutputError, O, LEVELS, BASIS;
|
|
37857
|
+
var OutputError, O, oneOf, LEVELS, BASIS;
|
|
37779
37858
|
var init_parse_result = __esm({
|
|
37780
37859
|
"src/parse-result.mjs"() {
|
|
37860
|
+
"use strict";
|
|
37781
37861
|
init_policy();
|
|
37782
37862
|
init_result_schema();
|
|
37783
37863
|
init_redact();
|
|
37784
37864
|
OutputError = class extends Error {
|
|
37865
|
+
/**
|
|
37866
|
+
* @param {string} message
|
|
37867
|
+
* @param {string} [detail]
|
|
37868
|
+
*/
|
|
37785
37869
|
constructor(message, detail) {
|
|
37786
37870
|
super(message);
|
|
37787
37871
|
this.name = "OutputError";
|
|
@@ -37789,6 +37873,7 @@ var init_parse_result = __esm({
|
|
|
37789
37873
|
}
|
|
37790
37874
|
};
|
|
37791
37875
|
O = POLICY.output;
|
|
37876
|
+
oneOf = (list, t) => list.some((x) => x === t);
|
|
37792
37877
|
LEVELS = ["high", "medium", "low"];
|
|
37793
37878
|
BASIS = ["sufficient", "thin", "insufficient"];
|
|
37794
37879
|
}
|
|
@@ -37806,6 +37891,7 @@ function isRetriable(kind) {
|
|
|
37806
37891
|
var RULES, AUTO_RETRY_KINDS;
|
|
37807
37892
|
var init_failures = __esm({
|
|
37808
37893
|
"src/failures.mjs"() {
|
|
37894
|
+
"use strict";
|
|
37809
37895
|
RULES = [
|
|
37810
37896
|
[
|
|
37811
37897
|
/\b(usage limit|quota (exceeded|reached|exhausted)|out of credits|reached your .* limit|insufficient_quota|credit balance)\b/i,
|
|
@@ -37846,8 +37932,8 @@ function childEnv(target, extra = {}, operator = {}) {
|
|
|
37846
37932
|
return { ...env, ...extra };
|
|
37847
37933
|
}
|
|
37848
37934
|
function providerEnvNames(target) {
|
|
37849
|
-
const provider = JSON.stringify(POLICY.clis
|
|
37850
|
-
return new Set([...provider.matchAll(/\{env:([A-Za-z_][A-Za-z0-9_]*)\}/g)].
|
|
37935
|
+
const provider = JSON.stringify(target === "opencode" ? POLICY.clis.opencode.provider : {});
|
|
37936
|
+
return new Set([...provider.matchAll(/\{env:([A-Za-z_][A-Za-z0-9_]*)\}/g)].flatMap(([, name]) => name ?? []));
|
|
37851
37937
|
}
|
|
37852
37938
|
function foreignSecret(target, key, keep) {
|
|
37853
37939
|
if (keep.has(key) || (OWN_PREFIX[target] ?? []).some((p) => key.startsWith(p))) return false;
|
|
@@ -37905,10 +37991,10 @@ function runChild({
|
|
|
37905
37991
|
let timedOut = false;
|
|
37906
37992
|
let cancelled = false;
|
|
37907
37993
|
let stalled = false;
|
|
37908
|
-
let settleTimer
|
|
37994
|
+
let settleTimer;
|
|
37909
37995
|
let settleAt = Infinity;
|
|
37910
37996
|
const watch = (line) => {
|
|
37911
|
-
const ms = settleAfter2(line);
|
|
37997
|
+
const ms = settleAfter2?.(line);
|
|
37912
37998
|
if (ms == null || Date.now() + ms >= settleAt) return;
|
|
37913
37999
|
settleAt = Date.now() + ms;
|
|
37914
38000
|
clearTimeout(settleTimer);
|
|
@@ -37940,7 +38026,7 @@ function runChild({
|
|
|
37940
38026
|
const compact = compactLine2 ?? ((l) => l);
|
|
37941
38027
|
const flushLines = () => {
|
|
37942
38028
|
const lines = pending.split("\n");
|
|
37943
|
-
pending = lines.pop();
|
|
38029
|
+
pending = lines.pop() ?? "";
|
|
37944
38030
|
for (const l of lines) {
|
|
37945
38031
|
if (settleAfter2) watch(l);
|
|
37946
38032
|
append("out", `${compact(l)}
|
|
@@ -37972,7 +38058,7 @@ function runChild({
|
|
|
37972
38058
|
const done = new Promise((resolve) => {
|
|
37973
38059
|
let settled2 = false;
|
|
37974
38060
|
let pipesForced = false;
|
|
37975
|
-
let orphanTimer
|
|
38061
|
+
let orphanTimer;
|
|
37976
38062
|
const finish = (payload) => {
|
|
37977
38063
|
if (settled2) return;
|
|
37978
38064
|
settled2 = true;
|
|
@@ -38019,6 +38105,7 @@ function runChild({
|
|
|
38019
38105
|
var DROP_EXACT, DROP_PREFIX, TARGET_DROP_PREFIX, TARGET_DROP_EXACT, SECRET_PREFIX, SECRET_SUFFIX, SECRET_EXACT, OWN_PREFIX, ProcHandle;
|
|
38020
38106
|
var init_run = __esm({
|
|
38021
38107
|
"src/run.mjs"() {
|
|
38108
|
+
"use strict";
|
|
38022
38109
|
init_platform();
|
|
38023
38110
|
init_policy();
|
|
38024
38111
|
DROP_EXACT = /* @__PURE__ */ new Set(["CLAUDECODE", "CLAUDE_PID", "CLAUDE_EFFORT", "AI_AGENT", "ANTHROPIC_LOG", "NODE_OPTIONS"]);
|
|
@@ -38048,6 +38135,10 @@ var init_run = __esm({
|
|
|
38048
38135
|
opencode: ["ZAI_"]
|
|
38049
38136
|
};
|
|
38050
38137
|
ProcHandle = class {
|
|
38138
|
+
/**
|
|
38139
|
+
* @param {import('node:child_process').ChildProcess} child
|
|
38140
|
+
* @param {() => string} [readStdout]
|
|
38141
|
+
*/
|
|
38051
38142
|
constructor(child, readStdout = () => "") {
|
|
38052
38143
|
this.child = child;
|
|
38053
38144
|
this.killed = false;
|
|
@@ -38057,6 +38148,7 @@ var init_run = __esm({
|
|
|
38057
38148
|
* Kill the child and every descendant it spawned -- also after the child
|
|
38058
38149
|
* itself has exited: a descendant still holding its pipes keeps the
|
|
38059
38150
|
* consultation from closing, and on POSIX it is still in the group.
|
|
38151
|
+
* @param {NodeJS.Signals} [signal]
|
|
38060
38152
|
*/
|
|
38061
38153
|
stop(signal = "SIGTERM") {
|
|
38062
38154
|
if (this.killed || !this.child) return;
|
|
@@ -38279,7 +38371,7 @@ function withSeatLock(fn) {
|
|
|
38279
38371
|
fs4.mkdirSync(lock);
|
|
38280
38372
|
break;
|
|
38281
38373
|
} catch (err) {
|
|
38282
|
-
if (err.code
|
|
38374
|
+
if (!(err instanceof Error && "code" in err && err.code === "EEXIST")) throw err;
|
|
38283
38375
|
try {
|
|
38284
38376
|
if (Date.now() - fs4.statSync(lock).mtimeMs > LOCK_STALE_MS) fs4.rmdirSync(lock);
|
|
38285
38377
|
} catch {
|
|
@@ -38322,7 +38414,7 @@ function takeSeats(seats, cap, held) {
|
|
|
38322
38414
|
} catch {
|
|
38323
38415
|
}
|
|
38324
38416
|
}
|
|
38325
|
-
for (const mine of seats.filter(
|
|
38417
|
+
for (const mine of seats.filter(continuesChain)) {
|
|
38326
38418
|
let other = live.find((x) => x.chain_id === mine.chain_id);
|
|
38327
38419
|
if (!other && fs4.existsSync(roundPath(mine.chain_id, mine.round))) {
|
|
38328
38420
|
try {
|
|
@@ -38389,9 +38481,10 @@ function cleanupJobDir(jobId) {
|
|
|
38389
38481
|
}
|
|
38390
38482
|
}
|
|
38391
38483
|
}
|
|
38392
|
-
var roundPath, JOB_ID, GROUP_ID, runningDir, runningFile, seatsDir, seatFile, LOCK_WAIT_MS, LOCK_STALE_MS, cancelFile, cancelRequested;
|
|
38484
|
+
var roundPath, JOB_ID, GROUP_ID, runningDir, runningFile, seatsDir, seatFile, LOCK_WAIT_MS, LOCK_STALE_MS, continuesChain, cancelFile, cancelRequested;
|
|
38393
38485
|
var init_store = __esm({
|
|
38394
38486
|
"src/store.mjs"() {
|
|
38487
|
+
"use strict";
|
|
38395
38488
|
init_policy();
|
|
38396
38489
|
roundPath = (chainId, round) => path6.join(POLICY.home, "history", chainId, `round-${String(round).padStart(2, "0")}.json`);
|
|
38397
38490
|
JOB_ID = /^job_[0-9a-f]{12}$/;
|
|
@@ -38402,6 +38495,7 @@ var init_store = __esm({
|
|
|
38402
38495
|
seatFile = (jobId) => path6.join(seatsDir(), `${jobId}.json`);
|
|
38403
38496
|
LOCK_WAIT_MS = 2e3;
|
|
38404
38497
|
LOCK_STALE_MS = 1e4;
|
|
38498
|
+
continuesChain = (seat) => Boolean(seat.chain_id);
|
|
38405
38499
|
cancelFile = (jobId) => path6.join(seatsDir(), `${jobId}.cancel`);
|
|
38406
38500
|
cancelRequested = (jobId) => JOB_ID.test(jobId) && fs4.existsSync(cancelFile(jobId));
|
|
38407
38501
|
}
|
|
@@ -38503,8 +38597,8 @@ function interpret({ stdout, stderr, code, lastMessageText }) {
|
|
|
38503
38597
|
for (const e of events3) walkUsage(e, usage);
|
|
38504
38598
|
let text2 = (lastMessageText || "").trim();
|
|
38505
38599
|
if (!text2) {
|
|
38506
|
-
for (
|
|
38507
|
-
|
|
38600
|
+
for (const e of events3.toReversed()) {
|
|
38601
|
+
if (text2) break;
|
|
38508
38602
|
const item = e.item ?? e;
|
|
38509
38603
|
const candidate = item && item.type && String(item.type).includes("agent_message") && (item.text ?? item.message) || e.type === "item.completed" && item && (item.text ?? item.message) || null;
|
|
38510
38604
|
if (typeof candidate === "string" && candidate.trim()) text2 = candidate.trim();
|
|
@@ -38570,9 +38664,15 @@ function toolCalls({ stdout }) {
|
|
|
38570
38664
|
return [...seen.values()].reduce((o, n) => ({ ...o, [n]: (o[n] ?? 0) + 1 }), {});
|
|
38571
38665
|
}
|
|
38572
38666
|
function usageRecord(raw) {
|
|
38573
|
-
const u =
|
|
38667
|
+
const u = (
|
|
38668
|
+
/** @type {Record<string, number>} */
|
|
38669
|
+
raw ?? {}
|
|
38670
|
+
);
|
|
38574
38671
|
const pick2 = (...names) => {
|
|
38575
|
-
for (const n of names)
|
|
38672
|
+
for (const n of names) {
|
|
38673
|
+
const v = u[n];
|
|
38674
|
+
if (typeof v === "number") return v;
|
|
38675
|
+
}
|
|
38576
38676
|
return null;
|
|
38577
38677
|
};
|
|
38578
38678
|
return {
|
|
@@ -38599,6 +38699,7 @@ function boundary({ args }) {
|
|
|
38599
38699
|
var FORBIDDEN_FLAGS, RESERVED_FLAGS, PROFILE, listDir;
|
|
38600
38700
|
var init_codex = __esm({
|
|
38601
38701
|
"src/adapters/codex.mjs"() {
|
|
38702
|
+
"use strict";
|
|
38602
38703
|
init_policy();
|
|
38603
38704
|
init_failures();
|
|
38604
38705
|
FORBIDDEN_FLAGS = [
|
|
@@ -38697,9 +38798,9 @@ function buildInvocation2({ workdir, guardrails, consultant, hasExposedPaths = f
|
|
|
38697
38798
|
return { command: t.cli, args, model: chosen, workdir };
|
|
38698
38799
|
}
|
|
38699
38800
|
function lastJsonObject(stdout) {
|
|
38700
|
-
const lines = (stdout || "").split("\n");
|
|
38701
|
-
for (
|
|
38702
|
-
const t =
|
|
38801
|
+
const lines = (stdout || "").split("\n").reverse();
|
|
38802
|
+
for (const line of lines) {
|
|
38803
|
+
const t = line.trim();
|
|
38703
38804
|
if (!t.startsWith("{")) continue;
|
|
38704
38805
|
try {
|
|
38705
38806
|
const parsed = JSON.parse(t);
|
|
@@ -38738,7 +38839,11 @@ function interpret2({ stdout, stderr, code }) {
|
|
|
38738
38839
|
}
|
|
38739
38840
|
return { ok: true, text: text2, payload, usageRaw: payload, denials: payload.permission_denials ?? [] };
|
|
38740
38841
|
}
|
|
38741
|
-
function usageRecord2(
|
|
38842
|
+
function usageRecord2(raw) {
|
|
38843
|
+
const payload = (
|
|
38844
|
+
/** @type {ClaudeResult | null} */
|
|
38845
|
+
raw
|
|
38846
|
+
);
|
|
38742
38847
|
if (!payload) return null;
|
|
38743
38848
|
const u = payload.usage ?? {};
|
|
38744
38849
|
const num2 = (v) => typeof v === "number" ? v : null;
|
|
@@ -38753,7 +38858,10 @@ function usageRecord2(payload) {
|
|
|
38753
38858
|
};
|
|
38754
38859
|
}
|
|
38755
38860
|
function boundary2({ args, hasExposedPaths }) {
|
|
38756
|
-
const tools =
|
|
38861
|
+
const tools = (
|
|
38862
|
+
/** @type {string} */
|
|
38863
|
+
args[args.indexOf("--tools") + 1]
|
|
38864
|
+
);
|
|
38757
38865
|
return {
|
|
38758
38866
|
reads: hasExposedPaths ? "the exposed copy only" : "whole disk",
|
|
38759
38867
|
shell: "none",
|
|
@@ -38767,6 +38875,7 @@ function boundary2({ args, hasExposedPaths }) {
|
|
|
38767
38875
|
var FORBIDDEN_FLAGS2, RESERVED_FLAGS2, posixAbsolute;
|
|
38768
38876
|
var init_claude_code = __esm({
|
|
38769
38877
|
"src/adapters/claude-code.mjs"() {
|
|
38878
|
+
"use strict";
|
|
38770
38879
|
init_policy();
|
|
38771
38880
|
init_failures();
|
|
38772
38881
|
init_result_schema();
|
|
@@ -38987,13 +39096,14 @@ function prepareSandbox({ workdir, network = "on" }) {
|
|
|
38987
39096
|
var SANDBOX_ALLOW, SANDBOX_DENY, ALLOWED_TOOLS, WEB_TOOLS, PATH_TOOLS, TOKEN_REL;
|
|
38988
39097
|
var init_antigravity_sandbox = __esm({
|
|
38989
39098
|
"src/adapters/antigravity-sandbox.mjs"() {
|
|
39099
|
+
"use strict";
|
|
38990
39100
|
init_policy();
|
|
38991
39101
|
init_store();
|
|
38992
39102
|
SANDBOX_ALLOW = Object.freeze(["read_url(*)", "read_file(*)"]);
|
|
38993
39103
|
SANDBOX_DENY = Object.freeze(["write_file(*)", "command(*)", "mcp(*)", "execute_url(*)", "unsandboxed(*)"]);
|
|
38994
|
-
ALLOWED_TOOLS = Object.freeze(["view_file", "search_web", "read_url_content", "finish"]);
|
|
39104
|
+
ALLOWED_TOOLS = Object.freeze(["view_file", "read_file", "search_web", "read_url_content", "finish"]);
|
|
38995
39105
|
WEB_TOOLS = /* @__PURE__ */ new Set(["search_web", "read_url_content"]);
|
|
38996
|
-
PATH_TOOLS = ["view_file", "read_url_content"];
|
|
39106
|
+
PATH_TOOLS = ["view_file", "read_file", "read_url_content"];
|
|
38997
39107
|
TOKEN_REL = path8.join(".gemini", "antigravity-cli", "antigravity-oauth-token");
|
|
38998
39108
|
}
|
|
38999
39109
|
});
|
|
@@ -39001,6 +39111,8 @@ var init_antigravity_sandbox = __esm({
|
|
|
39001
39111
|
// src/adapters/antigravity.mjs
|
|
39002
39112
|
var antigravity_exports = {};
|
|
39003
39113
|
__export(antigravity_exports, {
|
|
39114
|
+
ALLOWED_TOOLS: () => ALLOWED_TOOLS,
|
|
39115
|
+
FILE_READ_TOOLS: () => FILE_READ_TOOLS,
|
|
39004
39116
|
FORBIDDEN_FLAGS: () => FORBIDDEN_FLAGS3,
|
|
39005
39117
|
RESERVED_FLAGS: () => RESERVED_FLAGS3,
|
|
39006
39118
|
boundary: () => boundary3,
|
|
@@ -39047,19 +39159,17 @@ function events(stdout) {
|
|
|
39047
39159
|
return out;
|
|
39048
39160
|
}
|
|
39049
39161
|
function finalEnvelope(stdout) {
|
|
39050
|
-
const
|
|
39051
|
-
for (let i = all.length - 1; i >= 0; i--) {
|
|
39052
|
-
const e = all[i];
|
|
39162
|
+
for (const e of events(stdout).reverse()) {
|
|
39053
39163
|
if (e.event === "result" && e.result && typeof e.result === "object") return e.result;
|
|
39054
39164
|
if (e.event === void 0 && (e.status || e.response || e.structured_output)) return e;
|
|
39055
39165
|
}
|
|
39056
39166
|
return null;
|
|
39057
39167
|
}
|
|
39058
39168
|
function progress2({ stdout }) {
|
|
39059
|
-
const steps = events(stdout).
|
|
39060
|
-
|
|
39061
|
-
|
|
39062
|
-
const seen = new Set(steps.map((s2) => s2.
|
|
39169
|
+
const steps = events(stdout).flatMap((e) => e.event === "step_update" && e.step_update ? [e.step_update] : []);
|
|
39170
|
+
const last = steps.at(-1);
|
|
39171
|
+
if (!last) return null;
|
|
39172
|
+
const seen = new Set(steps.map((s2) => s2.step_index)).size;
|
|
39063
39173
|
const what = last.tool_name ? `tool ${last.tool_name}` : last.step_type ?? "a step";
|
|
39064
39174
|
return `${seen} step(s), last was ${what} (${last.state ?? "state unknown"})`;
|
|
39065
39175
|
}
|
|
@@ -39123,7 +39233,11 @@ function interpret3({ stdout, stderr, code }) {
|
|
|
39123
39233
|
}
|
|
39124
39234
|
return { ok: true, text: text2, usageRaw: payload, denials: payload.denied_actions ?? [] };
|
|
39125
39235
|
}
|
|
39126
|
-
function usageRecord3(
|
|
39236
|
+
function usageRecord3(raw) {
|
|
39237
|
+
const payload = (
|
|
39238
|
+
/** @type {AgyEnvelope | null} */
|
|
39239
|
+
raw
|
|
39240
|
+
);
|
|
39127
39241
|
if (!payload) return null;
|
|
39128
39242
|
const u = payload.usage ?? {};
|
|
39129
39243
|
const num2 = (v) => typeof v === "number" ? v : null;
|
|
@@ -39139,23 +39253,28 @@ function usageRecord3(payload) {
|
|
|
39139
39253
|
turns: num2(payload.num_turns),
|
|
39140
39254
|
// Which permissions the sandbox refused. A denied read_url usually explains
|
|
39141
39255
|
// a thin evidence basis, so it is worth keeping in the record.
|
|
39142
|
-
denied_actions: Array.isArray(payload.denied_actions) ? payload.denied_actions.
|
|
39256
|
+
denied_actions: Array.isArray(payload.denied_actions) ? payload.denied_actions.flatMap((d) => d?.action ? [d.action] : []) : null
|
|
39143
39257
|
};
|
|
39144
39258
|
}
|
|
39145
39259
|
function boundary3({ sandbox }) {
|
|
39260
|
+
const { tools } = (
|
|
39261
|
+
/** @type {Sandbox} */
|
|
39262
|
+
sandbox
|
|
39263
|
+
);
|
|
39146
39264
|
return {
|
|
39147
39265
|
reads: "whole disk",
|
|
39148
39266
|
shell: "none",
|
|
39149
|
-
web:
|
|
39267
|
+
web: tools.includes("search_web"),
|
|
39150
39268
|
writes: false,
|
|
39151
39269
|
mcp: false,
|
|
39152
39270
|
protected_paths: "denied (tool hook)",
|
|
39153
|
-
tools: [...
|
|
39271
|
+
tools: [...tools]
|
|
39154
39272
|
};
|
|
39155
39273
|
}
|
|
39156
|
-
var FORBIDDEN_FLAGS3, RESERVED_FLAGS3, stopSignal;
|
|
39274
|
+
var FORBIDDEN_FLAGS3, RESERVED_FLAGS3, FILE_READ_TOOLS, stopSignal;
|
|
39157
39275
|
var init_antigravity = __esm({
|
|
39158
39276
|
"src/adapters/antigravity.mjs"() {
|
|
39277
|
+
"use strict";
|
|
39159
39278
|
init_policy();
|
|
39160
39279
|
init_failures();
|
|
39161
39280
|
init_antigravity_sandbox();
|
|
@@ -39180,6 +39299,7 @@ var init_antigravity = __esm({
|
|
|
39180
39299
|
"-i"
|
|
39181
39300
|
];
|
|
39182
39301
|
RESERVED_FLAGS3 = ["--print", "-p", "--prompt", "--project", "--remote-control", "--effort"];
|
|
39302
|
+
FILE_READ_TOOLS = ["view_file", "read_file"];
|
|
39183
39303
|
stopSignal = "SIGQUIT";
|
|
39184
39304
|
}
|
|
39185
39305
|
});
|
|
@@ -39273,9 +39393,9 @@ function prepareSandbox2({ workdir, network = "on" }) {
|
|
|
39273
39393
|
fs8.rmSync(root, { recursive: true, force: true });
|
|
39274
39394
|
} catch {
|
|
39275
39395
|
}
|
|
39276
|
-
}
|
|
39396
|
+
},
|
|
39397
|
+
seed: (provider) => seedCredentialFromAuth(sandbox, provider)
|
|
39277
39398
|
};
|
|
39278
|
-
sandbox.seed = (provider) => seedCredentialFromAuth(sandbox, provider);
|
|
39279
39399
|
return sandbox;
|
|
39280
39400
|
}
|
|
39281
39401
|
function seedCredentialFromAuth(sandbox, provider) {
|
|
@@ -39330,6 +39450,7 @@ function seedCredentialFromAuth(sandbox, provider) {
|
|
|
39330
39450
|
var DatabaseSync, SANDBOX_DENY2, SANDBOX_ALLOW2, WEB_TOOLS2, SANDBOX_TOOLS_OFF, AUTH_REL;
|
|
39331
39451
|
var init_opencode_sandbox = __esm({
|
|
39332
39452
|
"src/adapters/opencode-sandbox.mjs"() {
|
|
39453
|
+
"use strict";
|
|
39333
39454
|
init_policy();
|
|
39334
39455
|
init_store();
|
|
39335
39456
|
DatabaseSync = null;
|
|
@@ -39366,6 +39487,7 @@ var init_opencode_sandbox = __esm({
|
|
|
39366
39487
|
// src/adapters/opencode.mjs
|
|
39367
39488
|
var opencode_exports = {};
|
|
39368
39489
|
__export(opencode_exports, {
|
|
39490
|
+
FILE_READ_TOOLS: () => FILE_READ_TOOLS2,
|
|
39369
39491
|
FORBIDDEN_FLAGS: () => FORBIDDEN_FLAGS4,
|
|
39370
39492
|
RESERVED_FLAGS: () => RESERVED_FLAGS4,
|
|
39371
39493
|
boundary: () => boundary4,
|
|
@@ -39512,14 +39634,14 @@ function interpret4({ stdout, stderr, code, signal = null, truncated = false })
|
|
|
39512
39634
|
let text2 = null;
|
|
39513
39635
|
let textIndex = -1;
|
|
39514
39636
|
let lastErrorIndex = -1;
|
|
39637
|
+
let lastError = "";
|
|
39515
39638
|
let finishedAfterText = false;
|
|
39516
39639
|
let tokens = null;
|
|
39517
39640
|
let cost = null;
|
|
39518
39641
|
let steps = 0;
|
|
39519
39642
|
const errors = [];
|
|
39520
39643
|
const earlierTextParts = [];
|
|
39521
|
-
for (
|
|
39522
|
-
const e = all[i];
|
|
39644
|
+
for (const [i, e] of all.entries()) {
|
|
39523
39645
|
const part = e.part;
|
|
39524
39646
|
if (e.type === "text" && part?.type === "text" && part.time?.end && typeof part.text === "string" && part.text.trim()) {
|
|
39525
39647
|
if (text2 !== null) earlierTextParts.push(text2);
|
|
@@ -39532,7 +39654,8 @@ function interpret4({ stdout, stderr, code, signal = null, truncated = false })
|
|
|
39532
39654
|
if (part.tokens && typeof part.tokens === "object") tokens = part.tokens;
|
|
39533
39655
|
if (typeof part.cost === "number") cost = part.cost;
|
|
39534
39656
|
} else if (e.type === "error" && e.error) {
|
|
39535
|
-
|
|
39657
|
+
lastError = errorMessage(e.error);
|
|
39658
|
+
errors.push(lastError);
|
|
39536
39659
|
lastErrorIndex = i;
|
|
39537
39660
|
}
|
|
39538
39661
|
}
|
|
@@ -39547,14 +39670,14 @@ function interpret4({ stdout, stderr, code, signal = null, truncated = false })
|
|
|
39547
39670
|
};
|
|
39548
39671
|
}
|
|
39549
39672
|
if (lastErrorIndex > textIndex) {
|
|
39550
|
-
const msg =
|
|
39673
|
+
const msg = lastError;
|
|
39551
39674
|
return { ok: false, failureKind: classifyMessage(msg), message: msg, usageRaw };
|
|
39552
39675
|
}
|
|
39553
39676
|
if (code !== 0) {
|
|
39554
39677
|
const { msg, kind } = failureMessage({ errors, stderr, stdout, code, signal, what: "after producing an answer" });
|
|
39555
39678
|
return { ok: false, failureKind: kind, message: msg, usageRaw };
|
|
39556
39679
|
}
|
|
39557
|
-
if (truncated && !(finishedAfterText && all.
|
|
39680
|
+
if (truncated && !(finishedAfterText && all.at(-1)?.type === "step_finish")) {
|
|
39558
39681
|
return {
|
|
39559
39682
|
ok: false,
|
|
39560
39683
|
failureKind: "invalid_output",
|
|
@@ -39577,14 +39700,19 @@ function toolCalls3({ stdout }) {
|
|
|
39577
39700
|
return [...seen.values()].reduce((o, n) => ({ ...o, [n]: (o[n] ?? 0) + 1 }), {});
|
|
39578
39701
|
}
|
|
39579
39702
|
function progress3({ stdout }) {
|
|
39580
|
-
const seen = events2(stdout).filter(
|
|
39581
|
-
|
|
39582
|
-
|
|
39703
|
+
const seen = events2(stdout).filter(
|
|
39704
|
+
(e) => e.type !== void 0 && ["step_start", "step_finish", "text", "tool_use"].includes(e.type)
|
|
39705
|
+
);
|
|
39706
|
+
const last = seen.at(-1);
|
|
39707
|
+
if (!last) return null;
|
|
39583
39708
|
const what = last.type === "tool_use" && last.part?.tool ? `tool ${last.part.tool}` : last.type;
|
|
39584
39709
|
return `${seen.length} event(s), last was ${what}`;
|
|
39585
39710
|
}
|
|
39586
39711
|
function usageRecord4(raw) {
|
|
39587
|
-
const r =
|
|
39712
|
+
const r = (
|
|
39713
|
+
/** @type {OpencodeUsage | null} */
|
|
39714
|
+
raw ?? {}
|
|
39715
|
+
);
|
|
39588
39716
|
const t = r.tokens && typeof r.tokens === "object" ? r.tokens : {};
|
|
39589
39717
|
const num2 = (v) => typeof v === "number" && Number.isFinite(v) ? v : null;
|
|
39590
39718
|
const cache = t.cache && typeof t.cache === "object" ? t.cache : {};
|
|
@@ -39601,19 +39729,24 @@ function usageRecord4(raw) {
|
|
|
39601
39729
|
};
|
|
39602
39730
|
}
|
|
39603
39731
|
function boundary4({ sandbox }) {
|
|
39732
|
+
const { tools } = (
|
|
39733
|
+
/** @type {Sandbox} */
|
|
39734
|
+
sandbox
|
|
39735
|
+
);
|
|
39604
39736
|
return {
|
|
39605
39737
|
reads: "whole disk",
|
|
39606
39738
|
shell: "none",
|
|
39607
|
-
web:
|
|
39739
|
+
web: tools.includes("webfetch"),
|
|
39608
39740
|
writes: false,
|
|
39609
39741
|
mcp: false,
|
|
39610
39742
|
protected_paths: "denied (permission rules)",
|
|
39611
|
-
tools: [...
|
|
39743
|
+
tools: [...tools]
|
|
39612
39744
|
};
|
|
39613
39745
|
}
|
|
39614
|
-
var FORBIDDEN_FLAGS4, standaloneSupport, probePromise, RESERVED_FLAGS4;
|
|
39746
|
+
var FORBIDDEN_FLAGS4, standaloneSupport, probePromise, RESERVED_FLAGS4, FILE_READ_TOOLS2;
|
|
39615
39747
|
var init_opencode = __esm({
|
|
39616
39748
|
"src/adapters/opencode.mjs"() {
|
|
39749
|
+
"use strict";
|
|
39617
39750
|
init_policy();
|
|
39618
39751
|
init_failures();
|
|
39619
39752
|
init_platform();
|
|
@@ -39642,6 +39775,7 @@ var init_opencode = __esm({
|
|
|
39642
39775
|
standaloneSupport = null;
|
|
39643
39776
|
probePromise = null;
|
|
39644
39777
|
RESERVED_FLAGS4 = ["--model", "--standalone", "--wizard"];
|
|
39778
|
+
FILE_READ_TOOLS2 = ["read", "grep"];
|
|
39645
39779
|
}
|
|
39646
39780
|
});
|
|
39647
39781
|
|
|
@@ -39662,7 +39796,7 @@ function ownerState(jobId, owner) {
|
|
|
39662
39796
|
process.kill(owner.pid, 0);
|
|
39663
39797
|
pidAlive = true;
|
|
39664
39798
|
} catch (err) {
|
|
39665
|
-
pidAlive = err.code === "EPERM";
|
|
39799
|
+
pidAlive = err instanceof Error && "code" in err && err.code === "EPERM";
|
|
39666
39800
|
}
|
|
39667
39801
|
if (!pidAlive) return "dead";
|
|
39668
39802
|
return Date.now() > Date.parse(owner.deadline_at) ? "overdue" : "alive";
|
|
@@ -39710,7 +39844,7 @@ async function settled(promise2, ms, signal) {
|
|
|
39710
39844
|
await (promise2 ? Promise.race([promise2, until]) : until);
|
|
39711
39845
|
} finally {
|
|
39712
39846
|
clearTimeout(timer);
|
|
39713
|
-
signal?.removeEventListener("abort", stop);
|
|
39847
|
+
if (stop) signal?.removeEventListener("abort", stop);
|
|
39714
39848
|
}
|
|
39715
39849
|
}
|
|
39716
39850
|
function parses(text2) {
|
|
@@ -39735,11 +39869,18 @@ function tally(values) {
|
|
|
39735
39869
|
return out;
|
|
39736
39870
|
}
|
|
39737
39871
|
function recordSummary(job) {
|
|
39738
|
-
const
|
|
39739
|
-
|
|
39872
|
+
const record2 = (
|
|
39873
|
+
/** @type {JobRecord} */
|
|
39874
|
+
job.record
|
|
39875
|
+
);
|
|
39876
|
+
const ids = recordableIds(
|
|
39877
|
+
/** @type {ConsultResult} */
|
|
39878
|
+
job.result
|
|
39879
|
+
);
|
|
39880
|
+
const written = new Set(record2.entries.map((e) => e.id));
|
|
39740
39881
|
return {
|
|
39741
|
-
...
|
|
39742
|
-
verdicts: tally(
|
|
39882
|
+
...record2,
|
|
39883
|
+
verdicts: tally(record2.entries.map((e) => e.verdict)),
|
|
39743
39884
|
coverage: { recordable: ids.length, recorded: written.size },
|
|
39744
39885
|
unrecorded: ids.filter((id2) => !written.has(id2))
|
|
39745
39886
|
};
|
|
@@ -39755,13 +39896,16 @@ function inherit(raw, round) {
|
|
|
39755
39896
|
const { brief } = round;
|
|
39756
39897
|
const out = raw.network === void 0 && round.network ? { ...raw, network: round.network } : { ...raw };
|
|
39757
39898
|
if (!brief) return out;
|
|
39758
|
-
|
|
39759
|
-
|
|
39760
|
-
|
|
39899
|
+
const topKeys = ["objective", "success_criteria", "constraints"];
|
|
39900
|
+
for (const k of topKeys) {
|
|
39901
|
+
const v = brief[k];
|
|
39902
|
+
if (out[k] === void 0 && raw.context?.[k] === void 0 && v != null && !(Array.isArray(v) && !v.length)) out[k] = v;
|
|
39761
39903
|
}
|
|
39762
39904
|
const ctx = { ...raw.context ?? {} };
|
|
39763
|
-
|
|
39764
|
-
|
|
39905
|
+
const contextKeys = ["facts", "proposal", "counterpoints"];
|
|
39906
|
+
for (const k of contextKeys) {
|
|
39907
|
+
const v = brief[k];
|
|
39908
|
+
if (ctx[k] === void 0 && v != null && !(Array.isArray(v) && !v.length)) ctx[k] = v;
|
|
39765
39909
|
}
|
|
39766
39910
|
const artifacts = [...brief.artifacts ?? []];
|
|
39767
39911
|
const last = artifacts.at(-1);
|
|
@@ -39793,8 +39937,22 @@ function adviceCaveat(normalized) {
|
|
|
39793
39937
|
}
|
|
39794
39938
|
return notes.length ? notes.join("; ") : null;
|
|
39795
39939
|
}
|
|
39940
|
+
function unreadCaveat(calls, readTools) {
|
|
39941
|
+
if (!calls || !readTools) return null;
|
|
39942
|
+
if (readTools.some((t) => calls[t])) return null;
|
|
39943
|
+
const failed = readTools.reduce((n, t) => n + (calls[`${t}:error`] ?? 0), 0);
|
|
39944
|
+
const how = failed ? `${failed} failed or were refused` : "none was made";
|
|
39945
|
+
return `files were exposed, but no call to read one succeeded (${how})`;
|
|
39946
|
+
}
|
|
39947
|
+
function refusedToolCaveat(calls, allowed) {
|
|
39948
|
+
if (!calls || !allowed) return null;
|
|
39949
|
+
const names = Object.keys(calls).filter((k) => k.endsWith(":error")).map((k) => k.slice(0, -":error".length)).filter((t) => !allowed.includes(t));
|
|
39950
|
+
if (!names.length) return null;
|
|
39951
|
+
return `the consultant's CLI called ${names.join(", ")}, which severally does not allow, and was refused; a new CLI release may have renamed a tool`;
|
|
39952
|
+
}
|
|
39796
39953
|
function sameVendorCaveat(caller, callerModel, consultant) {
|
|
39797
|
-
const
|
|
39954
|
+
const callerCli = CLI_IDS.find((c) => c === caller);
|
|
39955
|
+
const callerVendor = (callerModel ? vendorOf(callerModel) : null) ?? (callerCli ? CLI_VENDORS[callerCli] ?? null : null);
|
|
39798
39956
|
const vendor = consultant.vendor;
|
|
39799
39957
|
if (!callerVendor || !vendor || callerVendor !== vendor) return null;
|
|
39800
39958
|
const consultantModel = consultant.model;
|
|
@@ -39804,11 +39962,12 @@ function sameVendorCaveat(caller, callerModel, consultant) {
|
|
|
39804
39962
|
}
|
|
39805
39963
|
function comparison(members2, budget) {
|
|
39806
39964
|
const each = (budget - COMPARISON_NOTE_CHARS) / Math.max(1, members2.length) - COLUMN_FIXED_CHARS;
|
|
39807
|
-
|
|
39965
|
+
const firstPass = members2.map((m) => ({ m, column: byTarget(m, each) }));
|
|
39966
|
+
let columns = firstPass.map((x) => x.column);
|
|
39808
39967
|
const cutShort = columns.filter((c) => c.truncated).length;
|
|
39809
39968
|
if (each !== Infinity && cutShort) {
|
|
39810
39969
|
const spare = columns.reduce((n, c) => n + (c.truncated ? 0 : Math.max(0, each + COLUMN_FIXED_CHARS - printed(c))), 0);
|
|
39811
|
-
columns =
|
|
39970
|
+
columns = firstPass.map(({ m, column }) => column.truncated ? byTarget(m, each + spare / cutShort) : column);
|
|
39812
39971
|
}
|
|
39813
39972
|
return {
|
|
39814
39973
|
by_target: columns,
|
|
@@ -39938,8 +40097,12 @@ function pointsOf(view, ids) {
|
|
|
39938
40097
|
const verdicts = new Map((view.record?.entries ?? []).map((e) => [e.id, e]));
|
|
39939
40098
|
const lists = { finding: r.findings, unknown: r.unknowns, next_check: r.next_checks };
|
|
39940
40099
|
const points = [...new Set(ids)].map((id2) => {
|
|
39941
|
-
const [
|
|
39942
|
-
|
|
40100
|
+
const [point] = Object.entries(lists).flatMap(([kind, l]) => {
|
|
40101
|
+
const item = l.find((x) => x.id === id2);
|
|
40102
|
+
return item ? [{ kind, ...item }] : [];
|
|
40103
|
+
});
|
|
40104
|
+
if (!point) throw new RequestError(`"${id2}" is not a point in this answer`, "unknown_entry_id");
|
|
40105
|
+
return { ...point, recorded: verdicts.get(id2) ?? null };
|
|
39943
40106
|
});
|
|
39944
40107
|
return {
|
|
39945
40108
|
job_id: view.job_id,
|
|
@@ -40043,6 +40206,7 @@ function assertNoForbiddenFlags(target, args) {
|
|
|
40043
40206
|
var ADAPTERS, MIN_RETRY_MS, CANCEL_POLL_MS, API_KEY_VARS, CREDENTIAL_FILE_VARS, ALL_API_CREDENTIAL_VARS, USAGE_FIGURES, id, LIVE_HERE, JobManager, PARTIAL_TAIL_CHARS, GROUP_CHARS, COLUMN_FIXED_CHARS, COMPARISON_NOTE_CHARS, QUESTION_CHARS, FAILURE_MESSAGE_CHARS, SUMMARY_CHARS, CAVEAT_CHARS, ITEM_CHARS, MIN_TEXT_CHARS, flagNames;
|
|
40044
40207
|
var init_jobs = __esm({
|
|
40045
40208
|
"src/jobs.mjs"() {
|
|
40209
|
+
"use strict";
|
|
40046
40210
|
init_policy();
|
|
40047
40211
|
init_consultants();
|
|
40048
40212
|
init_schema();
|
|
@@ -40078,6 +40242,10 @@ var init_jobs = __esm({
|
|
|
40078
40242
|
// A consultation this process does not hold: finished (its round file), or
|
|
40079
40243
|
// still in progress (its marker). A marker whose owner is gone becomes a
|
|
40080
40244
|
// failed round, `interrupted`, written once by whichever process claims it.
|
|
40245
|
+
/**
|
|
40246
|
+
* @param {string} jobId
|
|
40247
|
+
* @returns {StoredRound | null}
|
|
40248
|
+
*/
|
|
40081
40249
|
#fromDisk(jobId) {
|
|
40082
40250
|
const done = loadRound(jobId);
|
|
40083
40251
|
if (done) return done;
|
|
@@ -40090,6 +40258,12 @@ var init_jobs = __esm({
|
|
|
40090
40258
|
// something the next reader finishes: the round is written (the index
|
|
40091
40259
|
// takes it once), then the marker goes. A round already on disk wins -- its
|
|
40092
40260
|
// owner finished and died before removing the marker.
|
|
40261
|
+
/**
|
|
40262
|
+
* @param {string} jobId
|
|
40263
|
+
* @param {StoredRound} rec
|
|
40264
|
+
* @param {'alive' | 'dead' | 'overdue'} state
|
|
40265
|
+
* @returns {Round}
|
|
40266
|
+
*/
|
|
40093
40267
|
#interrupt(jobId, rec, state) {
|
|
40094
40268
|
const done = loadRound(jobId);
|
|
40095
40269
|
if (done) {
|
|
@@ -40120,25 +40294,33 @@ var init_jobs = __esm({
|
|
|
40120
40294
|
for (const jobId of listRunning()) {
|
|
40121
40295
|
const rec = loadRunning(jobId);
|
|
40122
40296
|
const state = rec && ownerState(jobId, rec.owner);
|
|
40123
|
-
if (rec && state !== "alive") this.#interrupt(jobId, rec, state);
|
|
40297
|
+
if (rec && state && state !== "alive") this.#interrupt(jobId, rec, state);
|
|
40124
40298
|
}
|
|
40125
40299
|
}
|
|
40300
|
+
/**
|
|
40301
|
+
* @param {StoredRound} rec
|
|
40302
|
+
* @returns {FullView}
|
|
40303
|
+
*/
|
|
40126
40304
|
#diskView(rec) {
|
|
40127
|
-
const elsewhere = Boolean(rec.owner);
|
|
40128
40305
|
const { owner, ...view } = rec;
|
|
40129
40306
|
return {
|
|
40130
40307
|
...view,
|
|
40131
|
-
|
|
40308
|
+
// consult_cancel asks the process that owns it to stop it.
|
|
40309
|
+
cancellable: Boolean(owner) && (view.status === "running" || view.status === "queued"),
|
|
40132
40310
|
progress: null,
|
|
40133
40311
|
record: view.record ? recordSummary(view) : null,
|
|
40134
|
-
brief:
|
|
40135
|
-
limits:
|
|
40136
|
-
next_step:
|
|
40312
|
+
brief: owner ? null : view.brief,
|
|
40313
|
+
limits: owner ? null : limitsSummary(),
|
|
40314
|
+
next_step: owner ? `running in another severally process (pid ${owner.pid}); poll again later, or stop it with consult_cancel` : nextStep(view)
|
|
40137
40315
|
};
|
|
40138
40316
|
}
|
|
40139
40317
|
get running() {
|
|
40140
40318
|
return [...this.jobs.values()].filter((j) => j.status === "running");
|
|
40141
40319
|
}
|
|
40320
|
+
/**
|
|
40321
|
+
* @param {Job} job
|
|
40322
|
+
* @returns {Round}
|
|
40323
|
+
*/
|
|
40142
40324
|
#record(job) {
|
|
40143
40325
|
return {
|
|
40144
40326
|
job_id: job.job_id,
|
|
@@ -40181,13 +40363,25 @@ var init_jobs = __esm({
|
|
|
40181
40363
|
}
|
|
40182
40364
|
// `detail: "full"` returns the brief as stored; by default a finished
|
|
40183
40365
|
// consultation names it (size, sha256, round file) instead of repeating it.
|
|
40184
|
-
/**
|
|
40366
|
+
/**
|
|
40367
|
+
* @param {string} jobId
|
|
40368
|
+
* @param {{detail?: string | undefined}} [opts]
|
|
40369
|
+
* @returns {JobView | null}
|
|
40370
|
+
*/
|
|
40185
40371
|
view(jobId, { detail } = {}) {
|
|
40186
40372
|
const job = this.jobs.get(jobId);
|
|
40187
40373
|
if (!job) {
|
|
40188
|
-
const
|
|
40189
|
-
return
|
|
40374
|
+
const rec = this.#fromDisk(jobId);
|
|
40375
|
+
return rec ? compactBrief(this.#diskView(rec), detail) : null;
|
|
40190
40376
|
}
|
|
40377
|
+
return this.#jobView(job, detail);
|
|
40378
|
+
}
|
|
40379
|
+
/**
|
|
40380
|
+
* @param {Job} job
|
|
40381
|
+
* @param {string | undefined} detail
|
|
40382
|
+
* @returns {JobView}
|
|
40383
|
+
*/
|
|
40384
|
+
#jobView(job, detail) {
|
|
40191
40385
|
const rec = this.#record(job);
|
|
40192
40386
|
rec.cancellable = job.status === "running" || job.status === "queued";
|
|
40193
40387
|
rec.progress = job.status === "running" && job._handle ? ADAPTERS[job.cli]?.progress?.({ stdout: job._handle.stdoutSoFar() }) ?? null : null;
|
|
@@ -40207,30 +40401,35 @@ var init_jobs = __esm({
|
|
|
40207
40401
|
// job_id and group_id can still be followed up and recorded against. The
|
|
40208
40402
|
// index only picks and orders; each row shown is read from its round, which
|
|
40209
40403
|
// holds the verdicts written since. Memory wins over disk for the same job.
|
|
40404
|
+
/** @param {{limit?: number}} [opts] */
|
|
40210
40405
|
list({ limit: limit2 = 20 } = {}) {
|
|
40211
|
-
const
|
|
40212
|
-
const seen = new Set(
|
|
40213
|
-
const
|
|
40406
|
+
const candidates = [];
|
|
40407
|
+
const seen = /* @__PURE__ */ new Set();
|
|
40408
|
+
for (const jid of this.order) {
|
|
40409
|
+
const job = this.jobs.get(jid);
|
|
40410
|
+
if (!job) continue;
|
|
40411
|
+
seen.add(job.job_id);
|
|
40412
|
+
candidates.push({ at: job.created_at ?? "", load: () => job });
|
|
40413
|
+
}
|
|
40214
40414
|
for (const jobId of listRunning()) {
|
|
40215
40415
|
const live = seen.has(jobId) ? null : loadRunning(jobId);
|
|
40216
40416
|
if (!live) continue;
|
|
40217
40417
|
seen.add(jobId);
|
|
40218
40418
|
const state = ownerState(jobId, live.owner);
|
|
40219
40419
|
const saved = state === "alive" ? null : loadRoundAt(live);
|
|
40220
|
-
|
|
40221
|
-
|
|
40222
|
-
});
|
|
40420
|
+
const rec = state === "alive" ? live : saved ?? /** @type {Round} */
|
|
40421
|
+
{ ...live, status: "failed", failure: { kind: "interrupted" } };
|
|
40422
|
+
candidates.push({ at: rec.created_at ?? "", load: () => rec });
|
|
40223
40423
|
}
|
|
40224
40424
|
for (const row of indexRows())
|
|
40225
40425
|
if (!seen.has(row.job_id)) {
|
|
40226
40426
|
seen.add(row.job_id);
|
|
40227
|
-
|
|
40427
|
+
candidates.push({ at: row.created_at ?? "", load: () => loadRoundAt(row) });
|
|
40228
40428
|
}
|
|
40229
|
-
const at = (x) => x.created_at ?? x.rec?.created_at ?? x.row?.created_at ?? "";
|
|
40230
40429
|
const picked = [];
|
|
40231
|
-
for (const x of
|
|
40430
|
+
for (const x of candidates.sort((p, q) => q.at < p.at ? -1 : q.at > p.at ? 1 : 0)) {
|
|
40232
40431
|
if (picked.length >= limit2) break;
|
|
40233
|
-
const j = x.
|
|
40432
|
+
const j = x.load();
|
|
40234
40433
|
if (j) picked.push(j);
|
|
40235
40434
|
}
|
|
40236
40435
|
return picked.map((j) => {
|
|
@@ -40267,7 +40466,7 @@ var init_jobs = __esm({
|
|
|
40267
40466
|
const by = {};
|
|
40268
40467
|
const reported = {};
|
|
40269
40468
|
for (const j of this.jobs.values()) {
|
|
40270
|
-
const attempts = [...(j.retries ?? []).map((r) => r.usage), j.usage].filter(
|
|
40469
|
+
const attempts = [...(j.retries ?? []).map((r) => r.usage), j.usage].filter((u) => u != null);
|
|
40271
40470
|
if (!attempts.length) continue;
|
|
40272
40471
|
const t = by[j.target] ??= { consultations: 0, attempts: 0 };
|
|
40273
40472
|
const seen = reported[j.target] ??= /* @__PURE__ */ new Set();
|
|
@@ -40281,11 +40480,13 @@ var init_jobs = __esm({
|
|
|
40281
40480
|
}
|
|
40282
40481
|
}
|
|
40283
40482
|
}
|
|
40483
|
+
const out = {};
|
|
40284
40484
|
for (const [target, t] of Object.entries(by)) {
|
|
40285
|
-
t
|
|
40485
|
+
out[target] = { ...t, not_reported: USAGE_FIGURES.filter((k) => !reported[target]?.has(k)) };
|
|
40286
40486
|
}
|
|
40287
|
-
return
|
|
40487
|
+
return out;
|
|
40288
40488
|
}
|
|
40489
|
+
/** @param {RawRequest} rawRequest the tool arguments, not yet validated */
|
|
40289
40490
|
start(rawRequest) {
|
|
40290
40491
|
if (process.env.SEVERALLY_ACTIVE === "1") {
|
|
40291
40492
|
throw new RequestError(
|
|
@@ -40308,29 +40509,30 @@ var init_jobs = __esm({
|
|
|
40308
40509
|
fromGroup = this.#groupParents(followupTo);
|
|
40309
40510
|
rawRequest = { ...rawRequest, targets: fromGroup.links.map((l) => l.parent.target) };
|
|
40310
40511
|
} else if (followupTo) {
|
|
40311
|
-
|
|
40312
|
-
if (!
|
|
40313
|
-
if (
|
|
40512
|
+
const found = this.jobs.get(followupTo) ?? loadRound(followupTo);
|
|
40513
|
+
if (!found) throw new RequestError(`followup_to references unknown job "${followupTo}"`, "unknown_job");
|
|
40514
|
+
if (found.status !== "completed") {
|
|
40314
40515
|
throw new RequestError(
|
|
40315
|
-
`followup_to job "${followupTo}" is ${
|
|
40516
|
+
`followup_to job "${followupTo}" is ${found.status}; only a completed consultation can be followed up`,
|
|
40316
40517
|
"followup_not_completed"
|
|
40317
40518
|
);
|
|
40318
40519
|
}
|
|
40319
|
-
const rounds = this.#chainRounds(
|
|
40320
|
-
const later = rounds.find((j) => j.round >
|
|
40520
|
+
const rounds = this.#chainRounds(found.chain_id);
|
|
40521
|
+
const later = rounds.find((j) => j.round > found.round && !["failed", "cancelled"].includes(j.status));
|
|
40321
40522
|
if (later) {
|
|
40322
40523
|
throw new RequestError(
|
|
40323
|
-
`job "${followupTo}" is round ${
|
|
40524
|
+
`job "${followupTo}" is round ${found.round}, and this consultation already has round ${later.round} (job "${later.job_id}", ${later.status}); follow up the latest round`,
|
|
40324
40525
|
"followup_not_latest"
|
|
40325
40526
|
);
|
|
40326
40527
|
}
|
|
40327
|
-
parent = { ...
|
|
40528
|
+
parent = { ...found, nextRound: Math.max(...rounds.map((j) => j.round)) + 1 };
|
|
40328
40529
|
}
|
|
40329
40530
|
if (parent && (rawRequest.target ?? null) === null && !rawRequest.targets?.length) {
|
|
40330
40531
|
rawRequest = { ...rawRequest, target: parent.target };
|
|
40331
40532
|
}
|
|
40332
|
-
|
|
40333
|
-
|
|
40533
|
+
const first = fromGroup?.links[0];
|
|
40534
|
+
const inheritFrom = first ? first.groupMember ?? first.parent : parent;
|
|
40535
|
+
if (inheritFrom) rawRequest = inherit(rawRequest, inheritFrom);
|
|
40334
40536
|
const req = parseRequest(rawRequest, {
|
|
40335
40537
|
isFollowup: Boolean(parent || fromGroup),
|
|
40336
40538
|
parentMode: parent?.mode ?? fromGroup?.group.mode ?? null,
|
|
@@ -40357,18 +40559,21 @@ var init_jobs = __esm({
|
|
|
40357
40559
|
);
|
|
40358
40560
|
}
|
|
40359
40561
|
const chainOf = (i) => fromGroup ? fromGroup.links[i] : parent ? { parent, round } : null;
|
|
40360
|
-
const seats = req.targets.map((target, i) =>
|
|
40361
|
-
|
|
40362
|
-
|
|
40363
|
-
|
|
40364
|
-
|
|
40365
|
-
|
|
40366
|
-
|
|
40367
|
-
|
|
40368
|
-
|
|
40369
|
-
|
|
40370
|
-
|
|
40371
|
-
|
|
40562
|
+
const seats = req.targets.map((target, i) => {
|
|
40563
|
+
const link = chainOf(i);
|
|
40564
|
+
return {
|
|
40565
|
+
job_id: id("job"),
|
|
40566
|
+
...link ? { chain_id: link.parent.chain_id, round: link.round } : {},
|
|
40567
|
+
// Twice the budget plus an hour: the deadline is wall-clock, the budget is
|
|
40568
|
+
// not, so a suspended laptop must not make a live owner look overdue.
|
|
40569
|
+
owner: {
|
|
40570
|
+
pid: process.pid,
|
|
40571
|
+
deadline_at: new Date(
|
|
40572
|
+
Date.now() + 2 * timeoutMs(consultantByName(target)) + POLICY.killGraceMs + 36e5
|
|
40573
|
+
).toISOString()
|
|
40574
|
+
}
|
|
40575
|
+
};
|
|
40576
|
+
});
|
|
40372
40577
|
const taken = takeSeats(seats, POLICY.maxConcurrent, (seat) => ownerState(seat.job_id, seat.owner) === "alive");
|
|
40373
40578
|
if (taken.chainBusy) {
|
|
40374
40579
|
const { live, mine } = taken.chainBusy;
|
|
@@ -40395,6 +40600,10 @@ var init_jobs = __esm({
|
|
|
40395
40600
|
// round of its own chain. A member that is still running, never answered or
|
|
40396
40601
|
// has used its rounds is skipped and named; if that is every member, the
|
|
40397
40602
|
// request is refused.
|
|
40603
|
+
/**
|
|
40604
|
+
* @param {string} groupId
|
|
40605
|
+
* @returns {GroupParents}
|
|
40606
|
+
*/
|
|
40398
40607
|
#groupParents(groupId) {
|
|
40399
40608
|
const group = this.groups.get(groupId) ?? loadGroup(groupId);
|
|
40400
40609
|
if (!group) throw new RequestError(`followup_to references unknown group "${groupId}"`, "unknown_group");
|
|
@@ -40412,10 +40621,18 @@ var init_jobs = __esm({
|
|
|
40412
40621
|
}
|
|
40413
40622
|
const rounds = this.#chainRounds(member.chain_id);
|
|
40414
40623
|
const latest = rounds.filter((j) => !["failed", "cancelled"].includes(j.status)).sort((a, b) => b.round - a.round)[0];
|
|
40624
|
+
if (!latest) {
|
|
40625
|
+
skipped.push({
|
|
40626
|
+
job_id: member.job_id,
|
|
40627
|
+
target: member.target,
|
|
40628
|
+
reason: `${member.status}, so there is no answer to follow up`
|
|
40629
|
+
});
|
|
40630
|
+
continue;
|
|
40631
|
+
}
|
|
40415
40632
|
const nextRound = Math.max(...rounds.map((j) => j.round)) + 1;
|
|
40416
40633
|
const consultant = consultantByName(member.target);
|
|
40417
|
-
const reason =
|
|
40418
|
-
if (reason) skipped.push({ job_id:
|
|
40634
|
+
const reason = latest.status !== "completed" ? `round ${latest.round} is still ${latest.status}` : nextRound > POLICY.maxRounds ? `has used its ${POLICY.maxRounds} rounds` : !consultant || !consultantAvailable(consultant) ? "this consultant is no longer configured or available here" : null;
|
|
40635
|
+
if (reason) skipped.push({ job_id: latest.job_id, target: member.target, reason });
|
|
40419
40636
|
else links.push({ parent: { ...latest, nextRound }, round: nextRound, groupMember: member });
|
|
40420
40637
|
}
|
|
40421
40638
|
if (!links.length) {
|
|
@@ -40426,14 +40643,21 @@ var init_jobs = __esm({
|
|
|
40426
40643
|
}
|
|
40427
40644
|
return { group, links, skipped };
|
|
40428
40645
|
}
|
|
40646
|
+
/**
|
|
40647
|
+
* @param {ParsedRequest} req
|
|
40648
|
+
* @param {Array<{parent: Round | null, round: number, groupMember?: Round}>} links
|
|
40649
|
+
* @param {Seat[]} seats
|
|
40650
|
+
* @param {GroupParents | null} [fromGroup]
|
|
40651
|
+
*/
|
|
40429
40652
|
#admitted(req, links, seats, fromGroup = null) {
|
|
40430
|
-
const
|
|
40653
|
+
const first = links[0];
|
|
40654
|
+
const parent = first?.groupMember ?? first?.parent;
|
|
40431
40655
|
const groupId = id("group");
|
|
40432
40656
|
let exposeManifest = null;
|
|
40433
|
-
const parentEntries = req.context.expose_from_parent ? parent
|
|
40657
|
+
const parentEntries = req.context.expose_from_parent ? parent?.brief?.expose_paths?.entries ?? [] : [];
|
|
40434
40658
|
if (req.context.expose_from_parent && parentEntries.length === 0) {
|
|
40435
40659
|
throw new RequestError(
|
|
40436
|
-
`context.expose_from_parent: job "${parent
|
|
40660
|
+
`context.expose_from_parent: job "${parent?.job_id}" exposed no files, so there is nothing to copy again; name them in context.expose_paths`,
|
|
40437
40661
|
"nothing_to_inherit"
|
|
40438
40662
|
);
|
|
40439
40663
|
}
|
|
@@ -40454,7 +40678,10 @@ var init_jobs = __esm({
|
|
|
40454
40678
|
if (req.context.expose_diff) {
|
|
40455
40679
|
exposeManifest.skipped.push({ source: d.source, reason: "replaced", exposed_as: d.exposed_as });
|
|
40456
40680
|
} else {
|
|
40457
|
-
if (reexposeChange(snapshot, d, exposeManifest))
|
|
40681
|
+
if (reexposeChange(snapshot, d, exposeManifest)) {
|
|
40682
|
+
const added = exposeManifest.entries.at(-1);
|
|
40683
|
+
if (added) added.inherited = true;
|
|
40684
|
+
}
|
|
40458
40685
|
}
|
|
40459
40686
|
}
|
|
40460
40687
|
if (req.context.expose_diff) {
|
|
@@ -40463,28 +40690,32 @@ var init_jobs = __esm({
|
|
|
40463
40690
|
if (diff) {
|
|
40464
40691
|
req.context.artifacts = [
|
|
40465
40692
|
...req.context.artifacts,
|
|
40466
|
-
{ name: `git diff ${exposeManifest.entries.at(-1)
|
|
40693
|
+
{ name: `git diff ${exposeManifest.entries.at(-1)?.base}`, kind: "diff", language: "diff", excerpt: diff }
|
|
40467
40694
|
];
|
|
40468
40695
|
}
|
|
40469
40696
|
}
|
|
40470
40697
|
markSinceParent(exposeManifest, parentEntries);
|
|
40471
40698
|
if (parentEntries.length && !exposeManifest.entries.length && !req.context.facts.length && !req.context.artifacts.length) {
|
|
40472
40699
|
throw new RequestError(
|
|
40473
|
-
`context.expose_from_parent: nothing job "${parent
|
|
40700
|
+
`context.expose_from_parent: nothing job "${parent?.job_id}" exposed is there any more, and this request has no facts or artifacts`,
|
|
40474
40701
|
"nothing_to_inherit"
|
|
40475
40702
|
);
|
|
40476
40703
|
}
|
|
40477
40704
|
} catch (err) {
|
|
40478
40705
|
cleanupJobDir(groupId);
|
|
40479
|
-
if (err instanceof RequestError || !err.code) throw err;
|
|
40706
|
+
if (err instanceof RequestError || !(err instanceof Error) || !("code" in err) || !err.code) throw err;
|
|
40480
40707
|
throw new RequestError(err.message, err.code === "too_large" ? "expose_paths_too_large" : "expose_path_invalid");
|
|
40481
40708
|
}
|
|
40482
40709
|
req._snapshot = path11.join(snapshot, "workspace");
|
|
40483
40710
|
}
|
|
40484
40711
|
const members2 = req.targets.map((target, i) => {
|
|
40485
|
-
const
|
|
40712
|
+
const link = links[i];
|
|
40713
|
+
const seat = seats[i];
|
|
40714
|
+
if (!link || !seat) throw new Error(`no follow-up link or seat for target "${target}"`);
|
|
40715
|
+
const { parent: from, round: round2 } = link;
|
|
40486
40716
|
const chainId = from ? from.chain_id : id("chain");
|
|
40487
|
-
const jobId =
|
|
40717
|
+
const jobId = seat.job_id;
|
|
40718
|
+
const consultant = consultantNamed(target);
|
|
40488
40719
|
const job = {
|
|
40489
40720
|
job_id: jobId,
|
|
40490
40721
|
chain_id: chainId,
|
|
@@ -40519,9 +40750,9 @@ var init_jobs = __esm({
|
|
|
40519
40750
|
status: "queued",
|
|
40520
40751
|
// What this consultant is: the CLI it runs on, the model and its
|
|
40521
40752
|
// lineage, as the operator defined it.
|
|
40522
|
-
cli:
|
|
40523
|
-
model:
|
|
40524
|
-
vendor:
|
|
40753
|
+
cli: consultant.cli,
|
|
40754
|
+
model: consultant.model,
|
|
40755
|
+
vendor: consultant.vendor,
|
|
40525
40756
|
created_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
40526
40757
|
started_at: null,
|
|
40527
40758
|
finished_at: null,
|
|
@@ -40538,16 +40769,18 @@ var init_jobs = __esm({
|
|
|
40538
40769
|
this.order.push(jobId);
|
|
40539
40770
|
LIVE_HERE.add(jobId);
|
|
40540
40771
|
this.#watchCancels();
|
|
40541
|
-
job._owner =
|
|
40772
|
+
job._owner = seat.owner;
|
|
40542
40773
|
this.#mark(job);
|
|
40543
40774
|
return job;
|
|
40544
40775
|
});
|
|
40545
40776
|
this.#evict();
|
|
40777
|
+
const [firstMember] = members2;
|
|
40778
|
+
if (!firstMember) throw new Error("a consultation needs a target");
|
|
40546
40779
|
const group = {
|
|
40547
40780
|
group_id: groupId,
|
|
40548
40781
|
created_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
40549
40782
|
mode: req.mode,
|
|
40550
|
-
question:
|
|
40783
|
+
question: firstMember.question,
|
|
40551
40784
|
// already redacted, and byte-identical across the group
|
|
40552
40785
|
job_ids: members2.map((m) => m.job_id)
|
|
40553
40786
|
};
|
|
@@ -40564,13 +40797,13 @@ var init_jobs = __esm({
|
|
|
40564
40797
|
});
|
|
40565
40798
|
}
|
|
40566
40799
|
if (req._snapshot) cleanupJobDir(groupId);
|
|
40567
|
-
const { round } =
|
|
40800
|
+
const { round } = firstMember;
|
|
40568
40801
|
const sameRound = members2.every((m) => m.round === round);
|
|
40569
40802
|
const common = {
|
|
40570
40803
|
group_id: groupId,
|
|
40571
40804
|
...sameRound ? { round, rounds_remaining: POLICY.maxRounds - round } : {},
|
|
40572
40805
|
...fromGroup ? { followup_to_group: fromGroup.group.group_id, skipped: fromGroup.skipped } : {},
|
|
40573
|
-
...parentEntries.length ? { exposed_from_parent: inheritedSummary(exposeManifest) } : {},
|
|
40806
|
+
...parentEntries.length && exposeManifest ? { exposed_from_parent: inheritedSummary(exposeManifest) } : {},
|
|
40574
40807
|
mode: req.mode,
|
|
40575
40808
|
// Counted, not refused: a brief of facts alone is a legitimate
|
|
40576
40809
|
// consultation, but one about code that hands over no file is one the
|
|
@@ -40584,7 +40817,7 @@ var init_jobs = __esm({
|
|
|
40584
40817
|
limits: limitsSummary()
|
|
40585
40818
|
};
|
|
40586
40819
|
if (!req.fanout) {
|
|
40587
|
-
const job =
|
|
40820
|
+
const job = firstMember;
|
|
40588
40821
|
return {
|
|
40589
40822
|
...common,
|
|
40590
40823
|
job_id: job.job_id,
|
|
@@ -40615,13 +40848,24 @@ var init_jobs = __esm({
|
|
|
40615
40848
|
// (memory wins: it can hold a verdict written since the round was saved).
|
|
40616
40849
|
// Each point goes with the lead's verdict on it, so the consultant answers
|
|
40617
40850
|
// knowing which of its points were checked and how they came out.
|
|
40851
|
+
/**
|
|
40852
|
+
* @param {string} chainId
|
|
40853
|
+
* @returns {Round[]}
|
|
40854
|
+
*/
|
|
40618
40855
|
#chainRounds(chainId) {
|
|
40619
40856
|
const byRound = new Map(loadChain(chainId).map((j) => [j.round, j]));
|
|
40620
40857
|
for (const j of this.jobs.values()) if (j.chain_id === chainId) byRound.set(j.round, j);
|
|
40621
40858
|
return [...byRound.values()];
|
|
40622
40859
|
}
|
|
40860
|
+
/**
|
|
40861
|
+
* @param {string} chainId
|
|
40862
|
+
* @returns {PriorRound[]}
|
|
40863
|
+
*/
|
|
40623
40864
|
#chainHistory(chainId) {
|
|
40624
|
-
return this.#chainRounds(chainId).filter(
|
|
40865
|
+
return this.#chainRounds(chainId).filter(
|
|
40866
|
+
/** @returns {j is Round & {result: ConsultResult}} */
|
|
40867
|
+
(j) => j.status === "completed" && Boolean(j.result)
|
|
40868
|
+
).sort((a, b) => a.round - b.round).map((j) => {
|
|
40625
40869
|
const verdicts = new Map((j.record?.entries ?? []).map((e) => [e.id, e]));
|
|
40626
40870
|
const ordered = (items, shape) => [...items.filter((x) => verdicts.has(x.id)), ...items.filter((x) => !verdicts.has(x.id))].map((x) => ({
|
|
40627
40871
|
id: x.id,
|
|
@@ -40646,6 +40890,7 @@ var init_jobs = __esm({
|
|
|
40646
40890
|
#evict() {
|
|
40647
40891
|
while (this.order.length > POLICY.maxJobsRetained) {
|
|
40648
40892
|
const oldest = this.order.shift();
|
|
40893
|
+
if (oldest === void 0) break;
|
|
40649
40894
|
const j = this.jobs.get(oldest);
|
|
40650
40895
|
if (j && (j.status === "running" || j.status === "queued")) {
|
|
40651
40896
|
this.order.push(oldest);
|
|
@@ -40659,14 +40904,22 @@ var init_jobs = __esm({
|
|
|
40659
40904
|
// last member there is nothing left to compare, so drop the group rather than
|
|
40660
40905
|
// keep answering as a complete fan-out with no answers in it (and keep the
|
|
40661
40906
|
// Map, which holds the question text, from growing without bound).
|
|
40907
|
+
/** @param {string} groupId */
|
|
40662
40908
|
#dropGroupIfEmpty(groupId) {
|
|
40663
40909
|
const group = this.groups.get(groupId);
|
|
40664
40910
|
if (!group) return;
|
|
40665
40911
|
if (group.job_ids.some((jid) => this.jobs.has(jid))) return;
|
|
40666
40912
|
this.groups.delete(groupId);
|
|
40667
40913
|
}
|
|
40914
|
+
/**
|
|
40915
|
+
* @param {Job} job
|
|
40916
|
+
* @param {ParsedRequest} req
|
|
40917
|
+
* @param {import('./brief.mjs').BriefChain} chain
|
|
40918
|
+
* @param {string} [brief]
|
|
40919
|
+
* @returns {Promise<Job>}
|
|
40920
|
+
*/
|
|
40668
40921
|
async #execute(job, req, chain, brief) {
|
|
40669
|
-
const consultant =
|
|
40922
|
+
const consultant = consultantNamed(job.target);
|
|
40670
40923
|
const cli = consultant.cli;
|
|
40671
40924
|
const adapter = ADAPTERS[cli];
|
|
40672
40925
|
const workdir = makeWorkdir(job.job_id);
|
|
@@ -40674,7 +40927,7 @@ var init_jobs = __esm({
|
|
|
40674
40927
|
try {
|
|
40675
40928
|
fs9.cpSync(req._snapshot, path11.join(workdir, "workspace"), { recursive: true });
|
|
40676
40929
|
} catch (err) {
|
|
40677
|
-
return this.#fail(job, "spawn_error", `could not copy the exposed files: ${err.message}`);
|
|
40930
|
+
return this.#fail(job, "spawn_error", `could not copy the exposed files: ${err instanceof Error ? err.message : err}`);
|
|
40678
40931
|
}
|
|
40679
40932
|
}
|
|
40680
40933
|
const text2 = brief ?? renderBrief(req, chain);
|
|
@@ -40720,7 +40973,9 @@ var init_jobs = __esm({
|
|
|
40720
40973
|
env,
|
|
40721
40974
|
input: text2,
|
|
40722
40975
|
timeoutMs: timeout,
|
|
40723
|
-
onCancelSignal: (fn) =>
|
|
40976
|
+
onCancelSignal: (fn) => {
|
|
40977
|
+
job._cancelFns.push(fn);
|
|
40978
|
+
},
|
|
40724
40979
|
compactLine: adapter.compactLine,
|
|
40725
40980
|
settleAfter: adapter.settleAfter,
|
|
40726
40981
|
stopSignal: adapter.stopSignal
|
|
@@ -40761,7 +41016,7 @@ var init_jobs = __esm({
|
|
|
40761
41016
|
}
|
|
40762
41017
|
const lastMessageText = invocation.lastMessagePath ? readIfExists(invocation.lastMessagePath) : "";
|
|
40763
41018
|
interpreted = adapter.interpret({ ...run, lastMessageText });
|
|
40764
|
-
const
|
|
41019
|
+
const recoveredError = interpreted.ok ? interpreted.recoveredError : null;
|
|
40765
41020
|
if (interpreted.ok && recoveredError && !parses(interpreted.text)) {
|
|
40766
41021
|
interpreted = { ok: false, ...recoveredError, usageRaw: interpreted.usageRaw };
|
|
40767
41022
|
}
|
|
@@ -40845,7 +41100,9 @@ var init_jobs = __esm({
|
|
|
40845
41100
|
adviceCaveat(normalized),
|
|
40846
41101
|
sameVendorCaveat(job.caller, job.caller_model, consultant),
|
|
40847
41102
|
interpreted.recoveredError && `the consultant's CLI reported an error mid-run and then finished its answer: ${redact(interpreted.recoveredError.message).slice(0, 300)}`,
|
|
40848
|
-
leftover && "the answer parsed, but a process the consultant started kept its output open after it exited and was stopped (one that left its process group may still be running)"
|
|
41103
|
+
leftover && "the answer parsed, but a process the consultant started kept its output open after it exited and was stopped (one that left its process group may still be running)",
|
|
41104
|
+
req._snapshot && unreadCaveat(job.tool_calls, adapter.FILE_READ_TOOLS),
|
|
41105
|
+
refusedToolCaveat(job.tool_calls, adapter.ALLOWED_TOOLS)
|
|
40849
41106
|
].filter(Boolean);
|
|
40850
41107
|
job.quality = {
|
|
40851
41108
|
...normalized.quality,
|
|
@@ -40860,6 +41117,13 @@ var init_jobs = __esm({
|
|
|
40860
41117
|
sandbox?.cleanup();
|
|
40861
41118
|
}
|
|
40862
41119
|
}
|
|
41120
|
+
/**
|
|
41121
|
+
* @param {Job} job
|
|
41122
|
+
* @param {string} kind
|
|
41123
|
+
* @param {unknown} message
|
|
41124
|
+
* @param {string | null} [detail]
|
|
41125
|
+
* @returns {Job}
|
|
41126
|
+
*/
|
|
40863
41127
|
#fail(job, kind, message, detail) {
|
|
40864
41128
|
if (job.status === "completed" || job.status === "failed" || job.status === "cancelled") return job;
|
|
40865
41129
|
job.status = kind === "cancelled" ? "cancelled" : "failed";
|
|
@@ -40875,12 +41139,14 @@ var init_jobs = __esm({
|
|
|
40875
41139
|
return job;
|
|
40876
41140
|
}
|
|
40877
41141
|
// The in-progress marker other processes read (see store.writeRunning).
|
|
41142
|
+
/** @param {Job} job */
|
|
40878
41143
|
#mark(job) {
|
|
40879
41144
|
try {
|
|
40880
41145
|
writeRunning({ ...this.#record(job), owner: job._owner });
|
|
40881
41146
|
} catch {
|
|
40882
41147
|
}
|
|
40883
41148
|
}
|
|
41149
|
+
/** @param {Job} job */
|
|
40884
41150
|
#finish(job) {
|
|
40885
41151
|
try {
|
|
40886
41152
|
persistRound(this.#record(job));
|
|
@@ -40898,7 +41164,7 @@ var init_jobs = __esm({
|
|
|
40898
41164
|
// consultant did not produce and a word outside VERDICTS, then stores what
|
|
40899
41165
|
// was written. It never decides that a finding was adopted, and it never
|
|
40900
41166
|
// fills in a verdict the lead did not write.
|
|
40901
|
-
/** @param {
|
|
41167
|
+
/** @param {RawRecord} [input] */
|
|
40902
41168
|
record(input2 = {}) {
|
|
40903
41169
|
if (input2.records !== void 0) return this.#recordMany(input2);
|
|
40904
41170
|
const allowed = ["job_id", "entries", "reflection"];
|
|
@@ -40918,6 +41184,7 @@ var init_jobs = __esm({
|
|
|
40918
41184
|
// record is checked before anything is written, so a bad id leaves the
|
|
40919
41185
|
// history as it was; then each job is written on its own, and a write that
|
|
40920
41186
|
// fails is named in the answer rather than reported as saved.
|
|
41187
|
+
/** @param {RawRecord} input */
|
|
40921
41188
|
#recordMany(input2) {
|
|
40922
41189
|
const extra = Object.keys(input2).filter((k) => k !== "records");
|
|
40923
41190
|
if (extra.length) {
|
|
@@ -40960,7 +41227,10 @@ var init_jobs = __esm({
|
|
|
40960
41227
|
}
|
|
40961
41228
|
// Everything that can be wrong with a record, found before anything is
|
|
40962
41229
|
// written: what the job's record and reflection become, or a RequestError.
|
|
40963
|
-
/**
|
|
41230
|
+
/**
|
|
41231
|
+
* @param {RawRecord} rec
|
|
41232
|
+
* @returns {RecordChange}
|
|
41233
|
+
*/
|
|
40964
41234
|
#checkRecord({ job_id: jobId, entries, reflection }) {
|
|
40965
41235
|
if (entries === void 0 && reflection === void 0) {
|
|
40966
41236
|
throw new RequestError(
|
|
@@ -40984,7 +41254,7 @@ var init_jobs = __esm({
|
|
|
40984
41254
|
}
|
|
40985
41255
|
const ids = recordableIds(job.result);
|
|
40986
41256
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
40987
|
-
const
|
|
41257
|
+
const written = /* @__PURE__ */ new Map();
|
|
40988
41258
|
for (const entry of entries ?? []) {
|
|
40989
41259
|
const id2 = typeof entry?.id === "string" ? entry.id.trim() : "";
|
|
40990
41260
|
if (!ids.includes(id2)) {
|
|
@@ -41000,7 +41270,7 @@ var init_jobs = __esm({
|
|
|
41000
41270
|
"invalid_verdict"
|
|
41001
41271
|
);
|
|
41002
41272
|
}
|
|
41003
|
-
|
|
41273
|
+
written.set(id2, {
|
|
41004
41274
|
id: id2,
|
|
41005
41275
|
verdict,
|
|
41006
41276
|
effect: text(entry?.effect),
|
|
@@ -41025,21 +41295,28 @@ var init_jobs = __esm({
|
|
|
41025
41295
|
}
|
|
41026
41296
|
newReflection = { delta, related_item_ids: related, recorded_at: now };
|
|
41027
41297
|
}
|
|
41028
|
-
|
|
41029
|
-
return { job, live, record: newRecord, reflection: newReflection };
|
|
41298
|
+
return { job, live, entries: entries === void 0 ? void 0 : [...written.values()], reflection: newReflection };
|
|
41030
41299
|
}
|
|
41031
|
-
|
|
41300
|
+
/** @param {RecordChange} change */
|
|
41301
|
+
#saveRecord({ job, live, entries, reflection }) {
|
|
41032
41302
|
const was = { record: job.record, reflection: job.reflection };
|
|
41033
|
-
|
|
41034
|
-
|
|
41303
|
+
const disk = loadRoundAt(job);
|
|
41304
|
+
const base = disk?.result ? disk : job;
|
|
41305
|
+
job.reflection = reflection ?? base.reflection;
|
|
41306
|
+
if (entries !== void 0) {
|
|
41307
|
+
const merged = new Map((base.record?.entries ?? []).map((e) => [e.id, e]));
|
|
41308
|
+
for (const e of entries) merged.set(e.id, e);
|
|
41309
|
+
const ids = job.result ? recordableIds(job.result) : [];
|
|
41310
|
+
job.record = { updated_at: (/* @__PURE__ */ new Date()).toISOString(), entries: ids.flatMap((id2) => merged.get(id2) ?? []) };
|
|
41311
|
+
} else if (base.record) job.record = base.record;
|
|
41035
41312
|
try {
|
|
41036
|
-
persistRound(live ? this.#record(
|
|
41313
|
+
persistRound(live ? this.#record(live) : job, { appendIndex: false });
|
|
41037
41314
|
} catch (e) {
|
|
41038
41315
|
Object.assign(job, was);
|
|
41039
41316
|
return {
|
|
41040
41317
|
job_id: job.job_id,
|
|
41041
41318
|
error: "record_not_saved",
|
|
41042
|
-
message: `the verdict for "${job.job_id}" could not be written to the history (${e.message}); record it again`
|
|
41319
|
+
message: `the verdict for "${job.job_id}" could not be written to the history (${e instanceof Error ? e.message : e}); record it again`
|
|
41043
41320
|
};
|
|
41044
41321
|
}
|
|
41045
41322
|
const summary = job.record && recordSummary(job);
|
|
@@ -41051,6 +41328,12 @@ var init_jobs = __esm({
|
|
|
41051
41328
|
reflection_saved: job.reflection != null
|
|
41052
41329
|
};
|
|
41053
41330
|
}
|
|
41331
|
+
/**
|
|
41332
|
+
* @param {string} jobId
|
|
41333
|
+
* @param {number | undefined} waitMs
|
|
41334
|
+
* @param {{detail?: string | undefined}} [opts]
|
|
41335
|
+
* @param {AbortSignal} [signal]
|
|
41336
|
+
*/
|
|
41054
41337
|
async wait(jobId, waitMs, opts, signal) {
|
|
41055
41338
|
const job = this.jobs.get(jobId);
|
|
41056
41339
|
if (!job) return this.#waitOnDisk(() => this.view(jobId, opts), waitMs, signal);
|
|
@@ -41063,6 +41346,13 @@ var init_jobs = __esm({
|
|
|
41063
41346
|
}
|
|
41064
41347
|
// A consultation another process runs has no promise here, so its history
|
|
41065
41348
|
// is re-read once a second until it is no longer running or the wait is up.
|
|
41349
|
+
/**
|
|
41350
|
+
* @template {{status: string}} T
|
|
41351
|
+
* @param {() => T | null} read
|
|
41352
|
+
* @param {number | undefined} waitMs
|
|
41353
|
+
* @param {AbortSignal} [signal]
|
|
41354
|
+
* @returns {Promise<T | null>}
|
|
41355
|
+
*/
|
|
41066
41356
|
async #waitOnDisk(read, waitMs, signal) {
|
|
41067
41357
|
const until = Date.now() + Math.min(Math.max(0, waitMs ?? 0), POLICY.maxWaitMs);
|
|
41068
41358
|
let view = read();
|
|
@@ -41085,6 +41375,7 @@ var init_jobs = __esm({
|
|
|
41085
41375
|
}
|
|
41086
41376
|
}, CANCEL_POLL_MS).unref();
|
|
41087
41377
|
}
|
|
41378
|
+
/** @param {string} jobId */
|
|
41088
41379
|
cancel(jobId) {
|
|
41089
41380
|
const job = this.jobs.get(jobId);
|
|
41090
41381
|
if (!job) {
|
|
@@ -41098,17 +41389,21 @@ var init_jobs = __esm({
|
|
|
41098
41389
|
};
|
|
41099
41390
|
}
|
|
41100
41391
|
if (job.status !== "running" && job.status !== "queued") {
|
|
41101
|
-
return { ...this
|
|
41392
|
+
return { ...this.#jobView(job, void 0), cancel_effect: `job was already ${job.status}` };
|
|
41102
41393
|
}
|
|
41103
41394
|
job.cancelRequested = true;
|
|
41104
41395
|
for (const fn of job._cancelFns) fn();
|
|
41105
41396
|
if (job._handle) job._handle.stop();
|
|
41106
|
-
return { ...this
|
|
41397
|
+
return { ...this.#jobView(job, void 0), status: "cancelling", cancel_effect: "consultant process group signalled" };
|
|
41107
41398
|
}
|
|
41108
41399
|
// Every field here is derived from the one `members` array it is handed, so a
|
|
41109
41400
|
// group view can never report one status in `members` and another in
|
|
41110
41401
|
// `comparison` or `status` for the same job.
|
|
41111
|
-
/**
|
|
41402
|
+
/**
|
|
41403
|
+
* @param {Group} group
|
|
41404
|
+
* @param {JobView[]} members
|
|
41405
|
+
* @param {{detail?: string | undefined}} [opts]
|
|
41406
|
+
*/
|
|
41112
41407
|
#shapeGroup(group, members2, { detail } = {}) {
|
|
41113
41408
|
const full = detail === "full";
|
|
41114
41409
|
const cancelling = members2.some((m) => m.status === "cancelling");
|
|
@@ -41158,14 +41453,27 @@ var init_jobs = __esm({
|
|
|
41158
41453
|
limits: live || !full ? null : limitsSummary()
|
|
41159
41454
|
};
|
|
41160
41455
|
const anyFinished = members2.some((m) => ["completed", "failed", "cancelled"].includes(m.status));
|
|
41161
|
-
|
|
41162
|
-
return view;
|
|
41456
|
+
return anyFinished ? { ...view, comparison: comparison(members2, full ? Infinity : GROUP_CHARS - printed(view)) } : view;
|
|
41163
41457
|
}
|
|
41458
|
+
/**
|
|
41459
|
+
* @param {string} groupId
|
|
41460
|
+
* @param {{detail?: string | undefined}} [opts]
|
|
41461
|
+
*/
|
|
41164
41462
|
groupView(groupId, opts) {
|
|
41165
41463
|
const group = this.groups.get(groupId) ?? loadGroup(groupId);
|
|
41166
41464
|
if (!group) return null;
|
|
41167
|
-
return this.#shapeGroup(
|
|
41465
|
+
return this.#shapeGroup(
|
|
41466
|
+
group,
|
|
41467
|
+
group.job_ids.map((jid) => this.view(jid, opts)).filter((v) => v != null),
|
|
41468
|
+
opts
|
|
41469
|
+
);
|
|
41168
41470
|
}
|
|
41471
|
+
/**
|
|
41472
|
+
* @param {string} groupId
|
|
41473
|
+
* @param {number | undefined} waitMs
|
|
41474
|
+
* @param {{detail?: string | undefined}} [opts]
|
|
41475
|
+
* @param {AbortSignal} [signal]
|
|
41476
|
+
*/
|
|
41169
41477
|
async waitGroup(groupId, waitMs, opts, signal) {
|
|
41170
41478
|
const group = this.groups.get(groupId);
|
|
41171
41479
|
if (!group) return this.#waitOnDisk(() => this.groupView(groupId, opts), waitMs, signal);
|
|
@@ -41176,6 +41484,7 @@ var init_jobs = __esm({
|
|
|
41176
41484
|
}
|
|
41177
41485
|
return this.groupView(groupId, opts);
|
|
41178
41486
|
}
|
|
41487
|
+
/** @param {string} groupId */
|
|
41179
41488
|
cancelGroup(groupId) {
|
|
41180
41489
|
const group = this.groups.get(groupId);
|
|
41181
41490
|
if (!group) {
|
|
@@ -41191,7 +41500,7 @@ var init_jobs = __esm({
|
|
|
41191
41500
|
const j = this.jobs.get(jid);
|
|
41192
41501
|
return j && (j.status === "running" || j.status === "queued");
|
|
41193
41502
|
}).length;
|
|
41194
|
-
const members2 = group.job_ids.map((jid) => this.cancel(jid)).filter(
|
|
41503
|
+
const members2 = group.job_ids.map((jid) => this.cancel(jid)).filter((v) => v != null);
|
|
41195
41504
|
const total = group.job_ids.length;
|
|
41196
41505
|
return {
|
|
41197
41506
|
...this.#shapeGroup(group, members2),
|
|
@@ -41242,7 +41551,7 @@ var init_jobs = __esm({
|
|
|
41242
41551
|
MIN_TEXT_CHARS = 40;
|
|
41243
41552
|
flagNames = (a) => {
|
|
41244
41553
|
if (!a.startsWith("-") || a === "-") return [];
|
|
41245
|
-
const name = a.split("=")
|
|
41554
|
+
const [name = ""] = a.split("=");
|
|
41246
41555
|
return a.startsWith("--") ? [name] : [name, a.slice(0, 2), `-${name}`];
|
|
41247
41556
|
};
|
|
41248
41557
|
}
|
|
@@ -41465,13 +41774,13 @@ function exportChain({ chain_id: chainId, group_id: groupId } = {}) {
|
|
|
41465
41774
|
if (Boolean(chainId) === Boolean(groupId)) {
|
|
41466
41775
|
throw new ExportError("pass exactly one of chain_id or group_id", "export_target_required");
|
|
41467
41776
|
}
|
|
41468
|
-
const chainIds = groupId ? chainsOfGroup(groupId) : [chainId];
|
|
41777
|
+
const chainIds = groupId ? chainsOfGroup(groupId) : chainId ? [chainId] : [];
|
|
41469
41778
|
const perChain = chainIds.map((id2) => ({ chain_id: id2, rounds: roundsOf(id2) }));
|
|
41470
|
-
const
|
|
41471
|
-
if (
|
|
41779
|
+
const first = perChain.find((c) => c.rounds.length)?.rounds[0];
|
|
41780
|
+
if (!first) {
|
|
41472
41781
|
throw new ExportError(`no consultation recorded under "${chainId ?? groupId}" in ~/.severally/history`, "unknown_chain");
|
|
41473
41782
|
}
|
|
41474
|
-
const
|
|
41783
|
+
const total = perChain.reduce((n, c) => n + c.rounds.length, 0);
|
|
41475
41784
|
const lines = [
|
|
41476
41785
|
`# Consultation record: ${first.question}`,
|
|
41477
41786
|
"",
|
|
@@ -41501,9 +41810,14 @@ function exportChain({ chain_id: chainId, group_id: groupId } = {}) {
|
|
|
41501
41810
|
var ExportError, historyDir, bullets2;
|
|
41502
41811
|
var init_export = __esm({
|
|
41503
41812
|
"src/export.mjs"() {
|
|
41813
|
+
"use strict";
|
|
41504
41814
|
init_policy();
|
|
41505
41815
|
init_expose_paths();
|
|
41506
41816
|
ExportError = class extends Error {
|
|
41817
|
+
/**
|
|
41818
|
+
* @param {string} message
|
|
41819
|
+
* @param {string} code
|
|
41820
|
+
*/
|
|
41507
41821
|
constructor(message, code) {
|
|
41508
41822
|
super(message);
|
|
41509
41823
|
this.name = "ExportError";
|
|
@@ -41540,6 +41854,7 @@ function staleInstall(home = os5.homedir(), severallyHome = POLICY.home) {
|
|
|
41540
41854
|
// This release names it SEVERALLY_VERSION; older ones only had it in the server info.
|
|
41541
41855
|
runtime: bundle?.match(/(?:SEVERALLY_VERSION = |name: ["']severally["'], version: )["'](\d+\.\d+\.\d+)["']/)?.[1],
|
|
41542
41856
|
claude_skills_dir: (() => {
|
|
41857
|
+
if (plugin === null) return void 0;
|
|
41543
41858
|
try {
|
|
41544
41859
|
return JSON.parse(plugin).version;
|
|
41545
41860
|
} catch {
|
|
@@ -41548,28 +41863,22 @@ function staleInstall(home = os5.homedir(), severallyHome = POLICY.home) {
|
|
|
41548
41863
|
})()
|
|
41549
41864
|
};
|
|
41550
41865
|
const copies = { this_server: SEVERALLY_VERSION };
|
|
41551
|
-
for (const [name, v] of Object.entries(found)) if (/^\d+\.\d+\.\d+$/.test(v
|
|
41866
|
+
for (const [name, v] of Object.entries(found)) if (v !== void 0 && /^\d+\.\d+\.\d+$/.test(v)) copies[name] = v;
|
|
41552
41867
|
const versions = Object.values(copies);
|
|
41553
41868
|
if (versions.every((v) => v === SEVERALLY_VERSION)) return null;
|
|
41554
41869
|
const newest = versions.reduce((a, b) => older(a, b) ? b : a);
|
|
41555
41870
|
return {
|
|
41556
41871
|
copies,
|
|
41557
|
-
behind: Object.
|
|
41872
|
+
behind: Object.entries(copies).filter(([, v]) => older(v, newest)).map(([k]) => k),
|
|
41558
41873
|
fix: `bring every copy to ${newest}: run \`npx -y severally-mcp@${newest} install\` (or \`node scripts/install.mjs\` from a checkout of that version) (it updates the runtime and the ~/.claude/skills copy), run \`/plugin marketplace update severally\` for a marketplace install, then restart the clients`
|
|
41559
41874
|
};
|
|
41560
41875
|
}
|
|
41561
41876
|
function createServer(manager = new JobManager()) {
|
|
41562
41877
|
const stale = staleInstall();
|
|
41563
41878
|
const server = new McpServer({ name: "severally", version: SEVERALLY_VERSION }, { instructions: SERVER_INSTRUCTIONS });
|
|
41564
|
-
const ok = (payload) => ({ content: [{ type: (
|
|
41565
|
-
/** @type {const} */
|
|
41566
|
-
"text"
|
|
41567
|
-
), text: JSON.stringify(payload, null, 2) }] });
|
|
41879
|
+
const ok = (payload) => ({ content: [{ type: "text", text: JSON.stringify(payload, null, 2) }] });
|
|
41568
41880
|
const fail = (payload) => ({
|
|
41569
|
-
content: [{ type: (
|
|
41570
|
-
/** @type {const} */
|
|
41571
|
-
"text"
|
|
41572
|
-
), text: JSON.stringify(payload, null, 2) }],
|
|
41881
|
+
content: [{ type: "text", text: JSON.stringify(payload, null, 2) }],
|
|
41573
41882
|
isError: true
|
|
41574
41883
|
});
|
|
41575
41884
|
server.registerTool(
|
|
@@ -41588,7 +41897,7 @@ function createServer(manager = new JobManager()) {
|
|
|
41588
41897
|
if (err instanceof RequestError) {
|
|
41589
41898
|
return fail({ error: err.code, message: err.message, details: err.details ?? null, limits: limitsSummary() });
|
|
41590
41899
|
}
|
|
41591
|
-
return fail({ error: "internal_error", message:
|
|
41900
|
+
return fail({ error: "internal_error", message: errorMessage2(err) });
|
|
41592
41901
|
}
|
|
41593
41902
|
}
|
|
41594
41903
|
);
|
|
@@ -41631,9 +41940,15 @@ function createServer(manager = new JobManager()) {
|
|
|
41631
41940
|
}, PROGRESS_EVERY_MS);
|
|
41632
41941
|
let view;
|
|
41633
41942
|
try {
|
|
41634
|
-
view = group_id ? await manager.waitGroup(group_id, wait_ms ?? DEFAULT_WAIT_MS, { detail }, req?.signal) : await manager.wait(
|
|
41943
|
+
view = group_id ? await manager.waitGroup(group_id, wait_ms ?? DEFAULT_WAIT_MS, { detail }, req?.signal) : await manager.wait(
|
|
41944
|
+
/** @type {string} */
|
|
41945
|
+
job_id,
|
|
41946
|
+
wait_ms ?? DEFAULT_WAIT_MS,
|
|
41947
|
+
{ detail },
|
|
41948
|
+
req?.signal
|
|
41949
|
+
);
|
|
41635
41950
|
} finally {
|
|
41636
|
-
clearInterval(timer);
|
|
41951
|
+
clearInterval(timer ?? void 0);
|
|
41637
41952
|
}
|
|
41638
41953
|
if (!view) {
|
|
41639
41954
|
return fail({
|
|
@@ -41643,7 +41958,11 @@ function createServer(manager = new JobManager()) {
|
|
|
41643
41958
|
}
|
|
41644
41959
|
if (!ids || ["running", "queued", "cancelling"].includes(view.status)) return ok(view);
|
|
41645
41960
|
try {
|
|
41646
|
-
return ok(pointsOf(
|
|
41961
|
+
return ok(pointsOf(
|
|
41962
|
+
/** @type {import('./jobs.mjs').JobView} */
|
|
41963
|
+
view,
|
|
41964
|
+
ids
|
|
41965
|
+
));
|
|
41647
41966
|
} catch (err) {
|
|
41648
41967
|
if (err instanceof RequestError) return fail({ error: err.code, message: err.message });
|
|
41649
41968
|
throw err;
|
|
@@ -41665,7 +41984,10 @@ function createServer(manager = new JobManager()) {
|
|
|
41665
41984
|
if (Boolean(job_id) === Boolean(group_id)) {
|
|
41666
41985
|
return fail({ error: "invalid_request", message: "pass exactly one of job_id or group_id" });
|
|
41667
41986
|
}
|
|
41668
|
-
const view = group_id ? manager.cancelGroup(group_id) : manager.cancel(
|
|
41987
|
+
const view = group_id ? manager.cancelGroup(group_id) : manager.cancel(
|
|
41988
|
+
/** @type {string} */
|
|
41989
|
+
job_id
|
|
41990
|
+
);
|
|
41669
41991
|
if (!view) {
|
|
41670
41992
|
return fail({
|
|
41671
41993
|
error: "unknown_job",
|
|
@@ -41708,7 +42030,7 @@ function createServer(manager = new JobManager()) {
|
|
|
41708
42030
|
if (err instanceof RequestError) {
|
|
41709
42031
|
return fail({ error: err.code, message: err.message, details: err.details ?? null });
|
|
41710
42032
|
}
|
|
41711
|
-
return fail({ error: "internal_error", message:
|
|
42033
|
+
return fail({ error: "internal_error", message: errorMessage2(err) });
|
|
41712
42034
|
}
|
|
41713
42035
|
}
|
|
41714
42036
|
);
|
|
@@ -41728,7 +42050,7 @@ function createServer(manager = new JobManager()) {
|
|
|
41728
42050
|
return ok(exportChain({ chain_id, group_id }));
|
|
41729
42051
|
} catch (err) {
|
|
41730
42052
|
if (err instanceof ExportError) return fail({ error: err.code, message: err.message });
|
|
41731
|
-
return fail({ error: "internal_error", message:
|
|
42053
|
+
return fail({ error: "internal_error", message: errorMessage2(err) });
|
|
41732
42054
|
}
|
|
41733
42055
|
}
|
|
41734
42056
|
);
|
|
@@ -41779,9 +42101,10 @@ async function main() {
|
|
|
41779
42101
|
process.on("exit", () => manager.killNow());
|
|
41780
42102
|
serveStdio(() => createServer(manager).server);
|
|
41781
42103
|
}
|
|
41782
|
-
var SEVERALLY_VERSION, DEFAULT_WAIT_MS, PROGRESS_EVERY_MS, semver, older, SERVER_INSTRUCTIONS, startDescription, recordShape;
|
|
42104
|
+
var SEVERALLY_VERSION, DEFAULT_WAIT_MS, PROGRESS_EVERY_MS, semver, older, SERVER_INSTRUCTIONS, startDescription, recordShape, errorMessage2;
|
|
41783
42105
|
var init_server = __esm({
|
|
41784
42106
|
"src/server.mjs"() {
|
|
42107
|
+
"use strict";
|
|
41785
42108
|
init_zod();
|
|
41786
42109
|
init_dist();
|
|
41787
42110
|
init_stdio();
|
|
@@ -41789,13 +42112,17 @@ var init_server = __esm({
|
|
|
41789
42112
|
init_schema();
|
|
41790
42113
|
init_jobs();
|
|
41791
42114
|
init_export();
|
|
41792
|
-
SEVERALLY_VERSION = "2.4.
|
|
42115
|
+
SEVERALLY_VERSION = "2.4.2";
|
|
41793
42116
|
DEFAULT_WAIT_MS = process.env.SEVERALLY_MAX_WAIT_MS ? POLICY.maxWaitMs : Math.min(POLICY.maxWaitMs, 45e3);
|
|
41794
42117
|
PROGRESS_EVERY_MS = 15e3;
|
|
41795
42118
|
semver = (v) => String(v).split(".").map(Number);
|
|
41796
42119
|
older = (a, b) => {
|
|
41797
42120
|
const [x, y] = [semver(a), semver(b)];
|
|
41798
|
-
for (let i = 0; i < 3; i++)
|
|
42121
|
+
for (let i = 0; i < 3; i++) {
|
|
42122
|
+
const [p, q] = [x[i], y[i]];
|
|
42123
|
+
if (p === void 0 || q === void 0) return false;
|
|
42124
|
+
if (p !== q) return p < q;
|
|
42125
|
+
}
|
|
41799
42126
|
return false;
|
|
41800
42127
|
};
|
|
41801
42128
|
SERVER_INSTRUCTIONS = `severally lets you get a genuinely independent opinion from another coding agent:
|
|
@@ -41858,6 +42185,7 @@ Model, permissions, rounds, timeout and size caps are fixed by this server.`;
|
|
|
41858
42185
|
related_item_ids: external_exports.array(external_exports.string().min(1)).max(50).optional().describe("f1, u1, c1 ...")
|
|
41859
42186
|
}).optional().describe("written after reading the answer")
|
|
41860
42187
|
};
|
|
42188
|
+
errorMessage2 = (err) => String((typeof err === "object" && err !== null && "message" in err ? err.message : void 0) ?? err);
|
|
41861
42189
|
}
|
|
41862
42190
|
});
|
|
41863
42191
|
|
|
@@ -41875,7 +42203,7 @@ function registration({ cmd, checks }) {
|
|
|
41875
42203
|
ran = true;
|
|
41876
42204
|
if (/(^|[\s@:])severally([\s@:]|$)/m.test(out)) return "registered (listed by the client)";
|
|
41877
42205
|
} catch (err) {
|
|
41878
|
-
if (typeof err
|
|
42206
|
+
if (typeof thrown(err, "status") === "number") ran = true;
|
|
41879
42207
|
}
|
|
41880
42208
|
}
|
|
41881
42209
|
return ran ? "not registered" : `could not tell (${cmd} ${checks.map((a) => a.join(" ")).join(" / ")} failed)`;
|
|
@@ -41888,9 +42216,9 @@ function login(cli) {
|
|
|
41888
42216
|
let ok = true;
|
|
41889
42217
|
try {
|
|
41890
42218
|
out = execCommandSync(check2.cmd, check2.args, { encoding: "utf8", timeout: 2e4, stdio: ["ignore", "pipe", "pipe"] });
|
|
41891
|
-
} catch (
|
|
41892
|
-
if (typeof
|
|
41893
|
-
out = `${
|
|
42219
|
+
} catch (e) {
|
|
42220
|
+
if (typeof thrown(e, "status") !== "number") return `could not tell (${check2.cmd} ${check2.args.join(" ")} failed)`;
|
|
42221
|
+
out = `${thrown(e, "stdout") ?? ""}`;
|
|
41894
42222
|
ok = false;
|
|
41895
42223
|
}
|
|
41896
42224
|
try {
|
|
@@ -41928,9 +42256,10 @@ function status() {
|
|
|
41928
42256
|
`);
|
|
41929
42257
|
return problem ? 1 : 0;
|
|
41930
42258
|
}
|
|
41931
|
-
var CLIENTS, LOGIN;
|
|
42259
|
+
var CLIENTS, thrown, LOGIN;
|
|
41932
42260
|
var init_status = __esm({
|
|
41933
42261
|
"src/status.mjs"() {
|
|
42262
|
+
"use strict";
|
|
41934
42263
|
init_policy();
|
|
41935
42264
|
init_server();
|
|
41936
42265
|
init_platform();
|
|
@@ -41954,6 +42283,7 @@ var init_status = __esm({
|
|
|
41954
42283
|
{ label: "Antigravity", cmd: "agy", checks: [["mcp", "list"]] },
|
|
41955
42284
|
{ label: "OpenCode", cmd: "opencode", checks: [["mcp", "list"]] }
|
|
41956
42285
|
];
|
|
42286
|
+
thrown = (e, key) => typeof e === "object" && e !== null ? Reflect.get(e, key) : void 0;
|
|
41957
42287
|
LOGIN = {
|
|
41958
42288
|
"claude-code": { cmd: "claude", args: ["auth", "status", "--json"], read: (out) => JSON.parse(out).loggedIn === true },
|
|
41959
42289
|
// Codex prints its answer on stderr; the exit status carries the verdict.
|