@ordewell/core 0.5.1 → 0.5.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{ITerminalRunner-H1yT8LTy.d.ts → ITerminalRunner-Bd-vAJnw.d.ts} +18 -1
- package/dist/{ITerminalRunner-DCjsa2-I.d.mts → ITerminalRunner-ByeoLF57.d.mts} +18 -1
- package/dist/index.d.mts +72 -3
- package/dist/index.d.ts +72 -3
- package/dist/index.js +651 -378
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +643 -372
- package/dist/index.mjs.map +1 -1
- package/dist/testing.d.mts +1 -1
- package/dist/testing.d.ts +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -2637,33 +2637,33 @@ function grepOptions(args) {
|
|
|
2637
2637
|
};
|
|
2638
2638
|
}
|
|
2639
2639
|
var STOPPED_TOOL_RESULT = "Stopped by the user \u2014 this call was not executed, and no further tool calls will run this turn.";
|
|
2640
|
-
async function executeTool(name, args,
|
|
2641
|
-
const outcome = await runTool(name, args,
|
|
2640
|
+
async function executeTool(name, args, fs15, fetcher, signal) {
|
|
2641
|
+
const outcome = await runTool(name, args, fs15, fetcher, signal);
|
|
2642
2642
|
return { ...outcome, output: redactSecrets(outcome.output) };
|
|
2643
2643
|
}
|
|
2644
|
-
async function runTool(name, args,
|
|
2644
|
+
async function runTool(name, args, fs15, fetcher, signal) {
|
|
2645
2645
|
if (signal?.aborted) return { success: false, output: STOPPED_TOOL_RESULT, truncated: false };
|
|
2646
2646
|
switch (name) {
|
|
2647
2647
|
case "read_file": {
|
|
2648
2648
|
const offset = num(args.offset);
|
|
2649
2649
|
const limit = num(args.limit);
|
|
2650
2650
|
const maxBytes = num(args.maxBytes);
|
|
2651
|
-
return
|
|
2651
|
+
return fs15.readFile(String(args.path ?? ""), { offset, limit, maxBytes });
|
|
2652
2652
|
}
|
|
2653
2653
|
case "read_files": {
|
|
2654
2654
|
const paths = Array.isArray(args.paths) ? args.paths.map(String) : [];
|
|
2655
|
-
return
|
|
2655
|
+
return fs15.readFiles(paths);
|
|
2656
2656
|
}
|
|
2657
2657
|
case "glob":
|
|
2658
|
-
return
|
|
2658
|
+
return fs15.glob(String(args.pattern ?? ""), { path: str(args.path), headLimit: num(args.head_limit ?? args.headLimit) });
|
|
2659
2659
|
case "grep":
|
|
2660
|
-
return
|
|
2660
|
+
return fs15.grep(String(args.pattern ?? ""), grepOptions(args));
|
|
2661
2661
|
case "find_symbol":
|
|
2662
|
-
return
|
|
2662
|
+
return fs15.findSymbol(String(args.symbol ?? ""), { language: str(args.language), path: str(args.path) });
|
|
2663
2663
|
case "list_dir":
|
|
2664
|
-
return
|
|
2664
|
+
return fs15.listDir(String(args.path ?? ""), num(args.depth));
|
|
2665
2665
|
case "bash":
|
|
2666
|
-
return
|
|
2666
|
+
return fs15.bash(String(args.command ?? ""), signal);
|
|
2667
2667
|
case "fetch": {
|
|
2668
2668
|
if (!fetcher) return { success: false, output: "Web fetching is not available in this environment.", truncated: false };
|
|
2669
2669
|
const url = String(args.url ?? "");
|
|
@@ -2771,14 +2771,14 @@ var PendingApprovals = class {
|
|
|
2771
2771
|
ask(request) {
|
|
2772
2772
|
const id = `ap-${Date.now()}-${counter++}`;
|
|
2773
2773
|
const pending = { id, request, createdAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
2774
|
-
return new Promise((
|
|
2774
|
+
return new Promise((resolve7) => {
|
|
2775
2775
|
const settle = (granted) => {
|
|
2776
2776
|
const entry = this.entries.get(id);
|
|
2777
2777
|
if (!entry) return;
|
|
2778
2778
|
clearTimeout(entry.timer);
|
|
2779
2779
|
this.entries.delete(id);
|
|
2780
2780
|
this.opts.onSettled?.(id, granted);
|
|
2781
|
-
|
|
2781
|
+
resolve7(granted);
|
|
2782
2782
|
};
|
|
2783
2783
|
const timer = setTimeout(() => settle(false), this.opts.timeoutMs ?? DEFAULT_TIMEOUT_MS);
|
|
2784
2784
|
timer.unref?.();
|
|
@@ -2876,9 +2876,9 @@ function applyHeadLimit(stdout, headLimit) {
|
|
|
2876
2876
|
}
|
|
2877
2877
|
function formatSearchOutput(capped, root, opts) {
|
|
2878
2878
|
if (capped.rows.length === 0) return opts.emptyMessage;
|
|
2879
|
-
const
|
|
2880
|
-
const prefix = root ===
|
|
2881
|
-
const body = prefix ===
|
|
2879
|
+
const sep2 = opts.sep ?? path3.sep;
|
|
2880
|
+
const prefix = root === sep2 || root.endsWith(sep2) ? root : `${root}${sep2}`;
|
|
2881
|
+
const body = prefix === sep2 ? capped.rows.join("\n") : capped.rows.map((row) => row.startsWith(prefix) ? row.slice(prefix.length) : row).join("\n");
|
|
2882
2882
|
if (!capped.truncated) return body;
|
|
2883
2883
|
const hint = opts.hint ? ` ${opts.hint}` : "";
|
|
2884
2884
|
return `${body}
|
|
@@ -3469,6 +3469,15 @@ function configuredProviders(config) {
|
|
|
3469
3469
|
return out;
|
|
3470
3470
|
}
|
|
3471
3471
|
|
|
3472
|
+
// src/utils/maxParallel.ts
|
|
3473
|
+
var DEFAULT_MAX_PARALLEL = 3;
|
|
3474
|
+
function parseMaxParallel(value) {
|
|
3475
|
+
const text = String(value ?? "").trim();
|
|
3476
|
+
const n = typeof value === "number" ? value : Number(text);
|
|
3477
|
+
if (text === "" || !Number.isInteger(n) || n < 1) return null;
|
|
3478
|
+
return n;
|
|
3479
|
+
}
|
|
3480
|
+
|
|
3472
3481
|
// src/interfaces/BaseConfig.ts
|
|
3473
3482
|
function normalizeGeminiModel(id) {
|
|
3474
3483
|
return id.replace(/^gemini:/, "").replace(/^google\//, "");
|
|
@@ -3673,7 +3682,7 @@ var BaseConfig = class {
|
|
|
3673
3682
|
return "";
|
|
3674
3683
|
}
|
|
3675
3684
|
get maxParallelSessions() {
|
|
3676
|
-
return
|
|
3685
|
+
return parseMaxParallel(process.env.ORDEWELL_MAX_PARALLEL) ?? DEFAULT_MAX_PARALLEL;
|
|
3677
3686
|
}
|
|
3678
3687
|
get researchEnabled() {
|
|
3679
3688
|
return process.env.ORDEWELL_RESEARCH_ENABLED !== "false";
|
|
@@ -3824,28 +3833,28 @@ function posixBinDirs() {
|
|
|
3824
3833
|
];
|
|
3825
3834
|
}
|
|
3826
3835
|
function windowsBinDirs(env, home) {
|
|
3827
|
-
const
|
|
3828
|
-
const under = (base, ...parts) => base ? [
|
|
3836
|
+
const join23 = (...parts) => path4.win32.join(...parts);
|
|
3837
|
+
const under = (base, ...parts) => base ? [join23(base, ...parts)] : [];
|
|
3829
3838
|
const appData = env.APPDATA;
|
|
3830
3839
|
const localAppData = env.LOCALAPPDATA;
|
|
3831
3840
|
const programData = env.ProgramData;
|
|
3832
3841
|
return [
|
|
3833
3842
|
// Native per-user installers, PowerShell-driven and otherwise.
|
|
3834
|
-
|
|
3835
|
-
|
|
3836
|
-
|
|
3837
|
-
|
|
3843
|
+
join23(home, ".local", "bin"),
|
|
3844
|
+
join23(home, ".opencode", "bin"),
|
|
3845
|
+
join23(home, ".claude", "bin"),
|
|
3846
|
+
join23(home, ".codex", "bin"),
|
|
3838
3847
|
...under(localAppData, "Programs", "claude"),
|
|
3839
3848
|
// Node package managers. npm's global prefix is `%APPDATA%\npm`; the
|
|
3840
3849
|
// literal spelling is kept as a fallback for a host that does not export
|
|
3841
3850
|
// APPDATA (a service account, a stripped CI container).
|
|
3842
3851
|
...under(appData, "npm"),
|
|
3843
|
-
|
|
3852
|
+
join23(home, "AppData", "Roaming", "npm"),
|
|
3844
3853
|
...under(localAppData, "pnpm"),
|
|
3845
3854
|
...under(localAppData, "Yarn", "bin"),
|
|
3846
|
-
|
|
3855
|
+
join23(home, ".bun", "bin"),
|
|
3847
3856
|
// Windows package managers.
|
|
3848
|
-
|
|
3857
|
+
join23(home, "scoop", "shims"),
|
|
3849
3858
|
...under(env.SCOOP, "shims"),
|
|
3850
3859
|
...under(env.SCOOP_GLOBAL ?? programData, "scoop", "shims"),
|
|
3851
3860
|
...under(programData, "chocolatey", "bin"),
|
|
@@ -3855,7 +3864,7 @@ function windowsBinDirs(env, home) {
|
|
|
3855
3864
|
...under(localAppData, "Microsoft", "WindowsApps"),
|
|
3856
3865
|
// Version managers. Volta's Windows home is not the POSIX `~/.volta`.
|
|
3857
3866
|
...under(localAppData, "Volta", "bin"),
|
|
3858
|
-
|
|
3867
|
+
join23(home, ".volta", "bin")
|
|
3859
3868
|
];
|
|
3860
3869
|
}
|
|
3861
3870
|
function wellKnownBinDirs(deps = {}) {
|
|
@@ -5836,12 +5845,12 @@ function buildConflictRepairPrompt(task, conflict, integrationBranch) {
|
|
|
5836
5845
|
const files = conflict.conflictFiles ?? [];
|
|
5837
5846
|
const asked = `What the task was asked to do:
|
|
5838
5847
|
${task.prompt ?? task.description}`;
|
|
5839
|
-
const
|
|
5848
|
+
const resolve7 = "resolve every conflict so that both sides' intent survives: keep the work already integrated and add what the task contributed.";
|
|
5840
5849
|
const repos = conflict.repos.filter((repo) => repo !== SELF_REPO);
|
|
5841
5850
|
if (repos.length === 0) {
|
|
5842
5851
|
return [
|
|
5843
5852
|
`Task #${task.order} "${task.title}" passed, but merging its branch \`${branch}\` into \`${integrationBranch}\` conflicted${files.length > 0 ? ` in ${files.join(", ")}` : ""}. None of its work has landed yet.`,
|
|
5844
|
-
`This is the task's own worktree, on \`${branch}\`, with its work committed. Run \`git merge --no-edit ${integrationBranch}\` here and ${
|
|
5853
|
+
`This is the task's own worktree, on \`${branch}\`, with its work committed. Run \`git merge --no-edit ${integrationBranch}\` here and ${resolve7} Never drop either side wholesale, and never abort the merge or reset it away.`,
|
|
5845
5854
|
"Build and test the result the way this project does, commit the merge, and only then print the completion marker.",
|
|
5846
5855
|
"",
|
|
5847
5856
|
asked
|
|
@@ -5850,7 +5859,7 @@ ${task.prompt ?? task.description}`;
|
|
|
5850
5859
|
const stoppedIn = conflict.conflictRepo ?? repos[0];
|
|
5851
5860
|
return [
|
|
5852
5861
|
`Task #${task.order} "${task.title}" passed, but landing its branch \`${branch}\` on \`${integrationBranch}\` conflicted in ${stoppedIn}${files.length > 0 ? ` (${files.join(", ")})` : ""}. A task lands in every repository it changed or in none, so none of its work has landed yet.`,
|
|
5853
|
-
`This is the task's own workspace, with each repository at its usual path on \`${branch}\` and the task's work committed. In each repository the task changed \u2014 ${repos.join(", ")} \u2014 run \`git merge --no-edit ${integrationBranch}\` inside that repository's directory, and ${
|
|
5862
|
+
`This is the task's own workspace, with each repository at its usual path on \`${branch}\` and the task's work committed. In each repository the task changed \u2014 ${repos.join(", ")} \u2014 run \`git merge --no-edit ${integrationBranch}\` inside that repository's directory, and ${resolve7} Never drop either side wholesale, and never abort a merge or reset it away.`,
|
|
5854
5863
|
"Build and test the result the way this project does, commit the merge in each repository, and only then print the completion marker.",
|
|
5855
5864
|
"",
|
|
5856
5865
|
asked
|
|
@@ -6186,8 +6195,8 @@ function compactResearchResults(resultsText, maxChars = COMPACTION_LIMITS.resear
|
|
|
6186
6195
|
|
|
6187
6196
|
// src/services/ContextCollector.ts
|
|
6188
6197
|
var ORDEWELL_CONTEXT_MAX = 8e3;
|
|
6189
|
-
async function collectResearchContext(
|
|
6190
|
-
const collector = new ContextCollector(
|
|
6198
|
+
async function collectResearchContext(fs15, runners) {
|
|
6199
|
+
const collector = new ContextCollector(fs15);
|
|
6191
6200
|
let contextStr = "";
|
|
6192
6201
|
let firstContext = null;
|
|
6193
6202
|
for (const r of runners) {
|
|
@@ -6214,8 +6223,8 @@ ${firstContext.dirStructure}
|
|
|
6214
6223
|
return contextStr;
|
|
6215
6224
|
}
|
|
6216
6225
|
var ContextCollector = class {
|
|
6217
|
-
constructor(
|
|
6218
|
-
this.fs =
|
|
6226
|
+
constructor(fs15, registry) {
|
|
6227
|
+
this.fs = fs15;
|
|
6219
6228
|
this.registry = registry;
|
|
6220
6229
|
}
|
|
6221
6230
|
fs;
|
|
@@ -6263,22 +6272,22 @@ var ALLOWED_TOOL_NAMES = new Set(subagentToolSpecs().map((t) => t.name));
|
|
|
6263
6272
|
function refused(output) {
|
|
6264
6273
|
return { success: false, output, truncated: false };
|
|
6265
6274
|
}
|
|
6266
|
-
function nonPromptingFs(
|
|
6267
|
-
const withinWorkspace = (p) => resolveWithin(
|
|
6275
|
+
function nonPromptingFs(fs15) {
|
|
6276
|
+
const withinWorkspace = (p) => resolveWithin(fs15.getWorkspaceRoot(), p ?? ".").inside;
|
|
6268
6277
|
const outsideRefusal = (p) => refused(
|
|
6269
6278
|
`Path "${p}" is outside the workspace. Research agents are confined to the workspace root \u2014 note the gap in your digest and let the planner request it directly.`
|
|
6270
6279
|
);
|
|
6271
6280
|
return {
|
|
6272
|
-
getWorkspaceRoot: () =>
|
|
6273
|
-
readFile: (p, opts) => withinWorkspace(p) ?
|
|
6281
|
+
getWorkspaceRoot: () => fs15.getWorkspaceRoot(),
|
|
6282
|
+
readFile: (p, opts) => withinWorkspace(p) ? fs15.readFile(p, opts) : Promise.resolve(outsideRefusal(p)),
|
|
6274
6283
|
readFiles: (paths) => {
|
|
6275
6284
|
const escaping = paths.find((p) => !withinWorkspace(p));
|
|
6276
|
-
return escaping ? Promise.resolve(outsideRefusal(escaping)) :
|
|
6285
|
+
return escaping ? Promise.resolve(outsideRefusal(escaping)) : fs15.readFiles(paths);
|
|
6277
6286
|
},
|
|
6278
|
-
glob: (pattern, opts) => withinWorkspace(opts?.path) ?
|
|
6279
|
-
grep: (pattern, opts) => withinWorkspace(opts?.path) ?
|
|
6280
|
-
findSymbol: (symbol, opts) => withinWorkspace(opts?.path) ?
|
|
6281
|
-
listDir: (p, depth) => withinWorkspace(p) ?
|
|
6287
|
+
glob: (pattern, opts) => withinWorkspace(opts?.path) ? fs15.glob(pattern, opts) : Promise.resolve(outsideRefusal(opts.path)),
|
|
6288
|
+
grep: (pattern, opts) => withinWorkspace(opts?.path) ? fs15.grep(pattern, opts) : Promise.resolve(outsideRefusal(opts.path)),
|
|
6289
|
+
findSymbol: (symbol, opts) => withinWorkspace(opts?.path) ? fs15.findSymbol(symbol, opts) : Promise.resolve(outsideRefusal(opts.path)),
|
|
6290
|
+
listDir: (p, depth) => withinWorkspace(p) ? fs15.listDir(p, depth) : Promise.resolve(outsideRefusal(p)),
|
|
6282
6291
|
bash: async (command, signal) => {
|
|
6283
6292
|
const dialect = resolveResearchShell().dialect;
|
|
6284
6293
|
const { tier, reason } = classifyCommand(command, { dialect });
|
|
@@ -6289,13 +6298,13 @@ function nonPromptingFs(fs14) {
|
|
|
6289
6298
|
}
|
|
6290
6299
|
const escaping = pathLikeArgs(command, { dialect }).find((p) => !withinWorkspace(p));
|
|
6291
6300
|
if (escaping) return outsideRefusal(escaping);
|
|
6292
|
-
return
|
|
6301
|
+
return fs15.bash(command, signal);
|
|
6293
6302
|
}
|
|
6294
6303
|
};
|
|
6295
6304
|
}
|
|
6296
6305
|
async function runLoop(prompt, deps) {
|
|
6297
6306
|
if (deps.signal?.aborted) return "[research agent aborted before starting]";
|
|
6298
|
-
const
|
|
6307
|
+
const fs15 = nonPromptingFs(deps.fs);
|
|
6299
6308
|
const chat = deps.createChat((delta) => deps.onProgress?.({ type: "thinking", text: delta }));
|
|
6300
6309
|
let turn = await chat.sendMessage(prompt, deps.signal);
|
|
6301
6310
|
for (let step = 0; turn.hasToolCalls && step < SUBAGENT_LIMITS.maxSteps; step++) {
|
|
@@ -6312,7 +6321,7 @@ ${turn.text}`;
|
|
|
6312
6321
|
if (tc.name === "read_file" && !("maxBytes" in args) && !("limit" in args)) args.limit = 2e3;
|
|
6313
6322
|
const toolArgs = JSON.stringify(tc.args);
|
|
6314
6323
|
deps.onProgress?.({ type: "tool_call", tool: tc.name, toolArgs, toolCallId: tc.id });
|
|
6315
|
-
const res = await executeTool(tc.name, args,
|
|
6324
|
+
const res = await executeTool(tc.name, args, fs15, void 0, deps.signal);
|
|
6316
6325
|
const output = res.output.length > SUBAGENT_LIMITS.toolOutputMaxChars ? res.output.slice(0, SUBAGENT_LIMITS.toolOutputMaxChars) + `
|
|
6317
6326
|
[... truncated to ${SUBAGENT_LIMITS.toolOutputMaxChars} chars, total ${res.output.length}]` : res.output;
|
|
6318
6327
|
const stepEntry = {
|
|
@@ -6428,7 +6437,7 @@ var BaseAiService = class _BaseAiService {
|
|
|
6428
6437
|
* steering/failed result and the loop continues sequentially: a subagent can
|
|
6429
6438
|
* never fail a plan or a turn.
|
|
6430
6439
|
*/
|
|
6431
|
-
async executeSpawnAgent(args,
|
|
6440
|
+
async executeSpawnAgent(args, fs15, onProgress, signal, subagentId) {
|
|
6432
6441
|
const prompt = typeof args.prompt === "string" ? args.prompt.trim() : "";
|
|
6433
6442
|
if (!prompt) {
|
|
6434
6443
|
return { success: false, output: 'spawn_research_agent requires a non-empty "prompt" string: a self-contained task for the agent, including what its digest must report back.', truncated: false };
|
|
@@ -6439,20 +6448,20 @@ var BaseAiService = class _BaseAiService {
|
|
|
6439
6448
|
if (!chat) throw new Error("subagent chats are not available for this provider");
|
|
6440
6449
|
return chat;
|
|
6441
6450
|
},
|
|
6442
|
-
fs:
|
|
6451
|
+
fs: fs15,
|
|
6443
6452
|
signal,
|
|
6444
6453
|
onProgress: (progress) => onProgress({ ...progress, subagentId })
|
|
6445
6454
|
});
|
|
6446
6455
|
}
|
|
6447
6456
|
/** Collect project context for the planning phase. Shared with the harness backend. */
|
|
6448
|
-
static async collectResearchContext(
|
|
6449
|
-
return collectResearchContext(
|
|
6457
|
+
static async collectResearchContext(fs15, runners) {
|
|
6458
|
+
return collectResearchContext(fs15, runners);
|
|
6450
6459
|
}
|
|
6451
6460
|
/**
|
|
6452
6461
|
* Execute one round of tool calls and report each through onProgress.
|
|
6453
6462
|
* Returns the results to feed back plus the log entries produced.
|
|
6454
6463
|
*/
|
|
6455
|
-
async executeToolCalls(toolCalls,
|
|
6464
|
+
async executeToolCalls(toolCalls, fs15, onProgress, fetcher, stepIndex, thinking, signal) {
|
|
6456
6465
|
const toolResults = [];
|
|
6457
6466
|
const logEntries = [];
|
|
6458
6467
|
let resultsText = "";
|
|
@@ -6471,18 +6480,18 @@ var BaseAiService = class _BaseAiService {
|
|
|
6471
6480
|
const subagentId = uuidv4();
|
|
6472
6481
|
spawnSubagentIds.set(i, subagentId);
|
|
6473
6482
|
onProgress({ type: "tool_call", tool: toolCalls[i].name, toolArgs: JSON.stringify(toolCalls[i].args), subagentId, toolCallId: toolCalls[i].id });
|
|
6474
|
-
precomputed.set(i, await this.executeSpawnAgent({ ...toolCalls[i].args },
|
|
6483
|
+
precomputed.set(i, await this.executeSpawnAgent({ ...toolCalls[i].args }, fs15, onProgress, signal, subagentId));
|
|
6475
6484
|
}) : Promise.resolve(),
|
|
6476
6485
|
parallelIndexes.length > 1 ? mapWithConcurrency(parallelIndexes, TOOL_ROUND_CONCURRENCY, async (i) => {
|
|
6477
6486
|
onProgress({ type: "tool_call", tool: toolCalls[i].name, toolArgs: JSON.stringify(toolCalls[i].args), toolCallId: toolCalls[i].id });
|
|
6478
|
-
precomputed.set(i, await executeTool(toolCalls[i].name, normalizedArgs[i],
|
|
6487
|
+
precomputed.set(i, await executeTool(toolCalls[i].name, normalizedArgs[i], fs15, fetcher, signal));
|
|
6479
6488
|
}) : Promise.resolve()
|
|
6480
6489
|
]);
|
|
6481
6490
|
for (const [index, tc] of toolCalls.entries()) {
|
|
6482
6491
|
const toolArgs = normalizedArgs[index];
|
|
6483
6492
|
const preResult = precomputed.get(index);
|
|
6484
6493
|
if (!preResult) onProgress({ type: "tool_call", tool: tc.name, toolArgs: JSON.stringify(tc.args), toolCallId: tc.id });
|
|
6485
|
-
const toolResult = preResult ?? await executeTool(tc.name, toolArgs,
|
|
6494
|
+
const toolResult = preResult ?? await executeTool(tc.name, toolArgs, fs15, fetcher, signal);
|
|
6486
6495
|
const MAX_RESPONSE_CHARS = 5e4;
|
|
6487
6496
|
const truncatedResponse = toolResult.success && (toolResult.truncated || toolResult.output.length > MAX_RESPONSE_CHARS);
|
|
6488
6497
|
const llmOutput = toolResult.output.length > MAX_RESPONSE_CHARS ? toolResult.output.slice(0, MAX_RESPONSE_CHARS) + `
|
|
@@ -6668,7 +6677,7 @@ ${llmOutput}
|
|
|
6668
6677
|
* parsed tasks if a plan was emitted mid-loop, or null if the loop
|
|
6669
6678
|
* exhausted without a valid plan.
|
|
6670
6679
|
*/
|
|
6671
|
-
async runResearchLoop(chat, firstMessage,
|
|
6680
|
+
async runResearchLoop(chat, firstMessage, fs15, onProgress, runners, initialLog, fetcher, userGoal, runnerModes, autonomousDefault, signal) {
|
|
6672
6681
|
const researchLog = initialLog ? [...initialLog] : [];
|
|
6673
6682
|
if (!initialLog && userGoal) {
|
|
6674
6683
|
researchLog.push({
|
|
@@ -6695,7 +6704,7 @@ ${llmOutput}
|
|
|
6695
6704
|
if (!turn.hasToolCalls) break;
|
|
6696
6705
|
const { toolResults, logEntries, resultsText } = await this.executeToolCalls(
|
|
6697
6706
|
turn.toolCalls,
|
|
6698
|
-
|
|
6707
|
+
fs15,
|
|
6699
6708
|
onProgress,
|
|
6700
6709
|
fetcher,
|
|
6701
6710
|
step,
|
|
@@ -6930,11 +6939,11 @@ ${repairHint}` }] : parts;
|
|
|
6930
6939
|
return this.consumePlanStream(result, onToken);
|
|
6931
6940
|
}
|
|
6932
6941
|
// --- IAiService implementation ---
|
|
6933
|
-
async researchAndPlan(userDescription, runners, modelsByRunner,
|
|
6942
|
+
async researchAndPlan(userDescription, runners, modelsByRunner, fs15, onProgress, fetcher, runnerModes, modes = DEFAULT_PLANNER_MODES, signal) {
|
|
6934
6943
|
this.ensureInit();
|
|
6935
6944
|
const { autonomousDefault } = modes;
|
|
6936
6945
|
const genModel = this.getPlanningModel();
|
|
6937
|
-
const contextStr = await BaseAiService.collectResearchContext(
|
|
6946
|
+
const contextStr = await BaseAiService.collectResearchContext(fs15, runners);
|
|
6938
6947
|
const systemPrompt = buildResearchPrompt(userDescription, contextStr, modelsByRunner, runners, runnerModes, modes);
|
|
6939
6948
|
const firstMessage = `${buildResearchToolsPrompt()}
|
|
6940
6949
|
|
|
@@ -6945,17 +6954,17 @@ Explore the workspace to understand the codebase, then generate the plan.`;
|
|
|
6945
6954
|
generationConfig: { temperature: 0.3, topP: 0.95, maxOutputTokens: 16384 }
|
|
6946
6955
|
});
|
|
6947
6956
|
const researchChat = new GeminiResearchChat(chat);
|
|
6948
|
-
const result = await this.runResearchLoop(researchChat, firstMessage,
|
|
6957
|
+
const result = await this.runResearchLoop(researchChat, firstMessage, fs15, onProgress, runners, void 0, fetcher, userDescription, runnerModes, autonomousDefault, signal);
|
|
6949
6958
|
if (result.tasks) return { tasks: result.tasks, researchLog: result.researchLog, researchResults: result.researchResults };
|
|
6950
6959
|
const fallback = await this.generatePlanFallback(userDescription, contextStr, result.researchResults, modelsByRunner, runners, onProgress, result.researchLog, runnerModes, autonomousDefault, signal, modes);
|
|
6951
6960
|
return { tasks: fallback.tasks, researchLog: fallback.researchLog, researchResults: result.researchResults };
|
|
6952
6961
|
}
|
|
6953
|
-
async generatePlanDirect(userDescription, runners = ["claude-code"], modelsByRunner = {}, onToken,
|
|
6962
|
+
async generatePlanDirect(userDescription, runners = ["claude-code"], modelsByRunner = {}, onToken, fs15, _fetcher, runnerModes, modes = DEFAULT_PLANNER_MODES, _signal) {
|
|
6954
6963
|
this.ensureInit();
|
|
6955
6964
|
const { autonomousDefault } = modes;
|
|
6956
6965
|
let ctx = "";
|
|
6957
|
-
if (
|
|
6958
|
-
const collected = await new ContextCollector(
|
|
6966
|
+
if (fs15) {
|
|
6967
|
+
const collected = await new ContextCollector(fs15).collect(runners[0] ?? "claude-code");
|
|
6959
6968
|
if (collected.aiflowContext) ctx = `
|
|
6960
6969
|
<aiflow_context>
|
|
6961
6970
|
${collected.aiflowContext}
|
|
@@ -6971,12 +6980,12 @@ ${collected.aiflowContext}
|
|
|
6971
6980
|
autonomousDefault
|
|
6972
6981
|
);
|
|
6973
6982
|
}
|
|
6974
|
-
async modifyPlan(existingPlan, userRequest, modelsByRunner, onProgress,
|
|
6983
|
+
async modifyPlan(existingPlan, userRequest, modelsByRunner, onProgress, fs15, _fetcher, runnerModes, modes = DEFAULT_PLANNER_MODES, _signal) {
|
|
6975
6984
|
this.ensureInit();
|
|
6976
6985
|
const { autonomousDefault } = modes;
|
|
6977
6986
|
let aiflowContext;
|
|
6978
|
-
if (
|
|
6979
|
-
const collected = await new ContextCollector(
|
|
6987
|
+
if (fs15) {
|
|
6988
|
+
const collected = await new ContextCollector(fs15).collect(existingPlan.runners[0] ?? "claude-code");
|
|
6980
6989
|
if (collected.aiflowContext) aiflowContext = collected.aiflowContext;
|
|
6981
6990
|
}
|
|
6982
6991
|
const prompt = buildModifyPlanPrompt(existingPlan, userRequest, modelsByRunner, aiflowContext, runnerModes, autonomousDefault);
|
|
@@ -7212,11 +7221,11 @@ ${buildResearchToolsPrompt()}` }
|
|
|
7212
7221
|
}
|
|
7213
7222
|
}
|
|
7214
7223
|
// --- IAiService implementation ---
|
|
7215
|
-
async researchAndPlan(userDescription, runners, modelsByRunner,
|
|
7224
|
+
async researchAndPlan(userDescription, runners, modelsByRunner, fs15, onProgress, fetcher, runnerModes, modes = DEFAULT_PLANNER_MODES, signal) {
|
|
7216
7225
|
this.ensureInit();
|
|
7217
7226
|
const client = this.getClient();
|
|
7218
7227
|
const { autonomousDefault } = modes;
|
|
7219
|
-
const contextStr = await BaseAiService.collectResearchContext(
|
|
7228
|
+
const contextStr = await BaseAiService.collectResearchContext(fs15, runners);
|
|
7220
7229
|
const systemPrompt = buildResearchPrompt(userDescription, contextStr, modelsByRunner, runners, runnerModes, modes);
|
|
7221
7230
|
const userText = `User goal: ${userDescription}`;
|
|
7222
7231
|
const firstMessage = `${buildResearchToolsPrompt()}
|
|
@@ -7234,17 +7243,17 @@ ${userText}`;
|
|
|
7234
7243
|
toOpenAiTools(),
|
|
7235
7244
|
(delta) => onProgress({ type: "thinking", text: delta })
|
|
7236
7245
|
);
|
|
7237
|
-
const result = await this.runResearchLoop(researchChat, firstMessage,
|
|
7246
|
+
const result = await this.runResearchLoop(researchChat, firstMessage, fs15, onProgress, runners, void 0, fetcher, userDescription, runnerModes, autonomousDefault, signal);
|
|
7238
7247
|
if (result.tasks) return { tasks: result.tasks, researchLog: result.researchLog, researchResults: result.researchResults };
|
|
7239
7248
|
const fallback = await this.generatePlanFallback(userDescription, contextStr, result.researchResults, modelsByRunner, runners, onProgress, result.researchLog, runnerModes, autonomousDefault, signal, modes);
|
|
7240
7249
|
return { tasks: fallback.tasks, researchLog: fallback.researchLog, researchResults: result.researchResults };
|
|
7241
7250
|
}
|
|
7242
|
-
async generatePlanDirect(userDescription, runners = ["claude-code"], modelsByRunner = {}, onToken,
|
|
7251
|
+
async generatePlanDirect(userDescription, runners = ["claude-code"], modelsByRunner = {}, onToken, fs15, _fetcher, runnerModes, modes = DEFAULT_PLANNER_MODES, signal) {
|
|
7243
7252
|
this.ensureInit();
|
|
7244
7253
|
const { autonomousDefault } = modes;
|
|
7245
7254
|
let ctx = "";
|
|
7246
|
-
if (
|
|
7247
|
-
const collected = await new ContextCollector(
|
|
7255
|
+
if (fs15) {
|
|
7256
|
+
const collected = await new ContextCollector(fs15).collect(runners[0] ?? "claude-code");
|
|
7248
7257
|
if (collected.aiflowContext) ctx = `
|
|
7249
7258
|
<aiflow_context>
|
|
7250
7259
|
${collected.aiflowContext}
|
|
@@ -7260,12 +7269,12 @@ ${collected.aiflowContext}
|
|
|
7260
7269
|
autonomousDefault
|
|
7261
7270
|
);
|
|
7262
7271
|
}
|
|
7263
|
-
async modifyPlan(existingPlan, userRequest, modelsByRunner, onProgress,
|
|
7272
|
+
async modifyPlan(existingPlan, userRequest, modelsByRunner, onProgress, fs15, _fetcher, runnerModes, modes = DEFAULT_PLANNER_MODES, signal) {
|
|
7264
7273
|
this.ensureInit();
|
|
7265
7274
|
const { autonomousDefault } = modes;
|
|
7266
7275
|
let aiflowContext;
|
|
7267
|
-
if (
|
|
7268
|
-
const collected = await new ContextCollector(
|
|
7276
|
+
if (fs15) {
|
|
7277
|
+
const collected = await new ContextCollector(fs15).collect(existingPlan.runners[0] ?? "claude-code");
|
|
7269
7278
|
if (collected.aiflowContext) aiflowContext = collected.aiflowContext;
|
|
7270
7279
|
}
|
|
7271
7280
|
const prompt = buildModifyPlanPrompt(existingPlan, userRequest, modelsByRunner, aiflowContext, runnerModes, autonomousDefault);
|
|
@@ -7284,6 +7293,9 @@ ${collected.aiflowContext}
|
|
|
7284
7293
|
}
|
|
7285
7294
|
};
|
|
7286
7295
|
|
|
7296
|
+
// src/services/TaskOrchestrator.ts
|
|
7297
|
+
import * as path11 from "path";
|
|
7298
|
+
|
|
7287
7299
|
// src/services/promptAugment.ts
|
|
7288
7300
|
var MAX_TAIL_CHARS = 500;
|
|
7289
7301
|
var DEFAULT_PLAN_MAP_MAX_ENTRIES = 30;
|
|
@@ -7869,18 +7881,230 @@ ${line}
|
|
|
7869
7881
|
};
|
|
7870
7882
|
|
|
7871
7883
|
// src/services/transcriptCapture.ts
|
|
7872
|
-
import { readFileSync as
|
|
7884
|
+
import { readFileSync as readFileSync3, readdirSync as readdirSync3, statSync as statSync5, existsSync as existsSync4 } from "fs";
|
|
7885
|
+
import * as os4 from "os";
|
|
7886
|
+
import * as path10 from "path";
|
|
7887
|
+
|
|
7888
|
+
// src/services/workspaceEnv.ts
|
|
7889
|
+
import { execFile as execFile2 } from "child_process";
|
|
7890
|
+
import * as fs5 from "fs";
|
|
7873
7891
|
import * as os3 from "os";
|
|
7874
7892
|
import * as path9 from "path";
|
|
7893
|
+
|
|
7894
|
+
// src/services/settingsEnvAllowlist.ts
|
|
7895
|
+
var ORDEWELL_SETTABLE_ENV = [
|
|
7896
|
+
"AI_PROVIDER",
|
|
7897
|
+
"ORCHESTRATOR_MODEL",
|
|
7898
|
+
"ORDEWELL_SUBAGENT_MODEL",
|
|
7899
|
+
"ORDEWELL_PLANNER_EFFORT",
|
|
7900
|
+
"ORDEWELL_RESEARCH_ENABLED",
|
|
7901
|
+
"ORDEWELL_MAX_PARALLEL",
|
|
7902
|
+
"ORDEWELL_CONFLICT_REPAIR_ATTEMPTS"
|
|
7903
|
+
];
|
|
7904
|
+
var SETTINGS_ENV_REFUSED = [
|
|
7905
|
+
// Approval posture and pre-approved scope.
|
|
7906
|
+
"ORDEWELL_AUTONOMOUS_MODE",
|
|
7907
|
+
"ORDEWELL_APPROVAL_MODE",
|
|
7908
|
+
"ORDEWELL_APPROVAL_ALLOW",
|
|
7909
|
+
// Where settings are persisted.
|
|
7910
|
+
"ORDEWELL_SETTINGS_PATH",
|
|
7911
|
+
// Runtime options and dynamic loaders.
|
|
7912
|
+
"NODE_OPTIONS",
|
|
7913
|
+
"NODE_REPL_EXTERNAL_MODULE",
|
|
7914
|
+
"NODE_PATH",
|
|
7915
|
+
"ELECTRON_RUN_AS_NODE",
|
|
7916
|
+
"LD_PRELOAD",
|
|
7917
|
+
"LD_LIBRARY_PATH",
|
|
7918
|
+
"LD_AUDIT",
|
|
7919
|
+
"DYLD_INSERT_LIBRARIES",
|
|
7920
|
+
"DYLD_LIBRARY_PATH",
|
|
7921
|
+
"DYLD_FRAMEWORK_PATH",
|
|
7922
|
+
// Executable resolution.
|
|
7923
|
+
"PATH",
|
|
7924
|
+
"PATHEXT",
|
|
7925
|
+
"NODE"
|
|
7926
|
+
];
|
|
7927
|
+
var REFUSED = new Set(SETTINGS_ENV_REFUSED);
|
|
7928
|
+
function providerEnvKeys() {
|
|
7929
|
+
const keys = [];
|
|
7930
|
+
for (const meta of Object.values(ALL_PROVIDERS)) {
|
|
7931
|
+
if (meta.apiKeyEnvVar) keys.push(meta.apiKeyEnvVar);
|
|
7932
|
+
if (meta.baseUrlEnvVar) keys.push(meta.baseUrlEnvVar);
|
|
7933
|
+
for (const detect of meta.detectEnvVars) keys.push(detect);
|
|
7934
|
+
}
|
|
7935
|
+
return keys;
|
|
7936
|
+
}
|
|
7937
|
+
var PROVIDER_CREDENTIAL_ENV = new Set(providerEnvKeys());
|
|
7938
|
+
var SETTINGS_ENV_ALLOWLIST = new Set(
|
|
7939
|
+
[...providerEnvKeys(), ...ORDEWELL_SETTABLE_ENV].filter((key) => !REFUSED.has(key))
|
|
7940
|
+
);
|
|
7941
|
+
function admitSettingsEnv(env) {
|
|
7942
|
+
const accepted = {};
|
|
7943
|
+
const rejected = [];
|
|
7944
|
+
for (const [key, value] of Object.entries(env)) {
|
|
7945
|
+
if (REFUSED.has(key) || !SETTINGS_ENV_ALLOWLIST.has(key) || typeof value !== "string") {
|
|
7946
|
+
rejected.push(key);
|
|
7947
|
+
continue;
|
|
7948
|
+
}
|
|
7949
|
+
accepted[key] = value;
|
|
7950
|
+
}
|
|
7951
|
+
return { accepted, rejected };
|
|
7952
|
+
}
|
|
7953
|
+
|
|
7954
|
+
// src/services/workspaceEnv.ts
|
|
7955
|
+
var WORKSPACE_ENV_FILE = "env";
|
|
7956
|
+
var DIRENV_TIMEOUT_MS = 5e3;
|
|
7957
|
+
var KEY = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
7958
|
+
function defaultDeps() {
|
|
7959
|
+
return {
|
|
7960
|
+
run: (command, args, opts) => new Promise((resolve7) => {
|
|
7961
|
+
execFile2(command, args, { ...opts, timeout: DIRENV_TIMEOUT_MS, encoding: "utf8" }, (err, stdout, stderr) => {
|
|
7962
|
+
const code = err ? typeof err.code === "number" ? Number(err.code) : -1 : 0;
|
|
7963
|
+
resolve7({ code, stdout: String(stdout ?? ""), stderr: String(stderr ?? "") });
|
|
7964
|
+
});
|
|
7965
|
+
}),
|
|
7966
|
+
readFile: (file) => {
|
|
7967
|
+
try {
|
|
7968
|
+
return fs5.readFileSync(file, "utf8");
|
|
7969
|
+
} catch {
|
|
7970
|
+
return null;
|
|
7971
|
+
}
|
|
7972
|
+
},
|
|
7973
|
+
isDirectory: (dir) => {
|
|
7974
|
+
try {
|
|
7975
|
+
return fs5.statSync(dir).isDirectory();
|
|
7976
|
+
} catch {
|
|
7977
|
+
return false;
|
|
7978
|
+
}
|
|
7979
|
+
},
|
|
7980
|
+
baseEnv: process.env,
|
|
7981
|
+
home: os3.homedir()
|
|
7982
|
+
};
|
|
7983
|
+
}
|
|
7984
|
+
var REFUSED2 = new Set(SETTINGS_ENV_REFUSED);
|
|
7985
|
+
async function resolveWorkspaceEnv(cwd, overrides = {}) {
|
|
7986
|
+
const deps = { ...defaultDeps(), ...overrides };
|
|
7987
|
+
const result = { env: {}, blockedEnvrc: null, refused: [], trackedEnvFile: null };
|
|
7988
|
+
if (!deps.isDirectory(cwd)) return result;
|
|
7989
|
+
const collected = {};
|
|
7990
|
+
const direnv = await fromDirenv(checkoutDirOf(cwd, deps), deps);
|
|
7991
|
+
result.blockedEnvrc = direnv.blocked;
|
|
7992
|
+
Object.assign(collected, direnv.env);
|
|
7993
|
+
const file = findUp(cwd, path9.join(STATE_DIR, WORKSPACE_ENV_FILE), deps);
|
|
7994
|
+
if (file) {
|
|
7995
|
+
const content = deps.readFile(file);
|
|
7996
|
+
if (content !== null) {
|
|
7997
|
+
if (await isTracked(file, deps)) result.trackedEnvFile = file;
|
|
7998
|
+
else Object.assign(collected, parseEnvFile(content, { ...deps.baseEnv, ...collected }, deps.home));
|
|
7999
|
+
}
|
|
8000
|
+
}
|
|
8001
|
+
for (const [key, value] of Object.entries(collected)) {
|
|
8002
|
+
if (!KEY.test(key)) continue;
|
|
8003
|
+
if (REFUSED2.has(key) || key.toUpperCase() === "PATH") result.refused.push(key);
|
|
8004
|
+
else result.env[key] = value;
|
|
8005
|
+
}
|
|
8006
|
+
return result;
|
|
8007
|
+
}
|
|
8008
|
+
async function workspaceEnvOf(cwd) {
|
|
8009
|
+
return (await resolveWorkspaceEnv(cwd)).env;
|
|
8010
|
+
}
|
|
8011
|
+
function checkoutDirOf(cwd, deps) {
|
|
8012
|
+
const parts = path9.resolve(cwd).split(path9.sep);
|
|
8013
|
+
const at = parts.findIndex((part, i) => part === STATE_DIR && parts[i + 1] === "worktrees");
|
|
8014
|
+
if (at < 0 || parts.length < at + 4) return cwd;
|
|
8015
|
+
const root = parts.slice(0, at).join(path9.sep) || path9.sep;
|
|
8016
|
+
const mapped = path9.join(root, ...parts.slice(at + 4));
|
|
8017
|
+
return deps.isDirectory(mapped) ? mapped : root;
|
|
8018
|
+
}
|
|
8019
|
+
async function fromDirenv(cwd, deps) {
|
|
8020
|
+
const setting = deps.baseEnv.ORDEWELL_DIRENV;
|
|
8021
|
+
if (setting === "false" || setting === "0") return { env: {}, blocked: null };
|
|
8022
|
+
const baseEnv = Object.fromEntries(Object.entries(deps.baseEnv).filter(([key]) => !key.startsWith("DIRENV_")));
|
|
8023
|
+
let out;
|
|
8024
|
+
try {
|
|
8025
|
+
out = await deps.run("direnv", ["export", "json"], { cwd, env: baseEnv });
|
|
8026
|
+
} catch {
|
|
8027
|
+
return { env: {}, blocked: null };
|
|
8028
|
+
}
|
|
8029
|
+
const blocked = out.stderr.match(/error (\S*\.envrc) is blocked/);
|
|
8030
|
+
if (blocked) return { env: {}, blocked: blocked[1] };
|
|
8031
|
+
if (out.code !== 0 || !out.stdout.trim()) return { env: {}, blocked: null };
|
|
8032
|
+
let parsed;
|
|
8033
|
+
try {
|
|
8034
|
+
parsed = JSON.parse(out.stdout);
|
|
8035
|
+
} catch {
|
|
8036
|
+
return { env: {}, blocked: null };
|
|
8037
|
+
}
|
|
8038
|
+
if (!parsed || typeof parsed !== "object") return { env: {}, blocked: null };
|
|
8039
|
+
const env = {};
|
|
8040
|
+
for (const [key, value] of Object.entries(parsed)) {
|
|
8041
|
+
if (typeof value === "string" && !key.startsWith("DIRENV_")) env[key] = value;
|
|
8042
|
+
}
|
|
8043
|
+
return { env, blocked: null };
|
|
8044
|
+
}
|
|
8045
|
+
function findUp(start, relative5, deps) {
|
|
8046
|
+
let dir = path9.resolve(start);
|
|
8047
|
+
for (; ; ) {
|
|
8048
|
+
const candidate = path9.join(dir, relative5);
|
|
8049
|
+
if (deps.readFile(candidate) !== null) return candidate;
|
|
8050
|
+
const parent = path9.dirname(dir);
|
|
8051
|
+
if (parent === dir) return null;
|
|
8052
|
+
dir = parent;
|
|
8053
|
+
}
|
|
8054
|
+
}
|
|
8055
|
+
async function isTracked(file, deps) {
|
|
8056
|
+
try {
|
|
8057
|
+
const out = await deps.run("git", ["ls-files", "--error-unmatch", path9.basename(file)], { cwd: path9.dirname(file), env: deps.baseEnv });
|
|
8058
|
+
return out.code === 0;
|
|
8059
|
+
} catch {
|
|
8060
|
+
return false;
|
|
8061
|
+
}
|
|
8062
|
+
}
|
|
8063
|
+
function parseEnvFile(content, context, home) {
|
|
8064
|
+
const env = {};
|
|
8065
|
+
const lookup2 = (name) => env[name] ?? context[name] ?? "";
|
|
8066
|
+
const expand = (value) => {
|
|
8067
|
+
const tilde = value.startsWith("~/") ? path9.join(home, value.slice(2)) : value;
|
|
8068
|
+
return tilde.replace(/\$\{([A-Za-z_][A-Za-z0-9_]*)\}|\$([A-Za-z_][A-Za-z0-9_]*)/g, (_, braced, bare) => lookup2(braced ?? bare));
|
|
8069
|
+
};
|
|
8070
|
+
for (const raw of content.split(/\r?\n/)) {
|
|
8071
|
+
const line = raw.trim().replace(/^export\s+/, "");
|
|
8072
|
+
if (!line || line.startsWith("#")) continue;
|
|
8073
|
+
const eq = line.indexOf("=");
|
|
8074
|
+
if (eq <= 0) continue;
|
|
8075
|
+
const key = line.slice(0, eq).trim();
|
|
8076
|
+
if (!KEY.test(key)) continue;
|
|
8077
|
+
const rest = line.slice(eq + 1).trim();
|
|
8078
|
+
if (rest.startsWith("'")) {
|
|
8079
|
+
const end = rest.indexOf("'", 1);
|
|
8080
|
+
env[key] = end > 0 ? rest.slice(1, end) : rest.slice(1);
|
|
8081
|
+
} else if (rest.startsWith('"')) {
|
|
8082
|
+
const end = rest.lastIndexOf('"');
|
|
8083
|
+
env[key] = expand((end > 0 ? rest.slice(1, end) : rest.slice(1)).replace(/\\n/g, "\n").replace(/\\"/g, '"'));
|
|
8084
|
+
} else {
|
|
8085
|
+
env[key] = expand(rest.replace(/\s+#.*$/, ""));
|
|
8086
|
+
}
|
|
8087
|
+
}
|
|
8088
|
+
return env;
|
|
8089
|
+
}
|
|
8090
|
+
|
|
8091
|
+
// src/services/transcriptCapture.ts
|
|
7875
8092
|
var HomeTranscriptReader = class {
|
|
7876
8093
|
home;
|
|
8094
|
+
claudeConfigDirs;
|
|
7877
8095
|
constructor(opts = {}) {
|
|
7878
8096
|
const { homeDir } = opts;
|
|
7879
|
-
this.home = homeDir ? () => homeDir : () => process.env.HOME ||
|
|
8097
|
+
this.home = homeDir ? () => homeDir : () => process.env.HOME || os4.homedir();
|
|
8098
|
+
const workspaceEnv = opts.workspaceEnv ?? workspaceEnvOf;
|
|
8099
|
+
this.claudeConfigDirs = async (cwd) => [...new Set([
|
|
8100
|
+
(await workspaceEnv(cwd)).CLAUDE_CONFIG_DIR,
|
|
8101
|
+
homeDir ? void 0 : process.env.CLAUDE_CONFIG_DIR,
|
|
8102
|
+
path10.join(this.home(), ".claude")
|
|
8103
|
+
].filter((dir) => Boolean(dir)))];
|
|
7880
8104
|
}
|
|
7881
8105
|
async finalAssistantText(query, maxChars = 4e3) {
|
|
7882
8106
|
try {
|
|
7883
|
-
if (query.runner === "claude-code") return claudeFinal(this.
|
|
8107
|
+
if (query.runner === "claude-code") return claudeFinal(await this.claudeConfigDirs(query.cwd), query, maxChars);
|
|
7884
8108
|
if (query.runner === "opencode") return await opencodeFinal(this.home(), query, maxChars);
|
|
7885
8109
|
if (query.runner === "codex") return codexFinal(this.home(), query, maxChars);
|
|
7886
8110
|
} catch {
|
|
@@ -7889,22 +8113,22 @@ var HomeTranscriptReader = class {
|
|
|
7889
8113
|
}
|
|
7890
8114
|
};
|
|
7891
8115
|
var CLAUDE_PROJECT_NAME_MAX = 200;
|
|
7892
|
-
function claudeProjectDirs(
|
|
7893
|
-
const projects =
|
|
8116
|
+
function claudeProjectDirs(configDir2, cwd) {
|
|
8117
|
+
const projects = path10.join(configDir2, "projects");
|
|
7894
8118
|
const munged = cwd.replace(/[^a-zA-Z0-9]/g, "-");
|
|
7895
8119
|
if (munged.length <= CLAUDE_PROJECT_NAME_MAX) {
|
|
7896
|
-
const dir =
|
|
8120
|
+
const dir = path10.join(projects, munged);
|
|
7897
8121
|
return existsSync4(dir) ? [dir] : [];
|
|
7898
8122
|
}
|
|
7899
8123
|
if (!existsSync4(projects)) return [];
|
|
7900
8124
|
const prefix = `${munged.slice(0, CLAUDE_PROJECT_NAME_MAX)}-`;
|
|
7901
|
-
return readdirSync3(projects).filter((name) => name.startsWith(prefix)).map((name) =>
|
|
8125
|
+
return readdirSync3(projects).filter((name) => name.startsWith(prefix)).map((name) => path10.join(projects, name));
|
|
7902
8126
|
}
|
|
7903
|
-
function claudeFinal(
|
|
8127
|
+
function claudeFinal(configDirs, query, maxChars) {
|
|
7904
8128
|
const cutoff = query.startedAt ? Date.parse(query.startedAt) : 0;
|
|
7905
|
-
const candidates = claudeProjectDirs(
|
|
8129
|
+
const candidates = configDirs.flatMap((dir) => claudeProjectDirs(dir, query.cwd)).flatMap((dir) => readdirSync3(dir).filter((f) => f.endsWith(".jsonl")).map((f) => path10.join(dir, f))).filter((f) => cutoff ? statSync5(f).mtimeMs >= cutoff - 5e3 : true).sort((a, b) => statSync5(b).mtimeMs - statSync5(a).mtimeMs);
|
|
7906
8130
|
for (const file of candidates) {
|
|
7907
|
-
const raw =
|
|
8131
|
+
const raw = readFileSync3(file, "utf8");
|
|
7908
8132
|
if (!raw.includes(query.marker)) continue;
|
|
7909
8133
|
const text = claudeLastAssistant(raw);
|
|
7910
8134
|
if (text) return clamp(text, maxChars);
|
|
@@ -7938,7 +8162,7 @@ async function opencodeFinal(home, query, maxChars) {
|
|
|
7938
8162
|
} catch {
|
|
7939
8163
|
return null;
|
|
7940
8164
|
}
|
|
7941
|
-
const dbPath =
|
|
8165
|
+
const dbPath = path10.join(home, ".local", "share", "opencode", "opencode.db");
|
|
7942
8166
|
if (!existsSync4(dbPath)) return null;
|
|
7943
8167
|
let db;
|
|
7944
8168
|
try {
|
|
@@ -7991,7 +8215,7 @@ async function opencodeFinal(home, query, maxChars) {
|
|
|
7991
8215
|
}
|
|
7992
8216
|
}
|
|
7993
8217
|
function codexFinal(home, query, maxChars) {
|
|
7994
|
-
const root =
|
|
8218
|
+
const root = path10.join(home, ".codex", "sessions");
|
|
7995
8219
|
if (!existsSync4(root)) return null;
|
|
7996
8220
|
const cutoff = query.startedAt ? Date.parse(query.startedAt) : 0;
|
|
7997
8221
|
const files = [];
|
|
@@ -8005,10 +8229,10 @@ function codexFinal(home, query, maxChars) {
|
|
|
8005
8229
|
continue;
|
|
8006
8230
|
}
|
|
8007
8231
|
for (const e of entries) {
|
|
8008
|
-
const full =
|
|
8232
|
+
const full = path10.join(dir, e);
|
|
8009
8233
|
let st;
|
|
8010
8234
|
try {
|
|
8011
|
-
st =
|
|
8235
|
+
st = statSync5(full);
|
|
8012
8236
|
} catch {
|
|
8013
8237
|
continue;
|
|
8014
8238
|
}
|
|
@@ -8016,7 +8240,7 @@ function codexFinal(home, query, maxChars) {
|
|
|
8016
8240
|
else if (e.startsWith("rollout-") && e.endsWith(".jsonl") && (!cutoff || st.mtimeMs >= cutoff - 5e3)) files.push(full);
|
|
8017
8241
|
}
|
|
8018
8242
|
}
|
|
8019
|
-
files.sort((a, b) =>
|
|
8243
|
+
files.sort((a, b) => statSync5(b).mtimeMs - statSync5(a).mtimeMs);
|
|
8020
8244
|
for (const file of cutoff ? files : files.slice(0, 10)) {
|
|
8021
8245
|
const text = codexLastAssistant(file, query);
|
|
8022
8246
|
if (text) return clamp(text, maxChars);
|
|
@@ -8024,7 +8248,7 @@ function codexFinal(home, query, maxChars) {
|
|
|
8024
8248
|
return null;
|
|
8025
8249
|
}
|
|
8026
8250
|
function codexLastAssistant(file, query) {
|
|
8027
|
-
const raw =
|
|
8251
|
+
const raw = readFileSync3(file, "utf8");
|
|
8028
8252
|
if (!raw.includes(query.marker)) return null;
|
|
8029
8253
|
const lines = raw.split("\n").filter((l) => l.trim());
|
|
8030
8254
|
let sawCwd = false;
|
|
@@ -8456,6 +8680,43 @@ ${taskB.description}`,
|
|
|
8456
8680
|
}
|
|
8457
8681
|
};
|
|
8458
8682
|
|
|
8683
|
+
// src/utils/shell.ts
|
|
8684
|
+
var ANSI_RE = /\x1b\[[0-9;]*[A-Za-z]|\x1b\][^\x07]*\x07|\x1b[()][AB012]|\r/g;
|
|
8685
|
+
function stripAnsi(text) {
|
|
8686
|
+
return text.replace(ANSI_RE, "");
|
|
8687
|
+
}
|
|
8688
|
+
function posixShellQuote(s) {
|
|
8689
|
+
return `'${s.replace(/'/g, `'\\''`)}'`;
|
|
8690
|
+
}
|
|
8691
|
+
function buildShellInvocation(command, args) {
|
|
8692
|
+
const inner = [command, ...args].map(posixShellQuote).join(" ");
|
|
8693
|
+
return { shellPath: "/bin/bash", shellArgs: ["-lc", inner] };
|
|
8694
|
+
}
|
|
8695
|
+
function wrapWithPty(command, args, opts = {}) {
|
|
8696
|
+
const inner = [command, ...args].map(posixShellQuote).join(" ");
|
|
8697
|
+
const watcher = opts.controlChannel ? 'exec 4<&0; ( while read -r C R <&3; do stty cols "$C" rows "$R" <&4; done ) & ' : "";
|
|
8698
|
+
const setup = opts.size ? `stty cols ${opts.size.cols} rows ${opts.size.rows}; ` : "";
|
|
8699
|
+
return { command: "script", args: ["-q", "-e", "-f", "-c", `${watcher}${setup}${inner}`, "/dev/null"] };
|
|
8700
|
+
}
|
|
8701
|
+
|
|
8702
|
+
// src/services/blockingPrompts.ts
|
|
8703
|
+
var TAIL = 4096;
|
|
8704
|
+
var squash = (text) => stripAnsi(text).replace(/\s+/g, "").toLowerCase();
|
|
8705
|
+
function watchBlockingPrompts(session, prompts, onPrompt) {
|
|
8706
|
+
if (prompts.length === 0) return;
|
|
8707
|
+
const pending = prompts.map((prompt) => ({ prompt, phrase: squash(prompt.phrase) }));
|
|
8708
|
+
let tail = "";
|
|
8709
|
+
session.onOutput((text) => {
|
|
8710
|
+
if (pending.length === 0) return;
|
|
8711
|
+
tail = (tail + squash(text)).slice(-TAIL);
|
|
8712
|
+
for (let i = pending.length - 1; i >= 0; i--) {
|
|
8713
|
+
if (!tail.includes(pending[i].phrase)) continue;
|
|
8714
|
+
onPrompt(pending[i].prompt);
|
|
8715
|
+
pending.splice(i, 1);
|
|
8716
|
+
}
|
|
8717
|
+
});
|
|
8718
|
+
}
|
|
8719
|
+
|
|
8459
8720
|
// src/services/TaskOrchestrator.ts
|
|
8460
8721
|
var SHARED_ROOT_TAIL = "tasks run in the workspace root without worktree isolation.";
|
|
8461
8722
|
function sharedRootNotice(reason, repos) {
|
|
@@ -8519,6 +8780,25 @@ var TaskOrchestrator = class {
|
|
|
8519
8780
|
*/
|
|
8520
8781
|
retryCounts = /* @__PURE__ */ new Map();
|
|
8521
8782
|
spawnCounts = /* @__PURE__ */ new Map();
|
|
8783
|
+
/**
|
|
8784
|
+
* The terminal a verdict left open, by task. It stays so the user can read
|
|
8785
|
+
* the agent's output or keep talking to it — but only while its worktree
|
|
8786
|
+
* does: once that is removed the agent sits in a deleted directory, and a
|
|
8787
|
+
* newer attempt in the same worktree would share it with a second agent.
|
|
8788
|
+
* Without this, every task of every run left one agent process running
|
|
8789
|
+
* until the daemon stopped.
|
|
8790
|
+
*/
|
|
8791
|
+
lingering = /* @__PURE__ */ new Map();
|
|
8792
|
+
/**
|
|
8793
|
+
* A full-plan run a failure paused. Retrying the failed task is the explicit
|
|
8794
|
+
* resume the pause waits for — without this a retry only reset the task to
|
|
8795
|
+
* pending and nothing ran until the user also re-ran the whole plan.
|
|
8796
|
+
*/
|
|
8797
|
+
haltedByFailure = false;
|
|
8798
|
+
/** The workspace's own variables for a task's cwd (ADR-0016); swapped out in tests. */
|
|
8799
|
+
workspaceEnv = (cwd) => resolveWorkspaceEnv(cwd);
|
|
8800
|
+
/** What the workspace env has already warned about, so a run says it once, not per task. */
|
|
8801
|
+
envWarnings = /* @__PURE__ */ new Set();
|
|
8522
8802
|
/**
|
|
8523
8803
|
* Tasks pulled out of auto-scheduling (user-cancelled or failed to spawn).
|
|
8524
8804
|
* They stay 'pending' — "not executed" — but the scheduler skips them until
|
|
@@ -8562,6 +8842,32 @@ var TaskOrchestrator = class {
|
|
|
8562
8842
|
setWorkspaceRoot(fn) {
|
|
8563
8843
|
this.workspaceRootFn = fn;
|
|
8564
8844
|
}
|
|
8845
|
+
setWorkspaceEnvResolver(resolve7) {
|
|
8846
|
+
this.workspaceEnv = resolve7;
|
|
8847
|
+
}
|
|
8848
|
+
/**
|
|
8849
|
+
* The variables a task's agent gets from its workspace. What cannot be
|
|
8850
|
+
* applied is said once — silently starting without them is how agents ran
|
|
8851
|
+
* under the wrong account when an edited `.envrc` was left unallowed.
|
|
8852
|
+
*/
|
|
8853
|
+
async envForTask(cwd) {
|
|
8854
|
+
const resolved = await this.workspaceEnv(cwd);
|
|
8855
|
+
const warn = (key, message) => {
|
|
8856
|
+
if (this.envWarnings.has(key)) return;
|
|
8857
|
+
this.envWarnings.add(key);
|
|
8858
|
+
this.notifications.warn(message);
|
|
8859
|
+
};
|
|
8860
|
+
if (resolved.blockedEnvrc) {
|
|
8861
|
+
warn(`blocked:${resolved.blockedEnvrc}`, `direnv has blocked ${resolved.blockedEnvrc}, so tasks start without its variables. Run \`direnv allow\` in ${path11.dirname(resolved.blockedEnvrc)} to use them.`);
|
|
8862
|
+
}
|
|
8863
|
+
if (resolved.trackedEnvFile) {
|
|
8864
|
+
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.`);
|
|
8865
|
+
}
|
|
8866
|
+
if (resolved.refused.length > 0) {
|
|
8867
|
+
warn(`refused:${resolved.refused.join(",")}`, `Ignored ${resolved.refused.join(", ")} from the workspace environment: Ordewell never passes these to agents.`);
|
|
8868
|
+
}
|
|
8869
|
+
return resolved.env;
|
|
8870
|
+
}
|
|
8565
8871
|
setRegistry(registry) {
|
|
8566
8872
|
this.registry = registry;
|
|
8567
8873
|
}
|
|
@@ -8706,6 +9012,7 @@ var TaskOrchestrator = class {
|
|
|
8706
9012
|
return result;
|
|
8707
9013
|
}
|
|
8708
9014
|
async clearMergedRun(run) {
|
|
9015
|
+
this.closeLingeringOf(run);
|
|
8709
9016
|
try {
|
|
8710
9017
|
await this.isolation.discard(run, { integration: "delete-merged" });
|
|
8711
9018
|
} catch (err) {
|
|
@@ -8716,14 +9023,32 @@ var TaskOrchestrator = class {
|
|
|
8716
9023
|
}
|
|
8717
9024
|
/** Worktrees and task branches go; the integration branch and the record stay for review and merge. */
|
|
8718
9025
|
async cleanupRun() {
|
|
9026
|
+
this.closeLingeringOf(this.requireRun());
|
|
8719
9027
|
await this.isolation.discard(this.requireRun(), { integration: "keep" });
|
|
8720
9028
|
this.emit("onIsolationChanged");
|
|
8721
9029
|
}
|
|
8722
9030
|
/** The run and everything it made go, and the plan forgets it; the next run starts afresh. */
|
|
8723
9031
|
async discardRun() {
|
|
9032
|
+
this.closeLingeringOf(this.requireRun());
|
|
8724
9033
|
await this.isolation.discard(this.requireRun(), { integration: "delete" });
|
|
8725
9034
|
this.forgetRun();
|
|
8726
9035
|
}
|
|
9036
|
+
watchBlockingPrompts(task, attempt, session) {
|
|
9037
|
+
const manifest = this.registry?.get(attempt.runner)?.manifest;
|
|
9038
|
+
watchBlockingPrompts(session, manifest?.runner.blockingPrompts ?? [], (prompt) => {
|
|
9039
|
+
if (this.attempts.get(task.id) !== attempt) return;
|
|
9040
|
+
this.notifications.warn(`Task "${task.title}" is waiting for you: ${manifest?.displayName ?? attempt.runner} is asking ${prompt.asks}. Answer it in the task's terminal.`);
|
|
9041
|
+
});
|
|
9042
|
+
}
|
|
9043
|
+
closeLingering(taskId) {
|
|
9044
|
+
const sessionId = this.lingering.get(taskId);
|
|
9045
|
+
if (sessionId === void 0) return;
|
|
9046
|
+
this.lingering.delete(taskId);
|
|
9047
|
+
this.terminalRunner.stop(sessionId);
|
|
9048
|
+
}
|
|
9049
|
+
closeLingeringOf(run) {
|
|
9050
|
+
for (const taskId of Object.keys(run.tasks)) this.closeLingering(taskId);
|
|
9051
|
+
}
|
|
8727
9052
|
forgetRun() {
|
|
8728
9053
|
this.isolationRun = null;
|
|
8729
9054
|
this.resolvers = {};
|
|
@@ -8811,11 +9136,18 @@ var TaskOrchestrator = class {
|
|
|
8811
9136
|
if (!await this.openRun(() => this.start()) || this.running) return;
|
|
8812
9137
|
console.log(`[TaskOrchestrator] Starting with ${this.store.allTasks.length} tasks (${this.store.allTasks.filter((t) => t.type === "ai" && t.prompt).length} AI ready)`);
|
|
8813
9138
|
this.running = true;
|
|
9139
|
+
this.haltedByFailure = false;
|
|
8814
9140
|
this.planStatus = "running";
|
|
8815
9141
|
this.emit("onTaskChanged");
|
|
8816
9142
|
await this.tick();
|
|
8817
9143
|
}
|
|
9144
|
+
haltOnFailure() {
|
|
9145
|
+
if (this.running) this.haltedByFailure = true;
|
|
9146
|
+
this.running = false;
|
|
9147
|
+
this.planStatus = "approved";
|
|
9148
|
+
}
|
|
8818
9149
|
stop() {
|
|
9150
|
+
this.haltedByFailure = false;
|
|
8819
9151
|
this.running = false;
|
|
8820
9152
|
this.planStatus = "approved";
|
|
8821
9153
|
this.terminalRunner.stopAll();
|
|
@@ -8852,8 +9184,7 @@ ${summary || "(empty \u2014 no output captured)"}`);
|
|
|
8852
9184
|
await this.landPassed(task, landing);
|
|
8853
9185
|
} else {
|
|
8854
9186
|
this.store.markFailed(taskId);
|
|
8855
|
-
this.
|
|
8856
|
-
this.planStatus = "approved";
|
|
9187
|
+
this.haltOnFailure();
|
|
8857
9188
|
this.notifications.error(`Task "${task.title}" failed verification: ${verdict.reason}`);
|
|
8858
9189
|
if (attempt.worktree) await this.releaseWorktree(taskId, { keep: true });
|
|
8859
9190
|
}
|
|
@@ -8993,8 +9324,7 @@ ${summary || "(empty \u2014 no output captured)"}`);
|
|
|
8993
9324
|
if (whyNot) this.tell("info", whyNot);
|
|
8994
9325
|
} else {
|
|
8995
9326
|
this.store.markFailed(task.id);
|
|
8996
|
-
this.
|
|
8997
|
-
this.planStatus = "approved";
|
|
9327
|
+
this.haltOnFailure();
|
|
8998
9328
|
this.notifications.error(inRepo ? `Task "${task.title}" passed, but git could not integrate its work in ${inRepo}, so none of it landed. Its worktrees are kept for inspection.` : `Task "${task.title}" passed, but git could not integrate its work. Its worktree is kept for inspection.`);
|
|
8999
9329
|
}
|
|
9000
9330
|
}
|
|
@@ -9045,6 +9375,7 @@ ${summary || "(empty \u2014 no output captured)"}`);
|
|
|
9045
9375
|
await integration;
|
|
9046
9376
|
const run = this.isolationRun;
|
|
9047
9377
|
if (!run?.tasks[taskId]) return;
|
|
9378
|
+
if (!opts.keep) this.closeLingering(taskId);
|
|
9048
9379
|
try {
|
|
9049
9380
|
await this.isolation.release(run, taskId, opts);
|
|
9050
9381
|
} catch (err) {
|
|
@@ -9193,7 +9524,8 @@ ${summary || "(empty \u2014 no output captured)"}`);
|
|
|
9193
9524
|
this.onHold.delete(taskId);
|
|
9194
9525
|
this.emit("onTaskChanged");
|
|
9195
9526
|
await this.releaseWorktree(taskId, { keep: false }, ended?.integration);
|
|
9196
|
-
await this.
|
|
9527
|
+
if (this.haltedByFailure && !this.running) await this.start();
|
|
9528
|
+
else await this.tick();
|
|
9197
9529
|
}
|
|
9198
9530
|
/**
|
|
9199
9531
|
* Manually start a single AI task right now, bypassing dependency/readiness
|
|
@@ -9319,6 +9651,8 @@ ${summary || "(empty \u2014 no output captured)"}`);
|
|
|
9319
9651
|
// A merge to resolve is not new behaviour to drive test-first.
|
|
9320
9652
|
tddEnabled: !attempt.repair && this.tddEnabled()
|
|
9321
9653
|
});
|
|
9654
|
+
this.closeLingering(task.id);
|
|
9655
|
+
const env = await this.envForTask(cwd);
|
|
9322
9656
|
const session = await this.terminalRunner.spawn({
|
|
9323
9657
|
taskId: task.id,
|
|
9324
9658
|
runner: attempt.runner,
|
|
@@ -9330,13 +9664,15 @@ ${summary || "(empty \u2014 no output captured)"}`);
|
|
|
9330
9664
|
cwd,
|
|
9331
9665
|
registry: this.registry ?? void 0,
|
|
9332
9666
|
order: task.order,
|
|
9333
|
-
title: task.title
|
|
9667
|
+
title: task.title,
|
|
9668
|
+
env
|
|
9334
9669
|
});
|
|
9335
9670
|
if (this.attempts.get(task.id) !== attempt) return this.abandonSpawn(task, attempt, session);
|
|
9336
9671
|
attempt.phase = "running";
|
|
9337
9672
|
attempt.session = session;
|
|
9338
9673
|
this.output.attach(task.id, session);
|
|
9339
9674
|
this.verifier.watch(task, session);
|
|
9675
|
+
this.watchBlockingPrompts(task, attempt, session);
|
|
9340
9676
|
this.emit("onTaskChanged");
|
|
9341
9677
|
if (attempt.repair) {
|
|
9342
9678
|
const group = this.isolationRun?.repos.some((r) => r.path !== SELF_REPO);
|
|
@@ -9569,6 +9905,7 @@ ${summary || "(empty \u2014 no output captured)"}`);
|
|
|
9569
9905
|
const attempt = this.attempts.get(taskId);
|
|
9570
9906
|
this.attempts.delete(taskId);
|
|
9571
9907
|
if (attempt) this.output.detach(taskId);
|
|
9908
|
+
if (reason === "verdict" && attempt?.session) this.lingering.set(taskId, attempt.session.id);
|
|
9572
9909
|
if (reason === "cancel" || reason === "release" || reason === "complete" || reason === "retry") {
|
|
9573
9910
|
const task = this.store.get(taskId);
|
|
9574
9911
|
if (task) this.verifier.clear(task);
|
|
@@ -9711,29 +10048,29 @@ ${errors.map((e) => `- ${e}`).join("\n")}`
|
|
|
9711
10048
|
};
|
|
9712
10049
|
|
|
9713
10050
|
// src/services/ModelDiscovery.ts
|
|
9714
|
-
import * as
|
|
10051
|
+
import * as path14 from "path";
|
|
9715
10052
|
import { exec as exec3, spawn } from "child_process";
|
|
9716
10053
|
import { promisify as promisify3 } from "util";
|
|
9717
10054
|
import * as fsSync3 from "fs";
|
|
9718
10055
|
import * as osMod from "os";
|
|
9719
10056
|
|
|
9720
10057
|
// src/utils/globalDataDir.ts
|
|
9721
|
-
import * as
|
|
9722
|
-
import * as
|
|
9723
|
-
import * as
|
|
10058
|
+
import * as fs6 from "fs";
|
|
10059
|
+
import * as path12 from "path";
|
|
10060
|
+
import * as os5 from "os";
|
|
9724
10061
|
function globalDataDir() {
|
|
9725
|
-
return
|
|
10062
|
+
return path12.join(os5.homedir(), ".ordewell");
|
|
9726
10063
|
}
|
|
9727
10064
|
function copyDirSync(src, dest) {
|
|
9728
|
-
|
|
9729
|
-
const entries =
|
|
10065
|
+
fs6.mkdirSync(dest, { recursive: true });
|
|
10066
|
+
const entries = fs6.readdirSync(src, { withFileTypes: true });
|
|
9730
10067
|
for (const entry of entries) {
|
|
9731
|
-
const srcPath =
|
|
9732
|
-
const destPath =
|
|
10068
|
+
const srcPath = path12.join(src, entry.name);
|
|
10069
|
+
const destPath = path12.join(dest, entry.name);
|
|
9733
10070
|
if (entry.isDirectory()) {
|
|
9734
10071
|
copyDirSync(srcPath, destPath);
|
|
9735
10072
|
} else {
|
|
9736
|
-
|
|
10073
|
+
fs6.copyFileSync(srcPath, destPath);
|
|
9737
10074
|
}
|
|
9738
10075
|
}
|
|
9739
10076
|
}
|
|
@@ -9741,50 +10078,29 @@ var migrated = false;
|
|
|
9741
10078
|
function migrateOldConfigDir() {
|
|
9742
10079
|
if (migrated) return;
|
|
9743
10080
|
migrated = true;
|
|
9744
|
-
const oldDir =
|
|
9745
|
-
if (!
|
|
10081
|
+
const oldDir = path12.join(os5.homedir(), ".config", "ordewell");
|
|
10082
|
+
if (!fs6.existsSync(oldDir)) return;
|
|
9746
10083
|
const newDir = globalDataDir();
|
|
9747
10084
|
const liftFile = (name) => {
|
|
9748
|
-
const oldFile =
|
|
9749
|
-
const newFile =
|
|
9750
|
-
if (
|
|
9751
|
-
|
|
9752
|
-
|
|
10085
|
+
const oldFile = path12.join(oldDir, name);
|
|
10086
|
+
const newFile = path12.join(newDir, name);
|
|
10087
|
+
if (fs6.existsSync(oldFile) && !fs6.existsSync(newFile)) {
|
|
10088
|
+
fs6.mkdirSync(newDir, { recursive: true });
|
|
10089
|
+
fs6.copyFileSync(oldFile, newFile);
|
|
9753
10090
|
}
|
|
9754
10091
|
};
|
|
9755
10092
|
liftFile("settings.json");
|
|
9756
10093
|
liftFile(".env");
|
|
9757
|
-
const oldPlugins =
|
|
9758
|
-
const newPlugins =
|
|
9759
|
-
if (
|
|
10094
|
+
const oldPlugins = path12.join(oldDir, "plugins");
|
|
10095
|
+
const newPlugins = path12.join(newDir, "plugins");
|
|
10096
|
+
if (fs6.existsSync(oldPlugins) && !fs6.existsSync(newPlugins)) {
|
|
9760
10097
|
copyDirSync(oldPlugins, newPlugins);
|
|
9761
10098
|
}
|
|
9762
10099
|
}
|
|
9763
10100
|
|
|
9764
10101
|
// src/utils/launch.ts
|
|
9765
10102
|
import * as fsSync2 from "fs";
|
|
9766
|
-
import * as
|
|
9767
|
-
|
|
9768
|
-
// src/utils/shell.ts
|
|
9769
|
-
var ANSI_RE = /\x1b\[[0-9;]*[A-Za-z]|\x1b\][^\x07]*\x07|\x1b[()][AB012]|\r/g;
|
|
9770
|
-
function stripAnsi(text) {
|
|
9771
|
-
return text.replace(ANSI_RE, "");
|
|
9772
|
-
}
|
|
9773
|
-
function posixShellQuote(s) {
|
|
9774
|
-
return `'${s.replace(/'/g, `'\\''`)}'`;
|
|
9775
|
-
}
|
|
9776
|
-
function buildShellInvocation(command, args) {
|
|
9777
|
-
const inner = [command, ...args].map(posixShellQuote).join(" ");
|
|
9778
|
-
return { shellPath: "/bin/bash", shellArgs: ["-lc", inner] };
|
|
9779
|
-
}
|
|
9780
|
-
function wrapWithPty(command, args, opts = {}) {
|
|
9781
|
-
const inner = [command, ...args].map(posixShellQuote).join(" ");
|
|
9782
|
-
const watcher = opts.controlChannel ? 'exec 4<&0; ( while read -r C R <&3; do stty cols "$C" rows "$R" <&4; done ) & ' : "";
|
|
9783
|
-
const setup = opts.size ? `stty cols ${opts.size.cols} rows ${opts.size.rows}; ` : "";
|
|
9784
|
-
return { command: "script", args: ["-q", "-e", "-f", "-c", `${watcher}${setup}${inner}`, "/dev/null"] };
|
|
9785
|
-
}
|
|
9786
|
-
|
|
9787
|
-
// src/utils/launch.ts
|
|
10103
|
+
import * as path13 from "path";
|
|
9788
10104
|
var DIRECT_EXTENSIONS = [".exe", ".com"];
|
|
9789
10105
|
var BATCH_EXTENSIONS = [".cmd", ".bat"];
|
|
9790
10106
|
var POWERSHELL_EXTENSIONS = [".ps1"];
|
|
@@ -9792,7 +10108,7 @@ var CMD_EXE_MAX_COMMAND_LINE = 8191;
|
|
|
9792
10108
|
var WINDOWS_MAX_COMMAND_LINE = 32767;
|
|
9793
10109
|
function defaultPowerShell() {
|
|
9794
10110
|
const root = process.env.SystemRoot || process.env.windir;
|
|
9795
|
-
return root ?
|
|
10111
|
+
return root ? path13.win32.join(root, "System32", "WindowsPowerShell", "v1.0", "powershell.exe") : "powershell.exe";
|
|
9796
10112
|
}
|
|
9797
10113
|
var POWERSHELL_FLAGS = ["-NoLogo", "-NoProfile", "-NonInteractive", "-ExecutionPolicy", "Bypass", "-File"];
|
|
9798
10114
|
var CommandLineTooLongError = class extends Error {
|
|
@@ -9844,7 +10160,7 @@ Searched: ${searchedPath || "(empty PATH)"}`);
|
|
|
9844
10160
|
};
|
|
9845
10161
|
function isResolvableOnPosixPath(command, PATH, exists) {
|
|
9846
10162
|
if (command.includes("/")) return exists(command);
|
|
9847
|
-
return PATH.split(
|
|
10163
|
+
return PATH.split(path13.delimiter).map((d) => d.trim()).filter(Boolean).some((dir) => exists(path13.join(dir, command)));
|
|
9848
10164
|
}
|
|
9849
10165
|
function isExecutableResolved(command, plan, PATH, deps = {}) {
|
|
9850
10166
|
if ((deps.platform ?? process.platform) === "win32") return plan.file !== command;
|
|
@@ -9884,9 +10200,9 @@ function cmdArgs(line) {
|
|
|
9884
10200
|
return ["/d", "/s", "/c", `"${line}"`];
|
|
9885
10201
|
}
|
|
9886
10202
|
function candidateExtensions(command, pathExt) {
|
|
9887
|
-
const declared = pathExt.split(
|
|
10203
|
+
const declared = pathExt.split(path13.win32.delimiter).map((e) => e.trim().toLowerCase()).filter(Boolean);
|
|
9888
10204
|
const known = /* @__PURE__ */ new Set([...DIRECT_EXTENSIONS, ...BATCH_EXTENSIONS, ...POWERSHELL_EXTENSIONS, ...declared]);
|
|
9889
|
-
const ext =
|
|
10205
|
+
const ext = path13.win32.extname(command).toLowerCase();
|
|
9890
10206
|
if (ext && known.has(ext)) {
|
|
9891
10207
|
const route = BATCH_EXTENSIONS.includes(ext) ? "cmd" : POWERSHELL_EXTENSIONS.includes(ext) ? "powershell" : "direct";
|
|
9892
10208
|
return { direct: [], cmd: [], powershell: [], [route]: [""] };
|
|
@@ -9907,13 +10223,13 @@ async function findWindowsExecutables(command, deps) {
|
|
|
9907
10223
|
const exists = deps.exists ?? defaultExists2;
|
|
9908
10224
|
const pathExt = (deps.pathExt ?? (() => process.env.PATHEXT ?? ""))();
|
|
9909
10225
|
const byRoute = candidateExtensions(command, pathExt);
|
|
9910
|
-
const dirs =
|
|
9911
|
-
const base =
|
|
10226
|
+
const dirs = path13.win32.basename(command) !== command ? [path13.win32.dirname(path13.win32.resolve(command))] : (await (deps.resolvePath ?? augmentedPath)() || "").split(path13.win32.delimiter).map((d) => d.trim().replace(/^"|"$/g, "")).filter(Boolean);
|
|
10227
|
+
const base = path13.win32.basename(command);
|
|
9912
10228
|
const found = [];
|
|
9913
10229
|
for (const route of ROUTES) {
|
|
9914
10230
|
outer: for (const ext of byRoute[route]) {
|
|
9915
10231
|
for (const dir of dirs) {
|
|
9916
|
-
const candidate =
|
|
10232
|
+
const candidate = path13.win32.join(dir, `${base}${ext}`);
|
|
9917
10233
|
if (exists(candidate)) {
|
|
9918
10234
|
found.push({ file: candidate, route });
|
|
9919
10235
|
break outer;
|
|
@@ -9959,10 +10275,10 @@ async function planShellLaunch(command, args, deps = {}) {
|
|
|
9959
10275
|
}
|
|
9960
10276
|
|
|
9961
10277
|
// src/utils/processTree.ts
|
|
9962
|
-
import { execFile as
|
|
10278
|
+
import { execFile as execFile3 } from "child_process";
|
|
9963
10279
|
var HARD_KILL_DELAY_MS = 5e3;
|
|
9964
10280
|
function defaultExecFile2(file, args, cb) {
|
|
9965
|
-
|
|
10281
|
+
execFile3(file, args, (err) => cb(err ?? null));
|
|
9966
10282
|
}
|
|
9967
10283
|
function killTree(proc, deps = {}) {
|
|
9968
10284
|
if (!proc || proc.killed || typeof proc.exitCode === "number") return;
|
|
@@ -10008,7 +10324,7 @@ var defaultAppServerClient = async (command, args, options) => {
|
|
|
10008
10324
|
const PATH = await augmentedPath();
|
|
10009
10325
|
const launch = await planDirectLaunch(command, args).catch(() => null);
|
|
10010
10326
|
if (!launch) return null;
|
|
10011
|
-
return new Promise((
|
|
10327
|
+
return new Promise((resolve7) => {
|
|
10012
10328
|
let child;
|
|
10013
10329
|
try {
|
|
10014
10330
|
child = spawn(launch.file, launch.args, {
|
|
@@ -10017,7 +10333,7 @@ var defaultAppServerClient = async (command, args, options) => {
|
|
|
10017
10333
|
windowsVerbatimArguments: launch.verbatim
|
|
10018
10334
|
});
|
|
10019
10335
|
} catch {
|
|
10020
|
-
|
|
10336
|
+
resolve7(null);
|
|
10021
10337
|
return;
|
|
10022
10338
|
}
|
|
10023
10339
|
let settled = false;
|
|
@@ -10026,7 +10342,7 @@ var defaultAppServerClient = async (command, args, options) => {
|
|
|
10026
10342
|
settled = true;
|
|
10027
10343
|
clearTimeout(timer);
|
|
10028
10344
|
killTree(child);
|
|
10029
|
-
|
|
10345
|
+
resolve7(value);
|
|
10030
10346
|
};
|
|
10031
10347
|
const timer = setTimeout(() => finish(null), options?.timeout ?? 2e4);
|
|
10032
10348
|
child.on("error", () => finish(null));
|
|
@@ -10093,7 +10409,7 @@ function geminiDiscoveryCacheKey(apiKey) {
|
|
|
10093
10409
|
return {
|
|
10094
10410
|
key: `gemini-models:${hash}`,
|
|
10095
10411
|
dir,
|
|
10096
|
-
file:
|
|
10412
|
+
file: path14.join(dir, `gemini_models_${hash}.json`)
|
|
10097
10413
|
};
|
|
10098
10414
|
}
|
|
10099
10415
|
function readGeminiCache(apiKey) {
|
|
@@ -10616,8 +10932,8 @@ var ModelDiscovery = class {
|
|
|
10616
10932
|
};
|
|
10617
10933
|
|
|
10618
10934
|
// src/services/ModelCatalog.ts
|
|
10619
|
-
import * as
|
|
10620
|
-
import * as
|
|
10935
|
+
import * as fs7 from "fs";
|
|
10936
|
+
import * as path15 from "path";
|
|
10621
10937
|
var CACHE_KEY = "model-catalog";
|
|
10622
10938
|
var CACHE_TTL_MS = 60 * 60 * 1e3;
|
|
10623
10939
|
function storageKey(baseUrl) {
|
|
@@ -10669,17 +10985,17 @@ var ModelCatalog = class {
|
|
|
10669
10985
|
const url = (baseUrl || "https://openrouter.ai/api/v1").replace(/\/+$/, "");
|
|
10670
10986
|
const key = storageKey(url);
|
|
10671
10987
|
const cacheDir = globalCacheDir();
|
|
10672
|
-
const cacheFile =
|
|
10673
|
-
if (
|
|
10988
|
+
const cacheFile = path15.join(cacheDir, `${key.replace(/[:/]/g, "_")}.json`);
|
|
10989
|
+
if (fs7.existsSync(cacheFile)) fs7.rmSync(cacheFile);
|
|
10674
10990
|
} catch {
|
|
10675
10991
|
}
|
|
10676
10992
|
}
|
|
10677
10993
|
static readCache(key) {
|
|
10678
10994
|
try {
|
|
10679
10995
|
const cacheDir = globalCacheDir();
|
|
10680
|
-
const cacheFile =
|
|
10681
|
-
if (!
|
|
10682
|
-
const raw =
|
|
10996
|
+
const cacheFile = path15.join(cacheDir, `${key.replace(/[:/]/g, "_")}.json`);
|
|
10997
|
+
if (!fs7.existsSync(cacheFile)) return null;
|
|
10998
|
+
const raw = fs7.readFileSync(cacheFile, "utf8");
|
|
10683
10999
|
const data = JSON.parse(raw);
|
|
10684
11000
|
if (Date.now() - data.cachedAt > CACHE_TTL_MS) return null;
|
|
10685
11001
|
return JSON.stringify(data.models);
|
|
@@ -10690,9 +11006,9 @@ var ModelCatalog = class {
|
|
|
10690
11006
|
static writeCache(key, models) {
|
|
10691
11007
|
try {
|
|
10692
11008
|
const cacheDir = globalCacheDir();
|
|
10693
|
-
|
|
10694
|
-
const cacheFile =
|
|
10695
|
-
|
|
11009
|
+
fs7.mkdirSync(cacheDir, { recursive: true });
|
|
11010
|
+
const cacheFile = path15.join(cacheDir, `${key.replace(/[:/]/g, "_")}.json`);
|
|
11011
|
+
fs7.writeFileSync(
|
|
10696
11012
|
cacheFile,
|
|
10697
11013
|
JSON.stringify({ models, cachedAt: Date.now() }, null, 2)
|
|
10698
11014
|
);
|
|
@@ -11068,72 +11384,12 @@ var RunnerInstallation = class {
|
|
|
11068
11384
|
}
|
|
11069
11385
|
};
|
|
11070
11386
|
|
|
11071
|
-
// src/services/settingsEnvAllowlist.ts
|
|
11072
|
-
var ORDEWELL_SETTABLE_ENV = [
|
|
11073
|
-
"AI_PROVIDER",
|
|
11074
|
-
"ORCHESTRATOR_MODEL",
|
|
11075
|
-
"ORDEWELL_SUBAGENT_MODEL",
|
|
11076
|
-
"ORDEWELL_PLANNER_EFFORT",
|
|
11077
|
-
"ORDEWELL_RESEARCH_ENABLED",
|
|
11078
|
-
"ORDEWELL_MAX_PARALLEL",
|
|
11079
|
-
"ORDEWELL_CONFLICT_REPAIR_ATTEMPTS"
|
|
11080
|
-
];
|
|
11081
|
-
var SETTINGS_ENV_REFUSED = [
|
|
11082
|
-
// Approval posture and pre-approved scope.
|
|
11083
|
-
"ORDEWELL_AUTONOMOUS_MODE",
|
|
11084
|
-
"ORDEWELL_APPROVAL_MODE",
|
|
11085
|
-
"ORDEWELL_APPROVAL_ALLOW",
|
|
11086
|
-
// Where settings are persisted.
|
|
11087
|
-
"ORDEWELL_SETTINGS_PATH",
|
|
11088
|
-
// Runtime options and dynamic loaders.
|
|
11089
|
-
"NODE_OPTIONS",
|
|
11090
|
-
"NODE_REPL_EXTERNAL_MODULE",
|
|
11091
|
-
"NODE_PATH",
|
|
11092
|
-
"ELECTRON_RUN_AS_NODE",
|
|
11093
|
-
"LD_PRELOAD",
|
|
11094
|
-
"LD_LIBRARY_PATH",
|
|
11095
|
-
"LD_AUDIT",
|
|
11096
|
-
"DYLD_INSERT_LIBRARIES",
|
|
11097
|
-
"DYLD_LIBRARY_PATH",
|
|
11098
|
-
"DYLD_FRAMEWORK_PATH",
|
|
11099
|
-
// Executable resolution.
|
|
11100
|
-
"PATH",
|
|
11101
|
-
"PATHEXT",
|
|
11102
|
-
"NODE"
|
|
11103
|
-
];
|
|
11104
|
-
var REFUSED = new Set(SETTINGS_ENV_REFUSED);
|
|
11105
|
-
function providerEnvKeys() {
|
|
11106
|
-
const keys = [];
|
|
11107
|
-
for (const meta of Object.values(ALL_PROVIDERS)) {
|
|
11108
|
-
if (meta.apiKeyEnvVar) keys.push(meta.apiKeyEnvVar);
|
|
11109
|
-
if (meta.baseUrlEnvVar) keys.push(meta.baseUrlEnvVar);
|
|
11110
|
-
for (const detect of meta.detectEnvVars) keys.push(detect);
|
|
11111
|
-
}
|
|
11112
|
-
return keys;
|
|
11113
|
-
}
|
|
11114
|
-
var PROVIDER_CREDENTIAL_ENV = new Set(providerEnvKeys());
|
|
11115
|
-
var SETTINGS_ENV_ALLOWLIST = new Set(
|
|
11116
|
-
[...providerEnvKeys(), ...ORDEWELL_SETTABLE_ENV].filter((key) => !REFUSED.has(key))
|
|
11117
|
-
);
|
|
11118
|
-
function admitSettingsEnv(env) {
|
|
11119
|
-
const accepted = {};
|
|
11120
|
-
const rejected = [];
|
|
11121
|
-
for (const [key, value] of Object.entries(env)) {
|
|
11122
|
-
if (REFUSED.has(key) || !SETTINGS_ENV_ALLOWLIST.has(key) || typeof value !== "string") {
|
|
11123
|
-
rejected.push(key);
|
|
11124
|
-
continue;
|
|
11125
|
-
}
|
|
11126
|
-
accepted[key] = value;
|
|
11127
|
-
}
|
|
11128
|
-
return { accepted, rejected };
|
|
11129
|
-
}
|
|
11130
|
-
|
|
11131
11387
|
// src/services/harness/CliAgentAiService.ts
|
|
11132
11388
|
import { spawn as nodeSpawn } from "child_process";
|
|
11133
11389
|
|
|
11134
11390
|
// src/utils/workspace.ts
|
|
11135
|
-
import * as
|
|
11136
|
-
import * as
|
|
11391
|
+
import * as fs8 from "fs";
|
|
11392
|
+
import * as path16 from "path";
|
|
11137
11393
|
var WorkspaceNotFoundError = class extends Error {
|
|
11138
11394
|
constructor(workspace) {
|
|
11139
11395
|
super(`Workspace "${workspace}" does not exist.`);
|
|
@@ -11144,7 +11400,7 @@ var WorkspaceNotFoundError = class extends Error {
|
|
|
11144
11400
|
};
|
|
11145
11401
|
function defaultIsDirectory(candidate) {
|
|
11146
11402
|
try {
|
|
11147
|
-
return
|
|
11403
|
+
return fs8.statSync(candidate).isDirectory();
|
|
11148
11404
|
} catch {
|
|
11149
11405
|
return false;
|
|
11150
11406
|
}
|
|
@@ -11180,8 +11436,8 @@ var PROJECT_MARKER_FILES = [
|
|
|
11180
11436
|
"CMakeLists.txt"
|
|
11181
11437
|
];
|
|
11182
11438
|
function assertWorkspaceIsProject(workspace, deps = {}) {
|
|
11183
|
-
const realpath = deps.realpath ?? ((candidate) =>
|
|
11184
|
-
const exists = deps.exists ?? ((candidate) =>
|
|
11439
|
+
const realpath = deps.realpath ?? ((candidate) => fs8.realpathSync(candidate));
|
|
11440
|
+
const exists = deps.exists ?? ((candidate) => fs8.existsSync(candidate));
|
|
11185
11441
|
let resolved;
|
|
11186
11442
|
try {
|
|
11187
11443
|
resolved = realpath(workspace);
|
|
@@ -11189,7 +11445,7 @@ function assertWorkspaceIsProject(workspace, deps = {}) {
|
|
|
11189
11445
|
throw new WorkspaceNotAProjectError(workspace);
|
|
11190
11446
|
}
|
|
11191
11447
|
const hasMarker = [...PROJECT_MARKER_DIRS, ...PROJECT_MARKER_FILES].some(
|
|
11192
|
-
(marker2) => exists(
|
|
11448
|
+
(marker2) => exists(path16.join(resolved, marker2))
|
|
11193
11449
|
);
|
|
11194
11450
|
if (!hasMarker) throw new WorkspaceNotAProjectError(workspace);
|
|
11195
11451
|
}
|
|
@@ -11257,13 +11513,14 @@ var StdioAgentAdapter = class {
|
|
|
11257
11513
|
}
|
|
11258
11514
|
async start(opts) {
|
|
11259
11515
|
assertWorkspaceExists(opts.cwd, { isDirectory: this.deps.isDirectory });
|
|
11260
|
-
this.processEnded = new Promise((
|
|
11261
|
-
this.markEnded =
|
|
11516
|
+
this.processEnded = new Promise((resolve7) => {
|
|
11517
|
+
this.markEnded = resolve7;
|
|
11262
11518
|
});
|
|
11263
11519
|
const spec = this.spawnSpec(opts);
|
|
11264
11520
|
const resolvePath = this.deps.resolvePath ?? augmentedPath;
|
|
11265
11521
|
const PATH = await resolvePath();
|
|
11266
|
-
|
|
11522
|
+
const workspace = await (this.deps.workspaceEnv ?? workspaceEnvOf)(opts.cwd);
|
|
11523
|
+
this.spawnEnv = withPath(process.env, PATH, { ...workspace, ...spec.env });
|
|
11267
11524
|
const launch = await planDirectLaunch(spec.command, spec.args, {
|
|
11268
11525
|
platform: this.deps.platform,
|
|
11269
11526
|
resolvePath
|
|
@@ -11309,7 +11566,7 @@ ${err.message}`).slice(-STDERR_TAIL_CHARS);
|
|
|
11309
11566
|
onEvent({ type: "error", message: this.exitMessage() });
|
|
11310
11567
|
return;
|
|
11311
11568
|
}
|
|
11312
|
-
await new Promise((
|
|
11569
|
+
await new Promise((resolve7) => {
|
|
11313
11570
|
let settled = false;
|
|
11314
11571
|
const finish = () => {
|
|
11315
11572
|
if (settled) return;
|
|
@@ -11320,7 +11577,7 @@ ${err.message}`).slice(-STDERR_TAIL_CHARS);
|
|
|
11320
11577
|
signal?.removeEventListener("abort", onAbort);
|
|
11321
11578
|
this.process?.removeListener("exit", onExit);
|
|
11322
11579
|
this.process?.removeListener("error", onExit);
|
|
11323
|
-
|
|
11580
|
+
resolve7();
|
|
11324
11581
|
};
|
|
11325
11582
|
const emit = (event) => {
|
|
11326
11583
|
onEvent(event);
|
|
@@ -11535,14 +11792,14 @@ function codexSandboxUnavailableMessage() {
|
|
|
11535
11792
|
].join("\n");
|
|
11536
11793
|
}
|
|
11537
11794
|
function runProbe(deps, cwd, env, flags) {
|
|
11538
|
-
return new Promise((
|
|
11795
|
+
return new Promise((resolve7) => {
|
|
11539
11796
|
let output = "";
|
|
11540
11797
|
let settled = false;
|
|
11541
11798
|
const finish = (code) => {
|
|
11542
11799
|
if (settled) return;
|
|
11543
11800
|
settled = true;
|
|
11544
11801
|
clearTimeout(timer);
|
|
11545
|
-
|
|
11802
|
+
resolve7({ code, output });
|
|
11546
11803
|
};
|
|
11547
11804
|
const timer = setTimeout(() => finish(null), PROBE_TIMEOUT_MS);
|
|
11548
11805
|
timer.unref?.();
|
|
@@ -11625,8 +11882,8 @@ var CodexAdapter = class extends StdioAgentAdapter {
|
|
|
11625
11882
|
async handshake(opts) {
|
|
11626
11883
|
this.sandbox = await probeCodexSandbox(this.deps, opts.cwd, this.spawnEnv);
|
|
11627
11884
|
if (this.sandbox === "unavailable") throw new Error(codexSandboxUnavailableMessage());
|
|
11628
|
-
const ready = new Promise((
|
|
11629
|
-
this.settleHandshake =
|
|
11885
|
+
const ready = new Promise((resolve7) => {
|
|
11886
|
+
this.settleHandshake = resolve7;
|
|
11630
11887
|
});
|
|
11631
11888
|
this.writeLine({
|
|
11632
11889
|
jsonrpc: "2.0",
|
|
@@ -11639,8 +11896,8 @@ var CodexAdapter = class extends StdioAgentAdapter {
|
|
|
11639
11896
|
// A binary that rejects `app-server` outright dies immediately; waiting
|
|
11640
11897
|
// out the timeout would turn a one-line diagnostic into a 30s stall.
|
|
11641
11898
|
this.processEnded.then(() => false),
|
|
11642
|
-
new Promise((
|
|
11643
|
-
const t = setTimeout(() =>
|
|
11899
|
+
new Promise((resolve7) => {
|
|
11900
|
+
const t = setTimeout(() => resolve7(false), HANDSHAKE_TIMEOUT_MS);
|
|
11644
11901
|
t.unref?.();
|
|
11645
11902
|
})
|
|
11646
11903
|
]);
|
|
@@ -11917,12 +12174,12 @@ function describeError(err) {
|
|
|
11917
12174
|
return lines.join(": ");
|
|
11918
12175
|
}
|
|
11919
12176
|
function delay(ms, signal) {
|
|
11920
|
-
return new Promise((
|
|
11921
|
-
const timer = setTimeout(
|
|
12177
|
+
return new Promise((resolve7) => {
|
|
12178
|
+
const timer = setTimeout(resolve7, ms);
|
|
11922
12179
|
timer.unref?.();
|
|
11923
12180
|
signal?.addEventListener("abort", () => {
|
|
11924
12181
|
clearTimeout(timer);
|
|
11925
|
-
|
|
12182
|
+
resolve7();
|
|
11926
12183
|
}, { once: true });
|
|
11927
12184
|
});
|
|
11928
12185
|
}
|
|
@@ -11956,17 +12213,17 @@ var OpenCodeAdapter = class {
|
|
|
11956
12213
|
throw new ExecutableNotFoundError("opencode", PATH);
|
|
11957
12214
|
}
|
|
11958
12215
|
this.process = this.deps.spawn(launch.file, launch.args, {
|
|
11959
|
-
env: withPath(process.env, PATH),
|
|
12216
|
+
env: withPath(process.env, PATH, await (this.deps.workspaceEnv ?? workspaceEnvOf)(opts.cwd)),
|
|
11960
12217
|
stdio: ["pipe", "pipe", "pipe"],
|
|
11961
12218
|
cwd: opts.cwd,
|
|
11962
12219
|
windowsVerbatimArguments: launch.verbatim
|
|
11963
12220
|
});
|
|
11964
|
-
const banner = new Promise((
|
|
12221
|
+
const banner = new Promise((resolve7) => {
|
|
11965
12222
|
let seen = "";
|
|
11966
12223
|
const scan = (chunk) => {
|
|
11967
12224
|
seen += chunk.toString();
|
|
11968
12225
|
const match = seen.match(/https?:\/\/[^\s]+/);
|
|
11969
|
-
if (match)
|
|
12226
|
+
if (match) resolve7(match[0].replace(/[.,)]$/, ""));
|
|
11970
12227
|
};
|
|
11971
12228
|
this.process.stdout?.on("data", scan);
|
|
11972
12229
|
this.process.stderr?.on("data", (chunk) => {
|
|
@@ -11975,15 +12232,15 @@ var OpenCodeAdapter = class {
|
|
|
11975
12232
|
});
|
|
11976
12233
|
this.process.on("exit", () => {
|
|
11977
12234
|
this.exited = true;
|
|
11978
|
-
|
|
12235
|
+
resolve7(null);
|
|
11979
12236
|
});
|
|
11980
12237
|
this.process.on("error", (err) => {
|
|
11981
12238
|
this.exited = true;
|
|
11982
12239
|
this.stderrTail = (this.stderrTail + `
|
|
11983
12240
|
${err.message}`).slice(-STDERR_TAIL_CHARS2);
|
|
11984
|
-
|
|
12241
|
+
resolve7(null);
|
|
11985
12242
|
});
|
|
11986
|
-
const timer = setTimeout(() =>
|
|
12243
|
+
const timer = setTimeout(() => resolve7(null), SERVER_READY_TIMEOUT_MS);
|
|
11987
12244
|
timer.unref?.();
|
|
11988
12245
|
});
|
|
11989
12246
|
this.baseUrl = await banner;
|
|
@@ -12016,8 +12273,8 @@ ${this.stderrTail.trim()}` : ""}`);
|
|
|
12016
12273
|
const streamAbort = new AbortController();
|
|
12017
12274
|
let connected = () => {
|
|
12018
12275
|
};
|
|
12019
|
-
const streamReady = new Promise((
|
|
12020
|
-
connected =
|
|
12276
|
+
const streamReady = new Promise((resolve7) => {
|
|
12277
|
+
connected = resolve7;
|
|
12021
12278
|
});
|
|
12022
12279
|
const live = this.streamEvents(
|
|
12023
12280
|
streamAbort.signal,
|
|
@@ -12221,14 +12478,14 @@ ${part.text}` : part.text });
|
|
|
12221
12478
|
}
|
|
12222
12479
|
}
|
|
12223
12480
|
}
|
|
12224
|
-
async json(method,
|
|
12225
|
-
const response = await this.deps.fetch(`${this.baseUrl}${
|
|
12481
|
+
async json(method, path25, body, signal) {
|
|
12482
|
+
const response = await this.deps.fetch(`${this.baseUrl}${path25}`, {
|
|
12226
12483
|
method,
|
|
12227
12484
|
headers: { "content-type": "application/json" },
|
|
12228
12485
|
...body === void 0 ? {} : { body: JSON.stringify(body) },
|
|
12229
12486
|
...signal ? { signal } : {}
|
|
12230
12487
|
});
|
|
12231
|
-
if (!response.ok) throw new Error(`${method} ${
|
|
12488
|
+
if (!response.ok) throw new Error(`${method} ${path25} failed: ${response.status} ${response.statusText}`);
|
|
12232
12489
|
return await response.json().catch(() => null);
|
|
12233
12490
|
}
|
|
12234
12491
|
nativeSessionId() {
|
|
@@ -12682,8 +12939,8 @@ var CliAgentAiService = class {
|
|
|
12682
12939
|
this.lastNativeSessionId = previousSessionId;
|
|
12683
12940
|
}
|
|
12684
12941
|
}
|
|
12685
|
-
async researchAndPlan(userDescription, runners, modelsByRunner,
|
|
12686
|
-
const contextStr = await collectResearchContext(
|
|
12942
|
+
async researchAndPlan(userDescription, runners, modelsByRunner, fs15, onProgress, _fetcher, runnerModes, modes = DEFAULT_PLANNER_MODES, signal) {
|
|
12943
|
+
const contextStr = await collectResearchContext(fs15, runners);
|
|
12687
12944
|
const researchLog = [
|
|
12688
12945
|
{ id: `up-${Date.now()}`, type: "user_prompt", content: userDescription, timestamp: (/* @__PURE__ */ new Date()).toISOString() }
|
|
12689
12946
|
];
|
|
@@ -13582,25 +13839,25 @@ function retargetTaskRunner(task, runner, catalog) {
|
|
|
13582
13839
|
}
|
|
13583
13840
|
|
|
13584
13841
|
// src/services/SkillsService.ts
|
|
13585
|
-
import * as
|
|
13586
|
-
import * as
|
|
13842
|
+
import * as fs9 from "fs";
|
|
13843
|
+
import * as path18 from "path";
|
|
13587
13844
|
|
|
13588
13845
|
// src/services/builtinSkills.ts
|
|
13589
|
-
import * as
|
|
13846
|
+
import * as path17 from "path";
|
|
13590
13847
|
import { fileURLToPath } from "url";
|
|
13591
13848
|
function builtinSkillsDir() {
|
|
13592
|
-
const dir = typeof __dirname !== "undefined" && __dirname ? __dirname :
|
|
13849
|
+
const dir = typeof __dirname !== "undefined" && __dirname ? __dirname : path17.dirname(fileURLToPath(
|
|
13593
13850
|
// @ts-expect-error - import.meta is only valid under an ESM module setting
|
|
13594
13851
|
import.meta.url
|
|
13595
13852
|
));
|
|
13596
|
-
return
|
|
13853
|
+
return path17.join(dir, "..", "skills");
|
|
13597
13854
|
}
|
|
13598
13855
|
|
|
13599
13856
|
// src/services/SkillsService.ts
|
|
13600
13857
|
var BUILTIN_SKILL_NAMES = ["grilling", "to-spec", "improve-codebase-architecture"];
|
|
13601
13858
|
var RETIRED_BUILTIN_SKILL_NAMES = ["grill-me"];
|
|
13602
13859
|
function parseSkillFile(filePath, name, source) {
|
|
13603
|
-
const raw =
|
|
13860
|
+
const raw = fs9.readFileSync(filePath, "utf8");
|
|
13604
13861
|
const frontmatter = {};
|
|
13605
13862
|
let content = raw;
|
|
13606
13863
|
if (raw.startsWith("---\n")) {
|
|
@@ -13629,23 +13886,23 @@ function parseSkillFile(filePath, name, source) {
|
|
|
13629
13886
|
return { name, description: metadata.description, metadata, content, path: filePath, source };
|
|
13630
13887
|
}
|
|
13631
13888
|
function copyDirSync2(src, dest) {
|
|
13632
|
-
|
|
13633
|
-
const entries =
|
|
13889
|
+
fs9.mkdirSync(dest, { recursive: true });
|
|
13890
|
+
const entries = fs9.readdirSync(src, { withFileTypes: true });
|
|
13634
13891
|
for (const entry of entries) {
|
|
13635
|
-
const srcPath =
|
|
13636
|
-
const destPath =
|
|
13892
|
+
const srcPath = path18.join(src, entry.name);
|
|
13893
|
+
const destPath = path18.join(dest, entry.name);
|
|
13637
13894
|
if (entry.isDirectory()) copyDirSync2(srcPath, destPath);
|
|
13638
|
-
else
|
|
13895
|
+
else fs9.copyFileSync(srcPath, destPath);
|
|
13639
13896
|
}
|
|
13640
13897
|
}
|
|
13641
13898
|
function readDir(dir, source) {
|
|
13642
|
-
if (!
|
|
13643
|
-
const entries =
|
|
13899
|
+
if (!fs9.existsSync(dir)) return [];
|
|
13900
|
+
const entries = fs9.readdirSync(dir, { withFileTypes: true });
|
|
13644
13901
|
const skills = [];
|
|
13645
13902
|
for (const entry of entries) {
|
|
13646
13903
|
if (!entry.isDirectory()) continue;
|
|
13647
|
-
const skillFile =
|
|
13648
|
-
if (!
|
|
13904
|
+
const skillFile = path18.join(dir, entry.name, "SKILL.md");
|
|
13905
|
+
if (!fs9.existsSync(skillFile)) continue;
|
|
13649
13906
|
const parsed = parseSkillFile(skillFile, entry.name, source);
|
|
13650
13907
|
if (parsed) skills.push(parsed);
|
|
13651
13908
|
}
|
|
@@ -13657,22 +13914,22 @@ var SkillsService = class {
|
|
|
13657
13914
|
}
|
|
13658
13915
|
workspaceRoot;
|
|
13659
13916
|
localDir() {
|
|
13660
|
-
return this.workspaceRoot ?
|
|
13917
|
+
return this.workspaceRoot ? path18.join(this.workspaceRoot, ".ordewell", "skills") : void 0;
|
|
13661
13918
|
}
|
|
13662
13919
|
globalDir() {
|
|
13663
|
-
return
|
|
13920
|
+
return path18.join(globalDataDir(), "skills");
|
|
13664
13921
|
}
|
|
13665
13922
|
seed(name) {
|
|
13666
|
-
const dest =
|
|
13667
|
-
if (
|
|
13923
|
+
const dest = path18.join(this.globalDir(), name);
|
|
13924
|
+
if (fs9.existsSync(dest)) return false;
|
|
13668
13925
|
let srcDir;
|
|
13669
13926
|
try {
|
|
13670
|
-
srcDir =
|
|
13927
|
+
srcDir = path18.join(builtinSkillsDir(), name);
|
|
13671
13928
|
} catch (err) {
|
|
13672
13929
|
console.warn(`Could not locate built-in skills dir: ${String(err)}`);
|
|
13673
13930
|
return false;
|
|
13674
13931
|
}
|
|
13675
|
-
if (!
|
|
13932
|
+
if (!fs9.existsSync(srcDir)) {
|
|
13676
13933
|
console.warn(`Built-in skill not found in package: ${name}`);
|
|
13677
13934
|
return false;
|
|
13678
13935
|
}
|
|
@@ -13686,14 +13943,14 @@ var SkillsService = class {
|
|
|
13686
13943
|
isUnmodifiedRetiredSeed(dir, expectedName) {
|
|
13687
13944
|
let entries;
|
|
13688
13945
|
try {
|
|
13689
|
-
entries =
|
|
13946
|
+
entries = fs9.readdirSync(dir, { withFileTypes: true });
|
|
13690
13947
|
} catch {
|
|
13691
13948
|
return false;
|
|
13692
13949
|
}
|
|
13693
13950
|
if (entries.length !== 1 || entries[0].name !== "SKILL.md" || !entries[0].isFile()) return false;
|
|
13694
13951
|
let raw;
|
|
13695
13952
|
try {
|
|
13696
|
-
raw =
|
|
13953
|
+
raw = fs9.readFileSync(path18.join(dir, "SKILL.md"), "utf8");
|
|
13697
13954
|
} catch {
|
|
13698
13955
|
return false;
|
|
13699
13956
|
}
|
|
@@ -13710,11 +13967,11 @@ var SkillsService = class {
|
|
|
13710
13967
|
}
|
|
13711
13968
|
pruneRetired() {
|
|
13712
13969
|
for (const name of RETIRED_BUILTIN_SKILL_NAMES) {
|
|
13713
|
-
const dest =
|
|
13714
|
-
if (!
|
|
13970
|
+
const dest = path18.join(this.globalDir(), name);
|
|
13971
|
+
if (!fs9.existsSync(dest)) continue;
|
|
13715
13972
|
if (!this.isUnmodifiedRetiredSeed(dest, name)) continue;
|
|
13716
13973
|
try {
|
|
13717
|
-
|
|
13974
|
+
fs9.rmSync(dest, { recursive: true, force: true });
|
|
13718
13975
|
} catch (err) {
|
|
13719
13976
|
console.warn(`Could not prune retired skill ${name}: ${String(err)}`);
|
|
13720
13977
|
}
|
|
@@ -13725,11 +13982,11 @@ var SkillsService = class {
|
|
|
13725
13982
|
if (BUILTIN_SKILL_NAMES.includes(name)) this.seed(name);
|
|
13726
13983
|
const localDir = this.localDir();
|
|
13727
13984
|
if (localDir) {
|
|
13728
|
-
const localFile =
|
|
13729
|
-
if (
|
|
13985
|
+
const localFile = path18.join(localDir, name, "SKILL.md");
|
|
13986
|
+
if (fs9.existsSync(localFile)) return parseSkillFile(localFile, name, "local");
|
|
13730
13987
|
}
|
|
13731
|
-
const globalFile =
|
|
13732
|
-
if (
|
|
13988
|
+
const globalFile = path18.join(this.globalDir(), name, "SKILL.md");
|
|
13989
|
+
if (fs9.existsSync(globalFile)) return parseSkillFile(globalFile, name, "global");
|
|
13733
13990
|
return void 0;
|
|
13734
13991
|
}
|
|
13735
13992
|
listSkills() {
|
|
@@ -13756,8 +14013,8 @@ function createSkillsService(workspaceRoot) {
|
|
|
13756
14013
|
}
|
|
13757
14014
|
|
|
13758
14015
|
// src/utils/sessionStore.ts
|
|
13759
|
-
import * as
|
|
13760
|
-
import * as
|
|
14016
|
+
import * as fs10 from "fs";
|
|
14017
|
+
import * as path19 from "path";
|
|
13761
14018
|
|
|
13762
14019
|
// src/utils/sessionId.ts
|
|
13763
14020
|
import { randomBytes as randomBytes2 } from "crypto";
|
|
@@ -13768,7 +14025,7 @@ function mintSessionId() {
|
|
|
13768
14025
|
// src/utils/sessionStore.ts
|
|
13769
14026
|
var SESSIONS_SUBDIR = "sessions";
|
|
13770
14027
|
function getSessionsDir(baseDir) {
|
|
13771
|
-
return
|
|
14028
|
+
return path19.join(getStateDir(baseDir), SESSIONS_SUBDIR);
|
|
13772
14029
|
}
|
|
13773
14030
|
var REJECTION_MESSAGE = "This session was created with an older version of Ordewell. Please re-plan.";
|
|
13774
14031
|
function hasScalarRunner(parsed) {
|
|
@@ -13790,11 +14047,11 @@ function legacySessionFilename(session) {
|
|
|
13790
14047
|
return `${filenameStem(session)}_${session.id.slice(0, 8)}.json`;
|
|
13791
14048
|
}
|
|
13792
14049
|
function dropLegacyFile(sessionsDir, meta) {
|
|
13793
|
-
const legacy =
|
|
13794
|
-
if (legacy ===
|
|
14050
|
+
const legacy = path19.join(sessionsDir, legacySessionFilename(meta));
|
|
14051
|
+
if (legacy === path19.join(sessionsDir, sessionFilename(meta)) || !fs10.existsSync(legacy)) return;
|
|
13795
14052
|
try {
|
|
13796
|
-
const held = JSON.parse(
|
|
13797
|
-
if (held === meta.id)
|
|
14053
|
+
const held = JSON.parse(fs10.readFileSync(legacy, "utf-8")).meta?.id;
|
|
14054
|
+
if (held === meta.id) fs10.unlinkSync(legacy);
|
|
13798
14055
|
} catch {
|
|
13799
14056
|
}
|
|
13800
14057
|
}
|
|
@@ -13817,18 +14074,18 @@ function saveSession(plan, goal, baseDir, id) {
|
|
|
13817
14074
|
plan: JSON.parse(JSON.stringify(plan))
|
|
13818
14075
|
};
|
|
13819
14076
|
const filename = sessionFilename(meta);
|
|
13820
|
-
const filePath =
|
|
13821
|
-
|
|
14077
|
+
const filePath = path19.join(sessionsDir, filename);
|
|
14078
|
+
fs10.writeFileSync(filePath, JSON.stringify(session, null, 2));
|
|
13822
14079
|
dropLegacyFile(sessionsDir, meta);
|
|
13823
14080
|
return meta;
|
|
13824
14081
|
}
|
|
13825
14082
|
function listSessions(baseDir, logger = defaultLogger) {
|
|
13826
14083
|
const sessionsDir = getSessionsDir(baseDir);
|
|
13827
|
-
if (!
|
|
13828
|
-
const files =
|
|
14084
|
+
if (!fs10.existsSync(sessionsDir)) return [];
|
|
14085
|
+
const files = fs10.readdirSync(sessionsDir).filter((f) => f.endsWith(".json"));
|
|
13829
14086
|
return files.map((f) => {
|
|
13830
14087
|
try {
|
|
13831
|
-
const raw =
|
|
14088
|
+
const raw = fs10.readFileSync(path19.join(sessionsDir, f), "utf-8");
|
|
13832
14089
|
const parsed = JSON.parse(raw);
|
|
13833
14090
|
if (hasScalarRunner(parsed)) {
|
|
13834
14091
|
logger.warn("sessionStore", `session file ${f}: ${REJECTION_MESSAGE}`);
|
|
@@ -13843,10 +14100,10 @@ function listSessions(baseDir, logger = defaultLogger) {
|
|
|
13843
14100
|
}).filter((m) => m !== null).sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime());
|
|
13844
14101
|
}
|
|
13845
14102
|
function findSessionFile(sessionsDir, sessionId, logger) {
|
|
13846
|
-
const files =
|
|
14103
|
+
const files = fs10.readdirSync(sessionsDir).filter((f) => f.endsWith(".json"));
|
|
13847
14104
|
for (const f of files) {
|
|
13848
14105
|
try {
|
|
13849
|
-
const raw =
|
|
14106
|
+
const raw = fs10.readFileSync(path19.join(sessionsDir, f), "utf-8");
|
|
13850
14107
|
const session = JSON.parse(raw);
|
|
13851
14108
|
if (session.meta.id === sessionId) return f;
|
|
13852
14109
|
} catch (err) {
|
|
@@ -13857,11 +14114,11 @@ function findSessionFile(sessionsDir, sessionId, logger) {
|
|
|
13857
14114
|
}
|
|
13858
14115
|
function loadSession(sessionId, baseDir, logger = defaultLogger) {
|
|
13859
14116
|
const sessionsDir = getSessionsDir(baseDir);
|
|
13860
|
-
if (!
|
|
14117
|
+
if (!fs10.existsSync(sessionsDir)) return null;
|
|
13861
14118
|
try {
|
|
13862
14119
|
const file = findSessionFile(sessionsDir, sessionId, logger);
|
|
13863
14120
|
if (file) {
|
|
13864
|
-
const raw =
|
|
14121
|
+
const raw = fs10.readFileSync(path19.join(sessionsDir, file), "utf-8");
|
|
13865
14122
|
const parsed = JSON.parse(raw);
|
|
13866
14123
|
if (hasScalarRunner(parsed)) {
|
|
13867
14124
|
logger.warn("sessionStore", `session file ${file}: ${REJECTION_MESSAGE}`);
|
|
@@ -13892,11 +14149,11 @@ function getLatestSession(baseDir, logger = defaultLogger) {
|
|
|
13892
14149
|
}
|
|
13893
14150
|
function deleteSession(sessionId, baseDir, logger = defaultLogger) {
|
|
13894
14151
|
const sessionsDir = getSessionsDir(baseDir);
|
|
13895
|
-
if (!
|
|
14152
|
+
if (!fs10.existsSync(sessionsDir)) return false;
|
|
13896
14153
|
try {
|
|
13897
14154
|
const file = findSessionFile(sessionsDir, sessionId, logger);
|
|
13898
14155
|
if (file) {
|
|
13899
|
-
|
|
14156
|
+
fs10.unlinkSync(path19.join(sessionsDir, file));
|
|
13900
14157
|
return true;
|
|
13901
14158
|
}
|
|
13902
14159
|
} catch (err) {
|
|
@@ -14577,6 +14834,10 @@ var Session = class {
|
|
|
14577
14834
|
}
|
|
14578
14835
|
await this.orchestrator.runTask(taskId);
|
|
14579
14836
|
}
|
|
14837
|
+
/** Start whatever the scheduler can now fit — after the parallel limit was raised mid-run, say. */
|
|
14838
|
+
async reschedule() {
|
|
14839
|
+
await this.orchestrator.tick();
|
|
14840
|
+
}
|
|
14580
14841
|
async retryTask(taskId) {
|
|
14581
14842
|
await this.orchestrator.retryTask(taskId);
|
|
14582
14843
|
this.persist();
|
|
@@ -15457,7 +15718,7 @@ var HeadlessRunner = class extends AbstractRunner {
|
|
|
15457
15718
|
return {
|
|
15458
15719
|
launch: await planDirectLaunch(command, args, this.launchDeps),
|
|
15459
15720
|
resolvedPath,
|
|
15460
|
-
env: invocation.env,
|
|
15721
|
+
env: { ...opts.env, ...invocation.env },
|
|
15461
15722
|
pty,
|
|
15462
15723
|
submitPromptKey: invocation.submitPromptKey
|
|
15463
15724
|
};
|
|
@@ -15476,11 +15737,11 @@ var HeadlessRunner = class extends AbstractRunner {
|
|
|
15476
15737
|
};
|
|
15477
15738
|
|
|
15478
15739
|
// src/services/TmuxRunner.ts
|
|
15479
|
-
import { execFile as
|
|
15740
|
+
import { execFile as execFile4 } from "child_process";
|
|
15480
15741
|
import { promisify as promisify5 } from "util";
|
|
15481
15742
|
import { tmpdir } from "os";
|
|
15482
|
-
import { join as
|
|
15483
|
-
import { existsSync as existsSync11, readFileSync as
|
|
15743
|
+
import { join as join17 } from "path";
|
|
15744
|
+
import { existsSync as existsSync11, readFileSync as readFileSync8, unlinkSync as unlinkSync2, writeFileSync as writeFileSync6 } from "fs";
|
|
15484
15745
|
|
|
15485
15746
|
// src/utils/tmux.ts
|
|
15486
15747
|
import { execSync as execSync2 } from "child_process";
|
|
@@ -15531,7 +15792,7 @@ function clipboardCopyCommand(hasBin = defaultHasBin, platform = process.platfor
|
|
|
15531
15792
|
|
|
15532
15793
|
// src/services/TmuxRunner.ts
|
|
15533
15794
|
var EXIT_RE = /<<<ORDEWELL_TMUX_EXIT:(\d+)>>>/;
|
|
15534
|
-
var execFileAsync2 = promisify5(
|
|
15795
|
+
var execFileAsync2 = promisify5(execFile4);
|
|
15535
15796
|
var defaultExecFile3 = async (command, args) => {
|
|
15536
15797
|
const { stdout, stderr } = await execFileAsync2(command, args);
|
|
15537
15798
|
return { stdout: String(stdout), stderr: String(stderr) };
|
|
@@ -15587,7 +15848,7 @@ var TmuxSession = class extends AbstractTerminalSession {
|
|
|
15587
15848
|
if (this.exited) return;
|
|
15588
15849
|
let content;
|
|
15589
15850
|
try {
|
|
15590
|
-
content = existsSync11(this.logPath) ?
|
|
15851
|
+
content = existsSync11(this.logPath) ? readFileSync8(this.logPath, "utf8") : "";
|
|
15591
15852
|
} catch {
|
|
15592
15853
|
return;
|
|
15593
15854
|
}
|
|
@@ -15766,10 +16027,10 @@ var TmuxRunner = class extends AbstractRunner {
|
|
|
15766
16027
|
windowName,
|
|
15767
16028
|
this.execFileImpl,
|
|
15768
16029
|
this.pollIntervalMs,
|
|
15769
|
-
|
|
16030
|
+
join17(this.logDir, `${id}.log`),
|
|
15770
16031
|
this.socket
|
|
15771
16032
|
);
|
|
15772
|
-
invocation.env = { ...invocation.env, PATH: await this.resolvePath() };
|
|
16033
|
+
invocation.env = { ...opts.env, ...invocation.env, PATH: await this.resolvePath() };
|
|
15773
16034
|
await session.start(invocation.command, invocation.args, opts.cwd, invocation.env);
|
|
15774
16035
|
if (invocation.submitPromptKey) session.write("\r");
|
|
15775
16036
|
this.registerSession(id, session);
|
|
@@ -15779,8 +16040,8 @@ var TmuxRunner = class extends AbstractRunner {
|
|
|
15779
16040
|
|
|
15780
16041
|
// src/utils/daemonToken.ts
|
|
15781
16042
|
import { randomBytes as randomBytes3, timingSafeEqual } from "crypto";
|
|
15782
|
-
import { closeSync, mkdirSync as mkdirSync9, openSync, readFileSync as
|
|
15783
|
-
import { join as
|
|
16043
|
+
import { closeSync, mkdirSync as mkdirSync9, openSync, readFileSync as readFileSync9, unlinkSync as unlinkSync3, writeSync } from "fs";
|
|
16044
|
+
import { join as join18 } from "path";
|
|
15784
16045
|
var BEARER_PREFIX = "bearer ";
|
|
15785
16046
|
var DAEMON_TOKEN_SUBPROTOCOL_PREFIX = "ordewell.token.";
|
|
15786
16047
|
var DAEMON_SUBPROTOCOL = "ordewell.v1";
|
|
@@ -15788,7 +16049,7 @@ function configDir() {
|
|
|
15788
16049
|
return globalDataDir();
|
|
15789
16050
|
}
|
|
15790
16051
|
function daemonTokenPath(port) {
|
|
15791
|
-
return
|
|
16052
|
+
return join18(configDir(), `server-${port}.token`);
|
|
15792
16053
|
}
|
|
15793
16054
|
function mintDaemonToken(port) {
|
|
15794
16055
|
const token = randomBytes3(32).toString("base64url");
|
|
@@ -15808,7 +16069,7 @@ function mintDaemonToken(port) {
|
|
|
15808
16069
|
}
|
|
15809
16070
|
function readDaemonToken(port) {
|
|
15810
16071
|
try {
|
|
15811
|
-
const token =
|
|
16072
|
+
const token = readFileSync9(daemonTokenPath(port), "utf8").trim();
|
|
15812
16073
|
return token === "" ? void 0 : token;
|
|
15813
16074
|
} catch {
|
|
15814
16075
|
return void 0;
|
|
@@ -15849,18 +16110,18 @@ function tokensMatch(presented, expected) {
|
|
|
15849
16110
|
}
|
|
15850
16111
|
|
|
15851
16112
|
// src/plugins/RunnerRegistry.ts
|
|
15852
|
-
import * as
|
|
15853
|
-
import * as
|
|
15854
|
-
import * as
|
|
16113
|
+
import * as fs12 from "fs";
|
|
16114
|
+
import * as path22 from "path";
|
|
16115
|
+
import * as os6 from "os";
|
|
15855
16116
|
import { execFileSync } from "child_process";
|
|
15856
16117
|
import { randomBytes as randomBytes4 } from "crypto";
|
|
15857
16118
|
|
|
15858
16119
|
// src/plugins/FsPluginStore.ts
|
|
15859
|
-
import * as
|
|
15860
|
-
import * as
|
|
16120
|
+
import * as fs11 from "fs";
|
|
16121
|
+
import * as path21 from "path";
|
|
15861
16122
|
|
|
15862
16123
|
// src/plugins/pluginNames.ts
|
|
15863
|
-
import * as
|
|
16124
|
+
import * as path20 from "path";
|
|
15864
16125
|
var PLUGIN_NAME_PATTERN = /^[A-Za-z0-9](?:[A-Za-z0-9._-]*[A-Za-z0-9])?$/;
|
|
15865
16126
|
var MAX_PLUGIN_NAME_LENGTH = 64;
|
|
15866
16127
|
function isPlainPluginName(name) {
|
|
@@ -15875,10 +16136,10 @@ function assertPlainPluginName(name) {
|
|
|
15875
16136
|
}
|
|
15876
16137
|
function resolvePluginInstallDir(pluginsDir, name) {
|
|
15877
16138
|
assertPlainPluginName(name);
|
|
15878
|
-
const root =
|
|
15879
|
-
const dest =
|
|
15880
|
-
const relative5 =
|
|
15881
|
-
if (relative5 !== name || relative5 === "" ||
|
|
16139
|
+
const root = path20.resolve(pluginsDir);
|
|
16140
|
+
const dest = path20.resolve(root, name);
|
|
16141
|
+
const relative5 = path20.relative(root, dest);
|
|
16142
|
+
if (relative5 !== name || relative5 === "" || path20.isAbsolute(relative5)) {
|
|
15882
16143
|
throw new Error(`Plugin install path escapes the plugins directory: ${JSON.stringify(name)}`);
|
|
15883
16144
|
}
|
|
15884
16145
|
return dest;
|
|
@@ -15893,13 +16154,13 @@ function isValidManifest(obj) {
|
|
|
15893
16154
|
|
|
15894
16155
|
// src/plugins/FsPluginStore.ts
|
|
15895
16156
|
function userPluginsDir() {
|
|
15896
|
-
return
|
|
16157
|
+
return path21.join(globalDataDir(), "plugins");
|
|
15897
16158
|
}
|
|
15898
16159
|
function loadManifestFromDir(dir) {
|
|
15899
|
-
const manifestPath =
|
|
15900
|
-
if (!
|
|
16160
|
+
const manifestPath = path21.join(dir, "manifest.json");
|
|
16161
|
+
if (!fs11.existsSync(manifestPath)) return null;
|
|
15901
16162
|
try {
|
|
15902
|
-
const raw =
|
|
16163
|
+
const raw = fs11.readFileSync(manifestPath, "utf8");
|
|
15903
16164
|
const parsed = JSON.parse(raw);
|
|
15904
16165
|
if (!isValidManifest(parsed)) return null;
|
|
15905
16166
|
return parsed;
|
|
@@ -15909,29 +16170,29 @@ function loadManifestFromDir(dir) {
|
|
|
15909
16170
|
}
|
|
15910
16171
|
function copyDirSync3(src, dest) {
|
|
15911
16172
|
ensureDir(dest);
|
|
15912
|
-
const entries =
|
|
16173
|
+
const entries = fs11.readdirSync(src, { withFileTypes: true });
|
|
15913
16174
|
for (const entry of entries) {
|
|
15914
|
-
const srcPath =
|
|
15915
|
-
const destPath =
|
|
16175
|
+
const srcPath = path21.join(src, entry.name);
|
|
16176
|
+
const destPath = path21.join(dest, entry.name);
|
|
15916
16177
|
if (entry.isDirectory()) {
|
|
15917
16178
|
copyDirSync3(srcPath, destPath);
|
|
15918
16179
|
} else {
|
|
15919
|
-
|
|
16180
|
+
fs11.copyFileSync(srcPath, destPath);
|
|
15920
16181
|
}
|
|
15921
16182
|
}
|
|
15922
16183
|
}
|
|
15923
16184
|
function rmDirSync(dir) {
|
|
15924
|
-
if (!
|
|
15925
|
-
const entries =
|
|
16185
|
+
if (!fs11.existsSync(dir)) return;
|
|
16186
|
+
const entries = fs11.readdirSync(dir, { withFileTypes: true });
|
|
15926
16187
|
for (const entry of entries) {
|
|
15927
|
-
const p =
|
|
16188
|
+
const p = path21.join(dir, entry.name);
|
|
15928
16189
|
if (entry.isDirectory()) {
|
|
15929
16190
|
rmDirSync(p);
|
|
15930
16191
|
} else {
|
|
15931
|
-
|
|
16192
|
+
fs11.unlinkSync(p);
|
|
15932
16193
|
}
|
|
15933
16194
|
}
|
|
15934
|
-
|
|
16195
|
+
fs11.rmdirSync(dir);
|
|
15935
16196
|
}
|
|
15936
16197
|
var FsPluginStore = class {
|
|
15937
16198
|
getUserPluginsDir() {
|
|
@@ -15939,10 +16200,10 @@ var FsPluginStore = class {
|
|
|
15939
16200
|
}
|
|
15940
16201
|
listUserPluginDirs() {
|
|
15941
16202
|
const dir = userPluginsDir();
|
|
15942
|
-
if (!
|
|
16203
|
+
if (!fs11.existsSync(dir)) return [];
|
|
15943
16204
|
let entries;
|
|
15944
16205
|
try {
|
|
15945
|
-
entries =
|
|
16206
|
+
entries = fs11.readdirSync(dir, { withFileTypes: true });
|
|
15946
16207
|
} catch {
|
|
15947
16208
|
return [];
|
|
15948
16209
|
}
|
|
@@ -15950,7 +16211,7 @@ var FsPluginStore = class {
|
|
|
15950
16211
|
}
|
|
15951
16212
|
listDir(dir) {
|
|
15952
16213
|
try {
|
|
15953
|
-
return
|
|
16214
|
+
return fs11.readdirSync(dir);
|
|
15954
16215
|
} catch {
|
|
15955
16216
|
return [];
|
|
15956
16217
|
}
|
|
@@ -15968,20 +16229,20 @@ var FsPluginStore = class {
|
|
|
15968
16229
|
ensureDir(dir);
|
|
15969
16230
|
}
|
|
15970
16231
|
writeFile(filePath, content) {
|
|
15971
|
-
|
|
16232
|
+
fs11.writeFileSync(filePath, content);
|
|
15972
16233
|
}
|
|
15973
16234
|
readFile(filePath) {
|
|
15974
16235
|
try {
|
|
15975
|
-
return
|
|
16236
|
+
return fs11.readFileSync(filePath, "utf8");
|
|
15976
16237
|
} catch {
|
|
15977
16238
|
return null;
|
|
15978
16239
|
}
|
|
15979
16240
|
}
|
|
15980
|
-
dirExists(
|
|
15981
|
-
return
|
|
16241
|
+
dirExists(path25) {
|
|
16242
|
+
return fs11.existsSync(path25) && fs11.statSync(path25).isDirectory();
|
|
15982
16243
|
}
|
|
15983
|
-
exists(
|
|
15984
|
-
return
|
|
16244
|
+
exists(path25) {
|
|
16245
|
+
return fs11.existsSync(path25);
|
|
15985
16246
|
}
|
|
15986
16247
|
};
|
|
15987
16248
|
|
|
@@ -16008,7 +16269,15 @@ var CLAUDE_CODE_MANIFEST = {
|
|
|
16008
16269
|
"{{/if}}",
|
|
16009
16270
|
"{{prompt}}"
|
|
16010
16271
|
],
|
|
16011
|
-
promptInArgs: true
|
|
16272
|
+
promptInArgs: true,
|
|
16273
|
+
// Interactive sessions show these even with --dangerously-skip-permissions:
|
|
16274
|
+
// a folder Claude Code has never been trusted in (a fresh clone, or a repo
|
|
16275
|
+
// only ever planned in headlessly), and the first Bypass Permissions run.
|
|
16276
|
+
blockingPrompts: [
|
|
16277
|
+
{ phrase: "Is this a project you created or one you trust", asks: "whether to trust the task's folder" },
|
|
16278
|
+
{ phrase: "Do you trust the files in this folder", asks: "whether to trust the task's folder" },
|
|
16279
|
+
{ phrase: "running in Bypass Permissions mode", asks: "you to accept Bypass Permissions mode" }
|
|
16280
|
+
]
|
|
16012
16281
|
},
|
|
16013
16282
|
features: {
|
|
16014
16283
|
modelSelection: true,
|
|
@@ -16413,7 +16682,7 @@ var RunnerRegistry = class {
|
|
|
16413
16682
|
return entry?.source === "builtin";
|
|
16414
16683
|
}
|
|
16415
16684
|
installFromPath(sourcePath) {
|
|
16416
|
-
const absPath =
|
|
16685
|
+
const absPath = path22.resolve(sourcePath);
|
|
16417
16686
|
if (!this.store.dirExists(absPath)) {
|
|
16418
16687
|
throw new Error(`Plugin source must be a directory: ${absPath}`);
|
|
16419
16688
|
}
|
|
@@ -16425,7 +16694,7 @@ var RunnerRegistry = class {
|
|
|
16425
16694
|
}
|
|
16426
16695
|
installFromGit(url) {
|
|
16427
16696
|
const validated = assertInstallablePluginUrl(url);
|
|
16428
|
-
const tmpDir =
|
|
16697
|
+
const tmpDir = path22.join(os6.tmpdir(), `ordewell-plugin-${randomBytes4(12).toString("hex")}`);
|
|
16429
16698
|
this.store.ensureDir(tmpDir);
|
|
16430
16699
|
try {
|
|
16431
16700
|
try {
|
|
@@ -16438,7 +16707,7 @@ var RunnerRegistry = class {
|
|
|
16438
16707
|
const hasManifest = contents.some((f) => f === "manifest.json");
|
|
16439
16708
|
let sourceDir = tmpDir;
|
|
16440
16709
|
if (!hasManifest) {
|
|
16441
|
-
const onlyEntry = contents.length === 1 ?
|
|
16710
|
+
const onlyEntry = contents.length === 1 ? path22.join(tmpDir, contents[0]) : void 0;
|
|
16442
16711
|
if (!onlyEntry || !this.store.dirExists(onlyEntry)) {
|
|
16443
16712
|
throw new Error(`No manifest.json found in repository: ${validated.toString()}`);
|
|
16444
16713
|
}
|
|
@@ -16486,7 +16755,7 @@ var RunnerRegistry = class {
|
|
|
16486
16755
|
}
|
|
16487
16756
|
createSkeleton(name, outputDir) {
|
|
16488
16757
|
assertPlainPluginName(name);
|
|
16489
|
-
const dir =
|
|
16758
|
+
const dir = path22.resolve(outputDir, name);
|
|
16490
16759
|
this.store.ensureDir(dir);
|
|
16491
16760
|
const manifest = {
|
|
16492
16761
|
name,
|
|
@@ -16518,8 +16787,8 @@ var RunnerRegistry = class {
|
|
|
16518
16787
|
]
|
|
16519
16788
|
}
|
|
16520
16789
|
};
|
|
16521
|
-
this.store.writeFile(
|
|
16522
|
-
const runSh =
|
|
16790
|
+
this.store.writeFile(path22.join(dir, "manifest.json"), JSON.stringify(manifest, null, 2) + "\n");
|
|
16791
|
+
const runSh = path22.join(dir, "run.sh");
|
|
16523
16792
|
const scriptContent = `#!/usr/bin/env bash
|
|
16524
16793
|
# Custom runner script for ${name}
|
|
16525
16794
|
# Receives:
|
|
@@ -16545,30 +16814,30 @@ echo "Running ${name} with prompt: $prompt"
|
|
|
16545
16814
|
};
|
|
16546
16815
|
function fsChmodSync(fpath, mode) {
|
|
16547
16816
|
try {
|
|
16548
|
-
|
|
16817
|
+
fs12.chmodSync(fpath, mode);
|
|
16549
16818
|
} catch {
|
|
16550
16819
|
}
|
|
16551
16820
|
}
|
|
16552
16821
|
|
|
16553
16822
|
// src/utils/stateStore.ts
|
|
16554
|
-
import * as
|
|
16555
|
-
import * as
|
|
16823
|
+
import * as fs13 from "fs";
|
|
16824
|
+
import * as path23 from "path";
|
|
16556
16825
|
var STATE_FILE = "state.json";
|
|
16557
16826
|
function getStatePath(baseDir) {
|
|
16558
|
-
return
|
|
16827
|
+
return path23.join(getStateDir(baseDir), STATE_FILE);
|
|
16559
16828
|
}
|
|
16560
16829
|
function saveState(plan, baseDir) {
|
|
16561
16830
|
const statePath = getStatePath(baseDir ?? process.cwd());
|
|
16562
|
-
ensureDir(
|
|
16831
|
+
ensureDir(path23.dirname(statePath));
|
|
16563
16832
|
plan.lastUpdated = (/* @__PURE__ */ new Date()).toISOString();
|
|
16564
|
-
|
|
16833
|
+
fs13.writeFileSync(statePath, JSON.stringify(plan, null, 2));
|
|
16565
16834
|
}
|
|
16566
16835
|
function loadState(baseDir, logger = defaultLogger) {
|
|
16567
16836
|
const dir = baseDir ?? process.cwd();
|
|
16568
16837
|
const statePath = getStatePath(dir);
|
|
16569
|
-
if (!
|
|
16838
|
+
if (!fs13.existsSync(statePath)) return null;
|
|
16570
16839
|
try {
|
|
16571
|
-
const raw =
|
|
16840
|
+
const raw = fs13.readFileSync(statePath, "utf-8");
|
|
16572
16841
|
const parsed = JSON.parse(raw);
|
|
16573
16842
|
if (!Array.isArray(parsed.runners)) {
|
|
16574
16843
|
logger.warn("stateStore", `rejecting state from ${statePath}: old format (scalar "runner" instead of "runners" array). Please re-plan.`);
|
|
@@ -16585,16 +16854,16 @@ function loadState(baseDir, logger = defaultLogger) {
|
|
|
16585
16854
|
function clearState(baseDir) {
|
|
16586
16855
|
const dir = baseDir ?? process.cwd();
|
|
16587
16856
|
const statePath = getStatePath(dir);
|
|
16588
|
-
if (
|
|
16857
|
+
if (fs13.existsSync(statePath)) fs13.unlinkSync(statePath);
|
|
16589
16858
|
}
|
|
16590
16859
|
function stateExists(baseDir) {
|
|
16591
16860
|
const dir = baseDir ?? process.cwd();
|
|
16592
|
-
return
|
|
16861
|
+
return fs13.existsSync(getStatePath(dir));
|
|
16593
16862
|
}
|
|
16594
16863
|
|
|
16595
16864
|
// src/services/SettingsService.ts
|
|
16596
|
-
import * as
|
|
16597
|
-
import * as
|
|
16865
|
+
import * as fs14 from "fs";
|
|
16866
|
+
import * as path24 from "path";
|
|
16598
16867
|
var DEFAULTS = {
|
|
16599
16868
|
tdd: { enabled: true },
|
|
16600
16869
|
verification: { enabled: false }
|
|
@@ -16602,7 +16871,7 @@ var DEFAULTS = {
|
|
|
16602
16871
|
function getSettingsPath() {
|
|
16603
16872
|
const override = process.env.ORDEWELL_SETTINGS_PATH;
|
|
16604
16873
|
if (override && override.trim()) return override.trim();
|
|
16605
|
-
return
|
|
16874
|
+
return path24.join(globalDataDir(), "settings.json");
|
|
16606
16875
|
}
|
|
16607
16876
|
function readPlannerModels(raw) {
|
|
16608
16877
|
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return void 0;
|
|
@@ -16635,7 +16904,7 @@ var SettingsService = class {
|
|
|
16635
16904
|
}
|
|
16636
16905
|
fileMtimeMs() {
|
|
16637
16906
|
try {
|
|
16638
|
-
return
|
|
16907
|
+
return fs14.statSync(this.filePath).mtimeMs;
|
|
16639
16908
|
} catch {
|
|
16640
16909
|
return null;
|
|
16641
16910
|
}
|
|
@@ -16707,8 +16976,8 @@ var SettingsService = class {
|
|
|
16707
16976
|
load() {
|
|
16708
16977
|
migrateOldConfigDir();
|
|
16709
16978
|
try {
|
|
16710
|
-
if (
|
|
16711
|
-
const raw = JSON.parse(
|
|
16979
|
+
if (fs14.existsSync(this.filePath)) {
|
|
16980
|
+
const raw = JSON.parse(fs14.readFileSync(this.filePath, "utf-8"));
|
|
16712
16981
|
const settings = {
|
|
16713
16982
|
tdd: { enabled: raw.tdd?.enabled ?? DEFAULTS.tdd.enabled },
|
|
16714
16983
|
verification: { enabled: raw.verification?.enabled ?? DEFAULTS.verification.enabled }
|
|
@@ -16730,11 +16999,11 @@ var SettingsService = class {
|
|
|
16730
16999
|
return { ...DEFAULTS, tdd: { ...DEFAULTS.tdd }, verification: { ...DEFAULTS.verification } };
|
|
16731
17000
|
}
|
|
16732
17001
|
persist() {
|
|
16733
|
-
const dir =
|
|
16734
|
-
if (!
|
|
16735
|
-
|
|
17002
|
+
const dir = path24.dirname(this.filePath);
|
|
17003
|
+
if (!fs14.existsSync(dir)) {
|
|
17004
|
+
fs14.mkdirSync(dir, { recursive: true });
|
|
16736
17005
|
}
|
|
16737
|
-
|
|
17006
|
+
fs14.writeFileSync(this.filePath, JSON.stringify(this.cache, null, 2));
|
|
16738
17007
|
this.cachedMtimeMs = this.fileMtimeMs();
|
|
16739
17008
|
}
|
|
16740
17009
|
};
|
|
@@ -16800,6 +17069,7 @@ export {
|
|
|
16800
17069
|
ConversationEditError,
|
|
16801
17070
|
DAEMON_SUBPROTOCOL,
|
|
16802
17071
|
DAEMON_TOKEN_SUBPROTOCOL_PREFIX,
|
|
17072
|
+
DEFAULT_MAX_PARALLEL,
|
|
16803
17073
|
DENY_ALL,
|
|
16804
17074
|
EmbeddedNewlineError,
|
|
16805
17075
|
EnvConfig,
|
|
@@ -16982,6 +17252,7 @@ export {
|
|
|
16982
17252
|
modifyValidationFeedback,
|
|
16983
17253
|
normalizeAgentArgs,
|
|
16984
17254
|
normalizeGeminiModel,
|
|
17255
|
+
parseMaxParallel,
|
|
16985
17256
|
parsePartialPlan,
|
|
16986
17257
|
parsePlanJson,
|
|
16987
17258
|
parseTaskOpsJson,
|