@ordewell/core 0.11.1 → 0.11.3
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/AUTHORS.md +4 -0
- package/dist/{IRunner-B5l9PCTv.d.ts → IRunner-DOMtDTVt.d.ts} +50 -14
- package/dist/{IRunner-DBgDx_Mr.d.mts → IRunner-ihalYfTo.d.mts} +50 -14
- package/dist/{chunk-4QXOWEGC.mjs → chunk-7WNLN63J.mjs} +11 -4
- package/dist/chunk-7WNLN63J.mjs.map +1 -0
- package/dist/index.d.mts +48 -13
- package/dist/index.d.ts +48 -13
- package/dist/index.js +1116 -524
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1085 -500
- package/dist/index.mjs.map +1 -1
- package/dist/plan-utils.d.mts +2 -2
- package/dist/plan-utils.d.ts +2 -2
- package/dist/plan-utils.js +7 -0
- package/dist/plan-utils.js.map +1 -1
- package/dist/plan-utils.mjs +3 -1
- package/dist/{runSettingsText-BKwWs052.d.mts → runSettingsText-BuLVBlXS.d.mts} +113 -67
- package/dist/{runSettingsText-CAXiTpsj.d.ts → runSettingsText-DVOd7vKC.d.ts} +113 -67
- package/dist/testing.d.mts +1 -1
- package/dist/testing.d.ts +1 -1
- package/package.json +1 -1
- package/dist/chunk-4QXOWEGC.mjs.map +0 -1
package/dist/index.js
CHANGED
|
@@ -210,6 +210,7 @@ __export(index_exports, {
|
|
|
210
210
|
assignedRunnerOf: () => assignedRunnerOf,
|
|
211
211
|
assignmentIssue: () => assignmentIssue,
|
|
212
212
|
assignmentIssueText: () => assignmentIssueText,
|
|
213
|
+
attachingLabel: () => attachingLabel,
|
|
213
214
|
augmentedPath: () => augmentedPath,
|
|
214
215
|
automergeSummary: () => automergeSummary,
|
|
215
216
|
autonomyLevelLabel: () => autonomyLevelLabel,
|
|
@@ -471,6 +472,7 @@ __export(index_exports, {
|
|
|
471
472
|
repoBaseLine: () => repoBaseLine,
|
|
472
473
|
repoResultLines: () => repoResultLines,
|
|
473
474
|
reposWithWork: () => reposWithWork,
|
|
475
|
+
repositoryRunnerConfigPaths: () => repositoryRunnerConfigPaths,
|
|
474
476
|
researchShellWarning: () => researchShellWarning,
|
|
475
477
|
researchToolsPath: () => researchToolsPath,
|
|
476
478
|
resetInstalledPlanner: () => resetInstalledPlanner,
|
|
@@ -661,18 +663,18 @@ function dependencyTargetErrors(tasks, taskId, dependencies) {
|
|
|
661
663
|
}
|
|
662
664
|
function findDependencyCycle(tasks) {
|
|
663
665
|
const byId = new Map([...walkPlan(tasks)].map(({ task: task2 }) => [task2.id, task2]));
|
|
664
|
-
const
|
|
666
|
+
const path50 = [];
|
|
665
667
|
const done = /* @__PURE__ */ new Set();
|
|
666
668
|
const visit = (id2) => {
|
|
667
669
|
if (done.has(id2)) return null;
|
|
668
|
-
const at =
|
|
669
|
-
if (at >= 0) return
|
|
670
|
-
|
|
670
|
+
const at = path50.indexOf(id2);
|
|
671
|
+
if (at >= 0) return path50.slice(at);
|
|
672
|
+
path50.push(id2);
|
|
671
673
|
for (const dep of byId.get(id2)?.dependencies ?? []) {
|
|
672
674
|
const hit = visit(dep);
|
|
673
675
|
if (hit) return hit;
|
|
674
676
|
}
|
|
675
|
-
|
|
677
|
+
path50.pop();
|
|
676
678
|
done.add(id2);
|
|
677
679
|
return null;
|
|
678
680
|
};
|
|
@@ -1737,7 +1739,7 @@ function lastServedModel(events) {
|
|
|
1737
1739
|
return void 0;
|
|
1738
1740
|
}
|
|
1739
1741
|
function taskLogForSurface(events) {
|
|
1740
|
-
return events.map((event) => event.type === "task_skills" ? { type: "task_skills", skills: event.skills.map(({ name, source, path:
|
|
1742
|
+
return events.map((event) => event.type === "task_skills" ? { type: "task_skills", skills: event.skills.map(({ name, source, path: path50 }) => ({ name, source, path: path50 })) } : event);
|
|
1741
1743
|
}
|
|
1742
1744
|
function coalesceTaskLog(events) {
|
|
1743
1745
|
const out = [];
|
|
@@ -2780,9 +2782,9 @@ function literalCdTarget(seg) {
|
|
|
2780
2782
|
return target === "" || target.startsWith("-") || target.startsWith("~") || /[*?[]/.test(target) ? void 0 : target;
|
|
2781
2783
|
}
|
|
2782
2784
|
function followCd(segments) {
|
|
2783
|
-
if (segments.some((s) => s.joinedBy === "other" ||
|
|
2785
|
+
if (segments.some((s) => s.joinedBy === "other" || movesShell(s))) return;
|
|
2784
2786
|
const inPipeline = (i) => segments[i].joinedBy === "pipe" || segments[i + 1]?.joinedBy === "pipe";
|
|
2785
|
-
if (segments.some((s, i) => s.binary === "cd" && !inPipeline(i) && literalCdTarget(s) === void 0)) return;
|
|
2787
|
+
if (segments.some((s, i) => s.binary.toLowerCase() === "cd" && !inPipeline(i) && literalCdTarget(s) === void 0)) return;
|
|
2786
2788
|
let chain = [];
|
|
2787
2789
|
segments.forEach((seg, i) => {
|
|
2788
2790
|
seg.cwd = chain;
|
|
@@ -2791,7 +2793,7 @@ function followCd(segments) {
|
|
|
2791
2793
|
});
|
|
2792
2794
|
}
|
|
2793
2795
|
function markShellState(segments, bareSegment) {
|
|
2794
|
-
if (!bareSegment && !segments.some(
|
|
2796
|
+
if (!bareSegment && !segments.some(movesShell)) return;
|
|
2795
2797
|
for (const seg of segments) if (seg.binary === "cd") seg.cdSteered = true;
|
|
2796
2798
|
}
|
|
2797
2799
|
var SHELL_STATE_COMMANDS = /* @__PURE__ */ new Set([
|
|
@@ -2810,6 +2812,7 @@ var SHELL_STATE_COMMANDS = /* @__PURE__ */ new Set([
|
|
|
2810
2812
|
"exec",
|
|
2811
2813
|
"pushd",
|
|
2812
2814
|
"popd",
|
|
2815
|
+
"chdir",
|
|
2813
2816
|
"alias",
|
|
2814
2817
|
"unalias",
|
|
2815
2818
|
"builtin",
|
|
@@ -2818,6 +2821,9 @@ var SHELL_STATE_COMMANDS = /* @__PURE__ */ new Set([
|
|
|
2818
2821
|
"hash",
|
|
2819
2822
|
"trap"
|
|
2820
2823
|
]);
|
|
2824
|
+
function movesShell(seg) {
|
|
2825
|
+
return SHELL_STATE_COMMANDS.has(seg.binary.toLowerCase());
|
|
2826
|
+
}
|
|
2821
2827
|
|
|
2822
2828
|
// src/services/commandRunners.ts
|
|
2823
2829
|
var HELP_FLAGS = ["--help", "--version"];
|
|
@@ -3673,8 +3679,9 @@ var FLAG_POLICY = {
|
|
|
3673
3679
|
sort: {
|
|
3674
3680
|
cluster: true,
|
|
3675
3681
|
// Absent on purpose: `-o`/`--output` writes a file with no redirect for the
|
|
3676
|
-
// redirect check to see,
|
|
3677
|
-
//
|
|
3682
|
+
// redirect check to see, `-T`/`--temporary-directory` names where sort writes
|
|
3683
|
+
// its run files, and `--compress-program` names a program that sort runs
|
|
3684
|
+
// over its own temporary files — a permitted binary as an interpreter.
|
|
3678
3685
|
booleans: [
|
|
3679
3686
|
"-b",
|
|
3680
3687
|
"-c",
|
|
@@ -3713,11 +3720,9 @@ var FLAG_POLICY = {
|
|
|
3713
3720
|
"-k",
|
|
3714
3721
|
"-t",
|
|
3715
3722
|
"-S",
|
|
3716
|
-
"-T",
|
|
3717
3723
|
"--key",
|
|
3718
3724
|
"--field-separator",
|
|
3719
3725
|
"--buffer-size",
|
|
3720
|
-
"--temporary-directory",
|
|
3721
3726
|
"--random-source",
|
|
3722
3727
|
"--batch-size",
|
|
3723
3728
|
"--parallel",
|
|
@@ -4344,23 +4349,28 @@ function scanFlags(seg) {
|
|
|
4344
4349
|
const booleans = /* @__PURE__ */ new Set([...spec.booleans ?? [], ...UNIVERSAL_FLAGS]);
|
|
4345
4350
|
const values = new Set(spec.values ?? []);
|
|
4346
4351
|
const operands = [];
|
|
4352
|
+
const operandAt = [];
|
|
4353
|
+
const operand = (at) => {
|
|
4354
|
+
operands.push(seg.args[at]);
|
|
4355
|
+
operandAt.push(at);
|
|
4356
|
+
};
|
|
4347
4357
|
let i = 0;
|
|
4348
4358
|
while (i < seg.args.length) {
|
|
4349
4359
|
const token = seg.args[i];
|
|
4350
4360
|
if (token === "--" && !spec.expressionArgs) {
|
|
4351
|
-
|
|
4361
|
+
for (let j = i + 1; j < seg.args.length; j++) operand(j);
|
|
4352
4362
|
break;
|
|
4353
4363
|
}
|
|
4354
4364
|
if (!/^-./.test(token)) {
|
|
4355
|
-
|
|
4365
|
+
operand(i);
|
|
4356
4366
|
i++;
|
|
4357
4367
|
continue;
|
|
4358
4368
|
}
|
|
4359
4369
|
const shape = matchFlag(spec, booleans, values, token);
|
|
4360
|
-
if (shape === void 0) return { unknown: flagLabel(token), operands };
|
|
4370
|
+
if (shape === void 0) return { unknown: flagLabel(token), operands, operandAt };
|
|
4361
4371
|
i += shape === "value" && takesNextToken(seg.args[i + 1]) ? 2 : 1;
|
|
4362
4372
|
}
|
|
4363
|
-
return { operands };
|
|
4373
|
+
return { operands, operandAt };
|
|
4364
4374
|
}
|
|
4365
4375
|
function subcommandOf(seg) {
|
|
4366
4376
|
const scan = scanFlags(seg);
|
|
@@ -4513,6 +4523,8 @@ function sedRefusalFor(binary, scripts) {
|
|
|
4513
4523
|
return `The "${binary}" script runs a shell command (the "e" command or the "e" flag of "s"), which this classifier cannot inspect. ${INSPECT_OR_TASK}`;
|
|
4514
4524
|
case "write":
|
|
4515
4525
|
return `The "${binary}" script writes a file (the "w"/"W" command or the "w" flag of "s"). You are a read-only planner \u2014 print the output instead, or describe the write as a task.`;
|
|
4526
|
+
case "read":
|
|
4527
|
+
return `The "${binary}" script reads a file it names itself (the "r"/"R" command), which path confinement cannot see. Read that file directly instead.`;
|
|
4516
4528
|
default:
|
|
4517
4529
|
return `This classifier could not read the "${binary}" script well enough to rule out a shell command or a file write in it. Rewrite it with plain print, delete and substitute commands, or describe the work as a task.`;
|
|
4518
4530
|
}
|
|
@@ -4633,6 +4645,7 @@ function sedScriptHazard(script) {
|
|
|
4633
4645
|
if (cmd === void 0) return "unreadable";
|
|
4634
4646
|
if (cmd === "e") return "exec";
|
|
4635
4647
|
if (cmd === "w" || cmd === "W") return "write";
|
|
4648
|
+
if (cmd === "r" || cmd === "R") return "read";
|
|
4636
4649
|
if (cmd === "{" || cmd === "}") continue;
|
|
4637
4650
|
if (cmd === "#") {
|
|
4638
4651
|
while (i < n && script[i] !== "\n") i++;
|
|
@@ -4654,7 +4667,7 @@ function sedScriptHazard(script) {
|
|
|
4654
4667
|
if (!commandEnds()) return "unreadable";
|
|
4655
4668
|
continue;
|
|
4656
4669
|
}
|
|
4657
|
-
if ("
|
|
4670
|
+
if ("aic".includes(cmd)) {
|
|
4658
4671
|
while (i < n && script[i] !== "\n") i += script[i] === "\\" ? 2 : 1;
|
|
4659
4672
|
continue;
|
|
4660
4673
|
}
|
|
@@ -4754,6 +4767,12 @@ function awkRefusalFor(binary, programs) {
|
|
|
4754
4767
|
return `The "${binary}" program pipes to or from a command ("|"), which runs it through a shell this classifier cannot inspect. ${INSPECT_OR_TASK}`;
|
|
4755
4768
|
case "write":
|
|
4756
4769
|
return `The "${binary}" program redirects its output to a file (">"). You are a read-only planner \u2014 print to standard output instead, or describe the write as a task.`;
|
|
4770
|
+
case "read":
|
|
4771
|
+
return `The "${binary}" program reads a file it names itself ("getline <" or an "ARGV" entry), which path confinement cannot see. Name the file as an operand instead.`;
|
|
4772
|
+
case "environment":
|
|
4773
|
+
return `The "${binary}" program reads the environment ("ENVIRON"), which holds credentials. Run printenv for the one variable you need, which asks first.`;
|
|
4774
|
+
case "symbol-table":
|
|
4775
|
+
return `The "${binary}" program uses "SYMTAB", which gawk reads as every global by name, "ENVIRON" and "ARGV" included. Name the variable you need directly.`;
|
|
4757
4776
|
case "extension":
|
|
4758
4777
|
return `The "${binary}" program uses an "@" form, which loads an extension, includes a source file or calls a function by name \u2014 none of which this classifier can inspect. ${INSPECT_OR_TASK}`;
|
|
4759
4778
|
default:
|
|
@@ -4795,6 +4814,7 @@ function awkProgramHazard(program) {
|
|
|
4795
4814
|
let slash = "regex";
|
|
4796
4815
|
let depth = 0;
|
|
4797
4816
|
let printDepth;
|
|
4817
|
+
let getlineDepth;
|
|
4798
4818
|
let continues = false;
|
|
4799
4819
|
const readRegex = () => {
|
|
4800
4820
|
i++;
|
|
@@ -4861,7 +4881,10 @@ function awkProgramHazard(program) {
|
|
|
4861
4881
|
continue;
|
|
4862
4882
|
}
|
|
4863
4883
|
if (c === "\n" || c === ";") {
|
|
4864
|
-
if (c === ";" || !continues)
|
|
4884
|
+
if (c === ";" || !continues) {
|
|
4885
|
+
printDepth = void 0;
|
|
4886
|
+
getlineDepth = void 0;
|
|
4887
|
+
}
|
|
4865
4888
|
slash = "regex";
|
|
4866
4889
|
i++;
|
|
4867
4890
|
continue;
|
|
@@ -4912,6 +4935,12 @@ function awkProgramHazard(program) {
|
|
|
4912
4935
|
slash = "unclear";
|
|
4913
4936
|
continue;
|
|
4914
4937
|
}
|
|
4938
|
+
if (c === "<") {
|
|
4939
|
+
if (getlineDepth !== void 0 && depth >= getlineDepth) return "read";
|
|
4940
|
+
i++;
|
|
4941
|
+
slash = "unclear";
|
|
4942
|
+
continue;
|
|
4943
|
+
}
|
|
4915
4944
|
if (c === "(" || c === "[") {
|
|
4916
4945
|
depth++;
|
|
4917
4946
|
i++;
|
|
@@ -4920,12 +4949,16 @@ function awkProgramHazard(program) {
|
|
|
4920
4949
|
}
|
|
4921
4950
|
if (c === ")" || c === "]") {
|
|
4922
4951
|
depth--;
|
|
4952
|
+
if (getlineDepth !== void 0 && depth < getlineDepth) getlineDepth = void 0;
|
|
4923
4953
|
i++;
|
|
4924
4954
|
slash = "divide";
|
|
4925
4955
|
continue;
|
|
4926
4956
|
}
|
|
4927
4957
|
if (c === "{" || c === "}") {
|
|
4928
|
-
if (c === "}")
|
|
4958
|
+
if (c === "}") {
|
|
4959
|
+
printDepth = void 0;
|
|
4960
|
+
getlineDepth = void 0;
|
|
4961
|
+
}
|
|
4929
4962
|
i++;
|
|
4930
4963
|
slash = "regex";
|
|
4931
4964
|
continue;
|
|
@@ -4946,6 +4979,10 @@ function awkProgramHazard(program) {
|
|
|
4946
4979
|
while (i < n && /[A-Za-z0-9_.]/.test(program[i])) i++;
|
|
4947
4980
|
const word = program.slice(start, i);
|
|
4948
4981
|
if (word === "system") return "exec";
|
|
4982
|
+
if (word === "ENVIRON") return "environment";
|
|
4983
|
+
if (word === "ARGV") return "read";
|
|
4984
|
+
if (word === "SYMTAB") return "symbol-table";
|
|
4985
|
+
if (word === "getline") getlineDepth = depth;
|
|
4949
4986
|
if (word === "print" || word === "printf") printDepth = depth;
|
|
4950
4987
|
slash = AWK_KEYWORDS.includes(word) ? "unclear" : "divide";
|
|
4951
4988
|
continue;
|
|
@@ -5832,6 +5869,36 @@ var GIT_READONLY_SUBCOMMANDS = [
|
|
|
5832
5869
|
"whatchanged",
|
|
5833
5870
|
"cat-file"
|
|
5834
5871
|
];
|
|
5872
|
+
var onlyAction = (...reads) => (after) => after[0] === void 0 || reads.includes(after[0]);
|
|
5873
|
+
var GIT_PROMPTABLE = {
|
|
5874
|
+
"ls-remote": () => true,
|
|
5875
|
+
"for-each-ref": () => true,
|
|
5876
|
+
"show-ref": () => true,
|
|
5877
|
+
"name-rev": () => true,
|
|
5878
|
+
"merge-base": () => true,
|
|
5879
|
+
"count-objects": () => true,
|
|
5880
|
+
"cherry": () => true,
|
|
5881
|
+
"range-diff": () => true,
|
|
5882
|
+
"diff-tree": () => true,
|
|
5883
|
+
"diff-files": () => true,
|
|
5884
|
+
"diff-index": () => true,
|
|
5885
|
+
"show-branch": () => true,
|
|
5886
|
+
"annotate": () => true,
|
|
5887
|
+
"check-ignore": () => true,
|
|
5888
|
+
"check-attr": () => true,
|
|
5889
|
+
"check-ref-format": () => true,
|
|
5890
|
+
"check-mailmap": () => true,
|
|
5891
|
+
"var": () => true,
|
|
5892
|
+
"version": () => true,
|
|
5893
|
+
"worktree": onlyAction("list"),
|
|
5894
|
+
"notes": onlyAction("list", "show"),
|
|
5895
|
+
"bisect": onlyAction("log"),
|
|
5896
|
+
"sparse-checkout": onlyAction("list"),
|
|
5897
|
+
"reflog": onlyAction("show", "exists"),
|
|
5898
|
+
"symbolic-ref": (after) => after.filter((a) => !a.startsWith("-")).length <= 1,
|
|
5899
|
+
// `-w` writes the object; git's shared flag set keeps it for `git diff -w`.
|
|
5900
|
+
"hash-object": (after) => !after.some((a) => /^-[^-]*w/.test(a))
|
|
5901
|
+
};
|
|
5835
5902
|
var REFUSED_COMMANDS = [
|
|
5836
5903
|
"rm",
|
|
5837
5904
|
"rmdir",
|
|
@@ -5923,7 +5990,28 @@ var REFUSED_SUBCOMMANDS = {
|
|
|
5923
5990
|
"clone",
|
|
5924
5991
|
"fetch",
|
|
5925
5992
|
"pull",
|
|
5926
|
-
"submodule"
|
|
5993
|
+
"submodule",
|
|
5994
|
+
"add",
|
|
5995
|
+
"rm",
|
|
5996
|
+
"mv",
|
|
5997
|
+
"format-patch",
|
|
5998
|
+
"replace",
|
|
5999
|
+
"mergetool",
|
|
6000
|
+
"repack",
|
|
6001
|
+
"update-index",
|
|
6002
|
+
"read-tree",
|
|
6003
|
+
"checkout-index",
|
|
6004
|
+
"merge-file",
|
|
6005
|
+
"bundle",
|
|
6006
|
+
"commit-tree",
|
|
6007
|
+
"write-tree",
|
|
6008
|
+
"mktree",
|
|
6009
|
+
"mktag",
|
|
6010
|
+
"unpack-objects",
|
|
6011
|
+
"fast-import",
|
|
6012
|
+
"pack-refs",
|
|
6013
|
+
"prune-packed",
|
|
6014
|
+
"maintenance"
|
|
5927
6015
|
],
|
|
5928
6016
|
npm: ["publish", "unpublish", "deprecate", "owner", "access", "token", "login", "logout"],
|
|
5929
6017
|
yarn: ["publish", "npm"],
|
|
@@ -6133,7 +6221,7 @@ function pathRefs(command, opts = {}) {
|
|
|
6133
6221
|
return lexAll(command.trim(), dialect, opts.cdpathSet).segments.flatMap((seg) => {
|
|
6134
6222
|
const patterns = patternArgs(seg);
|
|
6135
6223
|
const cwd = seg.cwd ?? [];
|
|
6136
|
-
return [...seg.args.filter((_, i) => !patterns.has(i)), ...seg.inputs].flatMap(pathIn).map((
|
|
6224
|
+
return [...seg.args.filter((_, i) => !patterns.has(i)), ...seg.inputs].flatMap(pathIn).map((path50) => ({ path: path50, cwd }));
|
|
6137
6225
|
});
|
|
6138
6226
|
}
|
|
6139
6227
|
function excludedBy(command, allow, opts = {}) {
|
|
@@ -6240,7 +6328,9 @@ function patternArgs(seg) {
|
|
|
6240
6328
|
var SCOPE_LEAD_ARGS = 2;
|
|
6241
6329
|
function scopeFor(seg) {
|
|
6242
6330
|
const name = seg.binaryPath ?? seg.binary;
|
|
6243
|
-
|
|
6331
|
+
const program = programName(seg.binary);
|
|
6332
|
+
if (!MULTIPLEXERS.includes(program)) return name;
|
|
6333
|
+
if (program === "git") return gitScope(seg, name);
|
|
6244
6334
|
const lead = [];
|
|
6245
6335
|
for (const arg of seg.args) {
|
|
6246
6336
|
if (arg.startsWith("-")) break;
|
|
@@ -6249,6 +6339,14 @@ function scopeFor(seg) {
|
|
|
6249
6339
|
}
|
|
6250
6340
|
return [name, ...lead].join(" ");
|
|
6251
6341
|
}
|
|
6342
|
+
function gitScope(seg, name) {
|
|
6343
|
+
const scan = scanFlags({ ...seg, binary: "git" });
|
|
6344
|
+
if (!scan || scan.unknown || scan.operands[0] === "ls-remote") return [name, ...seg.args].map(scopeWord).join(" ");
|
|
6345
|
+
return [name, ...scan.operands.slice(0, SCOPE_LEAD_ARGS)].join(" ");
|
|
6346
|
+
}
|
|
6347
|
+
function scopeWord(word) {
|
|
6348
|
+
return word === "" || /[\s"'\\]/.test(word) ? JSON.stringify(word) : word;
|
|
6349
|
+
}
|
|
6252
6350
|
function climbsByGlob(seg) {
|
|
6253
6351
|
if (!seg.globbed) return false;
|
|
6254
6352
|
const patterns = patternArgs(seg);
|
|
@@ -6270,6 +6368,27 @@ function swallowsRefName(arg) {
|
|
|
6270
6368
|
const values = FLAG_POLICY.git.values ?? [];
|
|
6271
6369
|
return [...arg.slice(1)].some((c) => c !== "n" && values.includes(`-${c}`));
|
|
6272
6370
|
}
|
|
6371
|
+
function gitArgsAfterSubcommand(seg) {
|
|
6372
|
+
const at = scanFlags(seg)?.operandAt[0];
|
|
6373
|
+
return at === void 0 ? [] : seg.args.slice(at + 1);
|
|
6374
|
+
}
|
|
6375
|
+
var REMOTE_NAME = /^[A-Za-z0-9_][A-Za-z0-9_.-]*$/;
|
|
6376
|
+
function namesConfiguredRemote(seg, after) {
|
|
6377
|
+
const destination2 = scanFlags({ ...seg, args: after })?.operands[0];
|
|
6378
|
+
return destination2 === void 0 || REMOTE_NAME.test(destination2);
|
|
6379
|
+
}
|
|
6380
|
+
function programName(binary) {
|
|
6381
|
+
return binary.toLowerCase().replace(/\.exe$/, "");
|
|
6382
|
+
}
|
|
6383
|
+
function isRemoteListing(seg) {
|
|
6384
|
+
const git2 = { ...seg, binary: programName(seg.binary) };
|
|
6385
|
+
return git2.binary === "git" && subcommandOf(git2) === "ls-remote";
|
|
6386
|
+
}
|
|
6387
|
+
function remoteListingTexts(seg) {
|
|
6388
|
+
if (!isRemoteListing(seg)) return [];
|
|
6389
|
+
const git2 = { ...seg, binary: "git" };
|
|
6390
|
+
return scanFlags({ ...git2, args: gitArgsAfterSubcommand(git2) })?.operands ?? [];
|
|
6391
|
+
}
|
|
6273
6392
|
function refusalFor(seg, trustedResolves) {
|
|
6274
6393
|
const web = webCommand(seg, trustedResolves);
|
|
6275
6394
|
if (web?.reason) return web.reason;
|
|
@@ -6307,8 +6426,24 @@ function refusalFor(seg, trustedResolves) {
|
|
|
6307
6426
|
}
|
|
6308
6427
|
if (seg.binary === "git") {
|
|
6309
6428
|
const sub = subcommandOf(seg);
|
|
6429
|
+
const after = gitArgsAfterSubcommand(seg);
|
|
6430
|
+
if (sub !== void 0 && !GIT_READONLY_SUBCOMMANDS.includes(sub)) {
|
|
6431
|
+
const reads = GIT_PROMPTABLE[sub];
|
|
6432
|
+
if (!reads) {
|
|
6433
|
+
return `"git ${sub}" is not a git subcommand known only to read, so it may write the repository. You are a read-only planner: ${EFFECT_ROUTE}`;
|
|
6434
|
+
}
|
|
6435
|
+
if (!reads(after)) {
|
|
6436
|
+
return `"git ${[sub, ...after].join(" ")}" is not a read-only form of "git ${sub}". You are a read-only planner: ${EFFECT_ROUTE}`;
|
|
6437
|
+
}
|
|
6438
|
+
}
|
|
6439
|
+
if (sub === "ls-remote" && !namesConfiguredRemote(seg, after)) {
|
|
6440
|
+
return `"git ls-remote" given a URL, an address or a path reaches it through git's own transport, which resolves the host itself and follows a redirect, so the origin approval, address guard and pinned connection a web read gets (ADR-0027) cannot hold for it. Name a remote the repository configures, such as "git ls-remote origin", or fetch the page with your web fetch tool.`;
|
|
6441
|
+
}
|
|
6442
|
+
if (sub === "ls-remote" && (seg.expandable || seg.globbed || after.some((a) => a.includes("~")))) {
|
|
6443
|
+
return `The shell rewrites part of this "git ls-remote" before git sees it (a variable, substitution, glob or "~"), so what it sends the remote, and which repository it lists, cannot be checked for a secret or approved. Write the remote and patterns literally, in single quotes where they hold a "*", and re-run.`;
|
|
6444
|
+
}
|
|
6310
6445
|
if (sub === "branch" || sub === "tag") {
|
|
6311
|
-
const flag2 = seg.args.find((a) => ["-D", "-d", "-m", "-M", "--delete", "--move"].includes(a)) ??
|
|
6446
|
+
const flag2 = seg.args.find((a) => ["-D", "-d", "-m", "-M", "--delete", "--move"].includes(a)) ?? after.find(swallowsRefName);
|
|
6312
6447
|
if (flag2) {
|
|
6313
6448
|
return `"git ${sub} ${flag2}" mutates refs. You are a read-only planner: ${EFFECT_ROUTE}`;
|
|
6314
6449
|
}
|
|
@@ -6447,10 +6582,10 @@ function classifyCommand(command, opts = {}) {
|
|
|
6447
6582
|
return { tier: "refuse", scope: "", reason: `curl and wget are refused in Full and allowlist mode. ${web.effect ? EFFECT_REFUSAL_HINT : READ_REFUSAL_HINT}` };
|
|
6448
6583
|
}
|
|
6449
6584
|
const allow = opts.allow;
|
|
6450
|
-
if (web && allow && web.urls.some((url) => scopeExcluded(allow, `${seg.binary
|
|
6585
|
+
if (web && allow && web.urls.some((url) => scopeExcluded(allow, `${programName(seg.binary)} ${url.origin}/*`))) {
|
|
6451
6586
|
return { tier: "refuse", scope: "", reason: `The web origin is excluded by the planner allowlist. ${READ_REFUSAL_HINT}` };
|
|
6452
6587
|
}
|
|
6453
|
-
const reason = refusalFor({ ...seg, binary: seg.binary
|
|
6588
|
+
const reason = refusalFor({ ...seg, binary: programName(seg.binary) }, opts.trustedWebResolves);
|
|
6454
6589
|
if (reason) return { tier: "refuse", scope: "", reason: withWrapperNote(reason, wrappers) };
|
|
6455
6590
|
if (runner && !XARGS_TARGETS.includes(seg.binary)) {
|
|
6456
6591
|
return {
|
|
@@ -6460,23 +6595,24 @@ function classifyCommand(command, opts = {}) {
|
|
|
6460
6595
|
};
|
|
6461
6596
|
}
|
|
6462
6597
|
}
|
|
6463
|
-
if (unwrapped.some(({ seg }) => seg.binary
|
|
6598
|
+
if (unwrapped.some(({ seg }) => programName(seg.binary) === "wget") && wgetVersionCommands(command, opts.dialect).includes(void 0)) {
|
|
6464
6599
|
return { tier: "refuse", scope: "", reason: `The wget executable cannot be checked safely in this working directory. ${READ_REFUSAL_HINT}` };
|
|
6465
6600
|
}
|
|
6466
6601
|
const nonAuto = unwrapped.filter((u) => u.runner !== void 0 || u.wrappers.some((w) => w !== w.toLowerCase()) || !isAuto(u.seg, opts));
|
|
6467
6602
|
if (nonAuto.length === 0) return { tier: "auto", scope: "" };
|
|
6603
|
+
const changesDirectory = unwrapped.some(({ seg }) => seg.binary.toLowerCase() === "cd");
|
|
6468
6604
|
const lineScoped = nonAuto.some((u) => {
|
|
6469
6605
|
const name = u.seg.binary.toLowerCase();
|
|
6470
|
-
return name === "cd" || name !== "command" && SHELL_STATE_COMMANDS.has(name) || climbsByGlob(u.seg);
|
|
6606
|
+
return name === "cd" || name !== "command" && SHELL_STATE_COMMANDS.has(name) || climbsByGlob(u.seg) || isRemoteListing(u.seg) && (changesDirectory || u.wrappers.length > 0 || nonAuto.length > 1);
|
|
6471
6607
|
});
|
|
6472
6608
|
const webUrls = nonAuto.flatMap((u) => webCommand(u.seg, opts.trustedWebResolves)?.urls ?? []);
|
|
6473
|
-
const
|
|
6609
|
+
const outboundTexts = nonAuto.flatMap((u) => webCommand(u.seg, opts.trustedWebResolves)?.outboundTexts ?? remoteListingTexts(u.seg));
|
|
6474
6610
|
if (lineScoped && webUrls.length === 0) {
|
|
6475
|
-
return { tier: "ask", scope: trimmed2 };
|
|
6611
|
+
return { tier: "ask", scope: trimmed2, ...outboundTexts.length ? { outboundTexts } : {} };
|
|
6476
6612
|
}
|
|
6477
6613
|
const scopes = [...new Set(nonAuto.flatMap((u) => {
|
|
6478
6614
|
const web = webCommand(u.seg, opts.trustedWebResolves);
|
|
6479
|
-
return web ? web.urls.map((url) => `${u.seg.binary
|
|
6615
|
+
return web ? web.urls.map((url) => `${programName(u.seg.binary)} ${url.origin}/*`) : [lineScoped ? trimmed2 : u.runner ? `${u.runner} ${scopeFor(u.seg)}` : scopeFor(u.seg)];
|
|
6480
6616
|
}))].sort();
|
|
6481
6617
|
const scope = scopes.join(" + ");
|
|
6482
6618
|
const perInvocation = nonAuto.some((u) => webCommand(u.seg, opts.trustedWebResolves)?.perInvocation);
|
|
@@ -6484,7 +6620,8 @@ function classifyCommand(command, opts = {}) {
|
|
|
6484
6620
|
return {
|
|
6485
6621
|
tier: "ask",
|
|
6486
6622
|
scope,
|
|
6487
|
-
...webUrls.length ? { webUrls
|
|
6623
|
+
...webUrls.length ? { webUrls } : {},
|
|
6624
|
+
...outboundTexts.length ? { outboundTexts } : {},
|
|
6488
6625
|
...scopes.length > 1 && webUrls.length ? { scopes } : {},
|
|
6489
6626
|
...perInvocation ? { perInvocation } : {},
|
|
6490
6627
|
...allowedBy ? { allowedBy } : {}
|
|
@@ -6628,10 +6765,10 @@ function summarizeToolCall(tool2, argsJson, toolLabel) {
|
|
|
6628
6765
|
const scope = args.include ? ` in ${args.include}` : "";
|
|
6629
6766
|
return `grep ${ellipsize(String(args.pattern), 40)}${scope}${mode}`;
|
|
6630
6767
|
}
|
|
6631
|
-
const
|
|
6632
|
-
if (
|
|
6633
|
-
const
|
|
6634
|
-
return `${display} ${
|
|
6768
|
+
const path50 = args.filePath || args.file_path || args.path;
|
|
6769
|
+
if (path50) {
|
|
6770
|
+
const basename7 = String(path50).split("/").pop() || String(path50);
|
|
6771
|
+
return `${display} ${basename7}`;
|
|
6635
6772
|
}
|
|
6636
6773
|
if (args.pattern) {
|
|
6637
6774
|
return `${display} ${args.pattern}`;
|
|
@@ -8116,7 +8253,7 @@ ${TASK_CONTINUE}`);
|
|
|
8116
8253
|
command = disableWebCommandStartup(command, this.researchShell.dialect);
|
|
8117
8254
|
const { approval, root } = site;
|
|
8118
8255
|
const allow = approval.allowlist?.();
|
|
8119
|
-
const { tier, scope, reason, allowedBy, perInvocation, scopes: commandScopes, webUrls,
|
|
8256
|
+
const { tier, scope, reason, allowedBy, perInvocation, scopes: commandScopes, webUrls, outboundTexts } = classifyCommand(command, {
|
|
8120
8257
|
...this.policyOptions(),
|
|
8121
8258
|
...approval.commandPolicy?.(),
|
|
8122
8259
|
...allow ? { allow } : {}
|
|
@@ -8141,8 +8278,9 @@ ${TASK_CONTINUE}`);
|
|
|
8141
8278
|
if (site.task?.sealed) return this.runSealed(site, command, request, "after", signal);
|
|
8142
8279
|
if (tier === "refuse") return this.sealedInstead(site, command, request, refusal(`Command refused: ${reason}${site.task ? `
|
|
8143
8280
|
${TASK_UNSEALED} ${TASK_CONTINUE}` : ""}`), signal);
|
|
8144
|
-
|
|
8145
|
-
|
|
8281
|
+
const outbound = [...outboundTexts ?? [], ...(webUrls ?? []).map((url) => url.href)];
|
|
8282
|
+
if (outbound.length && carriesSecret(outbound, [...this.outboundSecrets(), ...sensitiveEnvValues(process.env)])) {
|
|
8283
|
+
return refusal(webUrls?.length ? "Command refused: a web command contains what looks like a secret. Fetch the page with your web fetch tool, without credentials." : "Command refused: what this command sends over the network contains what looks like a secret. Run it without the value.");
|
|
8146
8284
|
}
|
|
8147
8285
|
const vettedAddresses = /* @__PURE__ */ new Map();
|
|
8148
8286
|
for (const url of webUrls ?? []) {
|
|
@@ -8245,7 +8383,7 @@ ${outcome.output}${outcome.success ? "" : `
|
|
|
8245
8383
|
}
|
|
8246
8384
|
let shell;
|
|
8247
8385
|
try {
|
|
8248
|
-
const sealed2 = site.task?.gitBefore ? { ...site.sealed, gitEntries: site.task.gitBefore.entries.map(({ path:
|
|
8386
|
+
const sealed2 = site.task?.gitBefore ? { ...site.sealed, gitEntries: site.task.gitBefore.entries.map(({ path: path50, directory }) => ({ path: path50, directory })) } : site.sealed;
|
|
8249
8387
|
shell = await site.sandbox.launchSealed(this.researchShell, sealed2);
|
|
8250
8388
|
} catch (err) {
|
|
8251
8389
|
return { refused: refusal(`Command not run: its sealed sandbox could not be prepared (${err instanceof Error ? err.message : String(err)}).`) };
|
|
@@ -9794,8 +9932,8 @@ function firstLineOf(command) {
|
|
|
9794
9932
|
function text(value) {
|
|
9795
9933
|
return typeof value === "string" ? value : "";
|
|
9796
9934
|
}
|
|
9797
|
-
function searchArg(pattern,
|
|
9798
|
-
return `${pattern}${
|
|
9935
|
+
function searchArg(pattern, path50, include = "") {
|
|
9936
|
+
return `${pattern}${path50 ? ` in ${path50}` : ""}${include ? ` (${include})` : ""}`;
|
|
9799
9937
|
}
|
|
9800
9938
|
function hintArg(args) {
|
|
9801
9939
|
for (const field of HINT_FIELDS) {
|
|
@@ -9900,8 +10038,8 @@ function userMessage(id2, text6, skills, { turnId, forced } = {}) {
|
|
|
9900
10038
|
...forced ? { forced: true } : {}
|
|
9901
10039
|
};
|
|
9902
10040
|
}
|
|
9903
|
-
function skillLoadNotice({ invokedBy, name, source, path:
|
|
9904
|
-
return { invokedBy, name, source, path:
|
|
10041
|
+
function skillLoadNotice({ invokedBy, name, source, path: path50, attaches }) {
|
|
10042
|
+
return { invokedBy, name, source, path: path50, ...attaches ? { attaches } : {} };
|
|
9905
10043
|
}
|
|
9906
10044
|
var LOAD_SKILL_TOOL = /(?:^|_)load_skill$/;
|
|
9907
10045
|
function surfaceStep(step) {
|
|
@@ -9918,8 +10056,8 @@ function loadedSkillName(args) {
|
|
|
9918
10056
|
return void 0;
|
|
9919
10057
|
}
|
|
9920
10058
|
}
|
|
9921
|
-
function skillLoadBlock(id2, { invokedBy, name, source, path:
|
|
9922
|
-
return { type: "skill_load", id: id2, invokedBy, name, source, path:
|
|
10059
|
+
function skillLoadBlock(id2, { invokedBy, name, source, path: path50, attaches }, turnId) {
|
|
10060
|
+
return { type: "skill_load", id: id2, invokedBy, name, source, path: path50, ...attaches ? { attaches } : {}, ...turnId ? { turnId } : {} };
|
|
9923
10061
|
}
|
|
9924
10062
|
function subagentBlock(id2, { toolCallId, model, usage, turnId, ...fields }) {
|
|
9925
10063
|
return {
|
|
@@ -10693,6 +10831,7 @@ function enabledRunners(cfg) {
|
|
|
10693
10831
|
}
|
|
10694
10832
|
|
|
10695
10833
|
// src/plugins/builtin/claudeModels.ts
|
|
10834
|
+
var path13 = __toESM(require("path"));
|
|
10696
10835
|
var ANTHROPIC_EFFORT_LEVELS = ["low", "medium", "high", "xhigh", "max"];
|
|
10697
10836
|
var ANTHROPIC_VARIANT_LABELS = {
|
|
10698
10837
|
adaptive: "Adaptive",
|
|
@@ -10786,6 +10925,53 @@ function parseClaudeHelp(stdout) {
|
|
|
10786
10925
|
}
|
|
10787
10926
|
return models;
|
|
10788
10927
|
}
|
|
10928
|
+
function claudeConfigDir(env) {
|
|
10929
|
+
return env.CLAUDE_CONFIG_DIR?.trim() || "~/.claude";
|
|
10930
|
+
}
|
|
10931
|
+
function claudeManagedSettingsDir(platform = process.platform) {
|
|
10932
|
+
if (platform === "darwin") return "/Library/Application Support/ClaudeCode";
|
|
10933
|
+
if (platform === "win32") return "C:\\Program Files\\ClaudeCode";
|
|
10934
|
+
return "/etc/claude-code";
|
|
10935
|
+
}
|
|
10936
|
+
function readClaudeSettingsModels(files, platform = process.platform) {
|
|
10937
|
+
const managed = claudeManagedSettingsDir(platform);
|
|
10938
|
+
const join46 = platform === "win32" ? path13.win32.join : path13.posix.join;
|
|
10939
|
+
const dropIns = files.list(join46(managed, "managed-settings.d")).filter((name) => name.endsWith(".json") && !name.startsWith(".")).sort().map((name) => join46(managed, "managed-settings.d", name));
|
|
10940
|
+
const ranked = [...dropIns.reverse(), join46(managed, "managed-settings.json"), join46(claudeConfigDir(files.env), "settings.json")];
|
|
10941
|
+
for (const file of ranked) {
|
|
10942
|
+
const content = files.read(file);
|
|
10943
|
+
const lineup = content === null ? null : parseClaudeSettingsModels(content);
|
|
10944
|
+
if (lineup) return lineup;
|
|
10945
|
+
}
|
|
10946
|
+
return [];
|
|
10947
|
+
}
|
|
10948
|
+
function parseClaudeSettingsModels(content) {
|
|
10949
|
+
let parsed;
|
|
10950
|
+
try {
|
|
10951
|
+
parsed = JSON.parse(content);
|
|
10952
|
+
} catch {
|
|
10953
|
+
return null;
|
|
10954
|
+
}
|
|
10955
|
+
const picker = parsed && typeof parsed === "object" ? parsed.modelPicker : void 0;
|
|
10956
|
+
const options = picker && typeof picker === "object" ? picker.options : void 0;
|
|
10957
|
+
if (!Array.isArray(options)) return null;
|
|
10958
|
+
const seen = /* @__PURE__ */ new Set();
|
|
10959
|
+
const models = [];
|
|
10960
|
+
for (const row of options) {
|
|
10961
|
+
if (!row || typeof row !== "object") continue;
|
|
10962
|
+
const { model: raw, label } = row;
|
|
10963
|
+
const model = typeof raw === "string" ? raw.trim() : "";
|
|
10964
|
+
if (!model || seen.has(model)) continue;
|
|
10965
|
+
seen.add(model);
|
|
10966
|
+
models.push({
|
|
10967
|
+
modelId: model,
|
|
10968
|
+
modelLabel: typeof label === "string" && label.trim() ? label.trim() : model,
|
|
10969
|
+
runnerProvider: model.includes("/") ? model.split("/")[0] : void 0,
|
|
10970
|
+
variants: []
|
|
10971
|
+
});
|
|
10972
|
+
}
|
|
10973
|
+
return models;
|
|
10974
|
+
}
|
|
10789
10975
|
|
|
10790
10976
|
// src/plugins/builtin/claude-code.manifest.ts
|
|
10791
10977
|
var TURN_NOT_LOGGED_IN = /not logged in\s*·\s*please run \/login/i;
|
|
@@ -10824,18 +11010,24 @@ var CLAUDE_CODE_MANIFEST = {
|
|
|
10824
11010
|
// 3. canonicalAliases — stable --model contracts (opus/sonnet/haiku/fable)
|
|
10825
11011
|
// merged into any successful result to fill gaps, and used as the full
|
|
10826
11012
|
// last-resort list when both API and CLI are unavailable.
|
|
11013
|
+
//
|
|
11014
|
+
// Whichever source answers, the models the user added to Claude's own
|
|
11015
|
+
// picker (`modelPicker.options` in its managed or user settings, usually
|
|
11016
|
+
// gateway IDs) are appended (settingsModels): the CLI accepts them as
|
|
11017
|
+
// `--model` values though no catalog above lists them.
|
|
10827
11018
|
apiDiscovery: {
|
|
10828
11019
|
url: "https://api.anthropic.com/v1/models?limit=100",
|
|
10829
11020
|
headers: { "anthropic-version": "2023-06-01" },
|
|
10830
11021
|
auth: [
|
|
10831
11022
|
{ type: "env", varName: "ANTHROPIC_API_KEY", header: "X-Api-Key" },
|
|
10832
|
-
{ type: "file", path:
|
|
11023
|
+
{ type: "file", path: (env) => `${claudeConfigDir(env)}/.credentials.json`, jsonPath: "claudeAiOauth.accessToken", header: "Authorization", prefix: "Bearer " }
|
|
10833
11024
|
],
|
|
10834
11025
|
parser: parseAnthropicModels
|
|
10835
11026
|
},
|
|
10836
11027
|
discoveryCommands: [
|
|
10837
11028
|
{ command: "claude", args: ["--help"], parser: parseClaudeHelp }
|
|
10838
11029
|
],
|
|
11030
|
+
settingsModels: (files) => readClaudeSettingsModels(files),
|
|
10839
11031
|
canonicalAliases: [
|
|
10840
11032
|
{ modelId: "opus", modelLabel: "Opus" },
|
|
10841
11033
|
{ modelId: "sonnet", modelLabel: "Sonnet" },
|
|
@@ -10893,7 +11085,7 @@ var import_path5 = require("path");
|
|
|
10893
11085
|
|
|
10894
11086
|
// src/services/harness/repoConfig.ts
|
|
10895
11087
|
var import_fs6 = require("fs");
|
|
10896
|
-
var
|
|
11088
|
+
var path14 = __toESM(require("path"));
|
|
10897
11089
|
|
|
10898
11090
|
// src/services/mcp/OrdewellMcpServer.ts
|
|
10899
11091
|
var import_http = require("http");
|
|
@@ -11857,12 +12049,12 @@ function checkpointReply(answer2) {
|
|
|
11857
12049
|
return { text: `The checkpoint was withdrawn: ${answer2.why}`, isError: true };
|
|
11858
12050
|
}
|
|
11859
12051
|
}
|
|
11860
|
-
function fieldPath(
|
|
11861
|
-
return
|
|
12052
|
+
function fieldPath(path50) {
|
|
12053
|
+
return path50.reduce((text6, part) => typeof part === "number" ? `${text6}[${part}]` : text6 ? `${text6}.${String(part)}` : String(part), "");
|
|
11862
12054
|
}
|
|
11863
|
-
function valueAt(value,
|
|
12055
|
+
function valueAt(value, path50) {
|
|
11864
12056
|
let current = value;
|
|
11865
|
-
for (const part of
|
|
12057
|
+
for (const part of path50) {
|
|
11866
12058
|
if (typeof part === "number") {
|
|
11867
12059
|
if (!Array.isArray(current)) return void 0;
|
|
11868
12060
|
current = current[part];
|
|
@@ -11884,7 +12076,7 @@ function boundedRefusal(issue, args) {
|
|
|
11884
12076
|
var LEAKED_PARAMETER = /<\/parameter>|<parameter\s+name\s*=\s*"([^"]*)">/g;
|
|
11885
12077
|
function leakedMarkupIn(value) {
|
|
11886
12078
|
const found = [];
|
|
11887
|
-
const walk = (node,
|
|
12079
|
+
const walk = (node, path50) => {
|
|
11888
12080
|
if (typeof node === "string") {
|
|
11889
12081
|
const names = [];
|
|
11890
12082
|
let closing = false;
|
|
@@ -11892,25 +12084,25 @@ function leakedMarkupIn(value) {
|
|
|
11892
12084
|
if (match[1] !== void 0) names.push(match[1]);
|
|
11893
12085
|
else closing = true;
|
|
11894
12086
|
}
|
|
11895
|
-
if (names.length > 0 || closing) found.push({ path:
|
|
12087
|
+
if (names.length > 0 || closing) found.push({ path: path50, names: [...new Set(names)], closing });
|
|
11896
12088
|
return;
|
|
11897
12089
|
}
|
|
11898
12090
|
if (Array.isArray(node)) {
|
|
11899
|
-
node.forEach((item, i) => walk(item, `${
|
|
12091
|
+
node.forEach((item, i) => walk(item, `${path50}[${i}]`));
|
|
11900
12092
|
return;
|
|
11901
12093
|
}
|
|
11902
12094
|
if (node !== null && typeof node === "object") {
|
|
11903
|
-
for (const [key, item] of Object.entries(node)) walk(item,
|
|
12095
|
+
for (const [key, item] of Object.entries(node)) walk(item, path50 ? `${path50}.${key}` : key);
|
|
11904
12096
|
}
|
|
11905
12097
|
};
|
|
11906
12098
|
walk(value, "");
|
|
11907
12099
|
return found;
|
|
11908
12100
|
}
|
|
11909
12101
|
function markupRefusal(leaks) {
|
|
11910
|
-
return leaks.map(({ path:
|
|
12102
|
+
return leaks.map(({ path: path50, names, closing }) => {
|
|
11911
12103
|
const tags = [...names.map((n) => `<parameter name="${n}">`), ...closing ? ["</parameter>"] : []];
|
|
11912
|
-
const guide = names.length > 0 ? ` The parameters ${names.map((n) => JSON.stringify(n)).join(", ")} are inside it; pass them as separate arguments, not inside ${
|
|
11913
|
-
return `${
|
|
12104
|
+
const guide = names.length > 0 ? ` The parameters ${names.map((n) => JSON.stringify(n)).join(", ")} are inside it; pass them as separate arguments, not inside ${path50}.` : " Pass each argument separately, not inside one string.";
|
|
12105
|
+
return `${path50} contains leaked tool-call markup: ${tags.join(", ")}.${guide}`;
|
|
11914
12106
|
}).join("\n");
|
|
11915
12107
|
}
|
|
11916
12108
|
function invalidArguments(name, error, args) {
|
|
@@ -12375,9 +12567,9 @@ function mcpClientConfig(credential) {
|
|
|
12375
12567
|
}
|
|
12376
12568
|
function ownerOnlyConfigFile(fileName, contents) {
|
|
12377
12569
|
const dir = (0, import_fs5.mkdtempSync)((0, import_path4.join)((0, import_os.tmpdir)(), "ordewell-mcp-"));
|
|
12378
|
-
const
|
|
12379
|
-
(0, import_fs5.writeFileSync)(
|
|
12380
|
-
return { path:
|
|
12570
|
+
const path50 = (0, import_path4.join)(dir, fileName);
|
|
12571
|
+
(0, import_fs5.writeFileSync)(path50, contents, { mode: 384 });
|
|
12572
|
+
return { path: path50, remove: () => (0, import_fs5.rmSync)(dir, { recursive: true, force: true }) };
|
|
12381
12573
|
}
|
|
12382
12574
|
|
|
12383
12575
|
// src/services/harness/repoConfig.ts
|
|
@@ -12400,19 +12592,19 @@ function onDisk(target) {
|
|
|
12400
12592
|
try {
|
|
12401
12593
|
return import_fs6.realpathSync.native(target);
|
|
12402
12594
|
} catch {
|
|
12403
|
-
return
|
|
12595
|
+
return path14.resolve(target);
|
|
12404
12596
|
}
|
|
12405
12597
|
}
|
|
12406
12598
|
function insideOnDisk(root, target) {
|
|
12407
|
-
const rel =
|
|
12408
|
-
return rel === "" || !rel.startsWith("..") && !
|
|
12599
|
+
const rel = path14.relative(onDisk(root), onDisk(target));
|
|
12600
|
+
return rel === "" || !rel.startsWith("..") && !path14.isAbsolute(rel);
|
|
12409
12601
|
}
|
|
12410
12602
|
function repoLevels(workspace) {
|
|
12411
12603
|
const levels = [];
|
|
12412
|
-
for (let dir =
|
|
12604
|
+
for (let dir = path14.resolve(workspace); ; dir = path14.dirname(dir)) {
|
|
12413
12605
|
levels.unshift(dir);
|
|
12414
|
-
if ((0, import_fs6.existsSync)(
|
|
12415
|
-
if (
|
|
12606
|
+
if ((0, import_fs6.existsSync)(path14.join(dir, ".git"))) return levels;
|
|
12607
|
+
if (path14.dirname(dir) === dir) return [path14.resolve(workspace)];
|
|
12416
12608
|
}
|
|
12417
12609
|
}
|
|
12418
12610
|
function repoRoot(workspace) {
|
|
@@ -12506,33 +12698,33 @@ var TomlReader = class {
|
|
|
12506
12698
|
if (this.i >= this.src.length) return root;
|
|
12507
12699
|
if (this.src.startsWith("[[", this.i)) {
|
|
12508
12700
|
this.i += 2;
|
|
12509
|
-
const
|
|
12701
|
+
const path50 = this.key();
|
|
12510
12702
|
this.expect("]]");
|
|
12511
|
-
const parent = this.tableAt(root,
|
|
12512
|
-
const last =
|
|
12703
|
+
const parent = this.tableAt(root, path50.slice(0, -1));
|
|
12704
|
+
const last = path50[path50.length - 1];
|
|
12513
12705
|
const list = Object.hasOwn(parent, last) ? parent[last] : parent[last] = [];
|
|
12514
|
-
if (!Array.isArray(list)) throw this.fail(`${
|
|
12706
|
+
if (!Array.isArray(list)) throw this.fail(`${path50.join(".")} is not an array of tables`);
|
|
12515
12707
|
table = newTable();
|
|
12516
12708
|
list.push(table);
|
|
12517
12709
|
} else if (this.src[this.i] === "[") {
|
|
12518
12710
|
this.i += 1;
|
|
12519
|
-
const
|
|
12711
|
+
const path50 = this.key();
|
|
12520
12712
|
this.expect("]");
|
|
12521
|
-
table = this.tableAt(root,
|
|
12713
|
+
table = this.tableAt(root, path50);
|
|
12522
12714
|
} else {
|
|
12523
|
-
const
|
|
12715
|
+
const path50 = this.key();
|
|
12524
12716
|
this.skipBlank(false);
|
|
12525
12717
|
this.expect("=");
|
|
12526
12718
|
this.skipBlank(false);
|
|
12527
|
-
const owner = this.tableAt(table,
|
|
12528
|
-
owner[
|
|
12719
|
+
const owner = this.tableAt(table, path50.slice(0, -1));
|
|
12720
|
+
owner[path50[path50.length - 1]] = this.value();
|
|
12529
12721
|
}
|
|
12530
12722
|
this.endOfLine();
|
|
12531
12723
|
}
|
|
12532
12724
|
}
|
|
12533
|
-
tableAt(from,
|
|
12725
|
+
tableAt(from, path50) {
|
|
12534
12726
|
let table = from;
|
|
12535
|
-
for (const part of
|
|
12727
|
+
for (const part of path50) {
|
|
12536
12728
|
const next = Object.hasOwn(table, part) ? table[part] : void 0;
|
|
12537
12729
|
if (next === void 0) table = table[part] = newTable();
|
|
12538
12730
|
else if (isRecord2(next)) table = next;
|
|
@@ -12601,10 +12793,10 @@ var TomlReader = class {
|
|
|
12601
12793
|
this.i++;
|
|
12602
12794
|
return table;
|
|
12603
12795
|
}
|
|
12604
|
-
const
|
|
12796
|
+
const path50 = this.key();
|
|
12605
12797
|
this.expect("=");
|
|
12606
12798
|
this.skipBlank(false);
|
|
12607
|
-
this.tableAt(table,
|
|
12799
|
+
this.tableAt(table, path50.slice(0, -1))[path50[path50.length - 1]] = this.value();
|
|
12608
12800
|
this.skipBlank(true);
|
|
12609
12801
|
if (this.src[this.i] === ",") this.i++;
|
|
12610
12802
|
else if (this.src[this.i] !== "}") throw this.fail("expected , or } in an inline table");
|
|
@@ -12749,7 +12941,8 @@ var CODEX_MANIFEST = {
|
|
|
12749
12941
|
appServer: {
|
|
12750
12942
|
command: "codex",
|
|
12751
12943
|
args: ["app-server"],
|
|
12752
|
-
|
|
12944
|
+
// CODEX_HOME moves all of Codex's own files from `~/.codex`.
|
|
12945
|
+
cacheFile: (env) => `${env.CODEX_HOME?.trim() || "~/.codex"}/models_cache.json`
|
|
12753
12946
|
},
|
|
12754
12947
|
canonicalAliases: [
|
|
12755
12948
|
{ modelId: "gpt-5.6-sol", modelLabel: "GPT-5.6-Sol" },
|
|
@@ -12978,8 +13171,7 @@ var LineBuffer = class {
|
|
|
12978
13171
|
// src/services/harness/ClaudeCodeAdapter.ts
|
|
12979
13172
|
var import_crypto6 = require("crypto");
|
|
12980
13173
|
var import_fs11 = require("fs");
|
|
12981
|
-
var
|
|
12982
|
-
var path21 = __toESM(require("path"));
|
|
13174
|
+
var path23 = __toESM(require("path"));
|
|
12983
13175
|
|
|
12984
13176
|
// src/plugins/resolveArgs.ts
|
|
12985
13177
|
function claudeThinkingArgs(effort) {
|
|
@@ -13017,14 +13209,29 @@ function resolveTaskRunnerFlags(manifest, ctx) {
|
|
|
13017
13209
|
return flags;
|
|
13018
13210
|
}
|
|
13019
13211
|
|
|
13212
|
+
// src/utils/runnerHome.ts
|
|
13213
|
+
var os5 = __toESM(require("os"));
|
|
13214
|
+
function runnerHomeDir(env, platform = process.platform) {
|
|
13215
|
+
const name = platform === "win32" ? "USERPROFILE" : "HOME";
|
|
13216
|
+
const key = platform === "win32" ? Object.keys(env).filter((k) => k.toUpperCase() === name).pop() : name;
|
|
13217
|
+
const home = key === void 0 ? void 0 : env[key];
|
|
13218
|
+
return home ?? os5.homedir();
|
|
13219
|
+
}
|
|
13220
|
+
function expandRunnerHome(filePath, env = process.env) {
|
|
13221
|
+
if (!filePath.startsWith("~")) return filePath;
|
|
13222
|
+
const home = runnerHomeDir(env);
|
|
13223
|
+
return home ? home + filePath.slice(1) : null;
|
|
13224
|
+
}
|
|
13225
|
+
|
|
13020
13226
|
// src/services/harness/runnerProcess.ts
|
|
13227
|
+
var import_net = require("net");
|
|
13021
13228
|
var import_string_decoder = require("string_decoder");
|
|
13022
13229
|
|
|
13023
13230
|
// src/utils/shellPath.ts
|
|
13024
13231
|
var import_child_process3 = require("child_process");
|
|
13025
13232
|
var import_util = require("util");
|
|
13026
|
-
var
|
|
13027
|
-
var
|
|
13233
|
+
var os6 = __toESM(require("os"));
|
|
13234
|
+
var path15 = __toESM(require("path"));
|
|
13028
13235
|
var execAsync = (0, import_util.promisify)(import_child_process3.exec);
|
|
13029
13236
|
var LOGIN_SHELL_TIMEOUT_MS = 5e3;
|
|
13030
13237
|
var PATH_MARKER = "__ORDEWELL_PATH__";
|
|
@@ -13033,42 +13240,42 @@ var defaultExec = async (command, options) => {
|
|
|
13033
13240
|
return { stdout: String(stdout) };
|
|
13034
13241
|
};
|
|
13035
13242
|
function posixBinDirs() {
|
|
13036
|
-
const home =
|
|
13243
|
+
const home = os6.homedir();
|
|
13037
13244
|
return [
|
|
13038
|
-
|
|
13039
|
-
|
|
13040
|
-
|
|
13041
|
-
|
|
13042
|
-
|
|
13043
|
-
|
|
13044
|
-
|
|
13245
|
+
path15.join(home, ".local", "bin"),
|
|
13246
|
+
path15.join(home, ".opencode", "bin"),
|
|
13247
|
+
path15.join(home, ".claude", "bin"),
|
|
13248
|
+
path15.join(home, ".bun", "bin"),
|
|
13249
|
+
path15.join(home, ".volta", "bin"),
|
|
13250
|
+
path15.join(home, ".npm-global", "bin"),
|
|
13251
|
+
path15.join(home, "bin"),
|
|
13045
13252
|
"/usr/local/bin",
|
|
13046
13253
|
"/opt/homebrew/bin"
|
|
13047
13254
|
];
|
|
13048
13255
|
}
|
|
13049
13256
|
function windowsBinDirs(env, home) {
|
|
13050
|
-
const
|
|
13051
|
-
const under = (base, ...parts) => base ? [
|
|
13257
|
+
const join46 = (...parts) => path15.win32.join(...parts);
|
|
13258
|
+
const under = (base, ...parts) => base ? [join46(base, ...parts)] : [];
|
|
13052
13259
|
const appData = env.APPDATA;
|
|
13053
13260
|
const localAppData = env.LOCALAPPDATA;
|
|
13054
13261
|
const programData = env.ProgramData;
|
|
13055
13262
|
return [
|
|
13056
13263
|
// Native per-user installers, PowerShell-driven and otherwise.
|
|
13057
|
-
|
|
13058
|
-
|
|
13059
|
-
|
|
13060
|
-
|
|
13264
|
+
join46(home, ".local", "bin"),
|
|
13265
|
+
join46(home, ".opencode", "bin"),
|
|
13266
|
+
join46(home, ".claude", "bin"),
|
|
13267
|
+
join46(home, ".codex", "bin"),
|
|
13061
13268
|
...under(localAppData, "Programs", "claude"),
|
|
13062
13269
|
// Node package managers. npm's global prefix is `%APPDATA%\npm`; the
|
|
13063
13270
|
// literal spelling is kept as a fallback for a host that does not export
|
|
13064
13271
|
// APPDATA (a service account, a stripped CI container).
|
|
13065
13272
|
...under(appData, "npm"),
|
|
13066
|
-
|
|
13273
|
+
join46(home, "AppData", "Roaming", "npm"),
|
|
13067
13274
|
...under(localAppData, "pnpm"),
|
|
13068
13275
|
...under(localAppData, "Yarn", "bin"),
|
|
13069
|
-
|
|
13276
|
+
join46(home, ".bun", "bin"),
|
|
13070
13277
|
// Windows package managers.
|
|
13071
|
-
|
|
13278
|
+
join46(home, "scoop", "shims"),
|
|
13072
13279
|
...under(env.SCOOP, "shims"),
|
|
13073
13280
|
...under(env.SCOOP_GLOBAL ?? programData, "scoop", "shims"),
|
|
13074
13281
|
...under(programData, "chocolatey", "bin"),
|
|
@@ -13078,13 +13285,13 @@ function windowsBinDirs(env, home) {
|
|
|
13078
13285
|
...under(localAppData, "Microsoft", "WindowsApps"),
|
|
13079
13286
|
// Version managers. Volta's Windows home is not the POSIX `~/.volta`.
|
|
13080
13287
|
...under(localAppData, "Volta", "bin"),
|
|
13081
|
-
|
|
13288
|
+
join46(home, ".volta", "bin")
|
|
13082
13289
|
];
|
|
13083
13290
|
}
|
|
13084
13291
|
function wellKnownBinDirs(deps = {}) {
|
|
13085
13292
|
const platform = deps.platform ?? process.platform;
|
|
13086
13293
|
if (platform !== "win32") return posixBinDirs();
|
|
13087
|
-
return [...new Set(windowsBinDirs(deps.env ?? process.env, deps.home ??
|
|
13294
|
+
return [...new Set(windowsBinDirs(deps.env ?? process.env, deps.home ?? os6.homedir()))];
|
|
13088
13295
|
}
|
|
13089
13296
|
async function loginShellPath(execImpl) {
|
|
13090
13297
|
if (process.platform === "win32") return null;
|
|
@@ -13107,14 +13314,14 @@ function augmentedPath(execImpl = defaultExec) {
|
|
|
13107
13314
|
const segments = [];
|
|
13108
13315
|
const push = (p) => {
|
|
13109
13316
|
if (!p) return;
|
|
13110
|
-
for (const seg of p.split(
|
|
13317
|
+
for (const seg of p.split(path15.delimiter)) {
|
|
13111
13318
|
if (seg && !segments.includes(seg)) segments.push(seg);
|
|
13112
13319
|
}
|
|
13113
13320
|
};
|
|
13114
13321
|
push(process.env.PATH);
|
|
13115
13322
|
push(await loginShellPath(execImpl));
|
|
13116
13323
|
for (const dir of wellKnownBinDirs()) push(dir);
|
|
13117
|
-
return segments.join(
|
|
13324
|
+
return segments.join(path15.delimiter);
|
|
13118
13325
|
})();
|
|
13119
13326
|
}
|
|
13120
13327
|
return cachedPath;
|
|
@@ -13136,7 +13343,7 @@ function withPath(base, resolvedPath, overrides = {}) {
|
|
|
13136
13343
|
|
|
13137
13344
|
// src/utils/launch.ts
|
|
13138
13345
|
var fsSync4 = __toESM(require("fs"));
|
|
13139
|
-
var
|
|
13346
|
+
var path16 = __toESM(require("path"));
|
|
13140
13347
|
var DIRECT_EXTENSIONS = [".exe", ".com"];
|
|
13141
13348
|
var BATCH_EXTENSIONS = [".cmd", ".bat"];
|
|
13142
13349
|
var POWERSHELL_EXTENSIONS = [".ps1"];
|
|
@@ -13144,7 +13351,7 @@ var CMD_EXE_MAX_COMMAND_LINE = 8191;
|
|
|
13144
13351
|
var WINDOWS_MAX_COMMAND_LINE = 32767;
|
|
13145
13352
|
function defaultPowerShell() {
|
|
13146
13353
|
const root = process.env.SystemRoot || process.env.windir;
|
|
13147
|
-
return root ?
|
|
13354
|
+
return root ? path16.win32.join(root, "System32", "WindowsPowerShell", "v1.0", "powershell.exe") : "powershell.exe";
|
|
13148
13355
|
}
|
|
13149
13356
|
var POWERSHELL_FLAGS = ["-NoLogo", "-NoProfile", "-NonInteractive", "-ExecutionPolicy", "Bypass", "-File"];
|
|
13150
13357
|
var CommandLineTooLongError = class extends Error {
|
|
@@ -13210,7 +13417,7 @@ Searched: ${searchedPath || "(empty PATH)"}`);
|
|
|
13210
13417
|
};
|
|
13211
13418
|
function isResolvableOnPosixPath(command, PATH, exists) {
|
|
13212
13419
|
if (command.includes("/")) return exists(command);
|
|
13213
|
-
return PATH.split(
|
|
13420
|
+
return PATH.split(path16.delimiter).map((d) => d.trim()).filter(Boolean).some((dir) => exists(path16.join(dir, command)));
|
|
13214
13421
|
}
|
|
13215
13422
|
function isExecutableResolved(command, plan, PATH, deps = {}) {
|
|
13216
13423
|
if ((deps.platform ?? process.platform) === "win32") return plan.file !== command;
|
|
@@ -13250,9 +13457,9 @@ function cmdArgs(line) {
|
|
|
13250
13457
|
return ["/d", "/s", "/c", `"${line}"`];
|
|
13251
13458
|
}
|
|
13252
13459
|
function candidateExtensions(command, pathExt) {
|
|
13253
|
-
const declared = pathExt.split(
|
|
13460
|
+
const declared = pathExt.split(path16.win32.delimiter).map((e) => e.trim().toLowerCase()).filter(Boolean);
|
|
13254
13461
|
const known = /* @__PURE__ */ new Set([...DIRECT_EXTENSIONS, ...BATCH_EXTENSIONS, ...POWERSHELL_EXTENSIONS, ...declared]);
|
|
13255
|
-
const ext =
|
|
13462
|
+
const ext = path16.win32.extname(command).toLowerCase();
|
|
13256
13463
|
if (ext && known.has(ext)) {
|
|
13257
13464
|
const route = BATCH_EXTENSIONS.includes(ext) ? "cmd" : POWERSHELL_EXTENSIONS.includes(ext) ? "powershell" : "direct";
|
|
13258
13465
|
return { direct: [], cmd: [], powershell: [], [route]: [""] };
|
|
@@ -13273,13 +13480,13 @@ async function findWindowsExecutables(command, deps) {
|
|
|
13273
13480
|
const exists = deps.exists ?? defaultExists3;
|
|
13274
13481
|
const pathExt = (deps.pathExt ?? (() => process.env.PATHEXT ?? ""))();
|
|
13275
13482
|
const byRoute = candidateExtensions(command, pathExt);
|
|
13276
|
-
const dirs =
|
|
13277
|
-
const base =
|
|
13483
|
+
const dirs = path16.win32.basename(command) !== command ? [path16.win32.dirname(path16.win32.resolve(command))] : (await (deps.resolvePath ?? augmentedPath)() || "").split(path16.win32.delimiter).map((d) => d.trim().replace(/^"|"$/g, "")).filter(Boolean);
|
|
13484
|
+
const base = path16.win32.basename(command);
|
|
13278
13485
|
const found = [];
|
|
13279
13486
|
for (const route of ROUTES) {
|
|
13280
13487
|
outer: for (const ext of byRoute[route]) {
|
|
13281
13488
|
for (const dir of dirs) {
|
|
13282
|
-
const candidate =
|
|
13489
|
+
const candidate = path16.win32.join(dir, `${base}${ext}`);
|
|
13283
13490
|
if (exists(candidate)) {
|
|
13284
13491
|
found.push({ file: candidate, route });
|
|
13285
13492
|
break outer;
|
|
@@ -13327,7 +13534,7 @@ async function planDirectLaunch(command, args, deps = {}) {
|
|
|
13327
13534
|
|
|
13328
13535
|
// src/utils/workspace.ts
|
|
13329
13536
|
var fs3 = __toESM(require("fs"));
|
|
13330
|
-
var
|
|
13537
|
+
var path17 = __toESM(require("path"));
|
|
13331
13538
|
var WorkspaceNotFoundError = class extends Error {
|
|
13332
13539
|
constructor(workspace) {
|
|
13333
13540
|
super(`Workspace "${workspace}" does not exist.`);
|
|
@@ -13383,7 +13590,7 @@ function assertWorkspaceIsProject(workspace, deps = {}) {
|
|
|
13383
13590
|
throw new WorkspaceNotAProjectError(workspace);
|
|
13384
13591
|
}
|
|
13385
13592
|
const hasMarker = [...PROJECT_MARKER_DIRS, ...PROJECT_MARKER_FILES].some(
|
|
13386
|
-
(marker2) => exists(
|
|
13593
|
+
(marker2) => exists(path17.join(resolved, marker2))
|
|
13387
13594
|
);
|
|
13388
13595
|
if (!hasMarker) throw new WorkspaceNotAProjectError(workspace);
|
|
13389
13596
|
}
|
|
@@ -13391,12 +13598,12 @@ function assertWorkspaceIsProject(workspace, deps = {}) {
|
|
|
13391
13598
|
// src/services/workspaceEnv.ts
|
|
13392
13599
|
var import_child_process4 = require("child_process");
|
|
13393
13600
|
var fs5 = __toESM(require("fs"));
|
|
13394
|
-
var
|
|
13395
|
-
var
|
|
13601
|
+
var os7 = __toESM(require("os"));
|
|
13602
|
+
var path19 = __toESM(require("path"));
|
|
13396
13603
|
|
|
13397
13604
|
// src/utils/fsHelpers.ts
|
|
13398
13605
|
var fs4 = __toESM(require("fs"));
|
|
13399
|
-
var
|
|
13606
|
+
var path18 = __toESM(require("path"));
|
|
13400
13607
|
var import_crypto5 = require("crypto");
|
|
13401
13608
|
var STATE_DIR = ".ordewell";
|
|
13402
13609
|
function ensureDir(dir) {
|
|
@@ -13405,9 +13612,9 @@ function ensureDir(dir) {
|
|
|
13405
13612
|
}
|
|
13406
13613
|
}
|
|
13407
13614
|
function writeFileAtomic(filePath, content) {
|
|
13408
|
-
const dir =
|
|
13615
|
+
const dir = path18.dirname(filePath);
|
|
13409
13616
|
ensureDir(dir);
|
|
13410
|
-
const tempPath =
|
|
13617
|
+
const tempPath = path18.join(dir, `.${path18.basename(filePath)}.${(0, import_crypto5.randomUUID)()}.tmp`);
|
|
13411
13618
|
fs4.writeFileSync(tempPath, content, { flag: "wx" });
|
|
13412
13619
|
try {
|
|
13413
13620
|
fs4.renameSync(tempPath, filePath);
|
|
@@ -13420,7 +13627,7 @@ function writeFileAtomic(filePath, content) {
|
|
|
13420
13627
|
}
|
|
13421
13628
|
}
|
|
13422
13629
|
function getStateDir(baseDir) {
|
|
13423
|
-
return
|
|
13630
|
+
return path18.join(baseDir ?? process.cwd(), STATE_DIR);
|
|
13424
13631
|
}
|
|
13425
13632
|
var IGNORE_FILE = ".gitignore";
|
|
13426
13633
|
var STATE_DIR_IGNORE = "*\n!.gitignore\n!skills/\n!skills/**\n";
|
|
@@ -13428,7 +13635,7 @@ var LEGACY_IGNORES = /* @__PURE__ */ new Set(["*", "*\n"]);
|
|
|
13428
13635
|
function ensureStateDirIgnored(baseDir) {
|
|
13429
13636
|
const dir = getStateDir(baseDir);
|
|
13430
13637
|
ensureDir(dir);
|
|
13431
|
-
const file =
|
|
13638
|
+
const file = path18.join(dir, IGNORE_FILE);
|
|
13432
13639
|
try {
|
|
13433
13640
|
fs4.writeFileSync(file, STATE_DIR_IGNORE, { flag: "wx" });
|
|
13434
13641
|
return;
|
|
@@ -13963,7 +14170,7 @@ function defaultDeps() {
|
|
|
13963
14170
|
}
|
|
13964
14171
|
},
|
|
13965
14172
|
baseEnv: process.env,
|
|
13966
|
-
home:
|
|
14173
|
+
home: os7.homedir()
|
|
13967
14174
|
};
|
|
13968
14175
|
}
|
|
13969
14176
|
var REFUSED2 = new Set(SETTINGS_ENV_REFUSED);
|
|
@@ -13975,7 +14182,7 @@ async function resolveWorkspaceEnv(cwd, overrides = {}) {
|
|
|
13975
14182
|
const direnv = await fromDirenv(checkoutDirOf(cwd, deps), deps);
|
|
13976
14183
|
result.blockedEnvrc = direnv.blocked;
|
|
13977
14184
|
Object.assign(collected, direnv.env);
|
|
13978
|
-
const file = findUp(cwd,
|
|
14185
|
+
const file = findUp(cwd, path19.join(STATE_DIR, WORKSPACE_ENV_FILE), deps);
|
|
13979
14186
|
if (file) {
|
|
13980
14187
|
const content = deps.readFile(file);
|
|
13981
14188
|
if (content !== null) {
|
|
@@ -13992,7 +14199,7 @@ async function resolveWorkspaceEnv(cwd, overrides = {}) {
|
|
|
13992
14199
|
}
|
|
13993
14200
|
var evaluating = /* @__PURE__ */ new Map();
|
|
13994
14201
|
async function workspaceEnvOf(cwd) {
|
|
13995
|
-
const key =
|
|
14202
|
+
const key = path19.resolve(cwd);
|
|
13996
14203
|
let running2 = evaluating.get(key);
|
|
13997
14204
|
if (!running2) {
|
|
13998
14205
|
running2 = resolveWorkspaceEnv(cwd).then((result) => result.env).finally(() => evaluating.delete(key));
|
|
@@ -14001,11 +14208,11 @@ async function workspaceEnvOf(cwd) {
|
|
|
14001
14208
|
return { ...await running2 };
|
|
14002
14209
|
}
|
|
14003
14210
|
function checkoutDirOf(cwd, deps) {
|
|
14004
|
-
const parts =
|
|
14211
|
+
const parts = path19.resolve(cwd).split(path19.sep);
|
|
14005
14212
|
const at = parts.findIndex((part, i) => part === STATE_DIR && parts[i + 1] === "worktrees");
|
|
14006
14213
|
if (at < 0 || parts.length < at + 4) return cwd;
|
|
14007
|
-
const root = parts.slice(0, at).join(
|
|
14008
|
-
const mapped =
|
|
14214
|
+
const root = parts.slice(0, at).join(path19.sep) || path19.sep;
|
|
14215
|
+
const mapped = path19.join(root, ...parts.slice(at + 4));
|
|
14009
14216
|
return deps.isDirectory(mapped) ? mapped : root;
|
|
14010
14217
|
}
|
|
14011
14218
|
async function fromDirenv(cwd, deps) {
|
|
@@ -14035,18 +14242,18 @@ async function fromDirenv(cwd, deps) {
|
|
|
14035
14242
|
return { env, blocked: null };
|
|
14036
14243
|
}
|
|
14037
14244
|
function findUp(start, relative17, deps) {
|
|
14038
|
-
let dir =
|
|
14245
|
+
let dir = path19.resolve(start);
|
|
14039
14246
|
for (; ; ) {
|
|
14040
|
-
const candidate =
|
|
14247
|
+
const candidate = path19.join(dir, relative17);
|
|
14041
14248
|
if (deps.readFile(candidate) !== null) return candidate;
|
|
14042
|
-
const parent =
|
|
14249
|
+
const parent = path19.dirname(dir);
|
|
14043
14250
|
if (parent === dir) return null;
|
|
14044
14251
|
dir = parent;
|
|
14045
14252
|
}
|
|
14046
14253
|
}
|
|
14047
14254
|
async function isTracked(file, deps) {
|
|
14048
14255
|
try {
|
|
14049
|
-
const out = await deps.run("git", ["ls-files", "--error-unmatch",
|
|
14256
|
+
const out = await deps.run("git", ["ls-files", "--error-unmatch", path19.basename(file)], { cwd: path19.dirname(file), env: deps.baseEnv });
|
|
14050
14257
|
return out.code === 0;
|
|
14051
14258
|
} catch {
|
|
14052
14259
|
return false;
|
|
@@ -14056,7 +14263,7 @@ function parseEnvFile(content, context, home) {
|
|
|
14056
14263
|
const env = {};
|
|
14057
14264
|
const lookup2 = (name) => env[name] ?? context[name] ?? "";
|
|
14058
14265
|
const expand = (value) => {
|
|
14059
|
-
const tilde = value.startsWith("~/") ?
|
|
14266
|
+
const tilde = value.startsWith("~/") ? path19.join(home, value.slice(2)) : value;
|
|
14060
14267
|
return tilde.replace(/\$\{([A-Za-z_][A-Za-z0-9_]*)\}|\$([A-Za-z_][A-Za-z0-9_]*)/g, (_, braced, bare) => lookup2(braced ?? bare));
|
|
14061
14268
|
};
|
|
14062
14269
|
for (const raw of content.split(/\r?\n/)) {
|
|
@@ -14326,7 +14533,7 @@ function readOnlySessionRules(site) {
|
|
|
14326
14533
|
real = import_fs8.realpathSync.native(root);
|
|
14327
14534
|
} catch {
|
|
14328
14535
|
}
|
|
14329
|
-
for (const
|
|
14536
|
+
for (const path50 of /* @__PURE__ */ new Set([root, real])) rules.push({ action: "external_directory", resource: `${path50.replace(/[\\/]$/, "")}/*`, effect: "allow" });
|
|
14330
14537
|
}
|
|
14331
14538
|
return rules;
|
|
14332
14539
|
}
|
|
@@ -14452,6 +14659,14 @@ function runnerEnv(resolvedPath, workspace = {}, launch = {}, drop) {
|
|
|
14452
14659
|
// src/services/harness/runnerProcess.ts
|
|
14453
14660
|
var STDERR_TAIL_CHARS = 4e3;
|
|
14454
14661
|
var CLOSE_GRACE_MS = 2e3;
|
|
14662
|
+
function releasePipes(child) {
|
|
14663
|
+
for (const stream of [child.stdin, child.stdout, child.stderr]) {
|
|
14664
|
+
if (stream instanceof import_net.Socket) stream.unref();
|
|
14665
|
+
}
|
|
14666
|
+
for (const stream of [child.stdout, child.stderr]) {
|
|
14667
|
+
if (stream instanceof import_net.Socket) stream.resume();
|
|
14668
|
+
}
|
|
14669
|
+
}
|
|
14455
14670
|
var RunnerProcess = class {
|
|
14456
14671
|
constructor(deps) {
|
|
14457
14672
|
this.deps = deps;
|
|
@@ -14524,6 +14739,7 @@ var RunnerProcess = class {
|
|
|
14524
14739
|
const grace = setTimeout(() => {
|
|
14525
14740
|
for (const flush of [...this.stdoutEnds]) flush();
|
|
14526
14741
|
this.end({ code, signal });
|
|
14742
|
+
releasePipes(child);
|
|
14527
14743
|
}, CLOSE_GRACE_MS);
|
|
14528
14744
|
grace.unref?.();
|
|
14529
14745
|
void this.ended.then(() => clearTimeout(grace));
|
|
@@ -14622,11 +14838,11 @@ ${tail}` : ""}`;
|
|
|
14622
14838
|
|
|
14623
14839
|
// src/services/sandbox/nativeShellLauncher.ts
|
|
14624
14840
|
var import_fs9 = require("fs");
|
|
14625
|
-
var
|
|
14626
|
-
var
|
|
14841
|
+
var os8 = __toESM(require("os"));
|
|
14842
|
+
var path20 = __toESM(require("path"));
|
|
14627
14843
|
function nativeShellInvocation(platform, env = process.env) {
|
|
14628
14844
|
const shell = platform === "darwin" ? "/bin/zsh" : env.SHELL;
|
|
14629
|
-
return { shell: shell &&
|
|
14845
|
+
return { shell: shell && path20.posix.isAbsolute(shell) ? shell : "/bin/sh", args: ["-c"] };
|
|
14630
14846
|
}
|
|
14631
14847
|
var live = /* @__PURE__ */ new Set();
|
|
14632
14848
|
var exitHook = false;
|
|
@@ -14658,10 +14874,10 @@ function launcherScript(launch) {
|
|
|
14658
14874
|
}
|
|
14659
14875
|
async function createNativeShellLauncher(opts) {
|
|
14660
14876
|
const invocation = nativeShellInvocation(opts.platform ?? process.platform, opts.env);
|
|
14661
|
-
const root = (0, import_fs9.realpathSync)((0, import_fs9.mkdtempSync)(
|
|
14877
|
+
const root = (0, import_fs9.realpathSync)((0, import_fs9.mkdtempSync)(path20.join(os8.tmpdir(), "ordewell-native-shell-")));
|
|
14662
14878
|
let shortTmp = null;
|
|
14663
14879
|
try {
|
|
14664
|
-
shortTmp = (0, import_fs9.realpathSync)((0, import_fs9.mkdtempSync)(
|
|
14880
|
+
shortTmp = (0, import_fs9.realpathSync)((0, import_fs9.mkdtempSync)(path20.join("/tmp", "ow-")));
|
|
14665
14881
|
const tmpdir7 = shortTmp;
|
|
14666
14882
|
const launch = await opts.sandbox.launchNativeShell({
|
|
14667
14883
|
workspace: opts.workspace,
|
|
@@ -14670,7 +14886,7 @@ async function createNativeShellLauncher(opts) {
|
|
|
14670
14886
|
shellArgs: invocation.args,
|
|
14671
14887
|
...opts.env ? { env: opts.env } : {}
|
|
14672
14888
|
});
|
|
14673
|
-
const prefix =
|
|
14889
|
+
const prefix = path20.join(root, "run");
|
|
14674
14890
|
(0, import_fs9.writeFileSync)(prefix, launcherScript(launch), { mode: 448 });
|
|
14675
14891
|
(0, import_fs9.chmodSync)(prefix, 448);
|
|
14676
14892
|
keep(root);
|
|
@@ -15039,6 +15255,20 @@ ${lines2.map((line) => `${line}
|
|
|
15039
15255
|
}
|
|
15040
15256
|
|
|
15041
15257
|
// src/services/harness/claudeOrdewell.ts
|
|
15258
|
+
var path21 = __toESM(require("path"));
|
|
15259
|
+
function assertNoClaudeTaskCollision(cwd) {
|
|
15260
|
+
const text6 = readText(path21.join(cwd, ".mcp.json"));
|
|
15261
|
+
if (text6 === void 0) return;
|
|
15262
|
+
let parsed;
|
|
15263
|
+
try {
|
|
15264
|
+
parsed = JSON.parse(text6);
|
|
15265
|
+
} catch {
|
|
15266
|
+
return;
|
|
15267
|
+
}
|
|
15268
|
+
if (isRecord2(parsed) && isRecord2(parsed.mcpServers) && Object.hasOwn(parsed.mcpServers, ORDEWELL_MCP_SERVER_NAME)) {
|
|
15269
|
+
throw new Error(".mcp.json declares an MCP server named ordewell; rename or remove the ordewell server before starting this task.");
|
|
15270
|
+
}
|
|
15271
|
+
}
|
|
15042
15272
|
var CLAUDE_ORDEWELL = {
|
|
15043
15273
|
toolName: MCP_CLIENT_TOOL_NAMES.toolName,
|
|
15044
15274
|
toolNames: MCP_CLIENT_TOOL_NAMES.toolNames,
|
|
@@ -15186,9 +15416,8 @@ var ReplyRuns = class {
|
|
|
15186
15416
|
};
|
|
15187
15417
|
|
|
15188
15418
|
// src/services/harness/claudeRepoConfig.ts
|
|
15189
|
-
var
|
|
15190
|
-
var
|
|
15191
|
-
var MEMORY_FILES = ["CLAUDE.md", path20.join(".claude", "CLAUDE.md"), "CLAUDE.local.md"];
|
|
15419
|
+
var path22 = __toESM(require("path"));
|
|
15420
|
+
var MEMORY_FILES = ["CLAUDE.md", path22.join(".claude", "CLAUDE.md"), "CLAUDE.local.md"];
|
|
15192
15421
|
var MAX_IMPORT_DEPTH = 5;
|
|
15193
15422
|
function importsOf(text6) {
|
|
15194
15423
|
const prose = text6.replace(/```[\s\S]*?```/g, "").replace(/`[^`\n]*`/g, "");
|
|
@@ -15199,11 +15428,11 @@ function withImports(workspace, file, depth, seen, into) {
|
|
|
15199
15428
|
seen.add(file);
|
|
15200
15429
|
const text6 = readText(file);
|
|
15201
15430
|
if (text6 === void 0) return;
|
|
15202
|
-
into.push({ path:
|
|
15431
|
+
into.push({ path: path22.relative(workspace, file), text: text6 });
|
|
15203
15432
|
if (depth >= MAX_IMPORT_DEPTH) return;
|
|
15204
15433
|
for (const target of importsOf(text6)) {
|
|
15205
15434
|
if (target.startsWith("~")) continue;
|
|
15206
|
-
const abs =
|
|
15435
|
+
const abs = path22.resolve(path22.dirname(file), target);
|
|
15207
15436
|
if (insideOnDisk(workspace, abs)) withImports(workspace, abs, depth + 1, seen, into);
|
|
15208
15437
|
}
|
|
15209
15438
|
}
|
|
@@ -15224,12 +15453,12 @@ var CLAUDE_REPO_CONFIG = {
|
|
|
15224
15453
|
instructions(workspace) {
|
|
15225
15454
|
const files = [];
|
|
15226
15455
|
const seen = /* @__PURE__ */ new Set();
|
|
15227
|
-
for (const name of MEMORY_FILES) withImports(workspace,
|
|
15456
|
+
for (const name of MEMORY_FILES) withImports(workspace, path22.join(workspace, name), 0, seen, files);
|
|
15228
15457
|
return files;
|
|
15229
15458
|
},
|
|
15230
15459
|
projectServers(workspace) {
|
|
15231
15460
|
const source = ".mcp.json";
|
|
15232
|
-
const text6 = readText(
|
|
15461
|
+
const text6 = readText(path22.join(workspace, source));
|
|
15233
15462
|
if (text6 === void 0) return NO_SERVERS;
|
|
15234
15463
|
let parsed;
|
|
15235
15464
|
try {
|
|
@@ -15247,13 +15476,14 @@ var CLAUDE_REPO_CONFIG = {
|
|
|
15247
15476
|
return found;
|
|
15248
15477
|
}
|
|
15249
15478
|
};
|
|
15250
|
-
function claudeLocalServers(
|
|
15251
|
-
const
|
|
15252
|
-
const
|
|
15479
|
+
function claudeLocalServers(env, workspace) {
|
|
15480
|
+
const configDir2 = env.CLAUDE_CONFIG_DIR?.trim();
|
|
15481
|
+
const file = expandRunnerHome(configDir2 ? path22.join(configDir2, ".claude.json") : "~/.claude.json", env);
|
|
15482
|
+
const text6 = file === null ? void 0 : readText(file);
|
|
15253
15483
|
if (text6 === void 0) return {};
|
|
15254
15484
|
try {
|
|
15255
15485
|
const parsed = JSON.parse(text6);
|
|
15256
|
-
const project = isRecord2(parsed) && isRecord2(parsed.projects) ? parsed.projects[
|
|
15486
|
+
const project = isRecord2(parsed) && isRecord2(parsed.projects) ? parsed.projects[path22.resolve(workspace)] : void 0;
|
|
15257
15487
|
return isRecord2(project) && isRecord2(project.mcpServers) ? project.mcpServers : {};
|
|
15258
15488
|
} catch {
|
|
15259
15489
|
return {};
|
|
@@ -15508,13 +15738,13 @@ var NO_AUTO_MEMORY = { CLAUDE_CODE_DISABLE_AUTO_MEMORY: "1" };
|
|
|
15508
15738
|
function inSavedOutput(abs, configDir2, sessionId) {
|
|
15509
15739
|
let projects;
|
|
15510
15740
|
try {
|
|
15511
|
-
projects = import_fs11.realpathSync.native(
|
|
15741
|
+
projects = import_fs11.realpathSync.native(path23.join(configDir2, "projects"));
|
|
15512
15742
|
} catch {
|
|
15513
15743
|
return false;
|
|
15514
15744
|
}
|
|
15515
|
-
const rel =
|
|
15516
|
-
if (rel.startsWith("..") ||
|
|
15517
|
-
const [project, session, folder] = rel.split(
|
|
15745
|
+
const rel = path23.relative(projects, abs);
|
|
15746
|
+
if (rel.startsWith("..") || path23.isAbsolute(rel)) return false;
|
|
15747
|
+
const [project, session, folder] = rel.split(path23.sep);
|
|
15518
15748
|
return Boolean(project) && session === sessionId && folder === "tool-results";
|
|
15519
15749
|
}
|
|
15520
15750
|
var PATH_TOOLS = {
|
|
@@ -15752,7 +15982,7 @@ var ClaudeCodeAdapter = class extends StdioAgentAdapter {
|
|
|
15752
15982
|
"--append-system-prompt",
|
|
15753
15983
|
opts.systemPrompt
|
|
15754
15984
|
];
|
|
15755
|
-
if (opts.mcp) args.push(...this.ordewellServerArgs(opts.mcp, "planner", this.plannerServers(opts, workspace)));
|
|
15985
|
+
if (opts.mcp) args.push(...this.ordewellServerArgs(opts.mcp, "planner", this.plannerServers(opts, { ...process.env, ...workspace })));
|
|
15756
15986
|
if (opts.model) args.push("--model", opts.model);
|
|
15757
15987
|
if (opts.effort && opts.effort !== "adaptive") args.push("--effort", opts.effort);
|
|
15758
15988
|
if (opts.resumeSessionId) {
|
|
@@ -15760,7 +15990,8 @@ var ClaudeCodeAdapter = class extends StdioAgentAdapter {
|
|
|
15760
15990
|
this.countFrom(opts.resumeSessionId, opts.resumeCost);
|
|
15761
15991
|
this.pendingResume = opts.resumeSessionId;
|
|
15762
15992
|
}
|
|
15763
|
-
|
|
15993
|
+
const env = { ...process.env, ...workspace };
|
|
15994
|
+
this.configDir = expandRunnerHome(claudeConfigDir(env), env);
|
|
15764
15995
|
return {
|
|
15765
15996
|
command: "claude",
|
|
15766
15997
|
args,
|
|
@@ -15804,6 +16035,7 @@ var ClaudeCodeAdapter = class extends StdioAgentAdapter {
|
|
|
15804
16035
|
*/
|
|
15805
16036
|
taskSpawnSpec(opts) {
|
|
15806
16037
|
const readOnly = readOnlyTask(opts);
|
|
16038
|
+
if (!readOnly) assertNoClaudeTaskCollision(opts.cwd);
|
|
15807
16039
|
this.readOnlySite = readOnly ? opts : null;
|
|
15808
16040
|
this.requestedMode = readOnly ? PLANNER_PERMISSION_MODE : opts.flags.permissionMode;
|
|
15809
16041
|
this.followedModel = opts.model;
|
|
@@ -15830,13 +16062,12 @@ var ClaudeCodeAdapter = class extends StdioAgentAdapter {
|
|
|
15830
16062
|
/**
|
|
15831
16063
|
* What user-only settings stop the CLI loading that is not the
|
|
15832
16064
|
* repository's to choose (ADR-0028): the user's own local-scope servers,
|
|
15833
|
-
*
|
|
15834
|
-
* servers approved for it.
|
|
16065
|
+
* from the account in the environment the planner runs with, and the
|
|
16066
|
+
* project servers approved for it.
|
|
15835
16067
|
*/
|
|
15836
|
-
plannerServers(opts,
|
|
15837
|
-
const configDir2 = workspace.CLAUDE_CONFIG_DIR ?? process.env.CLAUDE_CONFIG_DIR;
|
|
16068
|
+
plannerServers(opts, env) {
|
|
15838
16069
|
return {
|
|
15839
|
-
...claudeLocalServers(
|
|
16070
|
+
...claudeLocalServers(env, opts.cwd),
|
|
15840
16071
|
...Object.fromEntries((opts.projectServers ?? []).map((server4) => [server4.name, server4.definition]))
|
|
15841
16072
|
};
|
|
15842
16073
|
}
|
|
@@ -16600,8 +16831,8 @@ function runProbe(deps, cwd, env, flags) {
|
|
|
16600
16831
|
}
|
|
16601
16832
|
|
|
16602
16833
|
// src/services/harness/codexRepoConfig.ts
|
|
16603
|
-
var
|
|
16604
|
-
var CONFIG_FILE =
|
|
16834
|
+
var path24 = __toESM(require("path"));
|
|
16835
|
+
var CONFIG_FILE = path24.join(".codex", "config.toml");
|
|
16605
16836
|
function stringsOf(value) {
|
|
16606
16837
|
return Array.isArray(value) ? value.filter((v) => typeof v === "string") : [];
|
|
16607
16838
|
}
|
|
@@ -16627,9 +16858,9 @@ var CODEX_REPO_CONFIG = {
|
|
|
16627
16858
|
const files = [];
|
|
16628
16859
|
for (const dir of repoLevels(workspace)) {
|
|
16629
16860
|
for (const name of ["AGENTS.override.md", "AGENTS.md"]) {
|
|
16630
|
-
const text6 = readText(
|
|
16861
|
+
const text6 = readText(path24.join(dir, name));
|
|
16631
16862
|
if (text6 === void 0) continue;
|
|
16632
|
-
files.push({ path:
|
|
16863
|
+
files.push({ path: path24.relative(workspace, path24.join(dir, name)) || name, text: text6 });
|
|
16633
16864
|
break;
|
|
16634
16865
|
}
|
|
16635
16866
|
}
|
|
@@ -16640,10 +16871,10 @@ var CODEX_REPO_CONFIG = {
|
|
|
16640
16871
|
const byName = /* @__PURE__ */ new Map();
|
|
16641
16872
|
const unreadable = [];
|
|
16642
16873
|
for (const dir of repoLevels(workspace)) {
|
|
16643
|
-
const file =
|
|
16874
|
+
const file = path24.join(dir, CONFIG_FILE);
|
|
16644
16875
|
const text6 = readText(file);
|
|
16645
16876
|
if (text6 === void 0) continue;
|
|
16646
|
-
const source =
|
|
16877
|
+
const source = path24.relative(workspace, file);
|
|
16647
16878
|
let parsed;
|
|
16648
16879
|
try {
|
|
16649
16880
|
parsed = parseToml(text6);
|
|
@@ -16663,9 +16894,9 @@ var CODEX_REPO_CONFIG = {
|
|
|
16663
16894
|
};
|
|
16664
16895
|
function untrustedProjects(workspace) {
|
|
16665
16896
|
const projects = {};
|
|
16666
|
-
for (let dir =
|
|
16897
|
+
for (let dir = path24.resolve(workspace); ; dir = path24.dirname(dir)) {
|
|
16667
16898
|
projects[dir] = { trust_level: "untrusted" };
|
|
16668
|
-
if (
|
|
16899
|
+
if (path24.dirname(dir) === dir) return projects;
|
|
16669
16900
|
}
|
|
16670
16901
|
}
|
|
16671
16902
|
|
|
@@ -17786,7 +18017,7 @@ ${this.exitMessage()}`);
|
|
|
17786
18017
|
case "webSearch":
|
|
17787
18018
|
return;
|
|
17788
18019
|
case "fileChange": {
|
|
17789
|
-
const paths = (item.changes ?? []).map((change) => change.path).filter((
|
|
18020
|
+
const paths = (item.changes ?? []).map((change) => change.path).filter((path50) => !!path50);
|
|
17790
18021
|
this.fileChangePaths.set(item.id, paths);
|
|
17791
18022
|
if (this.startOpts?.kind === "task") {
|
|
17792
18023
|
for (const change of item.changes ?? []) this.announceFileChange(item.id, change, emit, subagentId);
|
|
@@ -17889,20 +18120,20 @@ ${this.exitMessage()}`);
|
|
|
17889
18120
|
announceFileChange(itemId, change, emit, subagentId) {
|
|
17890
18121
|
const rows = this.fileChangeRows.get(itemId) ?? /* @__PURE__ */ new Map();
|
|
17891
18122
|
this.fileChangeRows.set(itemId, rows);
|
|
17892
|
-
const
|
|
17893
|
-
const known = rows.get(
|
|
18123
|
+
const path50 = this.shownChangePath(change);
|
|
18124
|
+
const known = rows.get(path50);
|
|
17894
18125
|
if (known) return known;
|
|
17895
|
-
const row = rows.size === 0 ? itemId : `${itemId}:${
|
|
17896
|
-
rows.set(
|
|
17897
|
-
emit({ type: "tool_call", id: row, name: changeName(change), args: { path:
|
|
18126
|
+
const row = rows.size === 0 ? itemId : `${itemId}:${path50}`;
|
|
18127
|
+
rows.set(path50, row);
|
|
18128
|
+
emit({ type: "tool_call", id: row, name: changeName(change), args: { path: path50 }, ...subagentId ? { subagentId } : {} });
|
|
17898
18129
|
return row;
|
|
17899
18130
|
}
|
|
17900
18131
|
/** Relative to the task's worktree, where every path but a stray one lies; a move names both ends. */
|
|
17901
18132
|
shownChangePath(change) {
|
|
17902
|
-
const shown = (
|
|
18133
|
+
const shown = (path50) => {
|
|
17903
18134
|
const cwd = this.startOpts?.cwd;
|
|
17904
|
-
const rel = cwd ? (0, import_path6.relative)(cwd,
|
|
17905
|
-
return rel && rel !== ".." && !rel.startsWith(`..${import_path6.sep}`) && !(0, import_path6.isAbsolute)(rel) ? rel :
|
|
18135
|
+
const rel = cwd ? (0, import_path6.relative)(cwd, path50) : path50;
|
|
18136
|
+
return rel && rel !== ".." && !rel.startsWith(`..${import_path6.sep}`) && !(0, import_path6.isAbsolute)(rel) ? rel : path50;
|
|
17906
18137
|
};
|
|
17907
18138
|
const from = shown(change.path ?? "");
|
|
17908
18139
|
return change.kind?.move_path ? `${from} \u2192 ${shown(change.kind.move_path)}` : from;
|
|
@@ -17953,7 +18184,7 @@ var import_fs12 = require("fs");
|
|
|
17953
18184
|
|
|
17954
18185
|
// src/services/harness/projectServers.ts
|
|
17955
18186
|
var import_crypto8 = require("crypto");
|
|
17956
|
-
var
|
|
18187
|
+
var path25 = __toESM(require("path"));
|
|
17957
18188
|
function canonical2(value) {
|
|
17958
18189
|
if (Array.isArray(value)) return value.map(canonical2);
|
|
17959
18190
|
if (!isRecord2(value)) return value;
|
|
@@ -17964,7 +18195,7 @@ function projectServerScope(server4) {
|
|
|
17964
18195
|
return `mcp-server:${server4.name}@${hash}`;
|
|
17965
18196
|
}
|
|
17966
18197
|
var scopeName = (scope) => scope.slice(0, scope.lastIndexOf("@"));
|
|
17967
|
-
function fileProjectServerApprovals(file =
|
|
18198
|
+
function fileProjectServerApprovals(file = path25.join(globalDataDir(), "project-mcp-servers.json")) {
|
|
17968
18199
|
const load = () => {
|
|
17969
18200
|
const text6 = readText(file);
|
|
17970
18201
|
if (text6 === void 0) return {};
|
|
@@ -19127,8 +19358,8 @@ var OpenCodeAdapter = class extends ProcessLifecycle {
|
|
|
19127
19358
|
this.v2 = new OpenCodeV2({
|
|
19128
19359
|
version,
|
|
19129
19360
|
nativeShell: this.nativeShell?.prefix ?? null,
|
|
19130
|
-
request: (method,
|
|
19131
|
-
json: (method,
|
|
19361
|
+
request: (method, path50, body, signal, timeoutMs) => this.request(method, path50, body, signal, timeoutMs),
|
|
19362
|
+
json: (method, path50, body, signal, timeoutMs) => this.json(method, path50, body, signal, timeoutMs),
|
|
19132
19363
|
openEvents: (signal) => this.deps.fetch(`${this.baseUrl}/api/event`, { signal, headers: this.headers() }),
|
|
19133
19364
|
processEnded: this.processEnded,
|
|
19134
19365
|
isExited: () => this.exited,
|
|
@@ -19765,15 +19996,15 @@ var OpenCodeAdapter = class extends ProcessLifecycle {
|
|
|
19765
19996
|
onActivity
|
|
19766
19997
|
);
|
|
19767
19998
|
}
|
|
19768
|
-
async json(method,
|
|
19769
|
-
const response = await this.request(method,
|
|
19770
|
-
if (!response.ok) throw new OpenCodeHttpError(`${method} ${
|
|
19999
|
+
async json(method, path50, body, signal, timeoutMs) {
|
|
20000
|
+
const response = await this.request(method, path50, body, signal, timeoutMs);
|
|
20001
|
+
if (!response.ok) throw new OpenCodeHttpError(`${method} ${path50} failed: ${response.status} ${response.statusText}`);
|
|
19771
20002
|
return await response.json().catch(() => null);
|
|
19772
20003
|
}
|
|
19773
20004
|
/** `timeoutMs` null is for a turn's own POST, which the server answers when the turn is done. */
|
|
19774
|
-
request(method,
|
|
20005
|
+
request(method, path50, body, signal, timeoutMs = REQUEST_TIMEOUT_MS) {
|
|
19775
20006
|
const deadline = withDeadline(signal, timeoutMs);
|
|
19776
|
-
return this.deps.fetch(`${this.baseUrl}${
|
|
20007
|
+
return this.deps.fetch(`${this.baseUrl}${path50}`, {
|
|
19777
20008
|
method,
|
|
19778
20009
|
headers: this.headers({ "content-type": "application/json" }),
|
|
19779
20010
|
...body === void 0 ? {} : { body: JSON.stringify(body) },
|
|
@@ -19834,8 +20065,8 @@ var OpenCodeAdapter = class extends ProcessLifecycle {
|
|
|
19834
20065
|
};
|
|
19835
20066
|
|
|
19836
20067
|
// src/services/harness/openCodeRepoConfig.ts
|
|
19837
|
-
var
|
|
19838
|
-
var CONFIG_FILES = ["opencode.json", "opencode.jsonc",
|
|
20068
|
+
var path26 = __toESM(require("path"));
|
|
20069
|
+
var CONFIG_FILES = ["opencode.json", "opencode.jsonc", path26.join(".opencode", "opencode.json"), path26.join(".opencode", "opencode.jsonc")];
|
|
19839
20070
|
var INSTRUCTION_NAMES = ["AGENTS.md", "CLAUDE.md", "CONTEXT.md"];
|
|
19840
20071
|
function server3(name, entry, source) {
|
|
19841
20072
|
if (!isRecord2(entry)) return "not an object";
|
|
@@ -19854,8 +20085,8 @@ var OPENCODE_REPO_CONFIG = {
|
|
|
19854
20085
|
const levels = repoLevels(workspace);
|
|
19855
20086
|
for (const name of INSTRUCTION_NAMES) {
|
|
19856
20087
|
const files = levels.flatMap((dir) => {
|
|
19857
|
-
const text6 = readText(
|
|
19858
|
-
return text6 === void 0 ? [] : [{ path:
|
|
20088
|
+
const text6 = readText(path26.join(dir, name));
|
|
20089
|
+
return text6 === void 0 ? [] : [{ path: path26.relative(workspace, path26.join(dir, name)) || name, text: text6 }];
|
|
19859
20090
|
});
|
|
19860
20091
|
if (files.length > 0) return files;
|
|
19861
20092
|
}
|
|
@@ -19867,10 +20098,10 @@ var OPENCODE_REPO_CONFIG = {
|
|
|
19867
20098
|
const unreadable = [];
|
|
19868
20099
|
for (const dir of repoLevels(workspace)) {
|
|
19869
20100
|
for (const name of CONFIG_FILES) {
|
|
19870
|
-
const file =
|
|
20101
|
+
const file = path26.join(dir, name);
|
|
19871
20102
|
const text6 = readText(file);
|
|
19872
20103
|
if (text6 === void 0) continue;
|
|
19873
|
-
const source =
|
|
20104
|
+
const source = path26.relative(workspace, file);
|
|
19874
20105
|
let parsed;
|
|
19875
20106
|
try {
|
|
19876
20107
|
parsed = parseJsonc(text6);
|
|
@@ -20059,7 +20290,7 @@ function knownModelId(value, optionIds, shortcuts) {
|
|
|
20059
20290
|
|
|
20060
20291
|
// src/services/ModelCatalog.ts
|
|
20061
20292
|
var fs6 = __toESM(require("fs"));
|
|
20062
|
-
var
|
|
20293
|
+
var path27 = __toESM(require("path"));
|
|
20063
20294
|
var CACHE_KEY = "model-catalog";
|
|
20064
20295
|
var CACHE_TTL_MS = 60 * 60 * 1e3;
|
|
20065
20296
|
function storageKey(baseUrl) {
|
|
@@ -20112,7 +20343,7 @@ var ModelCatalog = class {
|
|
|
20112
20343
|
const url = (baseUrl || "https://openrouter.ai/api/v1").replace(/\/+$/, "");
|
|
20113
20344
|
const key = storageKey(url);
|
|
20114
20345
|
const cacheDir = globalCacheDir();
|
|
20115
|
-
const cacheFile =
|
|
20346
|
+
const cacheFile = path27.join(cacheDir, `${key.replace(/[:/]/g, "_")}.json`);
|
|
20116
20347
|
if (fs6.existsSync(cacheFile)) fs6.rmSync(cacheFile);
|
|
20117
20348
|
} catch {
|
|
20118
20349
|
}
|
|
@@ -20120,7 +20351,7 @@ var ModelCatalog = class {
|
|
|
20120
20351
|
static readCache(key) {
|
|
20121
20352
|
try {
|
|
20122
20353
|
const cacheDir = globalCacheDir();
|
|
20123
|
-
const cacheFile =
|
|
20354
|
+
const cacheFile = path27.join(cacheDir, `${key.replace(/[:/]/g, "_")}.json`);
|
|
20124
20355
|
if (!fs6.existsSync(cacheFile)) return null;
|
|
20125
20356
|
const raw = fs6.readFileSync(cacheFile, "utf8");
|
|
20126
20357
|
const data = JSON.parse(raw);
|
|
@@ -20134,7 +20365,7 @@ var ModelCatalog = class {
|
|
|
20134
20365
|
try {
|
|
20135
20366
|
const cacheDir = globalCacheDir();
|
|
20136
20367
|
fs6.mkdirSync(cacheDir, { recursive: true });
|
|
20137
|
-
const cacheFile =
|
|
20368
|
+
const cacheFile = path27.join(cacheDir, `${key.replace(/[:/]/g, "_")}.json`);
|
|
20138
20369
|
fs6.writeFileSync(
|
|
20139
20370
|
cacheFile,
|
|
20140
20371
|
JSON.stringify({ models, cachedAt: Date.now() }, null, 2)
|
|
@@ -20145,15 +20376,17 @@ var ModelCatalog = class {
|
|
|
20145
20376
|
};
|
|
20146
20377
|
|
|
20147
20378
|
// src/services/ModelDiscovery.ts
|
|
20148
|
-
var
|
|
20379
|
+
var path28 = __toESM(require("path"));
|
|
20149
20380
|
var import_child_process6 = require("child_process");
|
|
20150
20381
|
var import_util2 = require("util");
|
|
20151
20382
|
var fsSync5 = __toESM(require("fs"));
|
|
20152
|
-
var osMod = __toESM(require("os"));
|
|
20153
20383
|
|
|
20154
20384
|
// src/services/harness/appServerModels.ts
|
|
20155
20385
|
var import_child_process5 = require("child_process");
|
|
20156
20386
|
var DEFAULT_TIMEOUT_MS3 = 2e4;
|
|
20387
|
+
function definedOnly(env) {
|
|
20388
|
+
return Object.fromEntries(Object.entries(env).filter((entry) => entry[1] !== void 0));
|
|
20389
|
+
}
|
|
20157
20390
|
function parseRpcLine(line) {
|
|
20158
20391
|
try {
|
|
20159
20392
|
const value = JSON.parse(line);
|
|
@@ -20166,10 +20399,9 @@ async function openAppServer(command, args, launch) {
|
|
|
20166
20399
|
const runner = new RunnerProcess({
|
|
20167
20400
|
spawn: import_child_process5.spawn,
|
|
20168
20401
|
fetch: globalThis.fetch,
|
|
20169
|
-
// The cwd is no workspace
|
|
20170
|
-
//
|
|
20171
|
-
//
|
|
20172
|
-
// that can choose whose account is read.
|
|
20402
|
+
// The cwd is no workspace, so it pays no direnv round trip before a spawn
|
|
20403
|
+
// with a deadline. Discovery and the account monitor pass the workspace's
|
|
20404
|
+
// environment themselves, since that can choose whose account is read.
|
|
20173
20405
|
workspaceEnv: async () => ({}),
|
|
20174
20406
|
...launch.deps
|
|
20175
20407
|
});
|
|
@@ -20222,7 +20454,11 @@ async function openAppServer(command, args, launch) {
|
|
|
20222
20454
|
}
|
|
20223
20455
|
var APP_SERVER_CLIENT_INFO = { name: "ordewell", title: "Ordewell", version: "0.1.0" };
|
|
20224
20456
|
async function discoverAppServerModels(command, args, options = {}) {
|
|
20225
|
-
const
|
|
20457
|
+
const env = options.env;
|
|
20458
|
+
const server4 = await openAppServer(command, args, {
|
|
20459
|
+
cwd: process.cwd(),
|
|
20460
|
+
deps: { ...env ? { workspaceEnv: async () => definedOnly(env) } : {}, ...options.deps }
|
|
20461
|
+
});
|
|
20226
20462
|
if (!server4) return null;
|
|
20227
20463
|
const { rpc } = server4;
|
|
20228
20464
|
const asked = (async () => {
|
|
@@ -20249,10 +20485,10 @@ async function discoverAppServerModels(command, args, options = {}) {
|
|
|
20249
20485
|
var execAsync2 = (0, import_util2.promisify)(import_child_process6.exec);
|
|
20250
20486
|
var defaultExec2 = async (command, options) => {
|
|
20251
20487
|
const PATH = await augmentedPath();
|
|
20252
|
-
const { stdout } = await execAsync2(command, {
|
|
20488
|
+
const { stdout } = await execAsync2(command, { timeout: options?.timeout, maxBuffer: 32 * 1024 * 1024, env: withPath(options?.env ?? process.env, PATH) });
|
|
20253
20489
|
return { stdout: String(stdout) };
|
|
20254
20490
|
};
|
|
20255
|
-
var defaultAppServerClient = (command, args, options) => discoverAppServerModels(command, args, { timeout: options?.timeout });
|
|
20491
|
+
var defaultAppServerClient = (command, args, options) => discoverAppServerModels(command, args, { timeout: options?.timeout, env: options?.env });
|
|
20256
20492
|
function effortVariant(effort) {
|
|
20257
20493
|
return { id: effort, label: effort.charAt(0).toUpperCase() + effort.slice(1) };
|
|
20258
20494
|
}
|
|
@@ -20286,7 +20522,7 @@ function geminiDiscoveryCacheKey(apiKey) {
|
|
|
20286
20522
|
return {
|
|
20287
20523
|
key: `gemini-models:${hash}`,
|
|
20288
20524
|
dir,
|
|
20289
|
-
file:
|
|
20525
|
+
file: path28.join(dir, `gemini_models_${hash}.json`)
|
|
20290
20526
|
};
|
|
20291
20527
|
}
|
|
20292
20528
|
function readGeminiCache(apiKey) {
|
|
@@ -20373,15 +20609,22 @@ function applyVariants(models, manifest) {
|
|
|
20373
20609
|
if (!variants || variants.length === 0) return models;
|
|
20374
20610
|
return models.map((m) => m.variants.length > 0 ? m : { ...m, variants });
|
|
20375
20611
|
}
|
|
20376
|
-
function
|
|
20612
|
+
function listDir(dir) {
|
|
20613
|
+
try {
|
|
20614
|
+
return fsSync5.readdirSync(dir);
|
|
20615
|
+
} catch {
|
|
20616
|
+
return [];
|
|
20617
|
+
}
|
|
20618
|
+
}
|
|
20619
|
+
function resolveApiAuth(authMethods, env) {
|
|
20377
20620
|
for (const method of authMethods) {
|
|
20378
20621
|
try {
|
|
20379
20622
|
let token;
|
|
20380
20623
|
if (method.type === "env") {
|
|
20381
|
-
token =
|
|
20624
|
+
token = env[method.varName];
|
|
20382
20625
|
} else {
|
|
20383
|
-
const filePath = method.path.
|
|
20384
|
-
if (!fsSync5.existsSync(filePath)) continue;
|
|
20626
|
+
const filePath = expandRunnerHome(typeof method.path === "function" ? method.path(env) : method.path, env);
|
|
20627
|
+
if (!filePath || !fsSync5.existsSync(filePath)) continue;
|
|
20385
20628
|
const data = JSON.parse(fsSync5.readFileSync(filePath, "utf8"));
|
|
20386
20629
|
token = method.jsonPath.split(".").reduce((obj, key) => {
|
|
20387
20630
|
if (obj && typeof obj === "object") return obj[key];
|
|
@@ -20397,8 +20640,8 @@ function resolveApiAuth(authMethods) {
|
|
|
20397
20640
|
}
|
|
20398
20641
|
return null;
|
|
20399
20642
|
}
|
|
20400
|
-
async function discoverFromApi(manifest, config, fetchImpl) {
|
|
20401
|
-
const auth = resolveApiAuth(config.auth);
|
|
20643
|
+
async function discoverFromApi(manifest, config, env, fetchImpl) {
|
|
20644
|
+
const auth = resolveApiAuth(config.auth, env);
|
|
20402
20645
|
if (!auth) return null;
|
|
20403
20646
|
try {
|
|
20404
20647
|
const headers = { ...config.headers, [auth.header]: auth.value };
|
|
@@ -20414,7 +20657,7 @@ async function discoverFromApi(manifest, config, fetchImpl) {
|
|
|
20414
20657
|
function wasKilled(err) {
|
|
20415
20658
|
return !!err && typeof err === "object" && err.killed === true;
|
|
20416
20659
|
}
|
|
20417
|
-
async function discoverFromCommand(manifest, execImpl) {
|
|
20660
|
+
async function discoverFromCommand(manifest, execImpl, env) {
|
|
20418
20661
|
const discovery = manifest.modelDiscovery;
|
|
20419
20662
|
const commands = discovery.discoveryCommands ?? [
|
|
20420
20663
|
{ command: discovery.command, args: discovery.args || [], parser: discovery.parser }
|
|
@@ -20423,7 +20666,7 @@ async function discoverFromCommand(manifest, execImpl) {
|
|
|
20423
20666
|
let timedOut2 = false;
|
|
20424
20667
|
for (const cmd of commands) {
|
|
20425
20668
|
try {
|
|
20426
|
-
const { stdout } = await execImpl(`${cmd.command} ${cmd.args.join(" ")}`, { timeout: 45e3 });
|
|
20669
|
+
const { stdout } = await execImpl(`${cmd.command} ${cmd.args.join(" ")}`, { timeout: 45e3, env });
|
|
20427
20670
|
const models = parseModelOutput(stdout, cmd.parser, manifest);
|
|
20428
20671
|
if (models.length > 0) return { models: applyVariants(mergeCanonicalAliases(models, manifest), manifest), timedOut: timedOut2 };
|
|
20429
20672
|
} catch (err) {
|
|
@@ -20489,21 +20732,26 @@ var ModelDiscovery = class {
|
|
|
20489
20732
|
* same background service.
|
|
20490
20733
|
*/
|
|
20491
20734
|
inflight = /* @__PURE__ */ new Map();
|
|
20735
|
+
/** Bumped by every `clear()`: a discovery begun before one answers its own lookups but fills no cache. */
|
|
20736
|
+
generation = 0;
|
|
20492
20737
|
registry;
|
|
20493
20738
|
execImpl;
|
|
20494
20739
|
fetchImpl;
|
|
20495
20740
|
appServerClient;
|
|
20496
20741
|
readFileImpl;
|
|
20497
20742
|
now;
|
|
20498
|
-
|
|
20743
|
+
env;
|
|
20744
|
+
constructor(registry, execImpl = defaultExec2, fetchImpl, appServerClient = defaultAppServerClient, readFileImpl, now = Date.now, env = async () => process.env) {
|
|
20499
20745
|
this.registry = registry;
|
|
20500
20746
|
this.execImpl = execImpl;
|
|
20501
20747
|
this.fetchImpl = fetchImpl;
|
|
20502
20748
|
this.appServerClient = appServerClient;
|
|
20503
20749
|
this.now = now;
|
|
20750
|
+
this.env = env;
|
|
20504
20751
|
this.readFileImpl = readFileImpl ?? ((filePath) => {
|
|
20505
20752
|
try {
|
|
20506
|
-
|
|
20753
|
+
const file = expandRunnerHome(filePath);
|
|
20754
|
+
return file ? fsSync5.readFileSync(file, "utf8") : null;
|
|
20507
20755
|
} catch {
|
|
20508
20756
|
return null;
|
|
20509
20757
|
}
|
|
@@ -20515,16 +20763,17 @@ var ModelDiscovery = class {
|
|
|
20515
20763
|
* null when both fail so `discover()` falls through to the remaining
|
|
20516
20764
|
* methods.
|
|
20517
20765
|
*/
|
|
20518
|
-
async discoverFromAppServer(manifest) {
|
|
20766
|
+
async discoverFromAppServer(manifest, env) {
|
|
20519
20767
|
const config = manifest.modelDiscovery.appServer;
|
|
20520
20768
|
if (!config) return null;
|
|
20521
|
-
const raw = await this.appServerClient(config.command, config.args);
|
|
20769
|
+
const raw = await this.appServerClient(config.command, config.args, { env });
|
|
20522
20770
|
if (raw) {
|
|
20523
20771
|
const models = parseAppServerModels(raw);
|
|
20524
20772
|
if (models.length > 0) return models;
|
|
20525
20773
|
}
|
|
20526
|
-
|
|
20527
|
-
|
|
20774
|
+
const cacheFile = config.cacheFile && expandRunnerHome(typeof config.cacheFile === "function" ? config.cacheFile(env) : config.cacheFile, env);
|
|
20775
|
+
if (cacheFile) {
|
|
20776
|
+
const content = this.readFileImpl(cacheFile);
|
|
20528
20777
|
if (content) {
|
|
20529
20778
|
const models = parseCodexModelsCache(content);
|
|
20530
20779
|
if (models.length > 0) return models;
|
|
@@ -20562,37 +20811,60 @@ var ModelDiscovery = class {
|
|
|
20562
20811
|
if (!manifest) {
|
|
20563
20812
|
return [];
|
|
20564
20813
|
}
|
|
20565
|
-
|
|
20814
|
+
const generation2 = this.generation;
|
|
20815
|
+
const env = await this.env().catch(() => process.env);
|
|
20816
|
+
const found = await this.discoverFromSources(manifest, env);
|
|
20817
|
+
const models = this.withSettingsModels(found.models, manifest, env);
|
|
20818
|
+
if (generation2 !== this.generation) return models;
|
|
20819
|
+
if (found.cacheable && models.length > 0) this.cache.set(runner, models);
|
|
20820
|
+
else this.misses.set(runner, { models, at: this.now() });
|
|
20821
|
+
return models;
|
|
20822
|
+
}
|
|
20823
|
+
async discoverFromSources(manifest, env) {
|
|
20566
20824
|
let cacheable = true;
|
|
20567
20825
|
const discovery = manifest.modelDiscovery;
|
|
20568
20826
|
if (discovery.appServer) {
|
|
20569
|
-
const appServerModels = await this.discoverFromAppServer(manifest);
|
|
20570
|
-
if (appServerModels && appServerModels.length > 0) {
|
|
20571
|
-
this.cache.set(runner, appServerModels);
|
|
20572
|
-
return appServerModels;
|
|
20573
|
-
}
|
|
20827
|
+
const appServerModels = await this.discoverFromAppServer(manifest, env);
|
|
20828
|
+
if (appServerModels && appServerModels.length > 0) return { models: appServerModels, cacheable: true };
|
|
20574
20829
|
cacheable = false;
|
|
20575
20830
|
}
|
|
20576
20831
|
if (discovery.apiDiscovery) {
|
|
20577
|
-
const apiModels = await discoverFromApi(manifest, discovery.apiDiscovery, this.fetchImpl);
|
|
20832
|
+
const apiModels = await discoverFromApi(manifest, discovery.apiDiscovery, env, this.fetchImpl);
|
|
20578
20833
|
if (apiModels && apiModels.length > 0) {
|
|
20579
|
-
models
|
|
20580
|
-
if (cacheable && models.length > 0) this.cache.set(runner, models);
|
|
20581
|
-
return models;
|
|
20834
|
+
return { models: applyVariants(mergeCanonicalAliases(apiModels, manifest), manifest), cacheable };
|
|
20582
20835
|
}
|
|
20583
20836
|
}
|
|
20584
20837
|
if (discovery.method === "command" && (discovery.command || discovery.discoveryCommands?.length)) {
|
|
20585
|
-
const result = await discoverFromCommand(manifest, this.execImpl);
|
|
20586
|
-
models
|
|
20587
|
-
cacheable = !result.fromFallback;
|
|
20588
|
-
} else {
|
|
20589
|
-
models = buildFallbackModels(manifest);
|
|
20838
|
+
const result = await discoverFromCommand(manifest, this.execImpl, env);
|
|
20839
|
+
return { models: result.models, cacheable: !result.fromFallback };
|
|
20590
20840
|
}
|
|
20591
|
-
|
|
20592
|
-
|
|
20593
|
-
|
|
20841
|
+
return { models: buildFallbackModels(manifest), cacheable };
|
|
20842
|
+
}
|
|
20843
|
+
/**
|
|
20844
|
+
* Append the models registered in the runner's own settings. Read on every
|
|
20845
|
+
* discovery rather than cached apart, so a refresh picks up an edit;
|
|
20846
|
+
* whatever the sources already offer keeps its entry and metadata.
|
|
20847
|
+
*/
|
|
20848
|
+
withSettingsModels(models, manifest, env) {
|
|
20849
|
+
const read2 = manifest.modelDiscovery.settingsModels;
|
|
20850
|
+
if (!read2) return models;
|
|
20851
|
+
const files = {
|
|
20852
|
+
read: (filePath) => {
|
|
20853
|
+
const file = expandRunnerHome(filePath, env);
|
|
20854
|
+
return file ? this.readFileImpl(file) : null;
|
|
20855
|
+
},
|
|
20856
|
+
list: (dir) => {
|
|
20857
|
+
const expanded = expandRunnerHome(dir, env);
|
|
20858
|
+
return expanded ? listDir(expanded) : [];
|
|
20859
|
+
},
|
|
20860
|
+
env
|
|
20861
|
+
};
|
|
20862
|
+
const seen = new Set(models.map((m) => m.modelId));
|
|
20863
|
+
const added = read2(files).filter((m) => !seen.has(m.modelId));
|
|
20864
|
+
return added.length > 0 ? [...models, ...applyVariants(added, manifest)] : models;
|
|
20594
20865
|
}
|
|
20595
20866
|
clear() {
|
|
20867
|
+
this.generation++;
|
|
20596
20868
|
this.cache.clear();
|
|
20597
20869
|
this.misses.clear();
|
|
20598
20870
|
this.inflight.clear();
|
|
@@ -20758,7 +21030,7 @@ function parseMaxParallel(value) {
|
|
|
20758
21030
|
|
|
20759
21031
|
// src/services/SettingsService.ts
|
|
20760
21032
|
var fs12 = __toESM(require("fs"));
|
|
20761
|
-
var
|
|
21033
|
+
var path35 = __toESM(require("path"));
|
|
20762
21034
|
|
|
20763
21035
|
// src/utils/fileLock.ts
|
|
20764
21036
|
var fs7 = __toESM(require("fs"));
|
|
@@ -20890,6 +21162,10 @@ var RECEIPT_LABELS = {
|
|
|
20890
21162
|
function receiptLabel(task2) {
|
|
20891
21163
|
return isTaskRunning(task2) && task2.completionReceived ? RECEIPT_LABELS[task2.completionReceived] : null;
|
|
20892
21164
|
}
|
|
21165
|
+
function attachingLabel(task2) {
|
|
21166
|
+
if (!isTaskRunning(task2) || !task2.attaching) return null;
|
|
21167
|
+
return task2.attaching === 2 ? "Connecting Ordewell's tools, retry" : "Connecting Ordewell's tools";
|
|
21168
|
+
}
|
|
20893
21169
|
function approvalLabel(count) {
|
|
20894
21170
|
if (!count) return null;
|
|
20895
21171
|
return count > 1 ? `Waiting for approval (${count})` : "Waiting for approval";
|
|
@@ -22359,7 +22635,7 @@ function createWakePolicy(options) {
|
|
|
22359
22635
|
}
|
|
22360
22636
|
|
|
22361
22637
|
// src/services/runnerExit.ts
|
|
22362
|
-
var USAGE_LIMIT_RE = /\b(?:usage|session|weekly|daily|monthly) limit\b|\brate limit (?:exceeded|reached)\b|\brate[- ]limited\b|\blimit (?:will )?reset\b|\bquota (?:exceeded|reached)\b|\btoo many requests\b/i;
|
|
22638
|
+
var USAGE_LIMIT_RE = /\b(?:usage|session|weekly|daily|monthly) limit\b|\byou['’]ve hit your (?:[\w'’-]+ ){0,3}?limit\b|\brate limit (?:exceeded|reached)\b|\brate[- ]limited\b|\blimit (?:will )?reset\b|\bquota (?:exceeded|reached)\b|\btoo many requests\b/i;
|
|
22363
22639
|
var USAGE_LIMIT_TAIL = 4096;
|
|
22364
22640
|
function classifyRunnerStop(said2) {
|
|
22365
22641
|
if (typeof said2 !== "string" && said2.usageLimit !== void 0) return said2.usageLimit ? "usage-limit" : "stopped";
|
|
@@ -22720,8 +22996,8 @@ function compactionCut(transcript) {
|
|
|
22720
22996
|
}
|
|
22721
22997
|
|
|
22722
22998
|
// src/services/skillInvocation.ts
|
|
22723
|
-
var
|
|
22724
|
-
function abbreviateHome(file, home =
|
|
22999
|
+
var os9 = __toESM(require("os"));
|
|
23000
|
+
function abbreviateHome(file, home = os9.homedir(), platform = process.platform) {
|
|
22725
23001
|
const win = platform === "win32";
|
|
22726
23002
|
const normal = (p) => win ? p.replace(/\\/g, "/") : p;
|
|
22727
23003
|
const shown = normal(file);
|
|
@@ -22736,13 +23012,13 @@ function modelInvocablePlannerSkills(skills) {
|
|
|
22736
23012
|
function modelInvocableTaskSkills(skills) {
|
|
22737
23013
|
return skills.filter((skill) => skill.appliesTo === "task" && skill.modelInvocable);
|
|
22738
23014
|
}
|
|
22739
|
-
function snapshotSkill(skill, invokedBy, home =
|
|
23015
|
+
function snapshotSkill(skill, invokedBy, home = os9.homedir()) {
|
|
22740
23016
|
return { invokedBy, name: skill.name, source: skill.source, path: abbreviateHome(skill.path, home), content: skill.content };
|
|
22741
23017
|
}
|
|
22742
23018
|
function attachSkill(skill, home) {
|
|
22743
23019
|
return { ...snapshotSkill(skill, "user", home), content: "", attaches: { description: skill.description } };
|
|
22744
23020
|
}
|
|
22745
|
-
function resolveSkillInvocation(text6, skillsService, home =
|
|
23021
|
+
function resolveSkillInvocation(text6, skillsService, home = os9.homedir()) {
|
|
22746
23022
|
const skills = skillTokens(text6).flatMap(({ name }) => {
|
|
22747
23023
|
const skill = skillsService.findSkill(name);
|
|
22748
23024
|
if (!skill?.userInvocable) return [];
|
|
@@ -22806,33 +23082,33 @@ ${skillBlock(entry.skill)}`, timestamp: entry.timestamp });
|
|
|
22806
23082
|
}
|
|
22807
23083
|
|
|
22808
23084
|
// src/services/taskSkills.ts
|
|
22809
|
-
var
|
|
23085
|
+
var path34 = __toESM(require("path"));
|
|
22810
23086
|
|
|
22811
23087
|
// src/services/SkillsService.ts
|
|
22812
23088
|
var import_crypto12 = require("crypto");
|
|
22813
23089
|
var fs11 = __toESM(require("fs"));
|
|
22814
|
-
var
|
|
23090
|
+
var path33 = __toESM(require("path"));
|
|
22815
23091
|
|
|
22816
23092
|
// src/services/builtinSkills.ts
|
|
22817
|
-
var
|
|
23093
|
+
var path29 = __toESM(require("path"));
|
|
22818
23094
|
var import_url = require("url");
|
|
22819
23095
|
function builtinSkillsDir() {
|
|
22820
|
-
const dir = typeof __dirname !== "undefined" && __dirname ? __dirname :
|
|
23096
|
+
const dir = typeof __dirname !== "undefined" && __dirname ? __dirname : path29.dirname((0, import_url.fileURLToPath)(
|
|
22821
23097
|
// @ts-expect-error - import.meta is only valid under an ESM module setting
|
|
22822
23098
|
__filename
|
|
22823
23099
|
));
|
|
22824
|
-
return
|
|
23100
|
+
return path29.join(dir, "..", "skills");
|
|
22825
23101
|
}
|
|
22826
23102
|
|
|
22827
23103
|
// src/services/repoGroup.ts
|
|
22828
23104
|
var fs10 = __toESM(require("fs"));
|
|
22829
|
-
var
|
|
23105
|
+
var path32 = __toESM(require("path"));
|
|
22830
23106
|
|
|
22831
23107
|
// src/services/worktreeBootstrap.ts
|
|
22832
23108
|
var import_child_process8 = require("child_process");
|
|
22833
23109
|
var import_util4 = require("util");
|
|
22834
23110
|
var fs9 = __toESM(require("fs"));
|
|
22835
|
-
var
|
|
23111
|
+
var path31 = __toESM(require("path"));
|
|
22836
23112
|
|
|
22837
23113
|
// src/services/gitExec.ts
|
|
22838
23114
|
var import_child_process7 = require("child_process");
|
|
@@ -22914,7 +23190,7 @@ function subcommandOf2(args) {
|
|
|
22914
23190
|
|
|
22915
23191
|
// src/services/worktreeLink.ts
|
|
22916
23192
|
var fs8 = __toESM(require("fs"));
|
|
22917
|
-
var
|
|
23193
|
+
var path30 = __toESM(require("path"));
|
|
22918
23194
|
function linkPath(source, target, platform, hardLink = fs8.linkSync) {
|
|
22919
23195
|
const isDir = fs8.statSync(source).isDirectory();
|
|
22920
23196
|
if (platform !== "win32") {
|
|
@@ -22939,8 +23215,8 @@ function mirrorDir(source, target, opts) {
|
|
|
22939
23215
|
const mirror = (from, to, prefix, top) => {
|
|
22940
23216
|
fs8.mkdirSync(to, { recursive: true });
|
|
22941
23217
|
for (const entry of fs8.readdirSync(from, { withFileTypes: true })) {
|
|
22942
|
-
const name =
|
|
22943
|
-
const at = { from:
|
|
23218
|
+
const name = path30.join(prefix, entry.name);
|
|
23219
|
+
const at = { from: path30.join(from, entry.name), to: path30.join(to, entry.name), name };
|
|
22944
23220
|
if (entry.isSymbolicLink()) links.push(at);
|
|
22945
23221
|
else if (top && entry.isDirectory() && (entry.name.startsWith("@") || entry.name === ".bin")) mirror(at.from, at.to, name, false);
|
|
22946
23222
|
else if (linkPath(at.from, at.to, opts.platform, opts.hardLink) === "copy") copied.push(name);
|
|
@@ -22960,16 +23236,16 @@ function mirrorDir(source, target, opts) {
|
|
|
22960
23236
|
}
|
|
22961
23237
|
function relink(from, to, opts) {
|
|
22962
23238
|
const text6 = fs8.readlinkSync(from);
|
|
22963
|
-
const pointee =
|
|
23239
|
+
const pointee = path30.isAbsolute(text6) ? rebased(text6, opts) : text6;
|
|
22964
23240
|
if (opts.platform !== "win32") {
|
|
22965
|
-
if (!fs8.existsSync(
|
|
22966
|
-
fs8.symlinkSync(rebased(
|
|
23241
|
+
if (!fs8.existsSync(path30.resolve(path30.dirname(to), pointee)) && fs8.existsSync(path30.resolve(path30.dirname(from), pointee))) {
|
|
23242
|
+
fs8.symlinkSync(rebased(path30.resolve(path30.dirname(from), pointee), opts), to);
|
|
22967
23243
|
return "symlink";
|
|
22968
23244
|
}
|
|
22969
23245
|
fs8.symlinkSync(pointee, to);
|
|
22970
23246
|
return "symlink";
|
|
22971
23247
|
}
|
|
22972
|
-
const resolved = fs8.existsSync(
|
|
23248
|
+
const resolved = fs8.existsSync(path30.resolve(path30.dirname(to), pointee)) ? path30.resolve(path30.dirname(to), pointee) : rebased(path30.resolve(path30.dirname(from), pointee), opts);
|
|
22973
23249
|
try {
|
|
22974
23250
|
fs8.statSync(resolved);
|
|
22975
23251
|
} catch {
|
|
@@ -22978,9 +23254,9 @@ function relink(from, to, opts) {
|
|
|
22978
23254
|
return linkPath(resolved, to, opts.platform, opts.hardLink);
|
|
22979
23255
|
}
|
|
22980
23256
|
function rebased(pointee, opts) {
|
|
22981
|
-
const rel =
|
|
22982
|
-
const inside2 = rel !== "" && !rel.startsWith("..") && !
|
|
22983
|
-
return inside2 && !rel.split(/[\\/]/).includes("node_modules") ?
|
|
23257
|
+
const rel = path30.relative(opts.from, pointee);
|
|
23258
|
+
const inside2 = rel !== "" && !rel.startsWith("..") && !path30.isAbsolute(rel);
|
|
23259
|
+
return inside2 && !rel.split(/[\\/]/).includes("node_modules") ? path30.join(opts.to, rel) : pointee;
|
|
22984
23260
|
}
|
|
22985
23261
|
|
|
22986
23262
|
// src/services/worktreeBootstrap.ts
|
|
@@ -22997,7 +23273,7 @@ function lexists(target) {
|
|
|
22997
23273
|
return false;
|
|
22998
23274
|
}
|
|
22999
23275
|
}
|
|
23000
|
-
function
|
|
23276
|
+
function listDir2(dir) {
|
|
23001
23277
|
try {
|
|
23002
23278
|
return fs9.readdirSync(dir).sort();
|
|
23003
23279
|
} catch {
|
|
@@ -23017,18 +23293,18 @@ function matchLinks(root, patterns) {
|
|
|
23017
23293
|
}
|
|
23018
23294
|
function segmentMatches(root, base, segment) {
|
|
23019
23295
|
const under = (name) => base ? `${base}/${name}` : name;
|
|
23020
|
-
if (!/[*?]/.test(segment)) return lexists(
|
|
23296
|
+
if (!/[*?]/.test(segment)) return lexists(path31.join(root, base, segment)) ? [under(segment)] : [];
|
|
23021
23297
|
const pattern = new RegExp(`^${segment.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*").replace(/\?/g, ".")}$`);
|
|
23022
|
-
return
|
|
23298
|
+
return listDir2(path31.join(root, base)).filter((name) => !NEVER_SCANNED.has(name) && pattern.test(name)).map(under);
|
|
23023
23299
|
}
|
|
23024
23300
|
function installDirs(root) {
|
|
23025
23301
|
const packages = matchLinks(root, workspaceGlobs(root)).map((pkg) => `${pkg}/node_modules`);
|
|
23026
|
-
return ["node_modules", ...packages].filter((dir) => lexists(
|
|
23302
|
+
return ["node_modules", ...packages].filter((dir) => lexists(path31.join(root, dir)));
|
|
23027
23303
|
}
|
|
23028
23304
|
function workspaceGlobs(root) {
|
|
23029
23305
|
let manifest;
|
|
23030
23306
|
try {
|
|
23031
|
-
manifest = JSON.parse(fs9.readFileSync(
|
|
23307
|
+
manifest = JSON.parse(fs9.readFileSync(path31.join(root, "package.json"), "utf8"));
|
|
23032
23308
|
} catch {
|
|
23033
23309
|
return [];
|
|
23034
23310
|
}
|
|
@@ -23043,19 +23319,19 @@ async function bootstrap(repo, cwd, opts) {
|
|
|
23043
23319
|
const linked = [];
|
|
23044
23320
|
const copied = [];
|
|
23045
23321
|
const link = (name) => {
|
|
23046
|
-
const target =
|
|
23322
|
+
const target = path31.join(cwd, name);
|
|
23047
23323
|
if (lexists(target)) return;
|
|
23048
|
-
fs9.mkdirSync(
|
|
23049
|
-
if (linkPath(
|
|
23324
|
+
fs9.mkdirSync(path31.dirname(target), { recursive: true });
|
|
23325
|
+
if (linkPath(path31.join(repo.root, name), target, opts.platform) === "copy") copied.push(name);
|
|
23050
23326
|
linked.push(name);
|
|
23051
23327
|
};
|
|
23052
23328
|
const mirror = (name) => {
|
|
23053
|
-
const target =
|
|
23054
|
-
if (lexists(target) || !fs9.existsSync(
|
|
23055
|
-
const source =
|
|
23329
|
+
const target = path31.join(cwd, name);
|
|
23330
|
+
if (lexists(target) || !fs9.existsSync(path31.dirname(target))) return;
|
|
23331
|
+
const source = path31.join(repo.root, name);
|
|
23056
23332
|
if (!fs9.lstatSync(source).isDirectory()) return link(name);
|
|
23057
23333
|
const mirrored = mirrorDir(source, target, { platform: opts.platform, from: repo.root, to: cwd });
|
|
23058
|
-
copied.push(...mirrored.map((entry) =>
|
|
23334
|
+
copied.push(...mirrored.map((entry) => path31.join(name, entry)));
|
|
23059
23335
|
linked.push(name);
|
|
23060
23336
|
};
|
|
23061
23337
|
if (!setup) {
|
|
@@ -23090,12 +23366,12 @@ async function runSetup(command, repo, cwd, resolvePath) {
|
|
|
23090
23366
|
|
|
23091
23367
|
// src/services/repoGroup.ts
|
|
23092
23368
|
function hasGitEntry(dir) {
|
|
23093
|
-
return fs10.existsSync(
|
|
23369
|
+
return fs10.existsSync(path32.join(dir, ".git"));
|
|
23094
23370
|
}
|
|
23095
23371
|
function groupPathOf(listed3) {
|
|
23096
23372
|
const slashed = listed3.trim().replace(/\\/g, "/");
|
|
23097
|
-
if (
|
|
23098
|
-
const rel =
|
|
23373
|
+
if (path32.posix.isAbsolute(slashed) || path32.win32.isAbsolute(slashed)) return null;
|
|
23374
|
+
const rel = path32.posix.normalize(slashed).replace(/\/+$/, "");
|
|
23099
23375
|
return rel === "" || rel === "." || rel === ".." || rel.startsWith("../") ? null : rel;
|
|
23100
23376
|
}
|
|
23101
23377
|
function childDirs(dir) {
|
|
@@ -23106,14 +23382,14 @@ function childDirs(dir) {
|
|
|
23106
23382
|
}
|
|
23107
23383
|
}
|
|
23108
23384
|
function groupPaths(workspaceRoot, listed3) {
|
|
23109
|
-
if (listed3.length === 0) return childDirs(workspaceRoot).filter((name) => hasGitEntry(
|
|
23110
|
-
const paths = listed3.map(groupPathOf).filter((rel) => rel !== null && hasGitEntry(
|
|
23385
|
+
if (listed3.length === 0) return childDirs(workspaceRoot).filter((name) => hasGitEntry(path32.join(workspaceRoot, name)));
|
|
23386
|
+
const paths = listed3.map(groupPathOf).filter((rel) => rel !== null && hasGitEntry(path32.join(workspaceRoot, rel)));
|
|
23111
23387
|
return [...new Set(paths)].sort();
|
|
23112
23388
|
}
|
|
23113
23389
|
function workspaceRepoGroup(workspaceRoot, listed3) {
|
|
23114
|
-
for (let dir =
|
|
23390
|
+
for (let dir = path32.resolve(workspaceRoot); ; dir = path32.dirname(dir)) {
|
|
23115
23391
|
if (hasGitEntry(dir)) return [SELF_REPO];
|
|
23116
|
-
if (
|
|
23392
|
+
if (path32.dirname(dir) === dir) break;
|
|
23117
23393
|
}
|
|
23118
23394
|
return groupPaths(workspaceRoot, listed3);
|
|
23119
23395
|
}
|
|
@@ -23194,8 +23470,8 @@ function copyDirSync(src, dest) {
|
|
|
23194
23470
|
fs11.mkdirSync(dest, { recursive: true });
|
|
23195
23471
|
const entries = fs11.readdirSync(src, { withFileTypes: true });
|
|
23196
23472
|
for (const entry of entries) {
|
|
23197
|
-
const srcPath =
|
|
23198
|
-
const destPath =
|
|
23473
|
+
const srcPath = path33.join(src, entry.name);
|
|
23474
|
+
const destPath = path33.join(dest, entry.name);
|
|
23199
23475
|
if (entry.isDirectory()) copyDirSync(srcPath, destPath);
|
|
23200
23476
|
else fs11.copyFileSync(srcPath, destPath);
|
|
23201
23477
|
}
|
|
@@ -23219,7 +23495,7 @@ function readDir(dir, source) {
|
|
|
23219
23495
|
if (!fs11.existsSync(dir)) return { skills, invalid };
|
|
23220
23496
|
for (const entry of fs11.readdirSync(dir, { withFileTypes: true })) {
|
|
23221
23497
|
if (!entry.isDirectory()) continue;
|
|
23222
|
-
const skillFile =
|
|
23498
|
+
const skillFile = path33.join(dir, entry.name, "SKILL.md");
|
|
23223
23499
|
if (!fs11.existsSync(skillFile)) continue;
|
|
23224
23500
|
if (!isSkillName(entry.name)) {
|
|
23225
23501
|
invalid.push({ name: entry.name, path: skillFile, source, reason: INVALID_SKILL_NAME_REASON });
|
|
@@ -23238,15 +23514,15 @@ function skillFileIn(dir, name) {
|
|
|
23238
23514
|
return void 0;
|
|
23239
23515
|
}
|
|
23240
23516
|
if (!entries.some((entry) => entry.isDirectory() && entry.name === name)) return void 0;
|
|
23241
|
-
const skillFile =
|
|
23517
|
+
const skillFile = path33.join(dir, name, "SKILL.md");
|
|
23242
23518
|
return fs11.existsSync(skillFile) ? skillFile : void 0;
|
|
23243
23519
|
}
|
|
23244
23520
|
function skillsDirOf(root) {
|
|
23245
|
-
return
|
|
23521
|
+
return path33.join(root, STATE_DIR, "skills");
|
|
23246
23522
|
}
|
|
23247
23523
|
function workspaceSkillRoots(workspaceRoot, repos, checkout = workspaceRoot) {
|
|
23248
23524
|
if (repos.length === 0 || repos.includes(SELF_REPO)) return [checkout];
|
|
23249
|
-
return [workspaceRoot, ...repos.map((repo) =>
|
|
23525
|
+
return [workspaceRoot, ...repos.map((repo) => path33.join(checkout, repo))];
|
|
23250
23526
|
}
|
|
23251
23527
|
var SkillsService = class _SkillsService {
|
|
23252
23528
|
workspaceRoots;
|
|
@@ -23269,11 +23545,11 @@ var SkillsService = class _SkillsService {
|
|
|
23269
23545
|
return this.workspaceRoots.map(skillsDirOf);
|
|
23270
23546
|
}
|
|
23271
23547
|
globalDir() {
|
|
23272
|
-
return
|
|
23548
|
+
return path33.join(globalDataDir(), "skills");
|
|
23273
23549
|
}
|
|
23274
23550
|
readSeedManifest() {
|
|
23275
23551
|
try {
|
|
23276
|
-
const parsed = JSON.parse(fs11.readFileSync(
|
|
23552
|
+
const parsed = JSON.parse(fs11.readFileSync(path33.join(this.globalDir(), SEED_MANIFEST), "utf8"));
|
|
23277
23553
|
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
23278
23554
|
return Object.fromEntries(
|
|
23279
23555
|
Object.entries(parsed).filter((entry) => typeof entry[1] === "string")
|
|
@@ -23289,7 +23565,7 @@ var SkillsService = class _SkillsService {
|
|
|
23289
23565
|
manifest[name] = hash;
|
|
23290
23566
|
try {
|
|
23291
23567
|
fs11.mkdirSync(this.globalDir(), { recursive: true });
|
|
23292
|
-
fs11.writeFileSync(
|
|
23568
|
+
fs11.writeFileSync(path33.join(this.globalDir(), SEED_MANIFEST), JSON.stringify(manifest, null, 2));
|
|
23293
23569
|
} catch (err) {
|
|
23294
23570
|
console.warn(`Could not record seeded skill ${name}: ${String(err)}`);
|
|
23295
23571
|
}
|
|
@@ -23300,11 +23576,11 @@ var SkillsService = class _SkillsService {
|
|
|
23300
23576
|
return this.readSeedManifest()[name] === currentHash || (PRIOR_BUILTIN_HASHES[name] ?? []).includes(currentHash);
|
|
23301
23577
|
}
|
|
23302
23578
|
seed(name) {
|
|
23303
|
-
const dest =
|
|
23579
|
+
const dest = path33.join(this.globalDir(), name);
|
|
23304
23580
|
const exists = fs11.existsSync(dest);
|
|
23305
23581
|
let srcDir;
|
|
23306
23582
|
try {
|
|
23307
|
-
srcDir =
|
|
23583
|
+
srcDir = path33.join(builtinSkillsDir(), name);
|
|
23308
23584
|
} catch (err) {
|
|
23309
23585
|
if (!exists) console.warn(`Could not locate built-in skills dir: ${String(err)}`);
|
|
23310
23586
|
return false;
|
|
@@ -23313,9 +23589,9 @@ var SkillsService = class _SkillsService {
|
|
|
23313
23589
|
if (!exists) console.warn(`Built-in skill not found in package: ${name}`);
|
|
23314
23590
|
return false;
|
|
23315
23591
|
}
|
|
23316
|
-
const srcHash = fileHash(
|
|
23592
|
+
const srcHash = fileHash(path33.join(srcDir, "SKILL.md"));
|
|
23317
23593
|
if (exists) {
|
|
23318
|
-
const currentHash = fileHash(
|
|
23594
|
+
const currentHash = fileHash(path33.join(dest, "SKILL.md"));
|
|
23319
23595
|
if (currentHash !== void 0 && currentHash === srcHash) {
|
|
23320
23596
|
this.recordSeed(name, currentHash);
|
|
23321
23597
|
return false;
|
|
@@ -23359,7 +23635,7 @@ var SkillsService = class _SkillsService {
|
|
|
23359
23635
|
if (entries.length !== 1 || entries[0].name !== "SKILL.md" || !entries[0].isFile()) return false;
|
|
23360
23636
|
let raw;
|
|
23361
23637
|
try {
|
|
23362
|
-
raw = readSkillText(
|
|
23638
|
+
raw = readSkillText(path33.join(dir, "SKILL.md"));
|
|
23363
23639
|
} catch {
|
|
23364
23640
|
return false;
|
|
23365
23641
|
}
|
|
@@ -23367,7 +23643,7 @@ var SkillsService = class _SkillsService {
|
|
|
23367
23643
|
}
|
|
23368
23644
|
pruneRetired() {
|
|
23369
23645
|
for (const name of RETIRED_BUILTIN_SKILL_NAMES) {
|
|
23370
|
-
const dest =
|
|
23646
|
+
const dest = path33.join(this.globalDir(), name);
|
|
23371
23647
|
if (!fs11.existsSync(dest)) continue;
|
|
23372
23648
|
if (!this.isUnmodifiedRetiredSeed(dest, name)) continue;
|
|
23373
23649
|
try {
|
|
@@ -23505,12 +23781,12 @@ var runGit = execFileWithTimeout(1e4);
|
|
|
23505
23781
|
async function committedInHead(files) {
|
|
23506
23782
|
const byDir = /* @__PURE__ */ new Map();
|
|
23507
23783
|
for (const file of new Set(files)) {
|
|
23508
|
-
const dir =
|
|
23784
|
+
const dir = path34.dirname(path34.dirname(file));
|
|
23509
23785
|
byDir.set(dir, [...byDir.get(dir) ?? [], file]);
|
|
23510
23786
|
}
|
|
23511
23787
|
const result = /* @__PURE__ */ new Map();
|
|
23512
23788
|
await Promise.all([...byDir].map(async ([dir, inDir]) => {
|
|
23513
|
-
const relative17 = (file) =>
|
|
23789
|
+
const relative17 = (file) => path34.relative(dir, file).split(path34.sep).join("/");
|
|
23514
23790
|
try {
|
|
23515
23791
|
const { stdout } = await runGit("git", ["ls-tree", "--name-only", "HEAD", "--", ...inDir.map(relative17)], { cwd: dir, env: cleanEnv() });
|
|
23516
23792
|
const listed3 = new Set(stdout.split("\n").map((line) => line.trim()).filter(Boolean));
|
|
@@ -23530,9 +23806,9 @@ function plannedSkillLookup(skillsAt, workspaceRoot, layout) {
|
|
|
23530
23806
|
searchedDirs: () => lookup2.searchedDirs(),
|
|
23531
23807
|
listSkills: () => lookup2.listSkills(),
|
|
23532
23808
|
uncommitted: async (skills) => {
|
|
23533
|
-
const checked = skills.filter((skill) => skill.source === "workspace" && !(readInPlace &&
|
|
23809
|
+
const checked = skills.filter((skill) => skill.source === "workspace" && !(readInPlace && path34.dirname(path34.dirname(skill.path)) === readInPlace));
|
|
23534
23810
|
const committed = await committedInHead(checked.map((skill) => skill.path));
|
|
23535
|
-
return new Map(checked.filter((skill) => committed.get(skill.path) === false).map((skill) => [skill.path,
|
|
23811
|
+
return new Map(checked.filter((skill) => committed.get(skill.path) === false).map((skill) => [skill.path, path34.relative(workspaceRoot, path34.dirname(skill.path)).split(path34.sep).join("/")]));
|
|
23536
23812
|
}
|
|
23537
23813
|
};
|
|
23538
23814
|
}
|
|
@@ -23545,13 +23821,13 @@ function checkPlanSkills(tasks, lookup2) {
|
|
|
23545
23821
|
function checkTaskSkillsEdit(task2, skills, lookup2) {
|
|
23546
23822
|
return check([{ taskId: task2.id, owner: `Task "${task2.title}"`, ...attached(skills) }], lookup2);
|
|
23547
23823
|
}
|
|
23548
|
-
function subtaskEntries(op, spec,
|
|
23824
|
+
function subtaskEntries(op, spec, path50 = []) {
|
|
23549
23825
|
const subtasks = spec?.subtasks;
|
|
23550
23826
|
if (!Array.isArray(subtasks)) return [];
|
|
23551
23827
|
return subtasks.flatMap((sub, i) => {
|
|
23552
23828
|
if (typeof sub !== "object" || sub === null) return [];
|
|
23553
23829
|
const child = sub;
|
|
23554
|
-
const at = [...
|
|
23830
|
+
const at = [...path50, typeof child.title === "string" && child.title.trim() ? `"${child.title}"` : `#${i + 1}`];
|
|
23555
23831
|
return [{ op, owner: `subtask ${at.join(" > ")}`, ...attached(child.skills) }, ...subtaskEntries(op, child, at)];
|
|
23556
23832
|
});
|
|
23557
23833
|
}
|
|
@@ -27421,7 +27697,7 @@ var RUN_SETTING_KEYS = Object.keys(RUN_SETTING_CHECKS);
|
|
|
27421
27697
|
function getSettingsPath() {
|
|
27422
27698
|
const override = process.env.ORDEWELL_SETTINGS_PATH;
|
|
27423
27699
|
if (override && override.trim()) return override.trim();
|
|
27424
|
-
return
|
|
27700
|
+
return path35.join(globalDataDir(), "settings.json");
|
|
27425
27701
|
}
|
|
27426
27702
|
function readPlannerModels(raw) {
|
|
27427
27703
|
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return void 0;
|
|
@@ -27501,8 +27777,8 @@ var SettingsService = class {
|
|
|
27501
27777
|
* a watch on the old one pointing at nothing.
|
|
27502
27778
|
*/
|
|
27503
27779
|
watch(listener) {
|
|
27504
|
-
const dir =
|
|
27505
|
-
const name =
|
|
27780
|
+
const dir = path35.dirname(this.filePath);
|
|
27781
|
+
const name = path35.basename(this.filePath);
|
|
27506
27782
|
let seen = this.fileStamp();
|
|
27507
27783
|
let timer;
|
|
27508
27784
|
let dead = false;
|
|
@@ -27511,7 +27787,7 @@ var SettingsService = class {
|
|
|
27511
27787
|
fs12.mkdirSync(dir, { recursive: true });
|
|
27512
27788
|
watcher = fs12.watch(dir, (_event, changed) => {
|
|
27513
27789
|
if (dead) return;
|
|
27514
|
-
if (changed && changed !== name && changed !==
|
|
27790
|
+
if (changed && changed !== name && changed !== path35.basename(dir)) return;
|
|
27515
27791
|
clearTimeout(timer);
|
|
27516
27792
|
timer = setTimeout(() => {
|
|
27517
27793
|
const stamp2 = this.fileStamp();
|
|
@@ -27744,7 +28020,7 @@ var SettingsService = class {
|
|
|
27744
28020
|
if (this.unreadable) {
|
|
27745
28021
|
throw new Error(`Cannot save settings: ${this.filePath} could not be read, so it will not be overwritten. Fix or remove it and try again.`);
|
|
27746
28022
|
}
|
|
27747
|
-
const dir =
|
|
28023
|
+
const dir = path35.dirname(this.filePath);
|
|
27748
28024
|
if (!fs12.existsSync(dir)) {
|
|
27749
28025
|
fs12.mkdirSync(dir, { recursive: true });
|
|
27750
28026
|
}
|
|
@@ -28188,7 +28464,7 @@ var BaseConfig = class {
|
|
|
28188
28464
|
const { model, effort, override = "" } = configured;
|
|
28189
28465
|
const named3 = isPlannerBackend(configured.setting ?? "") || isPlannerBackend(process.env.AI_PROVIDER ?? "");
|
|
28190
28466
|
const pickedElsewhere = configured.pickedFor !== void 0 && configured.pickedFor !== provider;
|
|
28191
|
-
const kept = configured.pickedFor === provider || !pickedElsewhere && (isApiProvider(provider) || named3 && !isForeignModel(provider, model, this.agentCatalog(provider)));
|
|
28467
|
+
const kept = configured.pickedFor === provider || !pickedElsewhere && (isApiProvider(provider) || named3 && !isForeignModel(provider, model, () => this.agentCatalog(provider)));
|
|
28192
28468
|
if (kept) return { model: override || model, effort, source: override ? "request" : "configured" };
|
|
28193
28469
|
const own = pickedElsewhere || named3 ? rememberedOrDefault(provider) : detectedAgentChoice(provider, configured);
|
|
28194
28470
|
return !override || override === own.model ? own : { model: override, effort: "", source: "request" };
|
|
@@ -28197,7 +28473,8 @@ var BaseConfig = class {
|
|
|
28197
28473
|
* The model ids a coding agent was last discovered serving, empty while
|
|
28198
28474
|
* discovery is cold. A host that holds a runner catalog supplies it: it is
|
|
28199
28475
|
* the only evidence that tells OpenCode's `provider/model` ids from an
|
|
28200
|
-
* OpenRouter slug.
|
|
28476
|
+
* OpenRouter slug. Asked only when a named agent's configured model is
|
|
28477
|
+
* neither its alias nor its remembered pick.
|
|
28201
28478
|
*/
|
|
28202
28479
|
agentCatalog(_provider) {
|
|
28203
28480
|
return [];
|
|
@@ -28222,8 +28499,10 @@ function isVendorModelFor(agent, model) {
|
|
|
28222
28499
|
return !discoveryOf(agent)?.providerQualifiedIds && SLUG_SHAPE.test(model);
|
|
28223
28500
|
}
|
|
28224
28501
|
function isForeignModel(agent, model, catalog) {
|
|
28225
|
-
if (!model || isStableAlias(agent, model) ||
|
|
28226
|
-
|
|
28502
|
+
if (!model || isStableAlias(agent, model) || rememberedChoice(agent)?.model === model) return false;
|
|
28503
|
+
const listed3 = catalog();
|
|
28504
|
+
if (listed3.includes(model)) return false;
|
|
28505
|
+
return isVendorModelFor(agent, model) || listed3.length > 0 && SLUG_SHAPE.test(model);
|
|
28227
28506
|
}
|
|
28228
28507
|
function rememberedChoice(provider) {
|
|
28229
28508
|
const entry = new SettingsService().getPlannerModel(provider);
|
|
@@ -28343,11 +28622,11 @@ function mergeOutcome(result, branch, group, repaired = []) {
|
|
|
28343
28622
|
// src/services/taskDiffRead.ts
|
|
28344
28623
|
var import_crypto13 = require("crypto");
|
|
28345
28624
|
var fs13 = __toESM(require("fs"));
|
|
28346
|
-
var
|
|
28625
|
+
var path36 = __toESM(require("path"));
|
|
28347
28626
|
var import_os3 = require("os");
|
|
28348
28627
|
var TASK_DIFF_UNAVAILABLE = "Task diff is not available for this run: no task-specific changes were recorded (shared runs cannot attribute workspace changes to one task).";
|
|
28349
28628
|
function allowedPath(file) {
|
|
28350
|
-
return file.length > 0 && !
|
|
28629
|
+
return file.length > 0 && !path36.isAbsolute(file) && !/^[A-Za-z]:/.test(file) && !file.split(/[\\/]/).some((part) => part === ".." || part === ".git" || part === ".ordewell");
|
|
28351
28630
|
}
|
|
28352
28631
|
function stats(raw) {
|
|
28353
28632
|
return raw.split("\0").flatMap((row) => {
|
|
@@ -28356,8 +28635,8 @@ function stats(raw) {
|
|
|
28356
28635
|
});
|
|
28357
28636
|
}
|
|
28358
28637
|
function inside(root, target) {
|
|
28359
|
-
const rel =
|
|
28360
|
-
return rel === "" || !rel.startsWith(`..${
|
|
28638
|
+
const rel = path36.relative(root, target);
|
|
28639
|
+
return rel === "" || !rel.startsWith(`..${path36.sep}`) && rel !== ".." && !path36.isAbsolute(rel);
|
|
28361
28640
|
}
|
|
28362
28641
|
function unquoted(quoted4) {
|
|
28363
28642
|
const bytes = [];
|
|
@@ -28405,10 +28684,10 @@ function patchFiles(patch) {
|
|
|
28405
28684
|
const sections = patch.toString("utf8").split(/(?<![^\n])(?=diff --git )/).filter((section) => section.startsWith("diff --git "));
|
|
28406
28685
|
const files = /* @__PURE__ */ new Map();
|
|
28407
28686
|
for (const section of sections) {
|
|
28408
|
-
const
|
|
28687
|
+
const path50 = sectionPath(section);
|
|
28409
28688
|
const counts = sectionStats(section);
|
|
28410
|
-
const earlier = files.get(
|
|
28411
|
-
files.set(
|
|
28689
|
+
const earlier = files.get(path50);
|
|
28690
|
+
files.set(path50, earlier ? { path: path50, section: earlier.section + section, added: summed(earlier.added, counts.added), removed: summed(earlier.removed, counts.removed) } : { path: path50, section, ...counts });
|
|
28412
28691
|
}
|
|
28413
28692
|
return [...files.values()];
|
|
28414
28693
|
}
|
|
@@ -28478,11 +28757,11 @@ async function readTaskDiff(invoker, run, taskId, options) {
|
|
|
28478
28757
|
files.push({ ...file, repo: repo.path, patch: () => git(reader, cwd, [...diffArgs, "--", `:(top,literal)${prefix}${file.path}`]) });
|
|
28479
28758
|
}
|
|
28480
28759
|
if (live3) {
|
|
28481
|
-
const workspace =
|
|
28760
|
+
const workspace = path36.join(entry.worktree, prefix);
|
|
28482
28761
|
const others = await git(reader, workspace, [...common, "ls-files", "--others", "--exclude-standard", "-z", "--", "."]);
|
|
28483
28762
|
for (const file of others.split("\0").filter((f) => allowed(f))) {
|
|
28484
|
-
const full =
|
|
28485
|
-
if (!inside(fs13.realpathSync(workspace), fs13.realpathSync(
|
|
28763
|
+
const full = path36.join(workspace, file);
|
|
28764
|
+
if (!inside(fs13.realpathSync(workspace), fs13.realpathSync(path36.dirname(full)))) continue;
|
|
28486
28765
|
const untracked = async (numstat) => {
|
|
28487
28766
|
const result = await tryGit(reader, workspace, [...common, "diff", "--no-index", "--no-renames", "--no-ext-diff", "--no-textconv", ...numstat ? ["--numstat", "-z"] : [], "--", import_os3.devNull, file]);
|
|
28488
28767
|
if (!result.ok && result.code !== 1) throw new Error(result.stderr.trim() || "Could not read an untracked file diff.");
|
|
@@ -28512,11 +28791,11 @@ ${await file.patch()}`);
|
|
|
28512
28791
|
var import_crypto14 = require("crypto");
|
|
28513
28792
|
var import_child_process9 = require("child_process");
|
|
28514
28793
|
var fs15 = __toESM(require("fs"));
|
|
28515
|
-
var
|
|
28794
|
+
var path38 = __toESM(require("path"));
|
|
28516
28795
|
|
|
28517
28796
|
// src/utils/prdStore.ts
|
|
28518
28797
|
var fs14 = __toESM(require("fs"));
|
|
28519
|
-
var
|
|
28798
|
+
var path37 = __toESM(require("path"));
|
|
28520
28799
|
var PRD_BLOCK_RE = /<!--\s*ORDEWELL_PRD_START\s+slug="([^"]+)"\s*-->\s*([\s\S]*?)\s*<!--\s*ORDEWELL_PRD_END\s*-->/;
|
|
28521
28800
|
function extractPrdBlock(text6) {
|
|
28522
28801
|
const match = PRD_BLOCK_RE.exec(text6);
|
|
@@ -28530,9 +28809,9 @@ function sanitizeSlug(raw) {
|
|
|
28530
28809
|
return raw.toLowerCase().replace(/[^a-z0-9-_]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 80);
|
|
28531
28810
|
}
|
|
28532
28811
|
function savePrdMarkdown(workspace, slug, markdown) {
|
|
28533
|
-
const dir =
|
|
28812
|
+
const dir = path37.join(workspace, ".scratch", sanitizeSlug(slug));
|
|
28534
28813
|
fs14.mkdirSync(dir, { recursive: true });
|
|
28535
|
-
const file =
|
|
28814
|
+
const file = path37.join(dir, "PRD.md");
|
|
28536
28815
|
fs14.writeFileSync(file, markdown.endsWith("\n") ? markdown : markdown + "\n", "utf8");
|
|
28537
28816
|
return file;
|
|
28538
28817
|
}
|
|
@@ -28564,12 +28843,12 @@ var NESTED_REPO_DEPTH = 2;
|
|
|
28564
28843
|
var GITLINK_MODE = "160000";
|
|
28565
28844
|
var READ_ONLY = ["--no-optional-locks", "-c", "core.fsmonitor=false"];
|
|
28566
28845
|
function isInside(parent, child) {
|
|
28567
|
-
const rel =
|
|
28568
|
-
return rel !== "" && !rel.startsWith("..") && !
|
|
28846
|
+
const rel = path38.relative(parent, child);
|
|
28847
|
+
return rel !== "" && !rel.startsWith("..") && !path38.isAbsolute(rel);
|
|
28569
28848
|
}
|
|
28570
28849
|
function resolvesAsNamed(base, p) {
|
|
28571
28850
|
try {
|
|
28572
|
-
return fs15.realpathSync(p) ===
|
|
28851
|
+
return fs15.realpathSync(p) === path38.join(fs15.realpathSync(base), path38.relative(base, p));
|
|
28573
28852
|
} catch {
|
|
28574
28853
|
return false;
|
|
28575
28854
|
}
|
|
@@ -28624,7 +28903,7 @@ function sameDir(a, b) {
|
|
|
28624
28903
|
try {
|
|
28625
28904
|
return fs15.realpathSync.native(p);
|
|
28626
28905
|
} catch {
|
|
28627
|
-
return
|
|
28906
|
+
return path38.resolve(p);
|
|
28628
28907
|
}
|
|
28629
28908
|
};
|
|
28630
28909
|
return real(a) === real(b);
|
|
@@ -28646,7 +28925,7 @@ function clearImmutableFlags(platform, dir) {
|
|
|
28646
28925
|
(0, import_child_process9.spawnSync)("/usr/bin/chflags", ["-R", "nouchg", dir], { stdio: "ignore" });
|
|
28647
28926
|
}
|
|
28648
28927
|
function leftoverLinks(dir) {
|
|
28649
|
-
const named3 =
|
|
28928
|
+
const named3 = listDir2(dir).filter((name) => LINKED_ARTIFACTS.has(name) || isEnvFile(name) || isLink(path38.join(dir, name)));
|
|
28650
28929
|
return [.../* @__PURE__ */ new Set([...named3, ...installDirs(dir)])];
|
|
28651
28930
|
}
|
|
28652
28931
|
function isLink(target) {
|
|
@@ -28719,7 +28998,7 @@ var GitWorktreeIsolation = class {
|
|
|
28719
28998
|
const head = await this.headOf(root);
|
|
28720
28999
|
const repo = { path: repoPath, head };
|
|
28721
29000
|
if (scan.toplevel !== void 0) {
|
|
28722
|
-
const top =
|
|
29001
|
+
const top = path38.relative(workspaceRoot, scan.toplevel).split(path38.sep).join("/");
|
|
28723
29002
|
if (top !== "") repo.top = top;
|
|
28724
29003
|
}
|
|
28725
29004
|
if (enabled && (head.kind === "branch" || head.kind === "detached")) {
|
|
@@ -28776,9 +29055,9 @@ var GitWorktreeIsolation = class {
|
|
|
28776
29055
|
async nestedRepos(workspaceRoot, toplevel) {
|
|
28777
29056
|
const found = [];
|
|
28778
29057
|
const walk = (rel, depth) => {
|
|
28779
|
-
for (const name of childDirs(
|
|
29058
|
+
for (const name of childDirs(path38.join(workspaceRoot, rel))) {
|
|
28780
29059
|
const child = rel ? `${rel}/${name}` : name;
|
|
28781
|
-
if (hasGitEntry(
|
|
29060
|
+
if (hasGitEntry(path38.join(workspaceRoot, child))) found.push(child);
|
|
28782
29061
|
else if (depth < NESTED_REPO_DEPTH) walk(child, depth + 1);
|
|
28783
29062
|
}
|
|
28784
29063
|
};
|
|
@@ -28796,7 +29075,7 @@ var GitWorktreeIsolation = class {
|
|
|
28796
29075
|
const [meta, file] = entry.split(" ");
|
|
28797
29076
|
if (file !== void 0 && meta.startsWith(`${GITLINK_MODE} `)) paths.add(file);
|
|
28798
29077
|
}
|
|
28799
|
-
const declared = await this.tryGit(workspaceRoot, ["config", "-z", "-f",
|
|
29078
|
+
const declared = await this.tryGit(workspaceRoot, ["config", "-z", "-f", path38.join(toplevel, ".gitmodules"), "--get-regexp", "^submodule\\..*\\.path$"]);
|
|
28800
29079
|
for (const entry of declared.stdout.split("\0")) {
|
|
28801
29080
|
const value = entry.split("\n")[1];
|
|
28802
29081
|
if (value) paths.add(value.replace(/\/+$/, ""));
|
|
@@ -28854,7 +29133,7 @@ var GitWorktreeIsolation = class {
|
|
|
28854
29133
|
async acceptsWorktree(run, repo) {
|
|
28855
29134
|
const dir = this.integrationDir(run, repo);
|
|
28856
29135
|
ensureStateDirIgnored(run.workspaceRoot);
|
|
28857
|
-
fs15.mkdirSync(
|
|
29136
|
+
fs15.mkdirSync(path38.dirname(dir), { recursive: true });
|
|
28858
29137
|
const added = await this.tryGit(repo.root, ["worktree", "add", "-q", "--no-checkout", dir, repo.integrationBranch]);
|
|
28859
29138
|
await this.removeWorktreeDir(run, repo, dir, []);
|
|
28860
29139
|
return added.ok;
|
|
@@ -28869,12 +29148,12 @@ var GitWorktreeIsolation = class {
|
|
|
28869
29148
|
if (isolated.includes(SELF_REPO)) return [];
|
|
28870
29149
|
const shared2 = [];
|
|
28871
29150
|
const walk = (rel) => {
|
|
28872
|
-
for (const name of
|
|
29151
|
+
for (const name of listDir2(path38.join(workspaceRoot, rel))) {
|
|
28873
29152
|
if (!rel && (name === STATE_DIR || name === ".git")) continue;
|
|
28874
29153
|
const child = rel ? `${rel}/${name}` : name;
|
|
28875
29154
|
if (isolated.includes(child)) continue;
|
|
28876
29155
|
if (isolated.some((p) => p.startsWith(`${child}/`))) walk(child);
|
|
28877
|
-
else if (resolves(
|
|
29156
|
+
else if (resolves(path38.join(workspaceRoot, child))) shared2.push(child);
|
|
28878
29157
|
}
|
|
28879
29158
|
};
|
|
28880
29159
|
walk("");
|
|
@@ -28914,9 +29193,9 @@ var GitWorktreeIsolation = class {
|
|
|
28914
29193
|
try {
|
|
28915
29194
|
ensureStateDirIgnored(run.workspaceRoot);
|
|
28916
29195
|
for (const repo of run.repos) {
|
|
28917
|
-
const worktree =
|
|
29196
|
+
const worktree = path38.join(dir, repo.path);
|
|
28918
29197
|
const tip = (await this.git(repo.root, ["rev-parse", repo.integrationBranch])).trim();
|
|
28919
|
-
fs15.mkdirSync(
|
|
29198
|
+
fs15.mkdirSync(path38.dirname(worktree), { recursive: true });
|
|
28920
29199
|
await this.git(repo.root, ["worktree", "add", "-q", "-B", branch, worktree, tip]);
|
|
28921
29200
|
record.repos[repo.path] = { worktree, linked: [], baseRef: tip };
|
|
28922
29201
|
}
|
|
@@ -28941,13 +29220,13 @@ var GitWorktreeIsolation = class {
|
|
|
28941
29220
|
setupFailed.push({ repo: repo.path, detail: err.detail });
|
|
28942
29221
|
}
|
|
28943
29222
|
entry.linked = boot.linked;
|
|
28944
|
-
copied.push(...boot.copied.map((name) =>
|
|
29223
|
+
copied.push(...boot.copied.map((name) => path38.posix.join(repo.path, name)));
|
|
28945
29224
|
}
|
|
28946
29225
|
for (const rel of run.shared) {
|
|
28947
|
-
const source =
|
|
28948
|
-
const target =
|
|
29226
|
+
const source = path38.join(run.workspaceRoot, rel);
|
|
29227
|
+
const target = path38.join(cwd, rel);
|
|
28949
29228
|
if (!resolves(source) || lexists(target)) continue;
|
|
28950
|
-
fs15.mkdirSync(
|
|
29229
|
+
fs15.mkdirSync(path38.dirname(target), { recursive: true });
|
|
28951
29230
|
if (linkPath(source, target, this.platform) === "copy") copied.push(rel);
|
|
28952
29231
|
const entry = self ? record.repos[SELF_REPO] : void 0;
|
|
28953
29232
|
if (entry) entry.linked.push(rel);
|
|
@@ -28979,7 +29258,7 @@ var GitWorktreeIsolation = class {
|
|
|
28979
29258
|
*/
|
|
28980
29259
|
async clearLeftovers(run, task2, names) {
|
|
28981
29260
|
const removal = noRemoval();
|
|
28982
|
-
const holder = Object.values(run.tasks).find((r) => r.taskId !== task2.id && (r.branch === names.branch ||
|
|
29261
|
+
const holder = Object.values(run.tasks).find((r) => r.taskId !== task2.id && (r.branch === names.branch || path38.resolve(r.workspace) === path38.resolve(names.dir) || isInside(r.workspace, names.dir)));
|
|
28983
29262
|
if (holder) throw new Error(`Its worktree path or branch is held by the record of task ${holder.order} (${holder.title}); Ordewell will not remove it`);
|
|
28984
29263
|
const found = [];
|
|
28985
29264
|
for (const repo of run.repos) {
|
|
@@ -28989,14 +29268,14 @@ var GitWorktreeIsolation = class {
|
|
|
28989
29268
|
worktree: /^worktree (.+)$/m.exec(block)?.[1],
|
|
28990
29269
|
branch: /^branch refs\/heads\/(.+)$/m.exec(block)?.[1]
|
|
28991
29270
|
}));
|
|
28992
|
-
const at = /* @__PURE__ */ new Set([
|
|
29271
|
+
const at = /* @__PURE__ */ new Set([path38.join(names.dir, repo.path)]);
|
|
28993
29272
|
for (const entry of registered) {
|
|
28994
|
-
if (entry.worktree && entry.branch === names.branch && isInside(this.runRoot(run),
|
|
29273
|
+
if (entry.worktree && entry.branch === names.branch && isInside(this.runRoot(run), path38.resolve(entry.worktree))) at.add(entry.worktree);
|
|
28995
29274
|
}
|
|
28996
|
-
const base = { branch: names.branch, name:
|
|
29275
|
+
const base = { branch: names.branch, name: path38.basename(names.dir), title: task2.title };
|
|
28997
29276
|
for (const worktree of at) {
|
|
28998
29277
|
if (!fs15.existsSync(worktree)) continue;
|
|
28999
|
-
const linked = leftoverLinks(
|
|
29278
|
+
const linked = leftoverLinks(path38.join(worktree, await this.prefixOf(repo)));
|
|
29000
29279
|
found.push({ repo, worktree, target: { ...base, worktree, linked } });
|
|
29001
29280
|
}
|
|
29002
29281
|
found.push({ repo, worktree: names.dir, target: { ...base, linked: [] } });
|
|
@@ -29089,7 +29368,7 @@ var GitWorktreeIsolation = class {
|
|
|
29089
29368
|
*/
|
|
29090
29369
|
async inWorkspacePlace(repo, worktree) {
|
|
29091
29370
|
const prefix = await this.prefixOf(repo);
|
|
29092
|
-
return prefix ?
|
|
29371
|
+
return prefix ? path38.join(worktree, prefix) : worktree;
|
|
29093
29372
|
}
|
|
29094
29373
|
async prefixOf(repo) {
|
|
29095
29374
|
return (await this.git(repo.root, ["rev-parse", "--show-prefix"])).trim();
|
|
@@ -29619,15 +29898,15 @@ var GitWorktreeIsolation = class {
|
|
|
29619
29898
|
return landingMessage(kind, from, this.deps.plannedWith?.() ?? false);
|
|
29620
29899
|
}
|
|
29621
29900
|
integrationDir(run, repo) {
|
|
29622
|
-
return
|
|
29901
|
+
return path38.join(this.runRoot(run), INTEGRATION_DIR, repo.path);
|
|
29623
29902
|
}
|
|
29624
29903
|
async ensureIntegrationWorktree(run, repo) {
|
|
29625
29904
|
return this.admin(run.workspaceRoot, async () => {
|
|
29626
29905
|
const dir = this.integrationDir(run, repo);
|
|
29627
|
-
if (fs15.existsSync(
|
|
29906
|
+
if (fs15.existsSync(path38.join(dir, ".git"))) return dir;
|
|
29628
29907
|
await this.tryGit(repo.root, ["worktree", "prune"]);
|
|
29629
29908
|
ensureStateDirIgnored(run.workspaceRoot);
|
|
29630
|
-
fs15.mkdirSync(
|
|
29909
|
+
fs15.mkdirSync(path38.dirname(dir), { recursive: true });
|
|
29631
29910
|
await this.git(repo.root, ["worktree", "add", "-q", dir, repo.integrationBranch]);
|
|
29632
29911
|
return dir;
|
|
29633
29912
|
});
|
|
@@ -29650,7 +29929,7 @@ var GitWorktreeIsolation = class {
|
|
|
29650
29929
|
const removal = noRemoval();
|
|
29651
29930
|
for (const repo of opts.preserve === false || record.readOnly ? [] : run.repos) {
|
|
29652
29931
|
const entry = record.repos[repo.path];
|
|
29653
|
-
const target = { worktree: entry?.worktree, linked: entry?.linked ?? [], branch: record.branch, name:
|
|
29932
|
+
const target = { worktree: entry?.worktree, linked: entry?.linked ?? [], branch: record.branch, name: path38.basename(record.workspace), title: record.title, commit: record.commit };
|
|
29654
29933
|
try {
|
|
29655
29934
|
const kept = await this.preserve(run, repo, target, opts.intent);
|
|
29656
29935
|
if (kept) removal.preserved.push({ task: task2, ...kept });
|
|
@@ -29731,7 +30010,7 @@ var GitWorktreeIsolation = class {
|
|
|
29731
30010
|
return;
|
|
29732
30011
|
}
|
|
29733
30012
|
for (const entry of entries) {
|
|
29734
|
-
const target =
|
|
30013
|
+
const target = path38.join(dir, entry.name);
|
|
29735
30014
|
if (entry.isSymbolicLink()) this.unlinkIfLink(target);
|
|
29736
30015
|
else if (entry.isDirectory()) this.unlinkLinksUnder(target);
|
|
29737
30016
|
}
|
|
@@ -29753,20 +30032,20 @@ var GitWorktreeIsolation = class {
|
|
|
29753
30032
|
return false;
|
|
29754
30033
|
}
|
|
29755
30034
|
};
|
|
29756
|
-
const owned = new Set(Object.values(run.tasks).map((r) =>
|
|
30035
|
+
const owned = new Set(Object.values(run.tasks).map((r) => path38.resolve(r.workspace)));
|
|
29757
30036
|
const root = this.runRoot(run);
|
|
29758
30037
|
if (fs15.existsSync(root)) {
|
|
29759
30038
|
for (const entry of fs15.readdirSync(root)) {
|
|
29760
|
-
const dir =
|
|
29761
|
-
if (entry === INTEGRATION_DIR || owned.has(
|
|
30039
|
+
const dir = path38.join(root, entry);
|
|
30040
|
+
if (entry === INTEGRATION_DIR || owned.has(path38.resolve(dir))) continue;
|
|
29762
30041
|
let kept = true;
|
|
29763
30042
|
for (const repo of run.repos) {
|
|
29764
|
-
const worktree =
|
|
29765
|
-
const linked = leftoverLinks(
|
|
30043
|
+
const worktree = path38.join(dir, repo.path);
|
|
30044
|
+
const linked = leftoverLinks(path38.join(worktree, await this.prefixOf(repo)));
|
|
29766
30045
|
if (!await keep2(repo, { worktree, linked, branch: null, name: entry, title: entry }, worktree)) kept = false;
|
|
29767
30046
|
}
|
|
29768
30047
|
if (!kept) continue;
|
|
29769
|
-
for (const repo of run.repos) await this.removeWorktreeDir(run, repo,
|
|
30048
|
+
for (const repo of run.repos) await this.removeWorktreeDir(run, repo, path38.join(dir, repo.path), []);
|
|
29770
30049
|
this.removeTaskWorkspace(run, dir);
|
|
29771
30050
|
}
|
|
29772
30051
|
}
|
|
@@ -29784,13 +30063,13 @@ var GitWorktreeIsolation = class {
|
|
|
29784
30063
|
}
|
|
29785
30064
|
async removeWorktreeDir(run, repo, dir, linked) {
|
|
29786
30065
|
for (const name of /* @__PURE__ */ new Set([...linked, ...installDirs(dir)])) {
|
|
29787
|
-
const target =
|
|
29788
|
-
if (!resolvesAsNamed(this.runRoot(run),
|
|
30066
|
+
const target = path38.join(dir, name);
|
|
30067
|
+
if (!resolvesAsNamed(this.runRoot(run), path38.dirname(target))) continue;
|
|
29789
30068
|
this.unlinkIfLink(target);
|
|
29790
30069
|
this.unlinkLinksUnder(target);
|
|
29791
30070
|
}
|
|
29792
30071
|
for (const name of resolvesAsNamed(this.runRoot(run), dir) ? fs15.readdirSync(dir) : []) {
|
|
29793
|
-
if (LINKED_ARTIFACTS.has(name) || isEnvFile(name)) this.unlinkIfLink(
|
|
30072
|
+
if (LINKED_ARTIFACTS.has(name) || isEnvFile(name)) this.unlinkIfLink(path38.join(dir, name));
|
|
29794
30073
|
}
|
|
29795
30074
|
await this.tryGit(repo.root, ["worktree", "remove", "--force", dir]);
|
|
29796
30075
|
if (fs15.existsSync(dir) && isInside(this.runRoot(run), dir)) {
|
|
@@ -29798,7 +30077,7 @@ var GitWorktreeIsolation = class {
|
|
|
29798
30077
|
fs15.rmSync(dir, { recursive: true, force: true });
|
|
29799
30078
|
}
|
|
29800
30079
|
await this.tryGit(repo.root, ["worktree", "prune"]);
|
|
29801
|
-
for (let parent =
|
|
30080
|
+
for (let parent = path38.dirname(dir); isInside(this.runRoot(run), parent); parent = path38.dirname(parent)) this.removeIfEmpty(parent);
|
|
29802
30081
|
}
|
|
29803
30082
|
unlinkIfLink(target) {
|
|
29804
30083
|
try {
|
|
@@ -29813,14 +30092,14 @@ var GitWorktreeIsolation = class {
|
|
|
29813
30092
|
}
|
|
29814
30093
|
}
|
|
29815
30094
|
runRoot(run) {
|
|
29816
|
-
return
|
|
30095
|
+
return path38.join(run.workspaceRoot, STATE_DIR, "worktrees", run.id);
|
|
29817
30096
|
}
|
|
29818
30097
|
namesFor(run, task2) {
|
|
29819
30098
|
const slug = sanitizeSlug(task2.title).slice(0, 40).replace(/-+$/, "") || "task";
|
|
29820
30099
|
let name = `${task2.order}-${slug}`;
|
|
29821
30100
|
const clash = Object.values(run.tasks).some((r) => r.taskId !== task2.id && r.branch === `ordewell/${run.id}/${name}`);
|
|
29822
30101
|
if (clash) name = `${name}-${sanitizeSlug(task2.id) || "x"}`;
|
|
29823
|
-
return { branch: `ordewell/${run.id}/${name}`, dir:
|
|
30102
|
+
return { branch: `ordewell/${run.id}/${name}`, dir: path38.join(this.runRoot(run), name) };
|
|
29824
30103
|
}
|
|
29825
30104
|
async unmergedPaths(cwd) {
|
|
29826
30105
|
const listed3 = await this.tryGit(cwd, ["diff", "--name-only", "-z", "--diff-filter=U"]);
|
|
@@ -29834,7 +30113,7 @@ var GitWorktreeIsolation = class {
|
|
|
29834
30113
|
async rebaseInProgress(cwd) {
|
|
29835
30114
|
for (const dir of ["rebase-merge", "rebase-apply"]) {
|
|
29836
30115
|
const found = await this.tryGit(cwd, ["rev-parse", "--git-path", dir]);
|
|
29837
|
-
if (found.ok && fs15.existsSync(
|
|
30116
|
+
if (found.ok && fs15.existsSync(path38.resolve(cwd, found.stdout.trim()))) return true;
|
|
29838
30117
|
}
|
|
29839
30118
|
return false;
|
|
29840
30119
|
}
|
|
@@ -29908,6 +30187,29 @@ function describeIsolationPreview(preview) {
|
|
|
29908
30187
|
}
|
|
29909
30188
|
}
|
|
29910
30189
|
|
|
30190
|
+
// src/services/repositoryRunnerConfig.ts
|
|
30191
|
+
var RUNNER_CONFIG_ROOTS = [".mcp.json", ".claude", ".claude-plugin", "opencode.json", "opencode.jsonc", ".opencode", ".codex"];
|
|
30192
|
+
function repositoryRunnerConfigPaths(paths) {
|
|
30193
|
+
return [...new Set(paths.filter(
|
|
30194
|
+
(path50) => (
|
|
30195
|
+
// Project MCP servers start commands or send data to a repository-chosen URL.
|
|
30196
|
+
path50 === ".mcp.json" || // Settings can run hooks and helpers, load plugins, or redirect traffic.
|
|
30197
|
+
path50 === ".claude/settings.json" || // Local settings have the same execution capabilities as project settings.
|
|
30198
|
+
path50 === ".claude/settings.local.json" || // Hook files provide commands invoked by the runner.
|
|
30199
|
+
path50.startsWith(".claude/hooks/") || // Plugin files can provide code and hooks loaded by the runner.
|
|
30200
|
+
path50.startsWith(".claude/plugins/") || // Plugin metadata can load plugins and their executable components.
|
|
30201
|
+
path50.startsWith(".claude-plugin/") || // Plugin and marketplace manifests can select code for the runner to load.
|
|
30202
|
+
/^\.claude\/(?:[^/]+\/)*(?:plugin|marketplace)\.json$/.test(path50) || // OpenCode config declares MCP servers and plugins; its plugin and tool
|
|
30203
|
+
// directories are code it loads, and an agent definition sets the prompt,
|
|
30204
|
+
// model and permissions of the built-in agent its id names, which a task
|
|
30205
|
+
// launches with. The rest of `.opencode/` is dependencies OpenCode
|
|
30206
|
+
// installs itself, which a later launch must not be held for.
|
|
30207
|
+
/^(?:\.opencode\/)?opencode\.jsonc?$/.test(path50) || /^\.opencode\/(?:plugins?|tools?|agents?)\//.test(path50) || // Codex project config carries MCP servers, hooks and `notify` commands.
|
|
30208
|
+
path50.startsWith(".codex/")
|
|
30209
|
+
)
|
|
30210
|
+
))].sort();
|
|
30211
|
+
}
|
|
30212
|
+
|
|
29911
30213
|
// src/services/mergeWording.ts
|
|
29912
30214
|
function isRepoGroup(handoff) {
|
|
29913
30215
|
return handoff.repos.some((r) => r.path !== ".");
|
|
@@ -29942,7 +30244,7 @@ function handoffBaseLine(handoff, length) {
|
|
|
29942
30244
|
return handoff.repos.map((repo) => `${group ? `${repo.path}: ` : ""}${repoBaseLine(repo, length)}`).join(", ");
|
|
29943
30245
|
}
|
|
29944
30246
|
function repoResultLines(handoff) {
|
|
29945
|
-
return handoff.repos.map(({ path:
|
|
30247
|
+
return handoff.repos.map(({ path: path50, landed: landed2 }) => `${path50}: ${landed2.length === 0 ? "nothing to merge" : `${landed2.length} task${landed2.length === 1 ? "" : "s"} landed`}`);
|
|
29946
30248
|
}
|
|
29947
30249
|
|
|
29948
30250
|
// src/interfaces/ILogger.ts
|
|
@@ -33001,6 +33303,7 @@ var IsolationRunController = class {
|
|
|
33001
33303
|
/** Resolver task id → the conflicted task it resolves; see {@link linkResolver}. */
|
|
33002
33304
|
resolvers = {};
|
|
33003
33305
|
opening = null;
|
|
33306
|
+
opened = 0;
|
|
33004
33307
|
/** Bumped by an interrupt, so an activation in flight across it does not open the run it ended. */
|
|
33005
33308
|
activations = 0;
|
|
33006
33309
|
/** The start a dirty tree turned away, handed back once the user chooses how to go on. */
|
|
@@ -33032,6 +33335,10 @@ var IsolationRunController = class {
|
|
|
33032
33335
|
get isOpen() {
|
|
33033
33336
|
return this.mode !== null;
|
|
33034
33337
|
}
|
|
33338
|
+
/** How many runs have opened: what tells one run's launches from the next one's. */
|
|
33339
|
+
get epoch() {
|
|
33340
|
+
return this.opened;
|
|
33341
|
+
}
|
|
33035
33342
|
/** The open run has decided whether it isolates. */
|
|
33036
33343
|
get decided() {
|
|
33037
33344
|
return this.mode === "isolated" || this.mode === "shared";
|
|
@@ -33129,6 +33436,7 @@ var IsolationRunController = class {
|
|
|
33129
33436
|
open() {
|
|
33130
33437
|
if (this.mode) return;
|
|
33131
33438
|
this.mode = "undecided";
|
|
33439
|
+
this.opened += 1;
|
|
33132
33440
|
}
|
|
33133
33441
|
/**
|
|
33134
33442
|
* Decide once per run, as its first change task starts, whether it executes
|
|
@@ -33769,15 +34077,21 @@ var Landing = class {
|
|
|
33769
34077
|
* has landed. Its branch is on the integration branch by then, so it merges
|
|
33770
34078
|
* clean — and one the resolver did not really bring along conflicts again
|
|
33771
34079
|
* instead of being taken at its word. Null when there is nothing to land.
|
|
34080
|
+
* The task is named before its landing settles, so the caller can hold
|
|
34081
|
+
* its worktree for as long as the merge is in it; one the caller says is
|
|
34082
|
+
* `leaving` the plan is not landed at all.
|
|
33772
34083
|
*/
|
|
33773
|
-
|
|
34084
|
+
landResolved(resolverId, leaving = () => false) {
|
|
33774
34085
|
const conflictedId = this.runs.takeResolver(resolverId);
|
|
33775
|
-
if (!conflictedId) return null;
|
|
34086
|
+
if (!conflictedId || leaving(conflictedId)) return null;
|
|
33776
34087
|
const task2 = this.tasks.get(conflictedId);
|
|
33777
34088
|
if (!task2 || this.runs.current?.tasks[conflictedId]?.status !== "conflict") return null;
|
|
34089
|
+
return { task: task2, outcome: this.landConflicted(task2) };
|
|
34090
|
+
}
|
|
34091
|
+
async landConflicted(task2) {
|
|
33778
34092
|
const landing2 = await this.runs.integrate(task2);
|
|
33779
|
-
if (landing2 !== "merged") return
|
|
33780
|
-
return {
|
|
34093
|
+
if (landing2 !== "merged") return this.unmerged(task2, landing2);
|
|
34094
|
+
return { kind: "landed", messages: [{ level: "info", text: `Task "${task2.title}" landed through its conflict resolution.` }] };
|
|
33781
34095
|
}
|
|
33782
34096
|
/** The repair a conflicted task is owed next; null when repair is off, used up, or there is no isolated run to repair it in. */
|
|
33783
34097
|
nextRepair(taskId) {
|
|
@@ -34122,7 +34436,7 @@ function heldFrom(state) {
|
|
|
34122
34436
|
}
|
|
34123
34437
|
|
|
34124
34438
|
// src/services/Attempts.ts
|
|
34125
|
-
var
|
|
34439
|
+
var path42 = __toESM(require("path"));
|
|
34126
34440
|
|
|
34127
34441
|
// src/services/VerdictEngine.ts
|
|
34128
34442
|
var IDLE_TIMEOUT_MS = 10 * 6e4;
|
|
@@ -34230,6 +34544,18 @@ var VerdictEngine = class {
|
|
|
34230
34544
|
this.resumeIdle(taskId);
|
|
34231
34545
|
this.toolCheckpoints.get(taskId)?.({ kind: "rejected", reason });
|
|
34232
34546
|
}
|
|
34547
|
+
/**
|
|
34548
|
+
* An attempt's runner is being started, with no session to watch yet. Its
|
|
34549
|
+
* silence counts from here, so one whose start never resolves — stuck
|
|
34550
|
+
* launching, or waiting for its tools to attach — is flagged quiet as well.
|
|
34551
|
+
* {@link watch} starts the count again once the runner is up.
|
|
34552
|
+
*/
|
|
34553
|
+
starting(taskId) {
|
|
34554
|
+
const gen = this.bumpGeneration(taskId);
|
|
34555
|
+
this.idlePaused.delete(taskId);
|
|
34556
|
+
this.openApprovals.delete(taskId);
|
|
34557
|
+
this.touchIdle(taskId, gen);
|
|
34558
|
+
}
|
|
34233
34559
|
/**
|
|
34234
34560
|
* Attach to a spawned session. The runner's `task_complete` call on the
|
|
34235
34561
|
* attempt's token is the only completion evidence (ADR-0022, V2); a session
|
|
@@ -34240,6 +34566,9 @@ var VerdictEngine = class {
|
|
|
34240
34566
|
*/
|
|
34241
34567
|
watch(task2, session) {
|
|
34242
34568
|
const gen = this.bumpGeneration(task2.id);
|
|
34569
|
+
this.idlePaused.delete(task2.id);
|
|
34570
|
+
this.openApprovals.delete(task2.id);
|
|
34571
|
+
this.touchIdle(task2.id, gen);
|
|
34243
34572
|
session.onOutput(() => {
|
|
34244
34573
|
if (this.generations.get(task2.id) === gen) this.touchIdle(task2.id, gen);
|
|
34245
34574
|
});
|
|
@@ -34495,10 +34824,132 @@ function reportedVerdict(report) {
|
|
|
34495
34824
|
return { outcome: "fail", reason: `The runner reported the task ${report.status}: ${why}`, checks, decidedAt, reported };
|
|
34496
34825
|
}
|
|
34497
34826
|
|
|
34827
|
+
// src/services/taskStartConfig.ts
|
|
34828
|
+
var path39 = __toESM(require("path"));
|
|
34829
|
+
var import_crypto15 = require("crypto");
|
|
34830
|
+
var import_fs13 = require("fs");
|
|
34831
|
+
var MAX_FILES = 2e3;
|
|
34832
|
+
var MAX_DEPTH = 8;
|
|
34833
|
+
function liveConfig(dir) {
|
|
34834
|
+
const found = /* @__PURE__ */ new Map();
|
|
34835
|
+
const visit = (rel, depth) => {
|
|
34836
|
+
if (found.size >= MAX_FILES || depth > MAX_DEPTH) return;
|
|
34837
|
+
const full = path39.join(dir, rel);
|
|
34838
|
+
try {
|
|
34839
|
+
const info = (0, import_fs13.statSync)(full);
|
|
34840
|
+
if (info.isFile()) {
|
|
34841
|
+
found.set(rel, (0, import_crypto15.createHash)("sha1").update((0, import_fs13.readFileSync)(full)).digest("hex"));
|
|
34842
|
+
} else if (info.isDirectory() && path39.basename(rel) !== "node_modules") {
|
|
34843
|
+
for (const name of (0, import_fs13.readdirSync)(full)) visit(path39.posix.join(rel, name), depth + 1);
|
|
34844
|
+
}
|
|
34845
|
+
} catch {
|
|
34846
|
+
}
|
|
34847
|
+
};
|
|
34848
|
+
for (const root of RUNNER_CONFIG_ROOTS) visit(root, 0);
|
|
34849
|
+
const selected = new Set(repositoryRunnerConfigPaths([...found.keys()]));
|
|
34850
|
+
return new Map([...found].filter(([name]) => selected.has(name)));
|
|
34851
|
+
}
|
|
34852
|
+
function linkedRoots(dir) {
|
|
34853
|
+
const linked = /* @__PURE__ */ new Set();
|
|
34854
|
+
for (const root of RUNNER_CONFIG_ROOTS) {
|
|
34855
|
+
try {
|
|
34856
|
+
if ((0, import_fs13.lstatSync)(path39.join(dir, root)).isSymbolicLink()) linked.add(root);
|
|
34857
|
+
} catch {
|
|
34858
|
+
}
|
|
34859
|
+
}
|
|
34860
|
+
return linked;
|
|
34861
|
+
}
|
|
34862
|
+
function readDirs(root) {
|
|
34863
|
+
const dirs = [{ dir: root, prefix: "" }];
|
|
34864
|
+
for (let dir = root, prefix = ""; !(0, import_fs13.existsSync)(path39.join(dir, ".git")); ) {
|
|
34865
|
+
const parent = path39.dirname(dir);
|
|
34866
|
+
if (parent === dir) return [dirs[0]];
|
|
34867
|
+
dir = parent;
|
|
34868
|
+
prefix += "../";
|
|
34869
|
+
dirs.push({ dir, prefix });
|
|
34870
|
+
}
|
|
34871
|
+
return dirs;
|
|
34872
|
+
}
|
|
34873
|
+
var TaskStartConfig = class {
|
|
34874
|
+
live = /* @__PURE__ */ new Map();
|
|
34875
|
+
/**
|
|
34876
|
+
* `epoch` names the run the check belongs to; a new run takes a new
|
|
34877
|
+
* baseline. `root` is the workspace root, the checkout a worktree run lands
|
|
34878
|
+
* in; `cwd` is where the runner starts, and `worktree` whether that is a
|
|
34879
|
+
* worktree prepared for it.
|
|
34880
|
+
*/
|
|
34881
|
+
async check(run, taskId, where) {
|
|
34882
|
+
const found = where.worktree ? await this.gitDifferences(run, taskId) : [];
|
|
34883
|
+
const id2 = String(where.epoch);
|
|
34884
|
+
const checkout = readDirs(where.root).flatMap(({ dir, prefix }) => this.liveDifferences(`${id2}:checkout:${dir}`, dir).map((name) => `${prefix}${name}`));
|
|
34885
|
+
if (!where.worktree) found.push(...checkout);
|
|
34886
|
+
else {
|
|
34887
|
+
const cwd = where.cwd ?? run?.tasks[taskId]?.workspace;
|
|
34888
|
+
if (cwd) found.push(...this.worktreeDifferences(id2, cwd, where.root));
|
|
34889
|
+
}
|
|
34890
|
+
return [...new Set(found)].sort();
|
|
34891
|
+
}
|
|
34892
|
+
/** The baselines go with the plan they belong to. */
|
|
34893
|
+
clear() {
|
|
34894
|
+
this.live.clear();
|
|
34895
|
+
}
|
|
34896
|
+
/**
|
|
34897
|
+
* What a worktree's runner reads that differs from where the run started.
|
|
34898
|
+
* The `.claude` and `.opencode` directories it links from the checkout are
|
|
34899
|
+
* the checkout's own, so they are compared with the checkout's first state,
|
|
34900
|
+
* which an earlier task's write there does not move. Anything else is the
|
|
34901
|
+
* worktree's, and is compared with its own first state: a setup command's
|
|
34902
|
+
* files are trusted, and a task's later write is not.
|
|
34903
|
+
*/
|
|
34904
|
+
worktreeDifferences(id2, dir, root) {
|
|
34905
|
+
const now = liveConfig(dir);
|
|
34906
|
+
const linked = linkedRoots(dir);
|
|
34907
|
+
const fromCheckout = (name) => linked.has(name.split("/")[0]);
|
|
34908
|
+
const key = `${id2}:worktree:${dir}`;
|
|
34909
|
+
let local = this.live.get(key);
|
|
34910
|
+
if (!local) {
|
|
34911
|
+
local = new Map([...now].filter(([name]) => !fromCheckout(name)));
|
|
34912
|
+
this.live.set(key, local);
|
|
34913
|
+
}
|
|
34914
|
+
const checkout = this.live.get(`${id2}:checkout:${root}`);
|
|
34915
|
+
return [...now].filter(([name, hash]) => fromCheckout(name) ? checkout?.get(name) !== hash : local.get(name) !== hash).map(([name]) => name);
|
|
34916
|
+
}
|
|
34917
|
+
liveDifferences(key, dir) {
|
|
34918
|
+
const now = liveConfig(dir);
|
|
34919
|
+
const before = this.live.get(key);
|
|
34920
|
+
if (!before) {
|
|
34921
|
+
this.live.set(key, now);
|
|
34922
|
+
return [];
|
|
34923
|
+
}
|
|
34924
|
+
return [...now].filter(([name, hash]) => before.get(name) !== hash).map(([name]) => name);
|
|
34925
|
+
}
|
|
34926
|
+
async gitDifferences(run, taskId) {
|
|
34927
|
+
if (!run) return [];
|
|
34928
|
+
const invoker = { exec: defaultExecFile2, resolvePath: augmentedPath };
|
|
34929
|
+
const found = [];
|
|
34930
|
+
for (const repo of run.repos) {
|
|
34931
|
+
const cwd = run.tasks[taskId]?.repos[repo.path]?.worktree;
|
|
34932
|
+
if (!cwd || !repo.baseRef || !(0, import_fs13.existsSync)(cwd)) continue;
|
|
34933
|
+
const prefix = repo.path === "." ? (await tryGit(invoker, repo.root, ["rev-parse", "--show-prefix"])).stdout.trim() : "";
|
|
34934
|
+
const roots = [...RUNNER_CONFIG_ROOTS, ...prefix ? RUNNER_CONFIG_ROOTS.map((r) => `${prefix}${r}`) : []];
|
|
34935
|
+
const diff = await tryGit(invoker, cwd, ["diff", "--no-ext-diff", "--no-textconv", "--no-renames", "--name-only", "--diff-filter=ACMRTU", "-z", repo.baseRef, "--"]);
|
|
34936
|
+
const untracked = await tryGit(invoker, cwd, ["ls-files", "--others", "-z", "--", ...roots]);
|
|
34937
|
+
if (!diff.ok || !untracked.ok) continue;
|
|
34938
|
+
for (const name of [...diff.stdout.split("\0"), ...untracked.stdout.split("\0")]) {
|
|
34939
|
+
const here = prefix && name.startsWith(prefix) ? name.slice(prefix.length) : void 0;
|
|
34940
|
+
if (repositoryRunnerConfigPaths([name]).length || here !== void 0 && repositoryRunnerConfigPaths([here]).length) {
|
|
34941
|
+
found.push(repo.path === "." ? name : path39.posix.join(repo.path, name));
|
|
34942
|
+
}
|
|
34943
|
+
}
|
|
34944
|
+
}
|
|
34945
|
+
return found;
|
|
34946
|
+
}
|
|
34947
|
+
};
|
|
34948
|
+
|
|
34498
34949
|
// src/services/ScriptRunner.ts
|
|
34499
34950
|
var import_child_process11 = require("child_process");
|
|
34500
34951
|
var fs16 = __toESM(require("fs"));
|
|
34501
|
-
var
|
|
34952
|
+
var path40 = __toESM(require("path"));
|
|
34502
34953
|
var import_events2 = require("events");
|
|
34503
34954
|
var import_string_decoder2 = require("string_decoder");
|
|
34504
34955
|
|
|
@@ -34639,7 +35090,7 @@ var ScriptSession = class extends AbstractRunnerSession {
|
|
|
34639
35090
|
async launchOf(step) {
|
|
34640
35091
|
if ("argv" in step) {
|
|
34641
35092
|
const [named3, ...args] = step.argv;
|
|
34642
|
-
const program = this.setup.platform === "win32" &&
|
|
35093
|
+
const program = this.setup.platform === "win32" && path40.win32.basename(named3) !== named3 && !path40.win32.isAbsolute(named3) ? path40.win32.resolve(this.setup.cwd, named3) : named3;
|
|
34643
35094
|
const plan = await planDirectLaunch(program, args, this.setup.launch);
|
|
34644
35095
|
return { file: plan.file, args: plan.args, shell: false, ...plan.verbatim ? { verbatim: true } : {} };
|
|
34645
35096
|
}
|
|
@@ -34872,10 +35323,10 @@ function passed(status) {
|
|
|
34872
35323
|
}
|
|
34873
35324
|
|
|
34874
35325
|
// src/services/PatchCapture.ts
|
|
34875
|
-
var
|
|
35326
|
+
var import_crypto16 = require("crypto");
|
|
34876
35327
|
var fs17 = __toESM(require("fs"));
|
|
34877
|
-
var
|
|
34878
|
-
var
|
|
35328
|
+
var os10 = __toESM(require("os"));
|
|
35329
|
+
var path41 = __toESM(require("path"));
|
|
34879
35330
|
|
|
34880
35331
|
// src/services/readTask.ts
|
|
34881
35332
|
function readTask2(args, readerId, plan, log) {
|
|
@@ -35142,7 +35593,7 @@ function linkInPatch(patch) {
|
|
|
35142
35593
|
function readOwn(scratch, name, max) {
|
|
35143
35594
|
let fd;
|
|
35144
35595
|
try {
|
|
35145
|
-
fd = fs17.openSync(
|
|
35596
|
+
fd = fs17.openSync(path41.join(scratch, name), fs17.constants.O_RDONLY | (fs17.constants.O_NOFOLLOW ?? 0));
|
|
35146
35597
|
} catch (err) {
|
|
35147
35598
|
throw new Refusal(`the capture left no ${name.split(".").pop()} Ordewell could read (${err.code ?? String(err)})`);
|
|
35148
35599
|
}
|
|
@@ -35179,8 +35630,8 @@ async function captureHeld(runs, opts, signal) {
|
|
|
35179
35630
|
if (!runs.touched) return { kind: "empty", reason: "no command of this task has run sealed, so its worktree holds only what Ordewell checked out.", flags: [] };
|
|
35180
35631
|
const [worktree] = runs.worktrees;
|
|
35181
35632
|
const scratch = runs.scratch;
|
|
35182
|
-
const tag = `ordewell-patch-${(0,
|
|
35183
|
-
const stem =
|
|
35633
|
+
const tag = `ordewell-patch-${(0, import_crypto16.randomBytes)(8).toString("hex")}`;
|
|
35634
|
+
const stem = path41.join(scratch, tag);
|
|
35184
35635
|
const own = (suffix, max = LISTING_BYTES) => readOwn(scratch, `${tag}.${suffix}`, max);
|
|
35185
35636
|
try {
|
|
35186
35637
|
const stage = await runs.run(stageScript(stem, opts.linked ?? []), worktree);
|
|
@@ -35218,7 +35669,7 @@ async function captureHeld(runs, opts, signal) {
|
|
|
35218
35669
|
return {
|
|
35219
35670
|
kind: "saved",
|
|
35220
35671
|
patch,
|
|
35221
|
-
stats: { base, ...countedIn(patch), bytes: patch.length, sha256: (0,
|
|
35672
|
+
stats: { base, ...countedIn(patch), bytes: patch.length, sha256: (0, import_crypto16.createHash)("sha256").update(patch).digest("hex"), flags: [...flags, ...flag("normalized", normalized2)] }
|
|
35222
35673
|
};
|
|
35223
35674
|
} catch (err) {
|
|
35224
35675
|
if (err instanceof Refusal) return { kind: "refused", reason: `${err.message}.` };
|
|
@@ -35272,8 +35723,8 @@ async function indexEntries(invoker, worktree) {
|
|
|
35272
35723
|
return entries;
|
|
35273
35724
|
}
|
|
35274
35725
|
async function trialApply(invoker, worktree, patchFile, scratch) {
|
|
35275
|
-
const index =
|
|
35276
|
-
const copy =
|
|
35726
|
+
const index = path41.resolve(worktree, (await git(invoker, worktree, [...APPLY_GIT, "rev-parse", "--git-path", "index"])).trim());
|
|
35727
|
+
const copy = path41.join(scratch, "index");
|
|
35277
35728
|
fs17.copyFileSync(index, copy);
|
|
35278
35729
|
const trial = { ...invoker, exec: (file, args, opts) => invoker.exec(file, args, { ...opts, env: { ...opts.env, GIT_INDEX_FILE: copy } }) };
|
|
35279
35730
|
const tried = await tryGit(trial, worktree, [...APPLY_GIT, "apply", "--cached", "--3way", "--whitespace=nowarn", patchFile]);
|
|
@@ -35293,7 +35744,7 @@ function gitRefused(lead, stderr) {
|
|
|
35293
35744
|
function onDisk3(worktree, names) {
|
|
35294
35745
|
return names.map((name) => {
|
|
35295
35746
|
try {
|
|
35296
|
-
const stat = fs17.lstatSync(
|
|
35747
|
+
const stat = fs17.lstatSync(path41.join(worktree, name), { bigint: true, throwIfNoEntry: false });
|
|
35297
35748
|
return stat ? `${stat.mode} ${stat.ino} ${stat.size} ${stat.mtimeNs} ${stat.ctimeNs}` : "none";
|
|
35298
35749
|
} catch (err) {
|
|
35299
35750
|
return err.code ?? "unreadable";
|
|
@@ -35333,7 +35784,7 @@ function leavesWorktree(worktree, names, linked, held2, left) {
|
|
|
35333
35784
|
if (i < parts.length && held2.get(at)?.some((entry) => entry.mode === SUBMODULE)) {
|
|
35334
35785
|
return `the patch writes ${name}, under ${at}, a submodule, whose files are another repository's.`;
|
|
35335
35786
|
}
|
|
35336
|
-
const full =
|
|
35787
|
+
const full = path41.join(worktree, ...parts.slice(0, i));
|
|
35337
35788
|
const stat = fs17.lstatSync(full, { throwIfNoEntry: false });
|
|
35338
35789
|
if (!stat) break;
|
|
35339
35790
|
const last = i === parts.length;
|
|
@@ -35354,14 +35805,14 @@ async function applyPatch(invoker, worktree, patch, saved, opts = {}) {
|
|
|
35354
35805
|
if (patch.length !== saved.bytes) {
|
|
35355
35806
|
return refused4(`the patch is ${byteSize(patch.length)} where its artifact says ${byteSize(saved.bytes)}, so it was cut short or changed after it was saved.`);
|
|
35356
35807
|
}
|
|
35357
|
-
if ((0,
|
|
35808
|
+
if ((0, import_crypto16.createHash)("sha256").update(patch).digest("hex") !== saved.sha256) {
|
|
35358
35809
|
return refused4("the patch is not the file that was saved: its hash is not the one its artifact records, so it was changed after it was saved.");
|
|
35359
35810
|
}
|
|
35360
35811
|
const link = linkInPatch(patch);
|
|
35361
35812
|
if (link !== void 0) {
|
|
35362
35813
|
return refused4(`the patch holds a link or a submodule (${JSON.stringify(link.slice(0, 80))}), which a saved patch never does, so it is not one that was saved.`);
|
|
35363
35814
|
}
|
|
35364
|
-
const dir = fs17.mkdtempSync(
|
|
35815
|
+
const dir = fs17.mkdtempSync(path41.join(os10.tmpdir(), "ordewell-apply-"));
|
|
35365
35816
|
try {
|
|
35366
35817
|
const toplevel = (await git(invoker, worktree, [...APPLY_GIT, "rev-parse", "--show-toplevel"])).trim();
|
|
35367
35818
|
if (fs17.realpathSync(toplevel) !== fs17.realpathSync(worktree)) return refused4(`${worktree} is not the root of a worktree of its own.`);
|
|
@@ -35371,7 +35822,7 @@ async function applyPatch(invoker, worktree, patch, saved, opts = {}) {
|
|
|
35371
35822
|
}
|
|
35372
35823
|
const before = await unmergedPaths(invoker, worktree);
|
|
35373
35824
|
if (before.length > 0) return refused4(`your worktree still has unresolved conflicts (${listed(before.slice(0, 10))}${before.length > 10 ? `, +${before.length - 10} more` : ""}). Resolve them and git add them, or undo them, then apply the patch.`);
|
|
35374
|
-
const file =
|
|
35825
|
+
const file = path41.join(dir, "saved.patch");
|
|
35375
35826
|
fs17.writeFileSync(file, patch, { flag: "wx", mode: 384 });
|
|
35376
35827
|
const held2 = await indexEntries(invoker, worktree);
|
|
35377
35828
|
const named3 = await pathsNamed(invoker, worktree, file);
|
|
@@ -35521,6 +35972,11 @@ var TaskWebReader = class {
|
|
|
35521
35972
|
};
|
|
35522
35973
|
|
|
35523
35974
|
// src/services/Attempts.ts
|
|
35975
|
+
var TaskStartConfigError = class extends Error {
|
|
35976
|
+
constructor(paths) {
|
|
35977
|
+
super(`repository configuration ${paths.map((p) => JSON.stringify(p)).join(", ")} differs from where this run started; remove it to continue, or start a new run once your branch holds it`);
|
|
35978
|
+
}
|
|
35979
|
+
};
|
|
35524
35980
|
var DEFAULT_GIT = { exec: defaultExecFile2, resolvePath: () => augmentedPath() };
|
|
35525
35981
|
var Attempts = class {
|
|
35526
35982
|
constructor(deps) {
|
|
@@ -35552,6 +36008,7 @@ var Attempts = class {
|
|
|
35552
36008
|
live = /* @__PURE__ */ new Map();
|
|
35553
36009
|
/** Bumped by every plan load: an ended attempt's wind-down belongs to the scope before it. */
|
|
35554
36010
|
planScope = 0;
|
|
36011
|
+
startConfig = new TaskStartConfig();
|
|
35555
36012
|
/** Each task's runners still winding down once stopped: a continue waits for them (ADR-0018, L1). */
|
|
35556
36013
|
windDowns = /* @__PURE__ */ new Map();
|
|
35557
36014
|
/**
|
|
@@ -35607,6 +36064,10 @@ var Attempts = class {
|
|
|
35607
36064
|
receipt(taskId) {
|
|
35608
36065
|
return this.live.get(taskId)?.receipt ?? null;
|
|
35609
36066
|
}
|
|
36067
|
+
/** Which spawn of the live attempt's runner waits for Ordewell's tools to connect; null when none does. */
|
|
36068
|
+
attaching(taskId) {
|
|
36069
|
+
return this.live.get(taskId)?.attaching ?? null;
|
|
36070
|
+
}
|
|
35610
36071
|
/** The kind a task's next attempt starts as, short of a repair or a continue: what is weighed before one exists. */
|
|
35611
36072
|
nextKind(taskId) {
|
|
35612
36073
|
const task2 = this.deps.store.get(taskId);
|
|
@@ -35647,7 +36108,9 @@ var Attempts = class {
|
|
|
35647
36108
|
shell: null,
|
|
35648
36109
|
web: null,
|
|
35649
36110
|
patch: null,
|
|
35650
|
-
receipt: null
|
|
36111
|
+
receipt: null,
|
|
36112
|
+
attaching: null,
|
|
36113
|
+
spawning: new AbortController()
|
|
35651
36114
|
};
|
|
35652
36115
|
this.spawnCounts.set(task2.id, attempt.attempt);
|
|
35653
36116
|
this.live.set(task2.id, attempt);
|
|
@@ -35662,7 +36125,8 @@ var Attempts = class {
|
|
|
35662
36125
|
/**
|
|
35663
36126
|
* The one way an attempt ends. Dropping the record is itself what
|
|
35664
36127
|
* invalidates a spawn still in flight ({@link run} kills the session it
|
|
35665
|
-
* receives for an attempt that is no longer current)
|
|
36128
|
+
* receives for an attempt that is no longer current), and its cancel takes
|
|
36129
|
+
* back a runner still starting meanwhile.
|
|
35666
36130
|
*
|
|
35667
36131
|
* A user interruption also bumps the verifier's generation *before* stopping
|
|
35668
36132
|
* the runner: a runner may fire onExit synchronously from
|
|
@@ -35678,6 +36142,7 @@ var Attempts = class {
|
|
|
35678
36142
|
const session = attempt?.session ?? null;
|
|
35679
36143
|
const nativeSessionId = session?.nativeSessionId() ?? null;
|
|
35680
36144
|
if (attempt) {
|
|
36145
|
+
attempt.spawning.abort();
|
|
35681
36146
|
store.endAttemptUsage(taskId, attempt.attempt, (/* @__PURE__ */ new Date()).toISOString(), session?.sessionCost?.() ?? void 0, nativeSessionId ?? void 0);
|
|
35682
36147
|
this.deps.output.detach(taskId);
|
|
35683
36148
|
attempt.shell?.end();
|
|
@@ -35713,6 +36178,7 @@ var Attempts = class {
|
|
|
35713
36178
|
const ended = this.endAll("load");
|
|
35714
36179
|
this.planScope += 1;
|
|
35715
36180
|
this.spawnCounts.clear();
|
|
36181
|
+
this.startConfig.clear();
|
|
35716
36182
|
return ended;
|
|
35717
36183
|
}
|
|
35718
36184
|
/** A task leaving the plan: no later attempt of it is numbered from its runs. */
|
|
@@ -35845,21 +36311,31 @@ var Attempts = class {
|
|
|
35845
36311
|
if (unavailable2) throw new Error(unavailable2);
|
|
35846
36312
|
}
|
|
35847
36313
|
if (kind.kind === "continuation") await this.windDowns.get(task2.id);
|
|
36314
|
+
if (!attempt.script && kind.kind === "repair") {
|
|
36315
|
+
const paths = await this.startConfig.check(runs.current, task2.id, { epoch: runs.epoch, root: this.deps.workspaceRoot(), cwd: null, worktree: true });
|
|
36316
|
+
if (paths.length) throw new TaskStartConfigError(paths);
|
|
36317
|
+
}
|
|
35848
36318
|
const { cwd, worktree, previousWork, workspace } = await attemptCwd(kind, task2, runs);
|
|
35849
36319
|
attempt.cwd = cwd;
|
|
35850
36320
|
attempt.worktree = worktree;
|
|
36321
|
+
if (!attempt.script && kind.kind !== "repair" && !landsNothing(kind)) {
|
|
36322
|
+
const paths = await this.startConfig.check(runs.current, task2.id, { epoch: runs.epoch, root: this.deps.workspaceRoot(), cwd, worktree });
|
|
36323
|
+
if (paths.length) throw new TaskStartConfigError(paths);
|
|
36324
|
+
}
|
|
35851
36325
|
if (checksTree(kind)) attempt.snapshot = await runs.snapshotWorkspace();
|
|
35852
36326
|
if (this.live.get(task2.id) !== attempt) {
|
|
35853
36327
|
if (attempt.worktree && !this.live.has(task2.id) && !this.host.completing(task2.id)) await runs.release(task2.id, { keep: kind.kind === "repair" });
|
|
35854
36328
|
this.abandonSpawn(task2, attempt);
|
|
35855
36329
|
return false;
|
|
35856
36330
|
}
|
|
36331
|
+
this.verifier.starting(task2.id);
|
|
35857
36332
|
const session = attempt.script ? await this.spawnScript(task2, attempt, cwd) : await this.spawnRunner(task2, attempt, cwd, worktree ? workspace : void 0, previousWork);
|
|
35858
36333
|
if (this.live.get(task2.id) !== attempt) {
|
|
35859
36334
|
this.abandonSpawn(task2, attempt, session);
|
|
35860
36335
|
return false;
|
|
35861
36336
|
}
|
|
35862
36337
|
attempt.phase = "running";
|
|
36338
|
+
attempt.attaching = null;
|
|
35863
36339
|
attempt.session = session;
|
|
35864
36340
|
store.setTaskRunnerSessionId(task2.id, void 0);
|
|
35865
36341
|
store.clearCarriedFrom(task2.id);
|
|
@@ -35928,6 +36404,9 @@ var Attempts = class {
|
|
|
35928
36404
|
return { kind: "usage-limited", taskId: task2.id, attempt, said: { message }, output: "" };
|
|
35929
36405
|
}
|
|
35930
36406
|
const failed = (failure2) => ({ kind: "spawn-failed", task: task2, attempt, failure: failure2 });
|
|
36407
|
+
if (err instanceof TaskStartConfigError) {
|
|
36408
|
+
return failed({ leaves: "held", hold: err.message, preserveWorktree: true, settles: false, notice: { level: "warn", text: `Task "${task2.title}" is on hold: ${err.message}` } });
|
|
36409
|
+
}
|
|
35931
36410
|
if (attempt.kind.kind === "continuation" && !attachTimeout) {
|
|
35932
36411
|
const refused4 = err instanceof RunnerStartupExit;
|
|
35933
36412
|
const reason = unresumedMessage(task2, refused4 ? `${attempt.runner} could not find its saved session` : `could not start: ${message}`);
|
|
@@ -36005,7 +36484,13 @@ var Attempts = class {
|
|
|
36005
36484
|
...web ? { webReads: web } : {},
|
|
36006
36485
|
applyPatch: (args) => this.applyPatch(task2, attempt, args),
|
|
36007
36486
|
workspaceRoot: this.deps.workspaceRoot(),
|
|
36008
|
-
onNotice: (message) => this.host.tell("warn", `Task "${task2.title}": ${message}`)
|
|
36487
|
+
onNotice: (message) => this.host.tell("warn", `Task "${task2.title}": ${message}`),
|
|
36488
|
+
cancelled: attempt.spawning.signal,
|
|
36489
|
+
onAttaching: (spawn) => {
|
|
36490
|
+
if (this.live.get(task2.id) !== attempt) return;
|
|
36491
|
+
attempt.attaching = spawn;
|
|
36492
|
+
this.host.emit("onTaskChanged");
|
|
36493
|
+
}
|
|
36009
36494
|
});
|
|
36010
36495
|
}
|
|
36011
36496
|
/**
|
|
@@ -36058,9 +36543,9 @@ var Attempts = class {
|
|
|
36058
36543
|
const repos = Object.values(this.deps.runs.openRecord(taskId)?.repos ?? {});
|
|
36059
36544
|
if (repos.length !== 1 || cwd === null) return [];
|
|
36060
36545
|
const [{ worktree, linked }] = repos;
|
|
36061
|
-
const prefix =
|
|
36062
|
-
if (prefix.startsWith("..") ||
|
|
36063
|
-
return linked.map((name) => (prefix ? `${prefix}/${name}` : name).split(
|
|
36546
|
+
const prefix = path42.relative(worktree, cwd).split(path42.sep).join("/");
|
|
36547
|
+
if (prefix.startsWith("..") || path42.isAbsolute(prefix)) return [];
|
|
36548
|
+
return linked.map((name) => (prefix ? `${prefix}/${name}` : name).split(path42.sep).join("/"));
|
|
36064
36549
|
}
|
|
36065
36550
|
/**
|
|
36066
36551
|
* A script task's start (ADR-0035): its body as the plan holds it now, run
|
|
@@ -36098,7 +36583,7 @@ var Attempts = class {
|
|
|
36098
36583
|
this.deps.notifications.warn(message);
|
|
36099
36584
|
};
|
|
36100
36585
|
if (resolved.blockedEnvrc) {
|
|
36101
|
-
warn(`blocked:${resolved.blockedEnvrc}`, `direnv has blocked ${resolved.blockedEnvrc}, so tasks start without its variables. Run \`direnv allow\` in ${
|
|
36586
|
+
warn(`blocked:${resolved.blockedEnvrc}`, `direnv has blocked ${resolved.blockedEnvrc}, so tasks start without its variables. Run \`direnv allow\` in ${path42.dirname(resolved.blockedEnvrc)} to use them.`);
|
|
36102
36587
|
}
|
|
36103
36588
|
if (resolved.trackedEnvFile) {
|
|
36104
36589
|
warn(`tracked:${resolved.trackedEnvFile}`, `Ignored ${resolved.trackedEnvFile}: git tracks it, and a committed file must not choose the environment agents run in. Untrack it to use it.`);
|
|
@@ -36124,7 +36609,7 @@ var Attempts = class {
|
|
|
36124
36609
|
uncommittedSkillFolder(name) {
|
|
36125
36610
|
const root = this.deps.workspaceRoot();
|
|
36126
36611
|
const skill = this.deps.skillsAt(this.skillRoots(root)).findSkill(name);
|
|
36127
|
-
return skill?.source === "workspace" ?
|
|
36612
|
+
return skill?.source === "workspace" ? path42.relative(root, path42.dirname(skill.path)).split(path42.sep).join("/") : void 0;
|
|
36128
36613
|
}
|
|
36129
36614
|
/**
|
|
36130
36615
|
* The roots whose `.ordewell/skills/` an attempt at `cwd` reads: in a repo
|
|
@@ -36554,29 +37039,29 @@ function ancestors(task2, store) {
|
|
|
36554
37039
|
|
|
36555
37040
|
// src/services/sessionLease.ts
|
|
36556
37041
|
var fs19 = __toESM(require("fs"));
|
|
36557
|
-
var
|
|
36558
|
-
var
|
|
37042
|
+
var path44 = __toESM(require("path"));
|
|
37043
|
+
var import_crypto18 = require("crypto");
|
|
36559
37044
|
|
|
36560
37045
|
// src/utils/sessionStore.ts
|
|
36561
37046
|
var fs18 = __toESM(require("fs"));
|
|
36562
|
-
var
|
|
37047
|
+
var path43 = __toESM(require("path"));
|
|
36563
37048
|
|
|
36564
37049
|
// src/utils/sessionId.ts
|
|
36565
|
-
var
|
|
37050
|
+
var import_crypto17 = require("crypto");
|
|
36566
37051
|
function mintSessionId() {
|
|
36567
|
-
return `session-${(0,
|
|
37052
|
+
return `session-${(0, import_crypto17.randomBytes)(16).toString("hex")}`;
|
|
36568
37053
|
}
|
|
36569
37054
|
|
|
36570
37055
|
// src/utils/sessionStore.ts
|
|
36571
37056
|
var SESSIONS_SUBDIR = "sessions";
|
|
36572
37057
|
function getSessionsDir(baseDir) {
|
|
36573
|
-
return
|
|
37058
|
+
return path43.join(getStateDir(baseDir), SESSIONS_SUBDIR);
|
|
36574
37059
|
}
|
|
36575
37060
|
function idSegment(id2) {
|
|
36576
37061
|
return /^[A-Za-z0-9_-][A-Za-z0-9._-]{0,127}$/.test(id2) ? id2 : `~${Buffer.from(id2, "utf-8").toString("hex")}`;
|
|
36577
37062
|
}
|
|
36578
37063
|
function sessionDataDir(sessionId, baseDir) {
|
|
36579
|
-
return
|
|
37064
|
+
return path43.join(getSessionsDir(baseDir), idSegment(sessionId));
|
|
36580
37065
|
}
|
|
36581
37066
|
var REJECTION_MESSAGE = "This session was created with an older version of Ordewell. Please re-plan.";
|
|
36582
37067
|
function isSessionMeta(value) {
|
|
@@ -36603,8 +37088,8 @@ function legacySessionFilename(session) {
|
|
|
36603
37088
|
return `${filenameStem(session)}_${session.id.slice(0, 8)}.json`;
|
|
36604
37089
|
}
|
|
36605
37090
|
function dropLegacyFile(sessionsDir, meta) {
|
|
36606
|
-
const legacy =
|
|
36607
|
-
if (legacy ===
|
|
37091
|
+
const legacy = path43.join(sessionsDir, legacySessionFilename(meta));
|
|
37092
|
+
if (legacy === path43.join(sessionsDir, sessionFilename(meta)) || !fs18.existsSync(legacy)) return;
|
|
36608
37093
|
try {
|
|
36609
37094
|
const held2 = JSON.parse(fs18.readFileSync(legacy, "utf-8")).meta?.id;
|
|
36610
37095
|
if (held2 === meta.id) fs18.unlinkSync(legacy);
|
|
@@ -36631,7 +37116,7 @@ function saveSession(plan, goal, baseDir, id2) {
|
|
|
36631
37116
|
};
|
|
36632
37117
|
dropRetiredPlanFields(session.plan);
|
|
36633
37118
|
const filename = sessionFilename(meta);
|
|
36634
|
-
const filePath =
|
|
37119
|
+
const filePath = path43.join(sessionsDir, filename);
|
|
36635
37120
|
writeFileAtomic(filePath, JSON.stringify(session, null, 2));
|
|
36636
37121
|
dropLegacyFile(sessionsDir, meta);
|
|
36637
37122
|
return meta;
|
|
@@ -36643,7 +37128,7 @@ function listSessions(baseDir, logger = defaultLogger) {
|
|
|
36643
37128
|
return files.map((f) => {
|
|
36644
37129
|
let parsed;
|
|
36645
37130
|
try {
|
|
36646
|
-
parsed = JSON.parse(fs18.readFileSync(
|
|
37131
|
+
parsed = JSON.parse(fs18.readFileSync(path43.join(sessionsDir, f), "utf-8"));
|
|
36647
37132
|
} catch (err) {
|
|
36648
37133
|
if (err instanceof SyntaxError) return moveAside(sessionsDir, f, "is not valid JSON", logger);
|
|
36649
37134
|
logger.warn("sessionStore", `failed to read session file ${f}; skipping`, err);
|
|
@@ -36664,7 +37149,7 @@ function listSessions(baseDir, logger = defaultLogger) {
|
|
|
36664
37149
|
function moveAside(sessionsDir, file, reason, logger) {
|
|
36665
37150
|
const aside = `${file}.${Date.now()}.corrupt`;
|
|
36666
37151
|
try {
|
|
36667
|
-
fs18.renameSync(
|
|
37152
|
+
fs18.renameSync(path43.join(sessionsDir, file), path43.join(sessionsDir, aside));
|
|
36668
37153
|
logger.warn("sessionStore", `session file ${file} ${reason}; moved aside to ${aside} rather than overwritten`);
|
|
36669
37154
|
} catch {
|
|
36670
37155
|
logger.warn("sessionStore", `failed to read session file ${file}; skipping`);
|
|
@@ -36675,7 +37160,7 @@ function findSessionFile(sessionsDir, sessionId, logger) {
|
|
|
36675
37160
|
const files = fs18.readdirSync(sessionsDir).filter((f) => f.endsWith(".json"));
|
|
36676
37161
|
for (const f of files) {
|
|
36677
37162
|
try {
|
|
36678
|
-
const raw = fs18.readFileSync(
|
|
37163
|
+
const raw = fs18.readFileSync(path43.join(sessionsDir, f), "utf-8");
|
|
36679
37164
|
const session = JSON.parse(raw);
|
|
36680
37165
|
if (session.meta.id === sessionId) return f;
|
|
36681
37166
|
} catch (err) {
|
|
@@ -36690,7 +37175,7 @@ function loadSession(sessionId, baseDir, logger = defaultLogger) {
|
|
|
36690
37175
|
try {
|
|
36691
37176
|
const file = findSessionFile(sessionsDir, sessionId, logger);
|
|
36692
37177
|
if (file) {
|
|
36693
|
-
const raw = fs18.readFileSync(
|
|
37178
|
+
const raw = fs18.readFileSync(path43.join(sessionsDir, file), "utf-8");
|
|
36694
37179
|
const parsed = JSON.parse(raw);
|
|
36695
37180
|
if (hasScalarRunner(parsed)) {
|
|
36696
37181
|
logger.warn("sessionStore", `session file ${file}: ${REJECTION_MESSAGE}`);
|
|
@@ -36732,7 +37217,7 @@ function deleteSession(sessionId, baseDir, logger = defaultLogger) {
|
|
|
36732
37217
|
try {
|
|
36733
37218
|
const file = findSessionFile(sessionsDir, sessionId, logger);
|
|
36734
37219
|
if (file) {
|
|
36735
|
-
fs18.unlinkSync(
|
|
37220
|
+
fs18.unlinkSync(path43.join(sessionsDir, file));
|
|
36736
37221
|
return true;
|
|
36737
37222
|
}
|
|
36738
37223
|
} catch (err) {
|
|
@@ -36785,7 +37270,7 @@ var UNLEASED = {
|
|
|
36785
37270
|
};
|
|
36786
37271
|
var hostsHere = /* @__PURE__ */ new Set();
|
|
36787
37272
|
function leaseFile(sessionId, workspace) {
|
|
36788
|
-
return
|
|
37273
|
+
return path44.join(sessionDataDir(sessionId, workspace), LEASE_FILE);
|
|
36789
37274
|
}
|
|
36790
37275
|
function isRecord9(value) {
|
|
36791
37276
|
if (typeof value !== "object" || value === null) return false;
|
|
@@ -36821,7 +37306,7 @@ var LeaseHost = class {
|
|
|
36821
37306
|
held = /* @__PURE__ */ new Map();
|
|
36822
37307
|
heartbeat = null;
|
|
36823
37308
|
constructor(options = {}) {
|
|
36824
|
-
this.id = options.id ?? (0,
|
|
37309
|
+
this.id = options.id ?? (0, import_crypto18.randomUUID)();
|
|
36825
37310
|
this.pid = options.pid ?? process.pid;
|
|
36826
37311
|
this.kindOf = options.kind ?? (() => void 0);
|
|
36827
37312
|
this.alive = options.alive ?? processAlive;
|
|
@@ -36874,7 +37359,7 @@ var LeaseHost = class {
|
|
|
36874
37359
|
*/
|
|
36875
37360
|
acquire(file, workspace, holder) {
|
|
36876
37361
|
ensureStateDirIgnored(workspace);
|
|
36877
|
-
ensurePrivateDir(
|
|
37362
|
+
ensurePrivateDir(path44.dirname(file));
|
|
36878
37363
|
const outcome = withFileLock(file, () => {
|
|
36879
37364
|
const current = readRecord(file);
|
|
36880
37365
|
const taken = current !== null && current.hostId !== this.id ? current : null;
|
|
@@ -36929,7 +37414,7 @@ var LeaseHost = class {
|
|
|
36929
37414
|
withFileLock(file, () => {
|
|
36930
37415
|
if (readRecord(file)?.hostId === this.id) fs19.rmSync(file, { force: true });
|
|
36931
37416
|
}, "release a session lease");
|
|
36932
|
-
fs19.rmdirSync(
|
|
37417
|
+
fs19.rmdirSync(path44.dirname(file));
|
|
36933
37418
|
} catch {
|
|
36934
37419
|
}
|
|
36935
37420
|
}
|
|
@@ -37122,10 +37607,14 @@ var TaskOrchestrator = class _TaskOrchestrator {
|
|
|
37122
37607
|
/** Conflict repairs owed with every slot taken and no scheduler to claim them: a freed slot starts them. */
|
|
37123
37608
|
owedRepairs = /* @__PURE__ */ new Set();
|
|
37124
37609
|
/**
|
|
37125
|
-
* Tasks Mark complete is landing after it ended their attempt
|
|
37126
|
-
*
|
|
37610
|
+
* Tasks Mark complete is landing after it ended their attempt, or that
|
|
37611
|
+
* their conflict resolver is landing, and that landing: in progress with
|
|
37612
|
+
* no attempt for as long as it takes, and not orphans for it. Both can be
|
|
37613
|
+
* landing one task at once, so each holds it for itself (see {@link holdWhileLanding}).
|
|
37127
37614
|
*/
|
|
37128
|
-
completing = /* @__PURE__ */ new
|
|
37615
|
+
completing = /* @__PURE__ */ new Map();
|
|
37616
|
+
/** Tasks {@link releaseTask} is tearing down once their landings have settled: none may begin another. */
|
|
37617
|
+
releasing = /* @__PURE__ */ new Set();
|
|
37129
37618
|
/** Tasks paused on a usage limit, the run's hold on them, and each runner's wait for its reset (ADR-0018, U1). */
|
|
37130
37619
|
limitPauses;
|
|
37131
37620
|
/**
|
|
@@ -37261,6 +37750,10 @@ var TaskOrchestrator = class _TaskOrchestrator {
|
|
|
37261
37750
|
getCompletionReceipt(taskId) {
|
|
37262
37751
|
return this.attempts.receipt(taskId);
|
|
37263
37752
|
}
|
|
37753
|
+
/** Which spawn of a live attempt's runner waits for Ordewell's tools to connect (ADR-0025); null when none does. */
|
|
37754
|
+
getToolsAttaching(taskId) {
|
|
37755
|
+
return this.attempts.attaching(taskId);
|
|
37756
|
+
}
|
|
37264
37757
|
/** Recent clean output of a task's latest attempt, running or ended; null if it never ran. */
|
|
37265
37758
|
getLiveOutput(taskId, opts) {
|
|
37266
37759
|
return this.attempts.liveTail(taskId, opts);
|
|
@@ -37425,12 +37918,13 @@ var TaskOrchestrator = class _TaskOrchestrator {
|
|
|
37425
37918
|
/**
|
|
37426
37919
|
* Something can still start that nobody has to ask for again: a start
|
|
37427
37920
|
* admitted and not yet claimed, a force start queued behind its
|
|
37428
|
-
* dependencies, a
|
|
37429
|
-
*
|
|
37430
|
-
* replay. A Stop is owed to
|
|
37921
|
+
* dependencies, a conflict repair owed a slot or the end of a start hold,
|
|
37922
|
+
* a task that resumes on its own at its usage limit's reset, or a start a
|
|
37923
|
+
* dirty checkout parked for Stash and continue to replay. A Stop is owed to
|
|
37924
|
+
* it, whether or not anything runs.
|
|
37431
37925
|
*/
|
|
37432
37926
|
get startsPending() {
|
|
37433
|
-
return this.admissions > 0 || this.queuedStarts.size > 0 || this.runs.blocked || this.store.allTasks.some(waitsForReset);
|
|
37927
|
+
return this.admissions > 0 || this.queuedStarts.size > 0 || this.owedRepairs.size > 0 || this.runs.blocked || this.store.allTasks.some(waitsForReset);
|
|
37434
37928
|
}
|
|
37435
37929
|
/**
|
|
37436
37930
|
* The plan's activity as one word for every surface: a start on its way,
|
|
@@ -37866,7 +38360,9 @@ var TaskOrchestrator = class _TaskOrchestrator {
|
|
|
37866
38360
|
this.store.markPending(task2.id);
|
|
37867
38361
|
this.hold(task2.id, failure2.hold, false);
|
|
37868
38362
|
}
|
|
37869
|
-
|
|
38363
|
+
if (failure2.leaves !== "held" || !failure2.preserveWorktree) {
|
|
38364
|
+
await this.runs.release(task2.id, { keep: failure2.leaves === "failed" && failure2.keepWorktree });
|
|
38365
|
+
}
|
|
37870
38366
|
const { level, text: text6, toastOnly } = failure2.notice;
|
|
37871
38367
|
if (toastOnly) this.notifications[level](text6);
|
|
37872
38368
|
else this.tell(level, text6);
|
|
@@ -37931,6 +38427,7 @@ var TaskOrchestrator = class _TaskOrchestrator {
|
|
|
37931
38427
|
* Work that did not land waits on the user — it never fails the task or
|
|
37932
38428
|
* blocks its dependents — unless its conflict is owed a repair, which takes
|
|
37933
38429
|
* the slot the ending attempt freed and never one more than the run allows.
|
|
38430
|
+
* A planner turn's start hold holds it as it holds any new spawn (ADR-0029).
|
|
37934
38431
|
* The repair follows on from the start whose work it repairs: a Stop since
|
|
37935
38432
|
* that was admitted withdraws it.
|
|
37936
38433
|
*/
|
|
@@ -37938,20 +38435,22 @@ var TaskOrchestrator = class _TaskOrchestrator {
|
|
|
37938
38435
|
this.store.markAwaitingUser(task2.id, "conflict");
|
|
37939
38436
|
this.say(outcome.messages);
|
|
37940
38437
|
if (outcome.kind !== "repair-needed") return;
|
|
37941
|
-
|
|
38438
|
+
const waitsFor = this.startHolds > 0 ? "the planner's turn ends" : this.slotsFull() ? "a slot is free" : null;
|
|
38439
|
+
if (!waitsFor) {
|
|
37942
38440
|
const claimed = this.claim(task2.id, admission);
|
|
37943
38441
|
if ("refused" in claimed) this.tell("warn", `The conflict of task "${task2.title}" is not repaired: ${claimed.refused}`);
|
|
37944
38442
|
else this.unawaited(claimed.spawned, `Could not start the repair of task "${task2.title}"`);
|
|
37945
38443
|
} else {
|
|
37946
38444
|
this.store.markPending(task2.id);
|
|
37947
38445
|
if (!this.isScheduling) this.owedRepairs.add(task2.id);
|
|
37948
|
-
this.tell("info", `Task "${task2.title}" is repaired once
|
|
38446
|
+
this.tell("info", `Task "${task2.title}" is repaired once ${waitsFor}.`);
|
|
37949
38447
|
}
|
|
37950
38448
|
}
|
|
37951
|
-
/** Start the repairs {@link settleUnlanded} left owed, as slots allow. */
|
|
38449
|
+
/** Start the repairs {@link settleUnlanded} left owed, as slots and any start hold allow. */
|
|
37952
38450
|
startOwedRepairs(admission) {
|
|
38451
|
+
if (this.startHolds > 0) return;
|
|
37953
38452
|
for (const id2 of [...this.owedRepairs]) {
|
|
37954
|
-
if (this.
|
|
38453
|
+
if (this.slotsFull()) return;
|
|
37955
38454
|
this.owedRepairs.delete(id2);
|
|
37956
38455
|
const task2 = this.store.get(id2);
|
|
37957
38456
|
if (!task2 || task2.status !== "pending" || this.attempts.has(id2) || !this.landing.nextRepair(id2)) continue;
|
|
@@ -37961,13 +38460,31 @@ var TaskOrchestrator = class _TaskOrchestrator {
|
|
|
37961
38460
|
}
|
|
37962
38461
|
}
|
|
37963
38462
|
async landResolved(resolverId, admission) {
|
|
37964
|
-
const resolved =
|
|
38463
|
+
const resolved = this.landing.landResolved(resolverId, (id2) => this.releasing.has(id2));
|
|
37965
38464
|
if (!resolved) return;
|
|
37966
|
-
const { task: task2
|
|
38465
|
+
const { task: task2 } = resolved;
|
|
38466
|
+
const outcome = await this.holdWhileLanding(task2.id, resolved.outcome);
|
|
38467
|
+
if (this.store.get(task2.id)?.status === "completed") return;
|
|
37967
38468
|
await this.applyLanding(task2, outcome, admission, () => {
|
|
37968
38469
|
if (task2.verdict) this.logAndArchive(task2, task2.verdict);
|
|
37969
38470
|
});
|
|
37970
38471
|
}
|
|
38472
|
+
/**
|
|
38473
|
+
* Hold a task for as long as `landing` is merging its worktree. Its end lets
|
|
38474
|
+
* go of its own hold only: another landing of the task begun meanwhile
|
|
38475
|
+
* still holds it.
|
|
38476
|
+
*/
|
|
38477
|
+
async holdWhileLanding(taskId, landing2) {
|
|
38478
|
+
const holds2 = this.completing.get(taskId) ?? /* @__PURE__ */ new Set();
|
|
38479
|
+
holds2.add(landing2);
|
|
38480
|
+
this.completing.set(taskId, holds2);
|
|
38481
|
+
try {
|
|
38482
|
+
return await landing2;
|
|
38483
|
+
} finally {
|
|
38484
|
+
holds2.delete(landing2);
|
|
38485
|
+
if (holds2.size === 0 && this.completing.get(taskId) === holds2) this.completing.delete(taskId);
|
|
38486
|
+
}
|
|
38487
|
+
}
|
|
37971
38488
|
getReadyTasks() {
|
|
37972
38489
|
return this.readiness().ready;
|
|
37973
38490
|
}
|
|
@@ -38075,17 +38592,27 @@ var TaskOrchestrator = class _TaskOrchestrator {
|
|
|
38075
38592
|
* as {@link cancelTask} does, but its worktree goes: no task is left to land
|
|
38076
38593
|
* it into. A spawn still in flight just loses its attempt,
|
|
38077
38594
|
* which is what makes {@link Attempts} kill the session it is about to
|
|
38078
|
-
* receive.
|
|
38079
|
-
*
|
|
38595
|
+
* receive. Every landing of it in flight — Mark complete's, its resolver's —
|
|
38596
|
+
* settles first, so its worktree is not torn down under a merge, and none
|
|
38597
|
+
* begins while it goes; a repair a landing claims is cancelled as any
|
|
38598
|
+
* running attempt is. The id's cross-attempt bookkeeping
|
|
38599
|
+
* goes too — a hold or retry count kept for a task that no longer exists
|
|
38600
|
+
* would be inherited by nothing.
|
|
38080
38601
|
*/
|
|
38081
38602
|
async releaseTask(taskId) {
|
|
38082
38603
|
this.limitPauses.cancel(taskId);
|
|
38083
|
-
|
|
38084
|
-
|
|
38085
|
-
|
|
38086
|
-
this.attempts.
|
|
38087
|
-
if (
|
|
38088
|
-
|
|
38604
|
+
for (let holds2 = this.completing.get(taskId); holds2; holds2 = this.completing.get(taskId)) await Promise.allSettled(holds2);
|
|
38605
|
+
this.releasing.add(taskId);
|
|
38606
|
+
try {
|
|
38607
|
+
const phase = this.attempts.phaseOf(taskId);
|
|
38608
|
+
if (phase === "running" || phase === "integrating") await this.cancelAttempt(taskId, { keep: false });
|
|
38609
|
+
else {
|
|
38610
|
+
this.attempts.end(taskId, "release");
|
|
38611
|
+
if (this.store.get(taskId)?.status === "in_progress") this.store.markPending(taskId);
|
|
38612
|
+
await this.runs.release(taskId, { keep: false });
|
|
38613
|
+
}
|
|
38614
|
+
} finally {
|
|
38615
|
+
this.releasing.delete(taskId);
|
|
38089
38616
|
}
|
|
38090
38617
|
this.onHold.delete(taskId);
|
|
38091
38618
|
this.retryCounts.delete(taskId);
|
|
@@ -38094,17 +38621,12 @@ var TaskOrchestrator = class _TaskOrchestrator {
|
|
|
38094
38621
|
async markTaskComplete(taskId) {
|
|
38095
38622
|
const task2 = this.store.get(taskId);
|
|
38096
38623
|
if (!task2 || task2.status === "completed") return;
|
|
38097
|
-
if (this.completing.has(taskId)) return;
|
|
38624
|
+
if (this.completing.has(taskId) || this.releasing.has(taskId)) return;
|
|
38098
38625
|
const admission = this.admittedNow();
|
|
38099
38626
|
const ended = this.attempts.end(taskId, "complete");
|
|
38100
38627
|
const verdict = this.attempts.markComplete(task2);
|
|
38101
|
-
this.
|
|
38102
|
-
|
|
38103
|
-
try {
|
|
38104
|
-
landing2 = await (ended?.integration ?? this.landing.landVouched(task2));
|
|
38105
|
-
} finally {
|
|
38106
|
-
this.completing.delete(taskId);
|
|
38107
|
-
}
|
|
38628
|
+
const landed2 = ended?.integration ?? this.landing.landVouched(task2);
|
|
38629
|
+
const landing2 = await this.holdWhileLanding(taskId, landed2);
|
|
38108
38630
|
this.store.setTaskVerdict(taskId, verdict);
|
|
38109
38631
|
const handoff = task2.type === "user" ? userTaskHandoff(task2.userSteps) : void 0;
|
|
38110
38632
|
this.attempts.handOver(taskId, summarizeOutput(verdict.reason, ""), handoff);
|
|
@@ -38393,7 +38915,7 @@ ${now.message}` : LIMIT_RESET_MESSAGE;
|
|
|
38393
38915
|
if (!await this.openForAdmitted(taskId, origin, resume, admission)) return;
|
|
38394
38916
|
if (this.attempts.has(taskId)) return;
|
|
38395
38917
|
this.refuseOpsDuringMerge(taskId, forced);
|
|
38396
|
-
if (forced.queued && this.holdsQueued(taskId, forced)) {
|
|
38918
|
+
if (forced.queued && (this.holdsQueued(taskId, forced) || this.slotsFull())) {
|
|
38397
38919
|
this.queuedStarts.set(taskId, { pastGate: forced.pastGate });
|
|
38398
38920
|
return;
|
|
38399
38921
|
}
|
|
@@ -38452,8 +38974,9 @@ ${now.message}` : LIMIT_RESET_MESSAGE;
|
|
|
38452
38974
|
this.queuedStarts.delete(id2);
|
|
38453
38975
|
continue;
|
|
38454
38976
|
}
|
|
38977
|
+
if (this.slotsFull()) continue;
|
|
38455
38978
|
const waits = this.startWaits(task2, task2.research ? void 0 : { queued: true, pastGate: queued.pastGate });
|
|
38456
|
-
if (task2.research &&
|
|
38979
|
+
if (task2.research && waits.length > 0) continue;
|
|
38457
38980
|
if (waits.some((w) => QUEUED_WAITS.has(w.kind))) continue;
|
|
38458
38981
|
if (this.leftToScheduler(task2) && queued.pastGate.length === 0) {
|
|
38459
38982
|
this.queuedStarts.delete(id2);
|
|
@@ -38482,6 +39005,9 @@ ${now.message}` : LIMIT_RESET_MESSAGE;
|
|
|
38482
39005
|
const task2 = this.store.get(taskId);
|
|
38483
39006
|
return !!task2 && this.startWaits(task2, forced).some((w) => QUEUED_WAITS.has(w.kind) || w.kind === "dependencies" || w.kind === "merge-gate");
|
|
38484
39007
|
}
|
|
39008
|
+
slotsFull() {
|
|
39009
|
+
return this.attempts.size >= this.config.maxParallelSessions;
|
|
39010
|
+
}
|
|
38485
39011
|
sayQueuedAtGate(task2, queued, unmerged) {
|
|
38486
39012
|
const titles = unmerged.map((id2) => `"${this.store.get(id2)?.title ?? id2}"`).join(", ");
|
|
38487
39013
|
const notice = `Task "${task2.title}" waits for Merge all: the work of ${titles} is not merged into your branch yet. Merge all to start it, or force start it again to start it without that work.`;
|
|
@@ -38582,7 +39108,7 @@ ${now.message}` : LIMIT_RESET_MESSAGE;
|
|
|
38582
39108
|
*/
|
|
38583
39109
|
async settleRun(after) {
|
|
38584
39110
|
if (!this.store.isFinished()) this.transition({ type: "reopen" });
|
|
38585
|
-
if (this.attempts.size > 0) return;
|
|
39111
|
+
if (this.attempts.size > 0 || this.owedRepairs.size > 0) return;
|
|
38586
39112
|
if (this.runState.kind === "held") return;
|
|
38587
39113
|
if (this.runState.kind !== "scheduling") {
|
|
38588
39114
|
if (after === "tick") {
|
|
@@ -38876,21 +39402,45 @@ var Planner = class {
|
|
|
38876
39402
|
};
|
|
38877
39403
|
|
|
38878
39404
|
// src/services/ModelResolver.ts
|
|
38879
|
-
var ModelResolver = class {
|
|
39405
|
+
var ModelResolver = class _ModelResolver {
|
|
38880
39406
|
constructor(registry, config, deps = {}) {
|
|
38881
39407
|
this.registry = registry;
|
|
38882
39408
|
this.config = config;
|
|
38883
|
-
this.discovery = new ModelDiscovery(registry, deps.execImpl, deps.fetchImpl);
|
|
39409
|
+
this.discovery = new ModelDiscovery(registry, deps.execImpl, deps.fetchImpl, void 0, void 0, void 0, deps.env);
|
|
38884
39410
|
this.fetchImpl = deps.fetchImpl;
|
|
39411
|
+
this.execImpl = deps.execImpl;
|
|
39412
|
+
this.shared = { vendor: { options: null, load: null, errors: {}, generation: 0 }, root: this, views: /* @__PURE__ */ new Map() };
|
|
38885
39413
|
}
|
|
38886
39414
|
registry;
|
|
38887
39415
|
config;
|
|
38888
39416
|
discovery;
|
|
38889
39417
|
fetchImpl;
|
|
38890
|
-
|
|
38891
|
-
|
|
38892
|
-
|
|
38893
|
-
|
|
39418
|
+
execImpl;
|
|
39419
|
+
shared;
|
|
39420
|
+
get vendor() {
|
|
39421
|
+
return this.shared.vendor;
|
|
39422
|
+
}
|
|
39423
|
+
/**
|
|
39424
|
+
* This resolver for runners launched with another environment, a
|
|
39425
|
+
* workspace's own: their catalogs are discovered and cached under `env`, as
|
|
39426
|
+
* `CLAUDE_CONFIG_DIR` there can name another account, while the vendor
|
|
39427
|
+
* catalogs stay this resolver's, fetched once. One view per `key`, made on
|
|
39428
|
+
* its first ask: a later ask with the same key gets that view, whatever
|
|
39429
|
+
* `env` it passes.
|
|
39430
|
+
*/
|
|
39431
|
+
withRunnerEnv(key, env) {
|
|
39432
|
+
let view = this.shared.views.get(key);
|
|
39433
|
+
if (!view) {
|
|
39434
|
+
view = new _ModelResolver(this.registry, this.config, { fetchImpl: this.fetchImpl, execImpl: this.execImpl, env });
|
|
39435
|
+
view.shared = this.shared;
|
|
39436
|
+
this.shared.views.set(key, view);
|
|
39437
|
+
}
|
|
39438
|
+
return view;
|
|
39439
|
+
}
|
|
39440
|
+
/** Every runner-environment catalog this resolver's family holds: its root's and each view's. */
|
|
39441
|
+
runnerCatalogs() {
|
|
39442
|
+
return [this.shared.root, ...this.shared.views.values()].map((resolver) => resolver.discovery);
|
|
39443
|
+
}
|
|
38894
39444
|
static builtinOptions() {
|
|
38895
39445
|
return toOrchestratorOptions({}, ORCHESTRATOR_SHORTCUTS);
|
|
38896
39446
|
}
|
|
@@ -38906,16 +39456,16 @@ var ModelResolver = class {
|
|
|
38906
39456
|
return Object.fromEntries(discovered);
|
|
38907
39457
|
}
|
|
38908
39458
|
async pickerOptions() {
|
|
38909
|
-
if (this.
|
|
38910
|
-
if (!this.
|
|
39459
|
+
if (this.vendor.options) return this.vendor.options;
|
|
39460
|
+
if (!this.vendor.load) {
|
|
38911
39461
|
const load = this.loadPickerOptions();
|
|
38912
|
-
this.
|
|
39462
|
+
this.vendor.load = load;
|
|
38913
39463
|
void load.finally(() => {
|
|
38914
|
-
if (this.
|
|
39464
|
+
if (this.vendor.load === load) this.vendor.load = null;
|
|
38915
39465
|
}).catch(() => {
|
|
38916
39466
|
});
|
|
38917
39467
|
}
|
|
38918
|
-
return this.
|
|
39468
|
+
return this.vendor.load;
|
|
38919
39469
|
}
|
|
38920
39470
|
/**
|
|
38921
39471
|
* Start loading the vendor catalogs without waiting for them, unless they
|
|
@@ -38923,20 +39473,25 @@ var ModelResolver = class {
|
|
|
38923
39473
|
* known yet reads it from them on a later call, never waiting on the fetch.
|
|
38924
39474
|
*/
|
|
38925
39475
|
warmPickerOptions() {
|
|
38926
|
-
if (this.
|
|
39476
|
+
if (this.vendor.options || this.vendor.load) return;
|
|
38927
39477
|
this.pickerOptions().catch(() => {
|
|
38928
39478
|
});
|
|
38929
39479
|
}
|
|
38930
39480
|
async loadPickerOptions() {
|
|
39481
|
+
const vendor = this.vendor;
|
|
39482
|
+
const generation2 = vendor.generation;
|
|
38931
39483
|
const { apiKeys, baseUrls } = collectProviderCredentials(this.config);
|
|
38932
39484
|
const { models, errors } = await fetchAllProviderModels({
|
|
38933
39485
|
apiKeys,
|
|
38934
39486
|
baseUrls,
|
|
38935
39487
|
fetchImpl: this.fetchImpl
|
|
38936
39488
|
});
|
|
38937
|
-
|
|
38938
|
-
|
|
38939
|
-
|
|
39489
|
+
const options = toOrchestratorOptions(models, ORCHESTRATOR_SHORTCUTS);
|
|
39490
|
+
if (vendor.generation === generation2) {
|
|
39491
|
+
vendor.errors = errors;
|
|
39492
|
+
vendor.options = options;
|
|
39493
|
+
}
|
|
39494
|
+
return options;
|
|
38940
39495
|
}
|
|
38941
39496
|
/**
|
|
38942
39497
|
* Per-provider failures from the most recent `pickerOptions()` fetch, keyed
|
|
@@ -38945,7 +39500,7 @@ var ModelResolver = class {
|
|
|
38945
39500
|
* key/endpoint is set but whose catalog could not be reached.
|
|
38946
39501
|
*/
|
|
38947
39502
|
getDiscoveryErrors() {
|
|
38948
|
-
return this.
|
|
39503
|
+
return this.vendor.errors;
|
|
38949
39504
|
}
|
|
38950
39505
|
async refresh() {
|
|
38951
39506
|
const options = await this.pickerOptions();
|
|
@@ -38956,8 +39511,25 @@ var ModelResolver = class {
|
|
|
38956
39511
|
this.config.setProviderModelLists(lists);
|
|
38957
39512
|
return lists;
|
|
38958
39513
|
}
|
|
39514
|
+
/** Forget every runner catalog, each workspace's included; the vendor catalogs stay. */
|
|
38959
39515
|
refreshRunnerModels() {
|
|
38960
|
-
this.discovery.clear();
|
|
39516
|
+
for (const discovery of this.runnerCatalogs()) discovery.clear();
|
|
39517
|
+
}
|
|
39518
|
+
/**
|
|
39519
|
+
* The models a pick for `provider` is judged against: a harness planner's
|
|
39520
|
+
* own runner catalog, or the vendor picker catalog (ADR-0009). Discovered
|
|
39521
|
+
* if not held. Never throws: a catalog that cannot be had is empty.
|
|
39522
|
+
*/
|
|
39523
|
+
async plannerCatalog(provider) {
|
|
39524
|
+
const runner = runnerForProvider(provider);
|
|
39525
|
+
if (runner) return candidatesOf((await this.modelsForRunners([runner]))[runner] ?? []);
|
|
39526
|
+
return (await this.pickerOptions().catch(() => [])).map((o) => ({ id: o.id }));
|
|
39527
|
+
}
|
|
39528
|
+
/** {@link plannerCatalog} from what is already held, so a cold catalog is empty; never discovers. */
|
|
39529
|
+
cachedPlannerCatalog(provider) {
|
|
39530
|
+
const runner = runnerForProvider(provider);
|
|
39531
|
+
if (runner) return candidatesOf(this.getCachedRunnerModels(runner));
|
|
39532
|
+
return this.getCachedPickerOptions().map((o) => ({ id: o.id }));
|
|
38961
39533
|
}
|
|
38962
39534
|
/**
|
|
38963
39535
|
* Synchronous reads of whatever the last `modelsForRunners`/`pickerOptions`
|
|
@@ -38968,7 +39540,7 @@ var ModelResolver = class {
|
|
|
38968
39540
|
return this.discovery.getCached(runner) ?? [];
|
|
38969
39541
|
}
|
|
38970
39542
|
getCachedPickerOptions() {
|
|
38971
|
-
return this.
|
|
39543
|
+
return this.vendor.options ?? [];
|
|
38972
39544
|
}
|
|
38973
39545
|
/**
|
|
38974
39546
|
* A model's context window from whatever catalog is already cached (#49),
|
|
@@ -38981,7 +39553,7 @@ var ModelResolver = class {
|
|
|
38981
39553
|
*/
|
|
38982
39554
|
contextWindowFor(source, model) {
|
|
38983
39555
|
if (!model) return void 0;
|
|
38984
|
-
const window2 = isApiProvider(source) ? this.
|
|
39556
|
+
const window2 = isApiProvider(source) ? this.vendor.options?.find((o) => o.apiProvider === source && o.id === prefixModelId(source, stripModelPrefix(model, source)))?.contextWindow : this.discovery.getCached(source)?.find((m) => m.modelId === model)?.contextWindow;
|
|
38985
39557
|
return window2 && window2 > 0 ? window2 : void 0;
|
|
38986
39558
|
}
|
|
38987
39559
|
/**
|
|
@@ -38991,14 +39563,16 @@ var ModelResolver = class {
|
|
|
38991
39563
|
*/
|
|
38992
39564
|
maxOutputTokensFor(source, model) {
|
|
38993
39565
|
if (!model || !isApiProvider(source)) return void 0;
|
|
38994
|
-
const cap = this.
|
|
39566
|
+
const cap = this.vendor.options?.find((o) => o.apiProvider === source && o.id === prefixModelId(source, stripModelPrefix(model, source)))?.maxOutputTokens;
|
|
38995
39567
|
return cap && cap > 0 ? cap : void 0;
|
|
38996
39568
|
}
|
|
39569
|
+
/** Forget every catalog: the vendor ones, and each runner environment's. */
|
|
38997
39570
|
invalidate() {
|
|
38998
|
-
this.
|
|
38999
|
-
this.
|
|
39000
|
-
this.
|
|
39001
|
-
this.
|
|
39571
|
+
this.refreshRunnerModels();
|
|
39572
|
+
this.vendor.generation++;
|
|
39573
|
+
this.vendor.options = null;
|
|
39574
|
+
this.vendor.load = null;
|
|
39575
|
+
this.vendor.errors = {};
|
|
39002
39576
|
if (!this.fetchImpl) {
|
|
39003
39577
|
for (const provider of PROVIDER_PRIORITY) {
|
|
39004
39578
|
const baseUrl = this.config.getProviderBaseUrl(provider);
|
|
@@ -39008,6 +39582,9 @@ var ModelResolver = class {
|
|
|
39008
39582
|
}
|
|
39009
39583
|
}
|
|
39010
39584
|
};
|
|
39585
|
+
function candidatesOf(models) {
|
|
39586
|
+
return models.map((m) => ({ id: m.modelId, variants: m.variants }));
|
|
39587
|
+
}
|
|
39011
39588
|
|
|
39012
39589
|
// src/services/RunnerInstallation.ts
|
|
39013
39590
|
var import_child_process12 = require("child_process");
|
|
@@ -39598,8 +40175,8 @@ function codexWindow(slot, raw) {
|
|
|
39598
40175
|
|
|
39599
40176
|
// src/services/harness/claudeAccountLimits.ts
|
|
39600
40177
|
var import_child_process13 = require("child_process");
|
|
39601
|
-
var
|
|
39602
|
-
var
|
|
40178
|
+
var import_crypto19 = require("crypto");
|
|
40179
|
+
var import_fs14 = require("fs");
|
|
39603
40180
|
var import_os5 = require("os");
|
|
39604
40181
|
var import_path8 = require("path");
|
|
39605
40182
|
var DEFAULT_TIMEOUT_MS5 = 2e4;
|
|
@@ -39633,10 +40210,10 @@ function claudeAccountLimitCollector(options) {
|
|
|
39633
40210
|
};
|
|
39634
40211
|
}
|
|
39635
40212
|
async function readClaudeAccountLimits(options, signal) {
|
|
39636
|
-
const cwd = (0,
|
|
40213
|
+
const cwd = (0, import_fs14.mkdtempSync)((0, import_path8.join)((0, import_os5.tmpdir)(), "ordewell-claude-limits-"));
|
|
39637
40214
|
const removeCwd = () => {
|
|
39638
40215
|
try {
|
|
39639
|
-
(0,
|
|
40216
|
+
(0, import_fs14.rmSync)(cwd, { recursive: true, force: true });
|
|
39640
40217
|
} catch {
|
|
39641
40218
|
}
|
|
39642
40219
|
};
|
|
@@ -39678,7 +40255,7 @@ async function readClaudeAccountLimits(options, signal) {
|
|
|
39678
40255
|
}
|
|
39679
40256
|
function claudeAccountOwner(account) {
|
|
39680
40257
|
const email = isRecord11(account) ? text5(account.email) : void 0;
|
|
39681
|
-
if (!isRecord11(account) || !email) return `unnamed\0${(0,
|
|
40258
|
+
if (!isRecord11(account) || !email) return `unnamed\0${(0, import_crypto19.randomUUID)()}`;
|
|
39682
40259
|
return [text5(account.apiProvider) ?? "", email, text5(account.organization) ?? ""].join("\0");
|
|
39683
40260
|
}
|
|
39684
40261
|
function claudeLimitReading(usage, owner, accountPlan) {
|
|
@@ -39720,8 +40297,8 @@ function isoInstant(value) {
|
|
|
39720
40297
|
var import_os7 = require("os");
|
|
39721
40298
|
|
|
39722
40299
|
// src/services/harness/openCodeCredentials.ts
|
|
39723
|
-
var
|
|
39724
|
-
var
|
|
40300
|
+
var import_crypto20 = require("crypto");
|
|
40301
|
+
var import_fs15 = require("fs");
|
|
39725
40302
|
var import_os6 = require("os");
|
|
39726
40303
|
var import_path9 = require("path");
|
|
39727
40304
|
var import_worker_threads = require("worker_threads");
|
|
@@ -39778,19 +40355,19 @@ function walStore(path) {
|
|
|
39778
40355
|
parentPort.postMessage(select());
|
|
39779
40356
|
`;
|
|
39780
40357
|
var isRecord12 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
39781
|
-
var digest = (...parts) => (0,
|
|
40358
|
+
var digest = (...parts) => (0, import_crypto20.createHash)("sha256").update(parts.join("\0")).digest("hex").slice(0, 32);
|
|
39782
40359
|
function openCodeStorePaths(env, home) {
|
|
39783
40360
|
const data = (0, import_path9.join)(env.XDG_DATA_HOME || (0, import_path9.join)(home, ".local", "share"), "opencode");
|
|
39784
40361
|
const file = env.OPENCODE_DB || "opencode.db";
|
|
39785
40362
|
return { db: file === ":memory:" ? null : (0, import_path9.resolve)(data, file), auth: (0, import_path9.join)(data, "auth.json") };
|
|
39786
40363
|
}
|
|
39787
40364
|
function openCodeGoLoginReader(deps = {}) {
|
|
39788
|
-
const
|
|
40365
|
+
const homedir10 = deps.homedir ?? import_os6.homedir;
|
|
39789
40366
|
let last;
|
|
39790
40367
|
return {
|
|
39791
40368
|
async read(env, signal) {
|
|
39792
40369
|
if (signal.aborted) return { kind: "unreadable" };
|
|
39793
|
-
const paths = openCodeStorePaths(env,
|
|
40370
|
+
const paths = openCodeStorePaths(env, homedir10());
|
|
39794
40371
|
const [db, wal, auth] = await Promise.all([
|
|
39795
40372
|
paths.db ? stamp(paths.db) : null,
|
|
39796
40373
|
paths.db ? stamp(`${paths.db}-wal`) : null,
|
|
@@ -39844,8 +40421,8 @@ function v2Login(db, row) {
|
|
|
39844
40421
|
login: { route: "console", owner: digest("console", db, row.id, account, orgID ?? ""), access: value.access, ...orgID ? { orgID } : {}, expires: value.expires }
|
|
39845
40422
|
};
|
|
39846
40423
|
}
|
|
39847
|
-
async function legacyLogin(
|
|
39848
|
-
const content = env.OPENCODE_AUTH_CONTENT ?? await readSmall(
|
|
40424
|
+
async function legacyLogin(path50, env) {
|
|
40425
|
+
const content = env.OPENCODE_AUTH_CONTENT ?? await readSmall(path50);
|
|
39849
40426
|
if (content === void 0) return env[ENV_KEY] ? { kind: "unsupported" } : { kind: "none" };
|
|
39850
40427
|
if (content === null) return { kind: "unreadable" };
|
|
39851
40428
|
const auth = parseJson(content);
|
|
@@ -39854,15 +40431,15 @@ async function legacyLogin(path47, env) {
|
|
|
39854
40431
|
if (entry === void 0) return env[ENV_KEY] ? { kind: "unsupported" } : { kind: "none" };
|
|
39855
40432
|
if (!isRecord12(entry) || entry.type !== "api") return { kind: "unsupported" };
|
|
39856
40433
|
if (typeof entry.key !== "string" || entry.key.length === 0) return { kind: "unreadable" };
|
|
39857
|
-
return { kind: "login", login: { route: "key", owner: digest("v1",
|
|
40434
|
+
return { kind: "login", login: { route: "key", owner: digest("v1", path50, entry.key), key: entry.key } };
|
|
39858
40435
|
}
|
|
39859
|
-
function queryStore(
|
|
40436
|
+
function queryStore(path50, signal, deps) {
|
|
39860
40437
|
return new Promise((settle) => {
|
|
39861
40438
|
let worker;
|
|
39862
40439
|
try {
|
|
39863
40440
|
worker = (deps.spawnWorker ?? ((source, options) => new import_worker_threads.Worker(source, options)))(WORKER_SOURCE, {
|
|
39864
40441
|
eval: true,
|
|
39865
|
-
workerData: { path:
|
|
40442
|
+
workerData: { path: path50, integrations: GO_INTEGRATIONS, busyMs: BUSY_MS, maxValueBytes: MAX_VALUE_BYTES },
|
|
39866
40443
|
execArgv: deps.workerExecArgv ?? [],
|
|
39867
40444
|
resourceLimits: { maxOldGenerationSizeMb: 64 }
|
|
39868
40445
|
});
|
|
@@ -39889,19 +40466,19 @@ function queryStore(path47, signal, deps) {
|
|
|
39889
40466
|
worker.once("exit", aborted);
|
|
39890
40467
|
});
|
|
39891
40468
|
}
|
|
39892
|
-
async function stamp(
|
|
40469
|
+
async function stamp(path50) {
|
|
39893
40470
|
try {
|
|
39894
|
-
const stat = await
|
|
40471
|
+
const stat = await import_fs15.promises.stat(path50);
|
|
39895
40472
|
return [stat.ino, stat.size, stat.mtimeMs];
|
|
39896
40473
|
} catch {
|
|
39897
40474
|
return null;
|
|
39898
40475
|
}
|
|
39899
40476
|
}
|
|
39900
|
-
async function readSmall(
|
|
40477
|
+
async function readSmall(path50) {
|
|
39901
40478
|
try {
|
|
39902
|
-
const stat = await
|
|
40479
|
+
const stat = await import_fs15.promises.stat(path50);
|
|
39903
40480
|
if (!stat.isFile() || stat.size > MAX_AUTH_FILE_BYTES) return null;
|
|
39904
|
-
return await
|
|
40481
|
+
return await import_fs15.promises.readFile(path50, "utf8");
|
|
39905
40482
|
} catch (err) {
|
|
39906
40483
|
return err.code === "ENOENT" ? void 0 : null;
|
|
39907
40484
|
}
|
|
@@ -41030,20 +41607,20 @@ function createAiService(config, deps) {
|
|
|
41030
41607
|
}
|
|
41031
41608
|
|
|
41032
41609
|
// src/services/createSession.ts
|
|
41033
|
-
var
|
|
41610
|
+
var path47 = __toESM(require("path"));
|
|
41034
41611
|
|
|
41035
41612
|
// src/services/handoffReferences.ts
|
|
41036
41613
|
var fs21 = __toESM(require("fs"));
|
|
41037
|
-
var
|
|
41614
|
+
var path45 = __toESM(require("path"));
|
|
41038
41615
|
var import_child_process15 = require("child_process");
|
|
41039
41616
|
function resolveHandoffReferences(handoff, workspace) {
|
|
41040
41617
|
const checked = /* @__PURE__ */ new Map();
|
|
41041
41618
|
const contained = (candidate) => {
|
|
41042
41619
|
try {
|
|
41043
41620
|
const root = fs21.realpathSync(workspace);
|
|
41044
|
-
const resolved = fs21.realpathSync(
|
|
41045
|
-
const relative17 =
|
|
41046
|
-
return relative17 !== ".." && !relative17.startsWith(`..${
|
|
41621
|
+
const resolved = fs21.realpathSync(path45.resolve(root, candidate));
|
|
41622
|
+
const relative17 = path45.relative(root, resolved);
|
|
41623
|
+
return relative17 !== ".." && !relative17.startsWith(`..${path45.sep}`) && !path45.isAbsolute(relative17) ? resolved : void 0;
|
|
41047
41624
|
} catch {
|
|
41048
41625
|
return void 0;
|
|
41049
41626
|
}
|
|
@@ -41064,7 +41641,7 @@ function resolveHandoffReferences(handoff, workspace) {
|
|
|
41064
41641
|
}
|
|
41065
41642
|
} else if (/^[0-9a-fA-F]{4,64}$/.test(ref.sha)) {
|
|
41066
41643
|
const repo = contained(ref.repo ?? ".");
|
|
41067
|
-
if (repo && fs21.existsSync(
|
|
41644
|
+
if (repo && fs21.existsSync(path45.join(repo, ".git"))) {
|
|
41068
41645
|
const result2 = (0, import_child_process15.spawnSync)("git", ["--no-optional-locks", "--no-replace-objects", "cat-file", "-t", ref.sha], {
|
|
41069
41646
|
cwd: repo,
|
|
41070
41647
|
encoding: "utf8",
|
|
@@ -42022,7 +42599,7 @@ function serializeTask(t) {
|
|
|
42022
42599
|
...t.loop && t.loopRound !== void 0 ? { loopRound: t.loopRound } : {}
|
|
42023
42600
|
};
|
|
42024
42601
|
}
|
|
42025
|
-
function serializeTaskStatus(t, idleSince = null, isolation = null, queued = [], awaitingApproval = 0, mergeGate2 = [], checkpoint = "", waitsForApproval2 = false, holdReason = null, completionReceived = null) {
|
|
42602
|
+
function serializeTaskStatus(t, idleSince = null, isolation = null, queued = [], awaitingApproval = 0, mergeGate2 = [], checkpoint = "", waitsForApproval2 = false, holdReason = null, completionReceived = null, attaching = null) {
|
|
42026
42603
|
return {
|
|
42027
42604
|
id: t.id,
|
|
42028
42605
|
type: t.type,
|
|
@@ -42030,6 +42607,7 @@ function serializeTaskStatus(t, idleSince = null, isolation = null, queued = [],
|
|
|
42030
42607
|
verdict: t.verdict ? { outcome: t.verdict.outcome, reason: t.verdict.reason, checks: t.verdict.checks || [], ...t.verdict.reported ? { reported: t.verdict.reported } : {} } : null,
|
|
42031
42608
|
idleSince,
|
|
42032
42609
|
...completionReceived ? { completionReceived } : {},
|
|
42610
|
+
...attaching ? { attaching } : {},
|
|
42033
42611
|
...t.attempted ? { attempted: true } : {},
|
|
42034
42612
|
...t.attemptUsage ? { attemptUsage: structuredClone(t.attemptUsage) } : {},
|
|
42035
42613
|
...isolation ? { isolation } : {},
|
|
@@ -42059,7 +42637,7 @@ function surfacePendingTurns(pending) {
|
|
|
42059
42637
|
function surfaceTask({ attemptSkills, subtasks, runnerSessionId: _runnerSessionId, ...task2 }) {
|
|
42060
42638
|
return {
|
|
42061
42639
|
...task2,
|
|
42062
|
-
...attemptSkills ? { attemptSkills: attemptSkills.map(({ name, source, path:
|
|
42640
|
+
...attemptSkills ? { attemptSkills: attemptSkills.map(({ name, source, path: path50 }) => ({ name, source, path: path50 })) } : {},
|
|
42063
42641
|
subtasks: (subtasks ?? []).map(surfaceTask)
|
|
42064
42642
|
};
|
|
42065
42643
|
}
|
|
@@ -42479,8 +43057,8 @@ var RunSupervision = class {
|
|
|
42479
43057
|
|
|
42480
43058
|
// src/services/runView.ts
|
|
42481
43059
|
var EMPTY_RUN = { tasks: [], runMerge: null, handoff: null, mergeResult: null, activity: IDLE_ACTIVITY };
|
|
42482
|
-
function split({ verdict, idleSince, completionReceived, isolation, checkpoint, queued, continuable, awaitingApproval, mergeGate: mergeGate2, forcedPastGate, waitsForApproval: waitsForApproval2, holdReason, handoff, attempted, ...plan }) {
|
|
42483
|
-
return { plan, status: { verdict, idleSince, completionReceived, isolation, checkpoint, queued, continuable, awaitingApproval, mergeGate: mergeGate2, forcedPastGate, waitsForApproval: waitsForApproval2, holdReason, handoff, attempted } };
|
|
43060
|
+
function split({ verdict, idleSince, completionReceived, attaching, isolation, checkpoint, queued, continuable, awaitingApproval, mergeGate: mergeGate2, forcedPastGate, waitsForApproval: waitsForApproval2, holdReason, handoff, attempted, ...plan }) {
|
|
43061
|
+
return { plan, status: { verdict, idleSince, completionReceived, attaching, isolation, checkpoint, queued, continuable, awaitingApproval, mergeGate: mergeGate2, forcedPastGate, waitsForApproval: waitsForApproval2, holdReason, handoff, attempted } };
|
|
42484
43062
|
}
|
|
42485
43063
|
function refresh(previous, tasks) {
|
|
42486
43064
|
const held2 = new Map([...walkPlan(previous)].map(({ task: task2 }) => [task2.id, task2]));
|
|
@@ -42716,7 +43294,8 @@ var SessionEventRelay = class {
|
|
|
42716
43294
|
this.checkpointQuestion(t.id),
|
|
42717
43295
|
this.waitsForApproval(t.id),
|
|
42718
43296
|
this.orchestrator.holdReason(t.id),
|
|
42719
|
-
this.orchestrator.getCompletionReceipt(t.id)
|
|
43297
|
+
this.orchestrator.getCompletionReceipt(t.id),
|
|
43298
|
+
this.orchestrator.getToolsAttaching(t.id)
|
|
42720
43299
|
)),
|
|
42721
43300
|
...runMerge ? { runMerge } : {},
|
|
42722
43301
|
activity: this.orchestrator.activity
|
|
@@ -42838,10 +43417,10 @@ var SessionEventRelay = class {
|
|
|
42838
43417
|
|
|
42839
43418
|
// src/utils/taskLogStore.ts
|
|
42840
43419
|
var fs22 = __toESM(require("fs"));
|
|
42841
|
-
var
|
|
43420
|
+
var path46 = __toESM(require("path"));
|
|
42842
43421
|
var EXTENSION = ".jsonl";
|
|
42843
43422
|
function taskDir({ baseDir, sessionId }, taskId) {
|
|
42844
|
-
return
|
|
43423
|
+
return path46.join(sessionDataDir(sessionId, baseDir), "tasks", idSegment(taskId));
|
|
42845
43424
|
}
|
|
42846
43425
|
function listTaskLogAttempts(location, taskId) {
|
|
42847
43426
|
let names;
|
|
@@ -42859,7 +43438,7 @@ function openTaskLog(location, taskId, attempt) {
|
|
|
42859
43438
|
const next = (listTaskLogAttempts(location, taskId).pop() ?? 0) + 1;
|
|
42860
43439
|
let number = attempt !== void 0 && Number.isInteger(attempt) && attempt >= 1 ? attempt : next;
|
|
42861
43440
|
for (; ; ) {
|
|
42862
|
-
const file =
|
|
43441
|
+
const file = path46.join(dir, `${number}${EXTENSION}`);
|
|
42863
43442
|
try {
|
|
42864
43443
|
fs22.writeFileSync(file, "", { flag: "wx" });
|
|
42865
43444
|
} catch (err) {
|
|
@@ -42883,15 +43462,15 @@ function prepareTaskOutputFile(location, taskId, attempt) {
|
|
|
42883
43462
|
ensureStateDirIgnored(location.baseDir);
|
|
42884
43463
|
const dir = taskDir(location, taskId);
|
|
42885
43464
|
fs22.mkdirSync(dir, { recursive: true });
|
|
42886
|
-
return
|
|
43465
|
+
return path46.join(dir, `${attempt}.output.txt`);
|
|
42887
43466
|
}
|
|
42888
43467
|
function taskPatchFile(location, taskId, attempt) {
|
|
42889
|
-
return
|
|
43468
|
+
return path46.join(taskDir(location, taskId), `${attempt}.patch`);
|
|
42890
43469
|
}
|
|
42891
43470
|
function saveTaskPatch(location, taskId, attempt, patch) {
|
|
42892
43471
|
ensureStateDirIgnored(location.baseDir);
|
|
42893
43472
|
const file = taskPatchFile(location, taskId, attempt);
|
|
42894
|
-
fs22.mkdirSync(
|
|
43473
|
+
fs22.mkdirSync(path46.dirname(file), { recursive: true });
|
|
42895
43474
|
const partial = `${file}.${process.pid}-${Date.now()}.partial`;
|
|
42896
43475
|
fs22.writeFileSync(partial, patch, { flag: "wx" });
|
|
42897
43476
|
fs22.renameSync(partial, file);
|
|
@@ -42912,7 +43491,7 @@ function readTaskLog(location, taskId, attempt) {
|
|
|
42912
43491
|
if (!Number.isInteger(attempt) || attempt < 1) return [];
|
|
42913
43492
|
let raw;
|
|
42914
43493
|
try {
|
|
42915
|
-
raw = fs22.readFileSync(
|
|
43494
|
+
raw = fs22.readFileSync(path46.join(taskDir(location, taskId), `${attempt}${EXTENSION}`), "utf-8");
|
|
42916
43495
|
} catch {
|
|
42917
43496
|
return [];
|
|
42918
43497
|
}
|
|
@@ -44974,7 +45553,7 @@ function createSession(deps) {
|
|
|
44974
45553
|
...deps.openTaskLog ? {} : {
|
|
44975
45554
|
keepPatch: (taskId, attempt, patch) => {
|
|
44976
45555
|
const location = session.taskLogLocation;
|
|
44977
|
-
return
|
|
45556
|
+
return path47.relative(location.baseDir, saveTaskPatch(location, taskId, attempt, patch)).split(path47.sep).join("/");
|
|
44978
45557
|
}
|
|
44979
45558
|
},
|
|
44980
45559
|
recordHandoff: (sessionId, taskId, handoff) => taskLogs.note(sessionId, taskId, { type: "handoff", handoff }),
|
|
@@ -47842,6 +48421,7 @@ function taskRowView(task2, context = {}) {
|
|
|
47842
48421
|
resumes: resumeLabel(task2, context.now),
|
|
47843
48422
|
approvals: approvalLabel(task2.awaitingApproval),
|
|
47844
48423
|
receipt: receiptLabel(task2),
|
|
48424
|
+
attaching: attachingLabel(task2),
|
|
47845
48425
|
waitsForApproval: task2.waitsForApproval && NOT_STARTED.has(task2.status) ? WAITS_FOR_APPROVAL_LABEL : null,
|
|
47846
48426
|
holdReason: task2.holdReason?.trim() || null,
|
|
47847
48427
|
research: task2.type === "ai" && task2.research === true,
|
|
@@ -48155,6 +48735,7 @@ var StructuredSession = class extends AbstractRunnerSession {
|
|
|
48155
48735
|
*/
|
|
48156
48736
|
async startAttached(opts) {
|
|
48157
48737
|
const { server: server4, scope, shell, savePatch, webReads } = this.launch.tools;
|
|
48738
|
+
this.launch.onAttaching?.(1);
|
|
48158
48739
|
const spawned = await spawnAttached({
|
|
48159
48740
|
runner: this.launch.runner,
|
|
48160
48741
|
create: () => this.launch.createAdapter(this.launch.runner, this.launch.deps),
|
|
@@ -48191,7 +48772,10 @@ var StructuredSession = class extends AbstractRunnerSession {
|
|
|
48191
48772
|
},
|
|
48192
48773
|
cancelled: this.ended.signal,
|
|
48193
48774
|
startupExit: () => this.startupExit,
|
|
48194
|
-
onRespawn: (failure2) =>
|
|
48775
|
+
onRespawn: (failure2) => {
|
|
48776
|
+
this.launch.onNotice?.(`${this.launch.runner} started without Ordewell's tools (${failure2}); respawning once.`);
|
|
48777
|
+
this.launch.onAttaching?.(2);
|
|
48778
|
+
},
|
|
48195
48779
|
failed: (first, second) => `Could not start ${this.launch.runner} with Ordewell's tools, so the task was not sent to it. First spawn: ${first}. Respawn: ${second}.`
|
|
48196
48780
|
});
|
|
48197
48781
|
if (!spawned) return;
|
|
@@ -48774,16 +49358,22 @@ var StructuredRunner = class extends AbstractRunner {
|
|
|
48774
49358
|
checkHandoff: opts.checkHandoff ?? ((completion) => checkHandoff(completion, opts.taskId, [])),
|
|
48775
49359
|
...opts.readTask ? { readTask: opts.readTask } : {},
|
|
48776
49360
|
...opts.applyPatch ? { applyPatch: opts.applyPatch } : {},
|
|
48777
|
-
onNotice: opts.onNotice
|
|
49361
|
+
onNotice: opts.onNotice,
|
|
49362
|
+
onAttaching: opts.onAttaching
|
|
48778
49363
|
});
|
|
48779
49364
|
console.error(`[structured] Starting ${opts.runner} [${opts.mode ?? "default"}] (${opts.modelId || "default"}) for task ${opts.taskId.slice(0, 8)}`);
|
|
48780
49365
|
this.registerSession(id2, session);
|
|
49366
|
+
const cancel = () => void session.stop();
|
|
49367
|
+
opts.cancelled?.addEventListener("abort", cancel, { once: true });
|
|
48781
49368
|
try {
|
|
49369
|
+
if (opts.cancelled?.aborted) cancel();
|
|
48782
49370
|
await session.start(opts.prompt);
|
|
48783
49371
|
} catch (err) {
|
|
48784
49372
|
session.kill();
|
|
48785
49373
|
this.sessions.delete(id2);
|
|
48786
49374
|
throw err;
|
|
49375
|
+
} finally {
|
|
49376
|
+
opts.cancelled?.removeEventListener("abort", cancel);
|
|
48787
49377
|
}
|
|
48788
49378
|
return session;
|
|
48789
49379
|
}
|
|
@@ -48857,7 +49447,7 @@ var RunnerRegistry = class {
|
|
|
48857
49447
|
|
|
48858
49448
|
// src/plugins/removedPluginNotice.ts
|
|
48859
49449
|
var fs23 = __toESM(require("fs"));
|
|
48860
|
-
var
|
|
49450
|
+
var path48 = __toESM(require("path"));
|
|
48861
49451
|
function isManifestFile(file) {
|
|
48862
49452
|
try {
|
|
48863
49453
|
return fs23.statSync(file).isFile();
|
|
@@ -48866,10 +49456,10 @@ function isManifestFile(file) {
|
|
|
48866
49456
|
}
|
|
48867
49457
|
}
|
|
48868
49458
|
function removedPluginNotice(dataDir = globalDataDir()) {
|
|
48869
|
-
const directory =
|
|
49459
|
+
const directory = path48.join(dataDir, "plugins");
|
|
48870
49460
|
try {
|
|
48871
|
-
const hasManifest = isManifestFile(
|
|
48872
|
-
(entry) => (entry.isDirectory() || entry.isSymbolicLink()) && isManifestFile(
|
|
49461
|
+
const hasManifest = isManifestFile(path48.join(directory, "manifest.json")) || fs23.readdirSync(directory, { withFileTypes: true }).some(
|
|
49462
|
+
(entry) => (entry.isDirectory() || entry.isSymbolicLink()) && isManifestFile(path48.join(directory, entry.name, "manifest.json"))
|
|
48873
49463
|
);
|
|
48874
49464
|
if (hasManifest) return "Plugin runners were removed; manifests in ~/.ordewell/plugins/ are ignored. See the CHANGELOG.";
|
|
48875
49465
|
} catch {
|
|
@@ -48880,15 +49470,15 @@ function removedPluginNotice(dataDir = globalDataDir()) {
|
|
|
48880
49470
|
|
|
48881
49471
|
// src/utils/stateStore.ts
|
|
48882
49472
|
var fs24 = __toESM(require("fs"));
|
|
48883
|
-
var
|
|
49473
|
+
var path49 = __toESM(require("path"));
|
|
48884
49474
|
var STATE_FILE2 = "state.json";
|
|
48885
49475
|
function getStatePath(baseDir) {
|
|
48886
|
-
return
|
|
49476
|
+
return path49.join(getStateDir(baseDir), STATE_FILE2);
|
|
48887
49477
|
}
|
|
48888
49478
|
var SESSION_ID_KEY = "sessionId";
|
|
48889
49479
|
function saveState(plan, baseDir, sessionId) {
|
|
48890
49480
|
const statePath = getStatePath(baseDir ?? process.cwd());
|
|
48891
|
-
ensureDir(
|
|
49481
|
+
ensureDir(path49.dirname(statePath));
|
|
48892
49482
|
plan.lastUpdated = (/* @__PURE__ */ new Date()).toISOString();
|
|
48893
49483
|
const file = sessionId ? { ...plan, [SESSION_ID_KEY]: sessionId } : plan;
|
|
48894
49484
|
dropRetiredPlanFields(file);
|
|
@@ -49175,6 +49765,7 @@ var PlannerModelMemory = class {
|
|
|
49175
49765
|
assignedRunnerOf,
|
|
49176
49766
|
assignmentIssue,
|
|
49177
49767
|
assignmentIssueText,
|
|
49768
|
+
attachingLabel,
|
|
49178
49769
|
augmentedPath,
|
|
49179
49770
|
automergeSummary,
|
|
49180
49771
|
autonomyLevelLabel,
|
|
@@ -49436,6 +50027,7 @@ var PlannerModelMemory = class {
|
|
|
49436
50027
|
repoBaseLine,
|
|
49437
50028
|
repoResultLines,
|
|
49438
50029
|
reposWithWork,
|
|
50030
|
+
repositoryRunnerConfigPaths,
|
|
49439
50031
|
researchShellWarning,
|
|
49440
50032
|
researchToolsPath,
|
|
49441
50033
|
resetInstalledPlanner,
|