@ordewell/core 0.5.1 → 0.5.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/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.js
CHANGED
|
@@ -56,6 +56,7 @@ __export(index_exports, {
|
|
|
56
56
|
ConversationEditError: () => ConversationEditError,
|
|
57
57
|
DAEMON_SUBPROTOCOL: () => DAEMON_SUBPROTOCOL,
|
|
58
58
|
DAEMON_TOKEN_SUBPROTOCOL_PREFIX: () => DAEMON_TOKEN_SUBPROTOCOL_PREFIX,
|
|
59
|
+
DEFAULT_MAX_PARALLEL: () => DEFAULT_MAX_PARALLEL,
|
|
59
60
|
DENY_ALL: () => DENY_ALL,
|
|
60
61
|
EmbeddedNewlineError: () => EmbeddedNewlineError,
|
|
61
62
|
EnvConfig: () => EnvConfig,
|
|
@@ -238,6 +239,7 @@ __export(index_exports, {
|
|
|
238
239
|
modifyValidationFeedback: () => modifyValidationFeedback,
|
|
239
240
|
normalizeAgentArgs: () => normalizeAgentArgs,
|
|
240
241
|
normalizeGeminiModel: () => normalizeGeminiModel,
|
|
242
|
+
parseMaxParallel: () => parseMaxParallel,
|
|
241
243
|
parsePartialPlan: () => parsePartialPlan,
|
|
242
244
|
parsePlanJson: () => parsePlanJson,
|
|
243
245
|
parseTaskOpsJson: () => parseTaskOpsJson,
|
|
@@ -3191,33 +3193,33 @@ function grepOptions(args) {
|
|
|
3191
3193
|
};
|
|
3192
3194
|
}
|
|
3193
3195
|
var STOPPED_TOOL_RESULT = "Stopped by the user \u2014 this call was not executed, and no further tool calls will run this turn.";
|
|
3194
|
-
async function executeTool(name, args,
|
|
3195
|
-
const outcome = await runTool(name, args,
|
|
3196
|
+
async function executeTool(name, args, fs15, fetcher, signal) {
|
|
3197
|
+
const outcome = await runTool(name, args, fs15, fetcher, signal);
|
|
3196
3198
|
return { ...outcome, output: redactSecrets(outcome.output) };
|
|
3197
3199
|
}
|
|
3198
|
-
async function runTool(name, args,
|
|
3200
|
+
async function runTool(name, args, fs15, fetcher, signal) {
|
|
3199
3201
|
if (signal?.aborted) return { success: false, output: STOPPED_TOOL_RESULT, truncated: false };
|
|
3200
3202
|
switch (name) {
|
|
3201
3203
|
case "read_file": {
|
|
3202
3204
|
const offset = num(args.offset);
|
|
3203
3205
|
const limit = num(args.limit);
|
|
3204
3206
|
const maxBytes = num(args.maxBytes);
|
|
3205
|
-
return
|
|
3207
|
+
return fs15.readFile(String(args.path ?? ""), { offset, limit, maxBytes });
|
|
3206
3208
|
}
|
|
3207
3209
|
case "read_files": {
|
|
3208
3210
|
const paths = Array.isArray(args.paths) ? args.paths.map(String) : [];
|
|
3209
|
-
return
|
|
3211
|
+
return fs15.readFiles(paths);
|
|
3210
3212
|
}
|
|
3211
3213
|
case "glob":
|
|
3212
|
-
return
|
|
3214
|
+
return fs15.glob(String(args.pattern ?? ""), { path: str(args.path), headLimit: num(args.head_limit ?? args.headLimit) });
|
|
3213
3215
|
case "grep":
|
|
3214
|
-
return
|
|
3216
|
+
return fs15.grep(String(args.pattern ?? ""), grepOptions(args));
|
|
3215
3217
|
case "find_symbol":
|
|
3216
|
-
return
|
|
3218
|
+
return fs15.findSymbol(String(args.symbol ?? ""), { language: str(args.language), path: str(args.path) });
|
|
3217
3219
|
case "list_dir":
|
|
3218
|
-
return
|
|
3220
|
+
return fs15.listDir(String(args.path ?? ""), num(args.depth));
|
|
3219
3221
|
case "bash":
|
|
3220
|
-
return
|
|
3222
|
+
return fs15.bash(String(args.command ?? ""), signal);
|
|
3221
3223
|
case "fetch": {
|
|
3222
3224
|
if (!fetcher) return { success: false, output: "Web fetching is not available in this environment.", truncated: false };
|
|
3223
3225
|
const url = String(args.url ?? "");
|
|
@@ -3325,14 +3327,14 @@ var PendingApprovals = class {
|
|
|
3325
3327
|
ask(request) {
|
|
3326
3328
|
const id = `ap-${Date.now()}-${counter++}`;
|
|
3327
3329
|
const pending = { id, request, createdAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
3328
|
-
return new Promise((
|
|
3330
|
+
return new Promise((resolve7) => {
|
|
3329
3331
|
const settle = (granted) => {
|
|
3330
3332
|
const entry = this.entries.get(id);
|
|
3331
3333
|
if (!entry) return;
|
|
3332
3334
|
clearTimeout(entry.timer);
|
|
3333
3335
|
this.entries.delete(id);
|
|
3334
3336
|
this.opts.onSettled?.(id, granted);
|
|
3335
|
-
|
|
3337
|
+
resolve7(granted);
|
|
3336
3338
|
};
|
|
3337
3339
|
const timer = setTimeout(() => settle(false), this.opts.timeoutMs ?? DEFAULT_TIMEOUT_MS);
|
|
3338
3340
|
timer.unref?.();
|
|
@@ -3430,9 +3432,9 @@ function applyHeadLimit(stdout, headLimit) {
|
|
|
3430
3432
|
}
|
|
3431
3433
|
function formatSearchOutput(capped, root, opts) {
|
|
3432
3434
|
if (capped.rows.length === 0) return opts.emptyMessage;
|
|
3433
|
-
const
|
|
3434
|
-
const prefix = root ===
|
|
3435
|
-
const body = prefix ===
|
|
3435
|
+
const sep2 = opts.sep ?? import_node_path.default.sep;
|
|
3436
|
+
const prefix = root === sep2 || root.endsWith(sep2) ? root : `${root}${sep2}`;
|
|
3437
|
+
const body = prefix === sep2 ? capped.rows.join("\n") : capped.rows.map((row) => row.startsWith(prefix) ? row.slice(prefix.length) : row).join("\n");
|
|
3436
3438
|
if (!capped.truncated) return body;
|
|
3437
3439
|
const hint = opts.hint ? ` ${opts.hint}` : "";
|
|
3438
3440
|
return `${body}
|
|
@@ -4023,6 +4025,15 @@ function configuredProviders(config) {
|
|
|
4023
4025
|
return out;
|
|
4024
4026
|
}
|
|
4025
4027
|
|
|
4028
|
+
// src/utils/maxParallel.ts
|
|
4029
|
+
var DEFAULT_MAX_PARALLEL = 3;
|
|
4030
|
+
function parseMaxParallel(value) {
|
|
4031
|
+
const text = String(value ?? "").trim();
|
|
4032
|
+
const n = typeof value === "number" ? value : Number(text);
|
|
4033
|
+
if (text === "" || !Number.isInteger(n) || n < 1) return null;
|
|
4034
|
+
return n;
|
|
4035
|
+
}
|
|
4036
|
+
|
|
4026
4037
|
// src/interfaces/BaseConfig.ts
|
|
4027
4038
|
function normalizeGeminiModel(id) {
|
|
4028
4039
|
return id.replace(/^gemini:/, "").replace(/^google\//, "");
|
|
@@ -4227,7 +4238,7 @@ var BaseConfig = class {
|
|
|
4227
4238
|
return "";
|
|
4228
4239
|
}
|
|
4229
4240
|
get maxParallelSessions() {
|
|
4230
|
-
return
|
|
4241
|
+
return parseMaxParallel(process.env.ORDEWELL_MAX_PARALLEL) ?? DEFAULT_MAX_PARALLEL;
|
|
4231
4242
|
}
|
|
4232
4243
|
get researchEnabled() {
|
|
4233
4244
|
return process.env.ORDEWELL_RESEARCH_ENABLED !== "false";
|
|
@@ -4468,28 +4479,28 @@ function posixBinDirs() {
|
|
|
4468
4479
|
];
|
|
4469
4480
|
}
|
|
4470
4481
|
function windowsBinDirs(env, home) {
|
|
4471
|
-
const
|
|
4472
|
-
const under = (base, ...parts) => base ? [
|
|
4482
|
+
const join24 = (...parts) => path5.win32.join(...parts);
|
|
4483
|
+
const under = (base, ...parts) => base ? [join24(base, ...parts)] : [];
|
|
4473
4484
|
const appData = env.APPDATA;
|
|
4474
4485
|
const localAppData = env.LOCALAPPDATA;
|
|
4475
4486
|
const programData = env.ProgramData;
|
|
4476
4487
|
return [
|
|
4477
4488
|
// Native per-user installers, PowerShell-driven and otherwise.
|
|
4478
|
-
|
|
4479
|
-
|
|
4480
|
-
|
|
4481
|
-
|
|
4489
|
+
join24(home, ".local", "bin"),
|
|
4490
|
+
join24(home, ".opencode", "bin"),
|
|
4491
|
+
join24(home, ".claude", "bin"),
|
|
4492
|
+
join24(home, ".codex", "bin"),
|
|
4482
4493
|
...under(localAppData, "Programs", "claude"),
|
|
4483
4494
|
// Node package managers. npm's global prefix is `%APPDATA%\npm`; the
|
|
4484
4495
|
// literal spelling is kept as a fallback for a host that does not export
|
|
4485
4496
|
// APPDATA (a service account, a stripped CI container).
|
|
4486
4497
|
...under(appData, "npm"),
|
|
4487
|
-
|
|
4498
|
+
join24(home, "AppData", "Roaming", "npm"),
|
|
4488
4499
|
...under(localAppData, "pnpm"),
|
|
4489
4500
|
...under(localAppData, "Yarn", "bin"),
|
|
4490
|
-
|
|
4501
|
+
join24(home, ".bun", "bin"),
|
|
4491
4502
|
// Windows package managers.
|
|
4492
|
-
|
|
4503
|
+
join24(home, "scoop", "shims"),
|
|
4493
4504
|
...under(env.SCOOP, "shims"),
|
|
4494
4505
|
...under(env.SCOOP_GLOBAL ?? programData, "scoop", "shims"),
|
|
4495
4506
|
...under(programData, "chocolatey", "bin"),
|
|
@@ -4499,7 +4510,7 @@ function windowsBinDirs(env, home) {
|
|
|
4499
4510
|
...under(localAppData, "Microsoft", "WindowsApps"),
|
|
4500
4511
|
// Version managers. Volta's Windows home is not the POSIX `~/.volta`.
|
|
4501
4512
|
...under(localAppData, "Volta", "bin"),
|
|
4502
|
-
|
|
4513
|
+
join24(home, ".volta", "bin")
|
|
4503
4514
|
];
|
|
4504
4515
|
}
|
|
4505
4516
|
function wellKnownBinDirs(deps = {}) {
|
|
@@ -6694,12 +6705,12 @@ function buildConflictRepairPrompt(task, conflict, integrationBranch) {
|
|
|
6694
6705
|
const files = conflict.conflictFiles ?? [];
|
|
6695
6706
|
const asked = `What the task was asked to do:
|
|
6696
6707
|
${task.prompt ?? task.description}`;
|
|
6697
|
-
const
|
|
6708
|
+
const resolve7 = "resolve every conflict so that both sides' intent survives: keep the work already integrated and add what the task contributed.";
|
|
6698
6709
|
const repos = conflict.repos.filter((repo) => repo !== SELF_REPO);
|
|
6699
6710
|
if (repos.length === 0) {
|
|
6700
6711
|
return [
|
|
6701
6712
|
`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.`,
|
|
6702
|
-
`This is the task's own worktree, on \`${branch}\`, with its work committed. Run \`git merge --no-edit ${integrationBranch}\` here and ${
|
|
6713
|
+
`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.`,
|
|
6703
6714
|
"Build and test the result the way this project does, commit the merge, and only then print the completion marker.",
|
|
6704
6715
|
"",
|
|
6705
6716
|
asked
|
|
@@ -6708,7 +6719,7 @@ ${task.prompt ?? task.description}`;
|
|
|
6708
6719
|
const stoppedIn = conflict.conflictRepo ?? repos[0];
|
|
6709
6720
|
return [
|
|
6710
6721
|
`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.`,
|
|
6711
|
-
`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 ${
|
|
6722
|
+
`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.`,
|
|
6712
6723
|
"Build and test the result the way this project does, commit the merge in each repository, and only then print the completion marker.",
|
|
6713
6724
|
"",
|
|
6714
6725
|
asked
|
|
@@ -8067,8 +8078,8 @@ function compactResearchResults(resultsText, maxChars = COMPACTION_LIMITS.resear
|
|
|
8067
8078
|
|
|
8068
8079
|
// src/services/ContextCollector.ts
|
|
8069
8080
|
var ORDEWELL_CONTEXT_MAX = 8e3;
|
|
8070
|
-
async function collectResearchContext(
|
|
8071
|
-
const collector = new ContextCollector(
|
|
8081
|
+
async function collectResearchContext(fs15, runners) {
|
|
8082
|
+
const collector = new ContextCollector(fs15);
|
|
8072
8083
|
let contextStr = "";
|
|
8073
8084
|
let firstContext = null;
|
|
8074
8085
|
for (const r of runners) {
|
|
@@ -8095,8 +8106,8 @@ ${firstContext.dirStructure}
|
|
|
8095
8106
|
return contextStr;
|
|
8096
8107
|
}
|
|
8097
8108
|
var ContextCollector = class {
|
|
8098
|
-
constructor(
|
|
8099
|
-
this.fs =
|
|
8109
|
+
constructor(fs15, registry) {
|
|
8110
|
+
this.fs = fs15;
|
|
8100
8111
|
this.registry = registry;
|
|
8101
8112
|
}
|
|
8102
8113
|
fs;
|
|
@@ -8177,10 +8188,10 @@ function summarizeToolCall(tool, argsJson, toolLabel) {
|
|
|
8177
8188
|
const scope = args.include ? ` in ${args.include}` : "";
|
|
8178
8189
|
return `grep ${ellipsize(String(args.pattern), 40)}${scope}${mode}`;
|
|
8179
8190
|
}
|
|
8180
|
-
const
|
|
8181
|
-
if (
|
|
8182
|
-
const
|
|
8183
|
-
return `${display} ${
|
|
8191
|
+
const path26 = args.filePath || args.file_path || args.path;
|
|
8192
|
+
if (path26) {
|
|
8193
|
+
const basename2 = String(path26).split("/").pop() || String(path26);
|
|
8194
|
+
return `${display} ${basename2}`;
|
|
8184
8195
|
}
|
|
8185
8196
|
if (args.pattern) {
|
|
8186
8197
|
return `${display} ${args.pattern}`;
|
|
@@ -8213,22 +8224,22 @@ var ALLOWED_TOOL_NAMES = new Set(subagentToolSpecs().map((t) => t.name));
|
|
|
8213
8224
|
function refused(output) {
|
|
8214
8225
|
return { success: false, output, truncated: false };
|
|
8215
8226
|
}
|
|
8216
|
-
function nonPromptingFs(
|
|
8217
|
-
const withinWorkspace = (p) => resolveWithin(
|
|
8227
|
+
function nonPromptingFs(fs15) {
|
|
8228
|
+
const withinWorkspace = (p) => resolveWithin(fs15.getWorkspaceRoot(), p ?? ".").inside;
|
|
8218
8229
|
const outsideRefusal = (p) => refused(
|
|
8219
8230
|
`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.`
|
|
8220
8231
|
);
|
|
8221
8232
|
return {
|
|
8222
|
-
getWorkspaceRoot: () =>
|
|
8223
|
-
readFile: (p, opts) => withinWorkspace(p) ?
|
|
8233
|
+
getWorkspaceRoot: () => fs15.getWorkspaceRoot(),
|
|
8234
|
+
readFile: (p, opts) => withinWorkspace(p) ? fs15.readFile(p, opts) : Promise.resolve(outsideRefusal(p)),
|
|
8224
8235
|
readFiles: (paths) => {
|
|
8225
8236
|
const escaping = paths.find((p) => !withinWorkspace(p));
|
|
8226
|
-
return escaping ? Promise.resolve(outsideRefusal(escaping)) :
|
|
8237
|
+
return escaping ? Promise.resolve(outsideRefusal(escaping)) : fs15.readFiles(paths);
|
|
8227
8238
|
},
|
|
8228
|
-
glob: (pattern, opts) => withinWorkspace(opts?.path) ?
|
|
8229
|
-
grep: (pattern, opts) => withinWorkspace(opts?.path) ?
|
|
8230
|
-
findSymbol: (symbol, opts) => withinWorkspace(opts?.path) ?
|
|
8231
|
-
listDir: (p, depth) => withinWorkspace(p) ?
|
|
8239
|
+
glob: (pattern, opts) => withinWorkspace(opts?.path) ? fs15.glob(pattern, opts) : Promise.resolve(outsideRefusal(opts.path)),
|
|
8240
|
+
grep: (pattern, opts) => withinWorkspace(opts?.path) ? fs15.grep(pattern, opts) : Promise.resolve(outsideRefusal(opts.path)),
|
|
8241
|
+
findSymbol: (symbol, opts) => withinWorkspace(opts?.path) ? fs15.findSymbol(symbol, opts) : Promise.resolve(outsideRefusal(opts.path)),
|
|
8242
|
+
listDir: (p, depth) => withinWorkspace(p) ? fs15.listDir(p, depth) : Promise.resolve(outsideRefusal(p)),
|
|
8232
8243
|
bash: async (command, signal) => {
|
|
8233
8244
|
const dialect = resolveResearchShell().dialect;
|
|
8234
8245
|
const { tier, reason } = classifyCommand(command, { dialect });
|
|
@@ -8239,13 +8250,13 @@ function nonPromptingFs(fs14) {
|
|
|
8239
8250
|
}
|
|
8240
8251
|
const escaping = pathLikeArgs(command, { dialect }).find((p) => !withinWorkspace(p));
|
|
8241
8252
|
if (escaping) return outsideRefusal(escaping);
|
|
8242
|
-
return
|
|
8253
|
+
return fs15.bash(command, signal);
|
|
8243
8254
|
}
|
|
8244
8255
|
};
|
|
8245
8256
|
}
|
|
8246
8257
|
async function runLoop(prompt, deps) {
|
|
8247
8258
|
if (deps.signal?.aborted) return "[research agent aborted before starting]";
|
|
8248
|
-
const
|
|
8259
|
+
const fs15 = nonPromptingFs(deps.fs);
|
|
8249
8260
|
const chat = deps.createChat((delta) => deps.onProgress?.({ type: "thinking", text: delta }));
|
|
8250
8261
|
let turn = await chat.sendMessage(prompt, deps.signal);
|
|
8251
8262
|
for (let step = 0; turn.hasToolCalls && step < SUBAGENT_LIMITS.maxSteps; step++) {
|
|
@@ -8262,7 +8273,7 @@ ${turn.text}`;
|
|
|
8262
8273
|
if (tc.name === "read_file" && !("maxBytes" in args) && !("limit" in args)) args.limit = 2e3;
|
|
8263
8274
|
const toolArgs = JSON.stringify(tc.args);
|
|
8264
8275
|
deps.onProgress?.({ type: "tool_call", tool: tc.name, toolArgs, toolCallId: tc.id });
|
|
8265
|
-
const res = await executeTool(tc.name, args,
|
|
8276
|
+
const res = await executeTool(tc.name, args, fs15, void 0, deps.signal);
|
|
8266
8277
|
const output = res.output.length > SUBAGENT_LIMITS.toolOutputMaxChars ? res.output.slice(0, SUBAGENT_LIMITS.toolOutputMaxChars) + `
|
|
8267
8278
|
[... truncated to ${SUBAGENT_LIMITS.toolOutputMaxChars} chars, total ${res.output.length}]` : res.output;
|
|
8268
8279
|
const stepEntry = {
|
|
@@ -8378,7 +8389,7 @@ var BaseAiService = class _BaseAiService {
|
|
|
8378
8389
|
* steering/failed result and the loop continues sequentially: a subagent can
|
|
8379
8390
|
* never fail a plan or a turn.
|
|
8380
8391
|
*/
|
|
8381
|
-
async executeSpawnAgent(args,
|
|
8392
|
+
async executeSpawnAgent(args, fs15, onProgress, signal, subagentId) {
|
|
8382
8393
|
const prompt = typeof args.prompt === "string" ? args.prompt.trim() : "";
|
|
8383
8394
|
if (!prompt) {
|
|
8384
8395
|
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 };
|
|
@@ -8389,20 +8400,20 @@ var BaseAiService = class _BaseAiService {
|
|
|
8389
8400
|
if (!chat) throw new Error("subagent chats are not available for this provider");
|
|
8390
8401
|
return chat;
|
|
8391
8402
|
},
|
|
8392
|
-
fs:
|
|
8403
|
+
fs: fs15,
|
|
8393
8404
|
signal,
|
|
8394
8405
|
onProgress: (progress) => onProgress({ ...progress, subagentId })
|
|
8395
8406
|
});
|
|
8396
8407
|
}
|
|
8397
8408
|
/** Collect project context for the planning phase. Shared with the harness backend. */
|
|
8398
|
-
static async collectResearchContext(
|
|
8399
|
-
return collectResearchContext(
|
|
8409
|
+
static async collectResearchContext(fs15, runners) {
|
|
8410
|
+
return collectResearchContext(fs15, runners);
|
|
8400
8411
|
}
|
|
8401
8412
|
/**
|
|
8402
8413
|
* Execute one round of tool calls and report each through onProgress.
|
|
8403
8414
|
* Returns the results to feed back plus the log entries produced.
|
|
8404
8415
|
*/
|
|
8405
|
-
async executeToolCalls(toolCalls,
|
|
8416
|
+
async executeToolCalls(toolCalls, fs15, onProgress, fetcher, stepIndex, thinking, signal) {
|
|
8406
8417
|
const toolResults = [];
|
|
8407
8418
|
const logEntries = [];
|
|
8408
8419
|
let resultsText = "";
|
|
@@ -8421,18 +8432,18 @@ var BaseAiService = class _BaseAiService {
|
|
|
8421
8432
|
const subagentId = (0, import_uuid3.v4)();
|
|
8422
8433
|
spawnSubagentIds.set(i, subagentId);
|
|
8423
8434
|
onProgress({ type: "tool_call", tool: toolCalls[i].name, toolArgs: JSON.stringify(toolCalls[i].args), subagentId, toolCallId: toolCalls[i].id });
|
|
8424
|
-
precomputed.set(i, await this.executeSpawnAgent({ ...toolCalls[i].args },
|
|
8435
|
+
precomputed.set(i, await this.executeSpawnAgent({ ...toolCalls[i].args }, fs15, onProgress, signal, subagentId));
|
|
8425
8436
|
}) : Promise.resolve(),
|
|
8426
8437
|
parallelIndexes.length > 1 ? mapWithConcurrency(parallelIndexes, TOOL_ROUND_CONCURRENCY, async (i) => {
|
|
8427
8438
|
onProgress({ type: "tool_call", tool: toolCalls[i].name, toolArgs: JSON.stringify(toolCalls[i].args), toolCallId: toolCalls[i].id });
|
|
8428
|
-
precomputed.set(i, await executeTool(toolCalls[i].name, normalizedArgs[i],
|
|
8439
|
+
precomputed.set(i, await executeTool(toolCalls[i].name, normalizedArgs[i], fs15, fetcher, signal));
|
|
8429
8440
|
}) : Promise.resolve()
|
|
8430
8441
|
]);
|
|
8431
8442
|
for (const [index, tc] of toolCalls.entries()) {
|
|
8432
8443
|
const toolArgs = normalizedArgs[index];
|
|
8433
8444
|
const preResult = precomputed.get(index);
|
|
8434
8445
|
if (!preResult) onProgress({ type: "tool_call", tool: tc.name, toolArgs: JSON.stringify(tc.args), toolCallId: tc.id });
|
|
8435
|
-
const toolResult = preResult ?? await executeTool(tc.name, toolArgs,
|
|
8446
|
+
const toolResult = preResult ?? await executeTool(tc.name, toolArgs, fs15, fetcher, signal);
|
|
8436
8447
|
const MAX_RESPONSE_CHARS = 5e4;
|
|
8437
8448
|
const truncatedResponse = toolResult.success && (toolResult.truncated || toolResult.output.length > MAX_RESPONSE_CHARS);
|
|
8438
8449
|
const llmOutput = toolResult.output.length > MAX_RESPONSE_CHARS ? toolResult.output.slice(0, MAX_RESPONSE_CHARS) + `
|
|
@@ -8618,7 +8629,7 @@ ${llmOutput}
|
|
|
8618
8629
|
* parsed tasks if a plan was emitted mid-loop, or null if the loop
|
|
8619
8630
|
* exhausted without a valid plan.
|
|
8620
8631
|
*/
|
|
8621
|
-
async runResearchLoop(chat, firstMessage,
|
|
8632
|
+
async runResearchLoop(chat, firstMessage, fs15, onProgress, runners, initialLog, fetcher, userGoal, runnerModes, autonomousDefault, signal) {
|
|
8622
8633
|
const researchLog = initialLog ? [...initialLog] : [];
|
|
8623
8634
|
if (!initialLog && userGoal) {
|
|
8624
8635
|
researchLog.push({
|
|
@@ -8645,7 +8656,7 @@ ${llmOutput}
|
|
|
8645
8656
|
if (!turn.hasToolCalls) break;
|
|
8646
8657
|
const { toolResults, logEntries, resultsText } = await this.executeToolCalls(
|
|
8647
8658
|
turn.toolCalls,
|
|
8648
|
-
|
|
8659
|
+
fs15,
|
|
8649
8660
|
onProgress,
|
|
8650
8661
|
fetcher,
|
|
8651
8662
|
step,
|
|
@@ -8878,11 +8889,11 @@ ${repairHint}` }] : parts;
|
|
|
8878
8889
|
return this.consumePlanStream(result, onToken);
|
|
8879
8890
|
}
|
|
8880
8891
|
// --- IAiService implementation ---
|
|
8881
|
-
async researchAndPlan(userDescription, runners, modelsByRunner,
|
|
8892
|
+
async researchAndPlan(userDescription, runners, modelsByRunner, fs15, onProgress, fetcher, runnerModes, modes = DEFAULT_PLANNER_MODES, signal) {
|
|
8882
8893
|
this.ensureInit();
|
|
8883
8894
|
const { autonomousDefault } = modes;
|
|
8884
8895
|
const genModel = this.getPlanningModel();
|
|
8885
|
-
const contextStr = await BaseAiService.collectResearchContext(
|
|
8896
|
+
const contextStr = await BaseAiService.collectResearchContext(fs15, runners);
|
|
8886
8897
|
const systemPrompt = buildResearchPrompt(userDescription, contextStr, modelsByRunner, runners, runnerModes, modes);
|
|
8887
8898
|
const firstMessage = `${buildResearchToolsPrompt()}
|
|
8888
8899
|
|
|
@@ -8893,17 +8904,17 @@ Explore the workspace to understand the codebase, then generate the plan.`;
|
|
|
8893
8904
|
generationConfig: { temperature: 0.3, topP: 0.95, maxOutputTokens: 16384 }
|
|
8894
8905
|
});
|
|
8895
8906
|
const researchChat = new GeminiResearchChat(chat);
|
|
8896
|
-
const result = await this.runResearchLoop(researchChat, firstMessage,
|
|
8907
|
+
const result = await this.runResearchLoop(researchChat, firstMessage, fs15, onProgress, runners, void 0, fetcher, userDescription, runnerModes, autonomousDefault, signal);
|
|
8897
8908
|
if (result.tasks) return { tasks: result.tasks, researchLog: result.researchLog, researchResults: result.researchResults };
|
|
8898
8909
|
const fallback = await this.generatePlanFallback(userDescription, contextStr, result.researchResults, modelsByRunner, runners, onProgress, result.researchLog, runnerModes, autonomousDefault, signal, modes);
|
|
8899
8910
|
return { tasks: fallback.tasks, researchLog: fallback.researchLog, researchResults: result.researchResults };
|
|
8900
8911
|
}
|
|
8901
|
-
async generatePlanDirect(userDescription, runners = ["claude-code"], modelsByRunner = {}, onToken,
|
|
8912
|
+
async generatePlanDirect(userDescription, runners = ["claude-code"], modelsByRunner = {}, onToken, fs15, _fetcher, runnerModes, modes = DEFAULT_PLANNER_MODES, _signal) {
|
|
8902
8913
|
this.ensureInit();
|
|
8903
8914
|
const { autonomousDefault } = modes;
|
|
8904
8915
|
let ctx = "";
|
|
8905
|
-
if (
|
|
8906
|
-
const collected = await new ContextCollector(
|
|
8916
|
+
if (fs15) {
|
|
8917
|
+
const collected = await new ContextCollector(fs15).collect(runners[0] ?? "claude-code");
|
|
8907
8918
|
if (collected.aiflowContext) ctx = `
|
|
8908
8919
|
<aiflow_context>
|
|
8909
8920
|
${collected.aiflowContext}
|
|
@@ -8919,12 +8930,12 @@ ${collected.aiflowContext}
|
|
|
8919
8930
|
autonomousDefault
|
|
8920
8931
|
);
|
|
8921
8932
|
}
|
|
8922
|
-
async modifyPlan(existingPlan, userRequest, modelsByRunner, onProgress,
|
|
8933
|
+
async modifyPlan(existingPlan, userRequest, modelsByRunner, onProgress, fs15, _fetcher, runnerModes, modes = DEFAULT_PLANNER_MODES, _signal) {
|
|
8923
8934
|
this.ensureInit();
|
|
8924
8935
|
const { autonomousDefault } = modes;
|
|
8925
8936
|
let aiflowContext;
|
|
8926
|
-
if (
|
|
8927
|
-
const collected = await new ContextCollector(
|
|
8937
|
+
if (fs15) {
|
|
8938
|
+
const collected = await new ContextCollector(fs15).collect(existingPlan.runners[0] ?? "claude-code");
|
|
8928
8939
|
if (collected.aiflowContext) aiflowContext = collected.aiflowContext;
|
|
8929
8940
|
}
|
|
8930
8941
|
const prompt = buildModifyPlanPrompt(existingPlan, userRequest, modelsByRunner, aiflowContext, runnerModes, autonomousDefault);
|
|
@@ -9160,11 +9171,11 @@ ${buildResearchToolsPrompt()}` }
|
|
|
9160
9171
|
}
|
|
9161
9172
|
}
|
|
9162
9173
|
// --- IAiService implementation ---
|
|
9163
|
-
async researchAndPlan(userDescription, runners, modelsByRunner,
|
|
9174
|
+
async researchAndPlan(userDescription, runners, modelsByRunner, fs15, onProgress, fetcher, runnerModes, modes = DEFAULT_PLANNER_MODES, signal) {
|
|
9164
9175
|
this.ensureInit();
|
|
9165
9176
|
const client = this.getClient();
|
|
9166
9177
|
const { autonomousDefault } = modes;
|
|
9167
|
-
const contextStr = await BaseAiService.collectResearchContext(
|
|
9178
|
+
const contextStr = await BaseAiService.collectResearchContext(fs15, runners);
|
|
9168
9179
|
const systemPrompt = buildResearchPrompt(userDescription, contextStr, modelsByRunner, runners, runnerModes, modes);
|
|
9169
9180
|
const userText = `User goal: ${userDescription}`;
|
|
9170
9181
|
const firstMessage = `${buildResearchToolsPrompt()}
|
|
@@ -9182,17 +9193,17 @@ ${userText}`;
|
|
|
9182
9193
|
toOpenAiTools(),
|
|
9183
9194
|
(delta) => onProgress({ type: "thinking", text: delta })
|
|
9184
9195
|
);
|
|
9185
|
-
const result = await this.runResearchLoop(researchChat, firstMessage,
|
|
9196
|
+
const result = await this.runResearchLoop(researchChat, firstMessage, fs15, onProgress, runners, void 0, fetcher, userDescription, runnerModes, autonomousDefault, signal);
|
|
9186
9197
|
if (result.tasks) return { tasks: result.tasks, researchLog: result.researchLog, researchResults: result.researchResults };
|
|
9187
9198
|
const fallback = await this.generatePlanFallback(userDescription, contextStr, result.researchResults, modelsByRunner, runners, onProgress, result.researchLog, runnerModes, autonomousDefault, signal, modes);
|
|
9188
9199
|
return { tasks: fallback.tasks, researchLog: fallback.researchLog, researchResults: result.researchResults };
|
|
9189
9200
|
}
|
|
9190
|
-
async generatePlanDirect(userDescription, runners = ["claude-code"], modelsByRunner = {}, onToken,
|
|
9201
|
+
async generatePlanDirect(userDescription, runners = ["claude-code"], modelsByRunner = {}, onToken, fs15, _fetcher, runnerModes, modes = DEFAULT_PLANNER_MODES, signal) {
|
|
9191
9202
|
this.ensureInit();
|
|
9192
9203
|
const { autonomousDefault } = modes;
|
|
9193
9204
|
let ctx = "";
|
|
9194
|
-
if (
|
|
9195
|
-
const collected = await new ContextCollector(
|
|
9205
|
+
if (fs15) {
|
|
9206
|
+
const collected = await new ContextCollector(fs15).collect(runners[0] ?? "claude-code");
|
|
9196
9207
|
if (collected.aiflowContext) ctx = `
|
|
9197
9208
|
<aiflow_context>
|
|
9198
9209
|
${collected.aiflowContext}
|
|
@@ -9208,12 +9219,12 @@ ${collected.aiflowContext}
|
|
|
9208
9219
|
autonomousDefault
|
|
9209
9220
|
);
|
|
9210
9221
|
}
|
|
9211
|
-
async modifyPlan(existingPlan, userRequest, modelsByRunner, onProgress,
|
|
9222
|
+
async modifyPlan(existingPlan, userRequest, modelsByRunner, onProgress, fs15, _fetcher, runnerModes, modes = DEFAULT_PLANNER_MODES, signal) {
|
|
9212
9223
|
this.ensureInit();
|
|
9213
9224
|
const { autonomousDefault } = modes;
|
|
9214
9225
|
let aiflowContext;
|
|
9215
|
-
if (
|
|
9216
|
-
const collected = await new ContextCollector(
|
|
9226
|
+
if (fs15) {
|
|
9227
|
+
const collected = await new ContextCollector(fs15).collect(existingPlan.runners[0] ?? "claude-code");
|
|
9217
9228
|
if (collected.aiflowContext) aiflowContext = collected.aiflowContext;
|
|
9218
9229
|
}
|
|
9219
9230
|
const prompt = buildModifyPlanPrompt(existingPlan, userRequest, modelsByRunner, aiflowContext, runnerModes, autonomousDefault);
|
|
@@ -9232,6 +9243,9 @@ ${collected.aiflowContext}
|
|
|
9232
9243
|
}
|
|
9233
9244
|
};
|
|
9234
9245
|
|
|
9246
|
+
// src/services/TaskOrchestrator.ts
|
|
9247
|
+
var path12 = __toESM(require("path"));
|
|
9248
|
+
|
|
9235
9249
|
// src/services/promptAugment.ts
|
|
9236
9250
|
var MAX_TAIL_CHARS = 500;
|
|
9237
9251
|
var DEFAULT_PLAN_MAP_MAX_ENTRIES = 30;
|
|
@@ -9818,17 +9832,229 @@ ${line}
|
|
|
9818
9832
|
|
|
9819
9833
|
// src/services/transcriptCapture.ts
|
|
9820
9834
|
var import_fs = require("fs");
|
|
9835
|
+
var os4 = __toESM(require("os"));
|
|
9836
|
+
var path11 = __toESM(require("path"));
|
|
9837
|
+
|
|
9838
|
+
// src/services/workspaceEnv.ts
|
|
9839
|
+
var import_child_process3 = require("child_process");
|
|
9840
|
+
var fs5 = __toESM(require("fs"));
|
|
9821
9841
|
var os3 = __toESM(require("os"));
|
|
9822
9842
|
var path10 = __toESM(require("path"));
|
|
9843
|
+
|
|
9844
|
+
// src/services/settingsEnvAllowlist.ts
|
|
9845
|
+
var ORDEWELL_SETTABLE_ENV = [
|
|
9846
|
+
"AI_PROVIDER",
|
|
9847
|
+
"ORCHESTRATOR_MODEL",
|
|
9848
|
+
"ORDEWELL_SUBAGENT_MODEL",
|
|
9849
|
+
"ORDEWELL_PLANNER_EFFORT",
|
|
9850
|
+
"ORDEWELL_RESEARCH_ENABLED",
|
|
9851
|
+
"ORDEWELL_MAX_PARALLEL",
|
|
9852
|
+
"ORDEWELL_CONFLICT_REPAIR_ATTEMPTS"
|
|
9853
|
+
];
|
|
9854
|
+
var SETTINGS_ENV_REFUSED = [
|
|
9855
|
+
// Approval posture and pre-approved scope.
|
|
9856
|
+
"ORDEWELL_AUTONOMOUS_MODE",
|
|
9857
|
+
"ORDEWELL_APPROVAL_MODE",
|
|
9858
|
+
"ORDEWELL_APPROVAL_ALLOW",
|
|
9859
|
+
// Where settings are persisted.
|
|
9860
|
+
"ORDEWELL_SETTINGS_PATH",
|
|
9861
|
+
// Runtime options and dynamic loaders.
|
|
9862
|
+
"NODE_OPTIONS",
|
|
9863
|
+
"NODE_REPL_EXTERNAL_MODULE",
|
|
9864
|
+
"NODE_PATH",
|
|
9865
|
+
"ELECTRON_RUN_AS_NODE",
|
|
9866
|
+
"LD_PRELOAD",
|
|
9867
|
+
"LD_LIBRARY_PATH",
|
|
9868
|
+
"LD_AUDIT",
|
|
9869
|
+
"DYLD_INSERT_LIBRARIES",
|
|
9870
|
+
"DYLD_LIBRARY_PATH",
|
|
9871
|
+
"DYLD_FRAMEWORK_PATH",
|
|
9872
|
+
// Executable resolution.
|
|
9873
|
+
"PATH",
|
|
9874
|
+
"PATHEXT",
|
|
9875
|
+
"NODE"
|
|
9876
|
+
];
|
|
9877
|
+
var REFUSED = new Set(SETTINGS_ENV_REFUSED);
|
|
9878
|
+
function providerEnvKeys() {
|
|
9879
|
+
const keys = [];
|
|
9880
|
+
for (const meta of Object.values(ALL_PROVIDERS)) {
|
|
9881
|
+
if (meta.apiKeyEnvVar) keys.push(meta.apiKeyEnvVar);
|
|
9882
|
+
if (meta.baseUrlEnvVar) keys.push(meta.baseUrlEnvVar);
|
|
9883
|
+
for (const detect of meta.detectEnvVars) keys.push(detect);
|
|
9884
|
+
}
|
|
9885
|
+
return keys;
|
|
9886
|
+
}
|
|
9887
|
+
var PROVIDER_CREDENTIAL_ENV = new Set(providerEnvKeys());
|
|
9888
|
+
var SETTINGS_ENV_ALLOWLIST = new Set(
|
|
9889
|
+
[...providerEnvKeys(), ...ORDEWELL_SETTABLE_ENV].filter((key) => !REFUSED.has(key))
|
|
9890
|
+
);
|
|
9891
|
+
function admitSettingsEnv(env) {
|
|
9892
|
+
const accepted = {};
|
|
9893
|
+
const rejected = [];
|
|
9894
|
+
for (const [key, value] of Object.entries(env)) {
|
|
9895
|
+
if (REFUSED.has(key) || !SETTINGS_ENV_ALLOWLIST.has(key) || typeof value !== "string") {
|
|
9896
|
+
rejected.push(key);
|
|
9897
|
+
continue;
|
|
9898
|
+
}
|
|
9899
|
+
accepted[key] = value;
|
|
9900
|
+
}
|
|
9901
|
+
return { accepted, rejected };
|
|
9902
|
+
}
|
|
9903
|
+
|
|
9904
|
+
// src/services/workspaceEnv.ts
|
|
9905
|
+
var WORKSPACE_ENV_FILE = "env";
|
|
9906
|
+
var DIRENV_TIMEOUT_MS = 5e3;
|
|
9907
|
+
var KEY = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
9908
|
+
function defaultDeps() {
|
|
9909
|
+
return {
|
|
9910
|
+
run: (command, args, opts) => new Promise((resolve7) => {
|
|
9911
|
+
(0, import_child_process3.execFile)(command, args, { ...opts, timeout: DIRENV_TIMEOUT_MS, encoding: "utf8" }, (err, stdout, stderr) => {
|
|
9912
|
+
const code = err ? typeof err.code === "number" ? Number(err.code) : -1 : 0;
|
|
9913
|
+
resolve7({ code, stdout: String(stdout ?? ""), stderr: String(stderr ?? "") });
|
|
9914
|
+
});
|
|
9915
|
+
}),
|
|
9916
|
+
readFile: (file) => {
|
|
9917
|
+
try {
|
|
9918
|
+
return fs5.readFileSync(file, "utf8");
|
|
9919
|
+
} catch {
|
|
9920
|
+
return null;
|
|
9921
|
+
}
|
|
9922
|
+
},
|
|
9923
|
+
isDirectory: (dir) => {
|
|
9924
|
+
try {
|
|
9925
|
+
return fs5.statSync(dir).isDirectory();
|
|
9926
|
+
} catch {
|
|
9927
|
+
return false;
|
|
9928
|
+
}
|
|
9929
|
+
},
|
|
9930
|
+
baseEnv: process.env,
|
|
9931
|
+
home: os3.homedir()
|
|
9932
|
+
};
|
|
9933
|
+
}
|
|
9934
|
+
var REFUSED2 = new Set(SETTINGS_ENV_REFUSED);
|
|
9935
|
+
async function resolveWorkspaceEnv(cwd, overrides = {}) {
|
|
9936
|
+
const deps = { ...defaultDeps(), ...overrides };
|
|
9937
|
+
const result = { env: {}, blockedEnvrc: null, refused: [], trackedEnvFile: null };
|
|
9938
|
+
if (!deps.isDirectory(cwd)) return result;
|
|
9939
|
+
const collected = {};
|
|
9940
|
+
const direnv = await fromDirenv(checkoutDirOf(cwd, deps), deps);
|
|
9941
|
+
result.blockedEnvrc = direnv.blocked;
|
|
9942
|
+
Object.assign(collected, direnv.env);
|
|
9943
|
+
const file = findUp(cwd, path10.join(STATE_DIR, WORKSPACE_ENV_FILE), deps);
|
|
9944
|
+
if (file) {
|
|
9945
|
+
const content = deps.readFile(file);
|
|
9946
|
+
if (content !== null) {
|
|
9947
|
+
if (await isTracked(file, deps)) result.trackedEnvFile = file;
|
|
9948
|
+
else Object.assign(collected, parseEnvFile(content, { ...deps.baseEnv, ...collected }, deps.home));
|
|
9949
|
+
}
|
|
9950
|
+
}
|
|
9951
|
+
for (const [key, value] of Object.entries(collected)) {
|
|
9952
|
+
if (!KEY.test(key)) continue;
|
|
9953
|
+
if (REFUSED2.has(key) || key.toUpperCase() === "PATH") result.refused.push(key);
|
|
9954
|
+
else result.env[key] = value;
|
|
9955
|
+
}
|
|
9956
|
+
return result;
|
|
9957
|
+
}
|
|
9958
|
+
async function workspaceEnvOf(cwd) {
|
|
9959
|
+
return (await resolveWorkspaceEnv(cwd)).env;
|
|
9960
|
+
}
|
|
9961
|
+
function checkoutDirOf(cwd, deps) {
|
|
9962
|
+
const parts = path10.resolve(cwd).split(path10.sep);
|
|
9963
|
+
const at = parts.findIndex((part, i) => part === STATE_DIR && parts[i + 1] === "worktrees");
|
|
9964
|
+
if (at < 0 || parts.length < at + 4) return cwd;
|
|
9965
|
+
const root = parts.slice(0, at).join(path10.sep) || path10.sep;
|
|
9966
|
+
const mapped = path10.join(root, ...parts.slice(at + 4));
|
|
9967
|
+
return deps.isDirectory(mapped) ? mapped : root;
|
|
9968
|
+
}
|
|
9969
|
+
async function fromDirenv(cwd, deps) {
|
|
9970
|
+
const setting = deps.baseEnv.ORDEWELL_DIRENV;
|
|
9971
|
+
if (setting === "false" || setting === "0") return { env: {}, blocked: null };
|
|
9972
|
+
const baseEnv = Object.fromEntries(Object.entries(deps.baseEnv).filter(([key]) => !key.startsWith("DIRENV_")));
|
|
9973
|
+
let out;
|
|
9974
|
+
try {
|
|
9975
|
+
out = await deps.run("direnv", ["export", "json"], { cwd, env: baseEnv });
|
|
9976
|
+
} catch {
|
|
9977
|
+
return { env: {}, blocked: null };
|
|
9978
|
+
}
|
|
9979
|
+
const blocked = out.stderr.match(/error (\S*\.envrc) is blocked/);
|
|
9980
|
+
if (blocked) return { env: {}, blocked: blocked[1] };
|
|
9981
|
+
if (out.code !== 0 || !out.stdout.trim()) return { env: {}, blocked: null };
|
|
9982
|
+
let parsed;
|
|
9983
|
+
try {
|
|
9984
|
+
parsed = JSON.parse(out.stdout);
|
|
9985
|
+
} catch {
|
|
9986
|
+
return { env: {}, blocked: null };
|
|
9987
|
+
}
|
|
9988
|
+
if (!parsed || typeof parsed !== "object") return { env: {}, blocked: null };
|
|
9989
|
+
const env = {};
|
|
9990
|
+
for (const [key, value] of Object.entries(parsed)) {
|
|
9991
|
+
if (typeof value === "string" && !key.startsWith("DIRENV_")) env[key] = value;
|
|
9992
|
+
}
|
|
9993
|
+
return { env, blocked: null };
|
|
9994
|
+
}
|
|
9995
|
+
function findUp(start, relative5, deps) {
|
|
9996
|
+
let dir = path10.resolve(start);
|
|
9997
|
+
for (; ; ) {
|
|
9998
|
+
const candidate = path10.join(dir, relative5);
|
|
9999
|
+
if (deps.readFile(candidate) !== null) return candidate;
|
|
10000
|
+
const parent = path10.dirname(dir);
|
|
10001
|
+
if (parent === dir) return null;
|
|
10002
|
+
dir = parent;
|
|
10003
|
+
}
|
|
10004
|
+
}
|
|
10005
|
+
async function isTracked(file, deps) {
|
|
10006
|
+
try {
|
|
10007
|
+
const out = await deps.run("git", ["ls-files", "--error-unmatch", path10.basename(file)], { cwd: path10.dirname(file), env: deps.baseEnv });
|
|
10008
|
+
return out.code === 0;
|
|
10009
|
+
} catch {
|
|
10010
|
+
return false;
|
|
10011
|
+
}
|
|
10012
|
+
}
|
|
10013
|
+
function parseEnvFile(content, context, home) {
|
|
10014
|
+
const env = {};
|
|
10015
|
+
const lookup2 = (name) => env[name] ?? context[name] ?? "";
|
|
10016
|
+
const expand = (value) => {
|
|
10017
|
+
const tilde = value.startsWith("~/") ? path10.join(home, value.slice(2)) : value;
|
|
10018
|
+
return tilde.replace(/\$\{([A-Za-z_][A-Za-z0-9_]*)\}|\$([A-Za-z_][A-Za-z0-9_]*)/g, (_, braced, bare) => lookup2(braced ?? bare));
|
|
10019
|
+
};
|
|
10020
|
+
for (const raw of content.split(/\r?\n/)) {
|
|
10021
|
+
const line = raw.trim().replace(/^export\s+/, "");
|
|
10022
|
+
if (!line || line.startsWith("#")) continue;
|
|
10023
|
+
const eq = line.indexOf("=");
|
|
10024
|
+
if (eq <= 0) continue;
|
|
10025
|
+
const key = line.slice(0, eq).trim();
|
|
10026
|
+
if (!KEY.test(key)) continue;
|
|
10027
|
+
const rest = line.slice(eq + 1).trim();
|
|
10028
|
+
if (rest.startsWith("'")) {
|
|
10029
|
+
const end = rest.indexOf("'", 1);
|
|
10030
|
+
env[key] = end > 0 ? rest.slice(1, end) : rest.slice(1);
|
|
10031
|
+
} else if (rest.startsWith('"')) {
|
|
10032
|
+
const end = rest.lastIndexOf('"');
|
|
10033
|
+
env[key] = expand((end > 0 ? rest.slice(1, end) : rest.slice(1)).replace(/\\n/g, "\n").replace(/\\"/g, '"'));
|
|
10034
|
+
} else {
|
|
10035
|
+
env[key] = expand(rest.replace(/\s+#.*$/, ""));
|
|
10036
|
+
}
|
|
10037
|
+
}
|
|
10038
|
+
return env;
|
|
10039
|
+
}
|
|
10040
|
+
|
|
10041
|
+
// src/services/transcriptCapture.ts
|
|
9823
10042
|
var HomeTranscriptReader = class {
|
|
9824
10043
|
home;
|
|
10044
|
+
claudeConfigDirs;
|
|
9825
10045
|
constructor(opts = {}) {
|
|
9826
10046
|
const { homeDir } = opts;
|
|
9827
|
-
this.home = homeDir ? () => homeDir : () => process.env.HOME ||
|
|
10047
|
+
this.home = homeDir ? () => homeDir : () => process.env.HOME || os4.homedir();
|
|
10048
|
+
const workspaceEnv = opts.workspaceEnv ?? workspaceEnvOf;
|
|
10049
|
+
this.claudeConfigDirs = async (cwd) => [...new Set([
|
|
10050
|
+
(await workspaceEnv(cwd)).CLAUDE_CONFIG_DIR,
|
|
10051
|
+
homeDir ? void 0 : process.env.CLAUDE_CONFIG_DIR,
|
|
10052
|
+
path11.join(this.home(), ".claude")
|
|
10053
|
+
].filter((dir) => Boolean(dir)))];
|
|
9828
10054
|
}
|
|
9829
10055
|
async finalAssistantText(query, maxChars = 4e3) {
|
|
9830
10056
|
try {
|
|
9831
|
-
if (query.runner === "claude-code") return claudeFinal(this.
|
|
10057
|
+
if (query.runner === "claude-code") return claudeFinal(await this.claudeConfigDirs(query.cwd), query, maxChars);
|
|
9832
10058
|
if (query.runner === "opencode") return await opencodeFinal(this.home(), query, maxChars);
|
|
9833
10059
|
if (query.runner === "codex") return codexFinal(this.home(), query, maxChars);
|
|
9834
10060
|
} catch {
|
|
@@ -9837,20 +10063,20 @@ var HomeTranscriptReader = class {
|
|
|
9837
10063
|
}
|
|
9838
10064
|
};
|
|
9839
10065
|
var CLAUDE_PROJECT_NAME_MAX = 200;
|
|
9840
|
-
function claudeProjectDirs(
|
|
9841
|
-
const projects =
|
|
10066
|
+
function claudeProjectDirs(configDir2, cwd) {
|
|
10067
|
+
const projects = path11.join(configDir2, "projects");
|
|
9842
10068
|
const munged = cwd.replace(/[^a-zA-Z0-9]/g, "-");
|
|
9843
10069
|
if (munged.length <= CLAUDE_PROJECT_NAME_MAX) {
|
|
9844
|
-
const dir =
|
|
10070
|
+
const dir = path11.join(projects, munged);
|
|
9845
10071
|
return (0, import_fs.existsSync)(dir) ? [dir] : [];
|
|
9846
10072
|
}
|
|
9847
10073
|
if (!(0, import_fs.existsSync)(projects)) return [];
|
|
9848
10074
|
const prefix = `${munged.slice(0, CLAUDE_PROJECT_NAME_MAX)}-`;
|
|
9849
|
-
return (0, import_fs.readdirSync)(projects).filter((name) => name.startsWith(prefix)).map((name) =>
|
|
10075
|
+
return (0, import_fs.readdirSync)(projects).filter((name) => name.startsWith(prefix)).map((name) => path11.join(projects, name));
|
|
9850
10076
|
}
|
|
9851
|
-
function claudeFinal(
|
|
10077
|
+
function claudeFinal(configDirs, query, maxChars) {
|
|
9852
10078
|
const cutoff = query.startedAt ? Date.parse(query.startedAt) : 0;
|
|
9853
|
-
const candidates = claudeProjectDirs(
|
|
10079
|
+
const candidates = configDirs.flatMap((dir) => claudeProjectDirs(dir, query.cwd)).flatMap((dir) => (0, import_fs.readdirSync)(dir).filter((f) => f.endsWith(".jsonl")).map((f) => path11.join(dir, f))).filter((f) => cutoff ? (0, import_fs.statSync)(f).mtimeMs >= cutoff - 5e3 : true).sort((a, b) => (0, import_fs.statSync)(b).mtimeMs - (0, import_fs.statSync)(a).mtimeMs);
|
|
9854
10080
|
for (const file of candidates) {
|
|
9855
10081
|
const raw = (0, import_fs.readFileSync)(file, "utf8");
|
|
9856
10082
|
if (!raw.includes(query.marker)) continue;
|
|
@@ -9886,7 +10112,7 @@ async function opencodeFinal(home, query, maxChars) {
|
|
|
9886
10112
|
} catch {
|
|
9887
10113
|
return null;
|
|
9888
10114
|
}
|
|
9889
|
-
const dbPath =
|
|
10115
|
+
const dbPath = path11.join(home, ".local", "share", "opencode", "opencode.db");
|
|
9890
10116
|
if (!(0, import_fs.existsSync)(dbPath)) return null;
|
|
9891
10117
|
let db;
|
|
9892
10118
|
try {
|
|
@@ -9939,7 +10165,7 @@ async function opencodeFinal(home, query, maxChars) {
|
|
|
9939
10165
|
}
|
|
9940
10166
|
}
|
|
9941
10167
|
function codexFinal(home, query, maxChars) {
|
|
9942
|
-
const root =
|
|
10168
|
+
const root = path11.join(home, ".codex", "sessions");
|
|
9943
10169
|
if (!(0, import_fs.existsSync)(root)) return null;
|
|
9944
10170
|
const cutoff = query.startedAt ? Date.parse(query.startedAt) : 0;
|
|
9945
10171
|
const files = [];
|
|
@@ -9953,7 +10179,7 @@ function codexFinal(home, query, maxChars) {
|
|
|
9953
10179
|
continue;
|
|
9954
10180
|
}
|
|
9955
10181
|
for (const e of entries) {
|
|
9956
|
-
const full =
|
|
10182
|
+
const full = path11.join(dir, e);
|
|
9957
10183
|
let st;
|
|
9958
10184
|
try {
|
|
9959
10185
|
st = (0, import_fs.statSync)(full);
|
|
@@ -10404,6 +10630,43 @@ ${taskB.description}`,
|
|
|
10404
10630
|
}
|
|
10405
10631
|
};
|
|
10406
10632
|
|
|
10633
|
+
// src/utils/shell.ts
|
|
10634
|
+
var ANSI_RE = /\x1b\[[0-9;]*[A-Za-z]|\x1b\][^\x07]*\x07|\x1b[()][AB012]|\r/g;
|
|
10635
|
+
function stripAnsi(text) {
|
|
10636
|
+
return text.replace(ANSI_RE, "");
|
|
10637
|
+
}
|
|
10638
|
+
function posixShellQuote(s) {
|
|
10639
|
+
return `'${s.replace(/'/g, `'\\''`)}'`;
|
|
10640
|
+
}
|
|
10641
|
+
function buildShellInvocation(command, args) {
|
|
10642
|
+
const inner = [command, ...args].map(posixShellQuote).join(" ");
|
|
10643
|
+
return { shellPath: "/bin/bash", shellArgs: ["-lc", inner] };
|
|
10644
|
+
}
|
|
10645
|
+
function wrapWithPty(command, args, opts = {}) {
|
|
10646
|
+
const inner = [command, ...args].map(posixShellQuote).join(" ");
|
|
10647
|
+
const watcher = opts.controlChannel ? 'exec 4<&0; ( while read -r C R <&3; do stty cols "$C" rows "$R" <&4; done ) & ' : "";
|
|
10648
|
+
const setup = opts.size ? `stty cols ${opts.size.cols} rows ${opts.size.rows}; ` : "";
|
|
10649
|
+
return { command: "script", args: ["-q", "-e", "-f", "-c", `${watcher}${setup}${inner}`, "/dev/null"] };
|
|
10650
|
+
}
|
|
10651
|
+
|
|
10652
|
+
// src/services/blockingPrompts.ts
|
|
10653
|
+
var TAIL = 4096;
|
|
10654
|
+
var squash = (text) => stripAnsi(text).replace(/\s+/g, "").toLowerCase();
|
|
10655
|
+
function watchBlockingPrompts(session, prompts, onPrompt) {
|
|
10656
|
+
if (prompts.length === 0) return;
|
|
10657
|
+
const pending = prompts.map((prompt) => ({ prompt, phrase: squash(prompt.phrase) }));
|
|
10658
|
+
let tail = "";
|
|
10659
|
+
session.onOutput((text) => {
|
|
10660
|
+
if (pending.length === 0) return;
|
|
10661
|
+
tail = (tail + squash(text)).slice(-TAIL);
|
|
10662
|
+
for (let i = pending.length - 1; i >= 0; i--) {
|
|
10663
|
+
if (!tail.includes(pending[i].phrase)) continue;
|
|
10664
|
+
onPrompt(pending[i].prompt);
|
|
10665
|
+
pending.splice(i, 1);
|
|
10666
|
+
}
|
|
10667
|
+
});
|
|
10668
|
+
}
|
|
10669
|
+
|
|
10407
10670
|
// src/services/TaskOrchestrator.ts
|
|
10408
10671
|
var SHARED_ROOT_TAIL = "tasks run in the workspace root without worktree isolation.";
|
|
10409
10672
|
function sharedRootNotice(reason, repos) {
|
|
@@ -10467,6 +10730,25 @@ var TaskOrchestrator = class {
|
|
|
10467
10730
|
*/
|
|
10468
10731
|
retryCounts = /* @__PURE__ */ new Map();
|
|
10469
10732
|
spawnCounts = /* @__PURE__ */ new Map();
|
|
10733
|
+
/**
|
|
10734
|
+
* The terminal a verdict left open, by task. It stays so the user can read
|
|
10735
|
+
* the agent's output or keep talking to it — but only while its worktree
|
|
10736
|
+
* does: once that is removed the agent sits in a deleted directory, and a
|
|
10737
|
+
* newer attempt in the same worktree would share it with a second agent.
|
|
10738
|
+
* Without this, every task of every run left one agent process running
|
|
10739
|
+
* until the daemon stopped.
|
|
10740
|
+
*/
|
|
10741
|
+
lingering = /* @__PURE__ */ new Map();
|
|
10742
|
+
/**
|
|
10743
|
+
* A full-plan run a failure paused. Retrying the failed task is the explicit
|
|
10744
|
+
* resume the pause waits for — without this a retry only reset the task to
|
|
10745
|
+
* pending and nothing ran until the user also re-ran the whole plan.
|
|
10746
|
+
*/
|
|
10747
|
+
haltedByFailure = false;
|
|
10748
|
+
/** The workspace's own variables for a task's cwd (ADR-0016); swapped out in tests. */
|
|
10749
|
+
workspaceEnv = (cwd) => resolveWorkspaceEnv(cwd);
|
|
10750
|
+
/** What the workspace env has already warned about, so a run says it once, not per task. */
|
|
10751
|
+
envWarnings = /* @__PURE__ */ new Set();
|
|
10470
10752
|
/**
|
|
10471
10753
|
* Tasks pulled out of auto-scheduling (user-cancelled or failed to spawn).
|
|
10472
10754
|
* They stay 'pending' — "not executed" — but the scheduler skips them until
|
|
@@ -10510,6 +10792,32 @@ var TaskOrchestrator = class {
|
|
|
10510
10792
|
setWorkspaceRoot(fn) {
|
|
10511
10793
|
this.workspaceRootFn = fn;
|
|
10512
10794
|
}
|
|
10795
|
+
setWorkspaceEnvResolver(resolve7) {
|
|
10796
|
+
this.workspaceEnv = resolve7;
|
|
10797
|
+
}
|
|
10798
|
+
/**
|
|
10799
|
+
* The variables a task's agent gets from its workspace. What cannot be
|
|
10800
|
+
* applied is said once — silently starting without them is how agents ran
|
|
10801
|
+
* under the wrong account when an edited `.envrc` was left unallowed.
|
|
10802
|
+
*/
|
|
10803
|
+
async envForTask(cwd) {
|
|
10804
|
+
const resolved = await this.workspaceEnv(cwd);
|
|
10805
|
+
const warn = (key, message) => {
|
|
10806
|
+
if (this.envWarnings.has(key)) return;
|
|
10807
|
+
this.envWarnings.add(key);
|
|
10808
|
+
this.notifications.warn(message);
|
|
10809
|
+
};
|
|
10810
|
+
if (resolved.blockedEnvrc) {
|
|
10811
|
+
warn(`blocked:${resolved.blockedEnvrc}`, `direnv has blocked ${resolved.blockedEnvrc}, so tasks start without its variables. Run \`direnv allow\` in ${path12.dirname(resolved.blockedEnvrc)} to use them.`);
|
|
10812
|
+
}
|
|
10813
|
+
if (resolved.trackedEnvFile) {
|
|
10814
|
+
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.`);
|
|
10815
|
+
}
|
|
10816
|
+
if (resolved.refused.length > 0) {
|
|
10817
|
+
warn(`refused:${resolved.refused.join(",")}`, `Ignored ${resolved.refused.join(", ")} from the workspace environment: Ordewell never passes these to agents.`);
|
|
10818
|
+
}
|
|
10819
|
+
return resolved.env;
|
|
10820
|
+
}
|
|
10513
10821
|
setRegistry(registry) {
|
|
10514
10822
|
this.registry = registry;
|
|
10515
10823
|
}
|
|
@@ -10654,6 +10962,7 @@ var TaskOrchestrator = class {
|
|
|
10654
10962
|
return result;
|
|
10655
10963
|
}
|
|
10656
10964
|
async clearMergedRun(run) {
|
|
10965
|
+
this.closeLingeringOf(run);
|
|
10657
10966
|
try {
|
|
10658
10967
|
await this.isolation.discard(run, { integration: "delete-merged" });
|
|
10659
10968
|
} catch (err) {
|
|
@@ -10664,14 +10973,32 @@ var TaskOrchestrator = class {
|
|
|
10664
10973
|
}
|
|
10665
10974
|
/** Worktrees and task branches go; the integration branch and the record stay for review and merge. */
|
|
10666
10975
|
async cleanupRun() {
|
|
10976
|
+
this.closeLingeringOf(this.requireRun());
|
|
10667
10977
|
await this.isolation.discard(this.requireRun(), { integration: "keep" });
|
|
10668
10978
|
this.emit("onIsolationChanged");
|
|
10669
10979
|
}
|
|
10670
10980
|
/** The run and everything it made go, and the plan forgets it; the next run starts afresh. */
|
|
10671
10981
|
async discardRun() {
|
|
10982
|
+
this.closeLingeringOf(this.requireRun());
|
|
10672
10983
|
await this.isolation.discard(this.requireRun(), { integration: "delete" });
|
|
10673
10984
|
this.forgetRun();
|
|
10674
10985
|
}
|
|
10986
|
+
watchBlockingPrompts(task, attempt, session) {
|
|
10987
|
+
const manifest = this.registry?.get(attempt.runner)?.manifest;
|
|
10988
|
+
watchBlockingPrompts(session, manifest?.runner.blockingPrompts ?? [], (prompt) => {
|
|
10989
|
+
if (this.attempts.get(task.id) !== attempt) return;
|
|
10990
|
+
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.`);
|
|
10991
|
+
});
|
|
10992
|
+
}
|
|
10993
|
+
closeLingering(taskId) {
|
|
10994
|
+
const sessionId = this.lingering.get(taskId);
|
|
10995
|
+
if (sessionId === void 0) return;
|
|
10996
|
+
this.lingering.delete(taskId);
|
|
10997
|
+
this.terminalRunner.stop(sessionId);
|
|
10998
|
+
}
|
|
10999
|
+
closeLingeringOf(run) {
|
|
11000
|
+
for (const taskId of Object.keys(run.tasks)) this.closeLingering(taskId);
|
|
11001
|
+
}
|
|
10675
11002
|
forgetRun() {
|
|
10676
11003
|
this.isolationRun = null;
|
|
10677
11004
|
this.resolvers = {};
|
|
@@ -10759,11 +11086,18 @@ var TaskOrchestrator = class {
|
|
|
10759
11086
|
if (!await this.openRun(() => this.start()) || this.running) return;
|
|
10760
11087
|
console.log(`[TaskOrchestrator] Starting with ${this.store.allTasks.length} tasks (${this.store.allTasks.filter((t) => t.type === "ai" && t.prompt).length} AI ready)`);
|
|
10761
11088
|
this.running = true;
|
|
11089
|
+
this.haltedByFailure = false;
|
|
10762
11090
|
this.planStatus = "running";
|
|
10763
11091
|
this.emit("onTaskChanged");
|
|
10764
11092
|
await this.tick();
|
|
10765
11093
|
}
|
|
11094
|
+
haltOnFailure() {
|
|
11095
|
+
if (this.running) this.haltedByFailure = true;
|
|
11096
|
+
this.running = false;
|
|
11097
|
+
this.planStatus = "approved";
|
|
11098
|
+
}
|
|
10766
11099
|
stop() {
|
|
11100
|
+
this.haltedByFailure = false;
|
|
10767
11101
|
this.running = false;
|
|
10768
11102
|
this.planStatus = "approved";
|
|
10769
11103
|
this.terminalRunner.stopAll();
|
|
@@ -10800,8 +11134,7 @@ ${summary || "(empty \u2014 no output captured)"}`);
|
|
|
10800
11134
|
await this.landPassed(task, landing);
|
|
10801
11135
|
} else {
|
|
10802
11136
|
this.store.markFailed(taskId);
|
|
10803
|
-
this.
|
|
10804
|
-
this.planStatus = "approved";
|
|
11137
|
+
this.haltOnFailure();
|
|
10805
11138
|
this.notifications.error(`Task "${task.title}" failed verification: ${verdict.reason}`);
|
|
10806
11139
|
if (attempt.worktree) await this.releaseWorktree(taskId, { keep: true });
|
|
10807
11140
|
}
|
|
@@ -10941,8 +11274,7 @@ ${summary || "(empty \u2014 no output captured)"}`);
|
|
|
10941
11274
|
if (whyNot) this.tell("info", whyNot);
|
|
10942
11275
|
} else {
|
|
10943
11276
|
this.store.markFailed(task.id);
|
|
10944
|
-
this.
|
|
10945
|
-
this.planStatus = "approved";
|
|
11277
|
+
this.haltOnFailure();
|
|
10946
11278
|
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.`);
|
|
10947
11279
|
}
|
|
10948
11280
|
}
|
|
@@ -10993,6 +11325,7 @@ ${summary || "(empty \u2014 no output captured)"}`);
|
|
|
10993
11325
|
await integration;
|
|
10994
11326
|
const run = this.isolationRun;
|
|
10995
11327
|
if (!run?.tasks[taskId]) return;
|
|
11328
|
+
if (!opts.keep) this.closeLingering(taskId);
|
|
10996
11329
|
try {
|
|
10997
11330
|
await this.isolation.release(run, taskId, opts);
|
|
10998
11331
|
} catch (err) {
|
|
@@ -11141,7 +11474,8 @@ ${summary || "(empty \u2014 no output captured)"}`);
|
|
|
11141
11474
|
this.onHold.delete(taskId);
|
|
11142
11475
|
this.emit("onTaskChanged");
|
|
11143
11476
|
await this.releaseWorktree(taskId, { keep: false }, ended?.integration);
|
|
11144
|
-
await this.
|
|
11477
|
+
if (this.haltedByFailure && !this.running) await this.start();
|
|
11478
|
+
else await this.tick();
|
|
11145
11479
|
}
|
|
11146
11480
|
/**
|
|
11147
11481
|
* Manually start a single AI task right now, bypassing dependency/readiness
|
|
@@ -11267,6 +11601,8 @@ ${summary || "(empty \u2014 no output captured)"}`);
|
|
|
11267
11601
|
// A merge to resolve is not new behaviour to drive test-first.
|
|
11268
11602
|
tddEnabled: !attempt.repair && this.tddEnabled()
|
|
11269
11603
|
});
|
|
11604
|
+
this.closeLingering(task.id);
|
|
11605
|
+
const env = await this.envForTask(cwd);
|
|
11270
11606
|
const session = await this.terminalRunner.spawn({
|
|
11271
11607
|
taskId: task.id,
|
|
11272
11608
|
runner: attempt.runner,
|
|
@@ -11278,13 +11614,15 @@ ${summary || "(empty \u2014 no output captured)"}`);
|
|
|
11278
11614
|
cwd,
|
|
11279
11615
|
registry: this.registry ?? void 0,
|
|
11280
11616
|
order: task.order,
|
|
11281
|
-
title: task.title
|
|
11617
|
+
title: task.title,
|
|
11618
|
+
env
|
|
11282
11619
|
});
|
|
11283
11620
|
if (this.attempts.get(task.id) !== attempt) return this.abandonSpawn(task, attempt, session);
|
|
11284
11621
|
attempt.phase = "running";
|
|
11285
11622
|
attempt.session = session;
|
|
11286
11623
|
this.output.attach(task.id, session);
|
|
11287
11624
|
this.verifier.watch(task, session);
|
|
11625
|
+
this.watchBlockingPrompts(task, attempt, session);
|
|
11288
11626
|
this.emit("onTaskChanged");
|
|
11289
11627
|
if (attempt.repair) {
|
|
11290
11628
|
const group = this.isolationRun?.repos.some((r) => r.path !== SELF_REPO);
|
|
@@ -11517,6 +11855,7 @@ ${summary || "(empty \u2014 no output captured)"}`);
|
|
|
11517
11855
|
const attempt = this.attempts.get(taskId);
|
|
11518
11856
|
this.attempts.delete(taskId);
|
|
11519
11857
|
if (attempt) this.output.detach(taskId);
|
|
11858
|
+
if (reason === "verdict" && attempt?.session) this.lingering.set(taskId, attempt.session.id);
|
|
11520
11859
|
if (reason === "cancel" || reason === "release" || reason === "complete" || reason === "retry") {
|
|
11521
11860
|
const task = this.store.get(taskId);
|
|
11522
11861
|
if (task) this.verifier.clear(task);
|
|
@@ -11659,29 +11998,29 @@ ${errors.map((e) => `- ${e}`).join("\n")}`
|
|
|
11659
11998
|
};
|
|
11660
11999
|
|
|
11661
12000
|
// src/services/ModelDiscovery.ts
|
|
11662
|
-
var
|
|
11663
|
-
var
|
|
12001
|
+
var path15 = __toESM(require("path"));
|
|
12002
|
+
var import_child_process5 = require("child_process");
|
|
11664
12003
|
var import_util3 = require("util");
|
|
11665
12004
|
var fsSync3 = __toESM(require("fs"));
|
|
11666
12005
|
var osMod = __toESM(require("os"));
|
|
11667
12006
|
|
|
11668
12007
|
// src/utils/globalDataDir.ts
|
|
11669
|
-
var
|
|
11670
|
-
var
|
|
11671
|
-
var
|
|
12008
|
+
var fs6 = __toESM(require("fs"));
|
|
12009
|
+
var path13 = __toESM(require("path"));
|
|
12010
|
+
var os5 = __toESM(require("os"));
|
|
11672
12011
|
function globalDataDir() {
|
|
11673
|
-
return
|
|
12012
|
+
return path13.join(os5.homedir(), ".ordewell");
|
|
11674
12013
|
}
|
|
11675
12014
|
function copyDirSync(src, dest) {
|
|
11676
|
-
|
|
11677
|
-
const entries =
|
|
12015
|
+
fs6.mkdirSync(dest, { recursive: true });
|
|
12016
|
+
const entries = fs6.readdirSync(src, { withFileTypes: true });
|
|
11678
12017
|
for (const entry of entries) {
|
|
11679
|
-
const srcPath =
|
|
11680
|
-
const destPath =
|
|
12018
|
+
const srcPath = path13.join(src, entry.name);
|
|
12019
|
+
const destPath = path13.join(dest, entry.name);
|
|
11681
12020
|
if (entry.isDirectory()) {
|
|
11682
12021
|
copyDirSync(srcPath, destPath);
|
|
11683
12022
|
} else {
|
|
11684
|
-
|
|
12023
|
+
fs6.copyFileSync(srcPath, destPath);
|
|
11685
12024
|
}
|
|
11686
12025
|
}
|
|
11687
12026
|
}
|
|
@@ -11689,50 +12028,29 @@ var migrated = false;
|
|
|
11689
12028
|
function migrateOldConfigDir() {
|
|
11690
12029
|
if (migrated) return;
|
|
11691
12030
|
migrated = true;
|
|
11692
|
-
const oldDir =
|
|
11693
|
-
if (!
|
|
12031
|
+
const oldDir = path13.join(os5.homedir(), ".config", "ordewell");
|
|
12032
|
+
if (!fs6.existsSync(oldDir)) return;
|
|
11694
12033
|
const newDir = globalDataDir();
|
|
11695
12034
|
const liftFile = (name) => {
|
|
11696
|
-
const oldFile =
|
|
11697
|
-
const newFile =
|
|
11698
|
-
if (
|
|
11699
|
-
|
|
11700
|
-
|
|
12035
|
+
const oldFile = path13.join(oldDir, name);
|
|
12036
|
+
const newFile = path13.join(newDir, name);
|
|
12037
|
+
if (fs6.existsSync(oldFile) && !fs6.existsSync(newFile)) {
|
|
12038
|
+
fs6.mkdirSync(newDir, { recursive: true });
|
|
12039
|
+
fs6.copyFileSync(oldFile, newFile);
|
|
11701
12040
|
}
|
|
11702
12041
|
};
|
|
11703
12042
|
liftFile("settings.json");
|
|
11704
12043
|
liftFile(".env");
|
|
11705
|
-
const oldPlugins =
|
|
11706
|
-
const newPlugins =
|
|
11707
|
-
if (
|
|
12044
|
+
const oldPlugins = path13.join(oldDir, "plugins");
|
|
12045
|
+
const newPlugins = path13.join(newDir, "plugins");
|
|
12046
|
+
if (fs6.existsSync(oldPlugins) && !fs6.existsSync(newPlugins)) {
|
|
11708
12047
|
copyDirSync(oldPlugins, newPlugins);
|
|
11709
12048
|
}
|
|
11710
12049
|
}
|
|
11711
12050
|
|
|
11712
12051
|
// src/utils/launch.ts
|
|
11713
12052
|
var fsSync2 = __toESM(require("fs"));
|
|
11714
|
-
var
|
|
11715
|
-
|
|
11716
|
-
// src/utils/shell.ts
|
|
11717
|
-
var ANSI_RE = /\x1b\[[0-9;]*[A-Za-z]|\x1b\][^\x07]*\x07|\x1b[()][AB012]|\r/g;
|
|
11718
|
-
function stripAnsi(text) {
|
|
11719
|
-
return text.replace(ANSI_RE, "");
|
|
11720
|
-
}
|
|
11721
|
-
function posixShellQuote(s) {
|
|
11722
|
-
return `'${s.replace(/'/g, `'\\''`)}'`;
|
|
11723
|
-
}
|
|
11724
|
-
function buildShellInvocation(command, args) {
|
|
11725
|
-
const inner = [command, ...args].map(posixShellQuote).join(" ");
|
|
11726
|
-
return { shellPath: "/bin/bash", shellArgs: ["-lc", inner] };
|
|
11727
|
-
}
|
|
11728
|
-
function wrapWithPty(command, args, opts = {}) {
|
|
11729
|
-
const inner = [command, ...args].map(posixShellQuote).join(" ");
|
|
11730
|
-
const watcher = opts.controlChannel ? 'exec 4<&0; ( while read -r C R <&3; do stty cols "$C" rows "$R" <&4; done ) & ' : "";
|
|
11731
|
-
const setup = opts.size ? `stty cols ${opts.size.cols} rows ${opts.size.rows}; ` : "";
|
|
11732
|
-
return { command: "script", args: ["-q", "-e", "-f", "-c", `${watcher}${setup}${inner}`, "/dev/null"] };
|
|
11733
|
-
}
|
|
11734
|
-
|
|
11735
|
-
// src/utils/launch.ts
|
|
12053
|
+
var path14 = __toESM(require("path"));
|
|
11736
12054
|
var DIRECT_EXTENSIONS = [".exe", ".com"];
|
|
11737
12055
|
var BATCH_EXTENSIONS = [".cmd", ".bat"];
|
|
11738
12056
|
var POWERSHELL_EXTENSIONS = [".ps1"];
|
|
@@ -11740,7 +12058,7 @@ var CMD_EXE_MAX_COMMAND_LINE = 8191;
|
|
|
11740
12058
|
var WINDOWS_MAX_COMMAND_LINE = 32767;
|
|
11741
12059
|
function defaultPowerShell() {
|
|
11742
12060
|
const root = process.env.SystemRoot || process.env.windir;
|
|
11743
|
-
return root ?
|
|
12061
|
+
return root ? path14.win32.join(root, "System32", "WindowsPowerShell", "v1.0", "powershell.exe") : "powershell.exe";
|
|
11744
12062
|
}
|
|
11745
12063
|
var POWERSHELL_FLAGS = ["-NoLogo", "-NoProfile", "-NonInteractive", "-ExecutionPolicy", "Bypass", "-File"];
|
|
11746
12064
|
var CommandLineTooLongError = class extends Error {
|
|
@@ -11792,7 +12110,7 @@ Searched: ${searchedPath || "(empty PATH)"}`);
|
|
|
11792
12110
|
};
|
|
11793
12111
|
function isResolvableOnPosixPath(command, PATH, exists) {
|
|
11794
12112
|
if (command.includes("/")) return exists(command);
|
|
11795
|
-
return PATH.split(
|
|
12113
|
+
return PATH.split(path14.delimiter).map((d) => d.trim()).filter(Boolean).some((dir) => exists(path14.join(dir, command)));
|
|
11796
12114
|
}
|
|
11797
12115
|
function isExecutableResolved(command, plan, PATH, deps = {}) {
|
|
11798
12116
|
if ((deps.platform ?? process.platform) === "win32") return plan.file !== command;
|
|
@@ -11832,9 +12150,9 @@ function cmdArgs(line) {
|
|
|
11832
12150
|
return ["/d", "/s", "/c", `"${line}"`];
|
|
11833
12151
|
}
|
|
11834
12152
|
function candidateExtensions(command, pathExt) {
|
|
11835
|
-
const declared = pathExt.split(
|
|
12153
|
+
const declared = pathExt.split(path14.win32.delimiter).map((e) => e.trim().toLowerCase()).filter(Boolean);
|
|
11836
12154
|
const known = /* @__PURE__ */ new Set([...DIRECT_EXTENSIONS, ...BATCH_EXTENSIONS, ...POWERSHELL_EXTENSIONS, ...declared]);
|
|
11837
|
-
const ext =
|
|
12155
|
+
const ext = path14.win32.extname(command).toLowerCase();
|
|
11838
12156
|
if (ext && known.has(ext)) {
|
|
11839
12157
|
const route = BATCH_EXTENSIONS.includes(ext) ? "cmd" : POWERSHELL_EXTENSIONS.includes(ext) ? "powershell" : "direct";
|
|
11840
12158
|
return { direct: [], cmd: [], powershell: [], [route]: [""] };
|
|
@@ -11855,13 +12173,13 @@ async function findWindowsExecutables(command, deps) {
|
|
|
11855
12173
|
const exists = deps.exists ?? defaultExists2;
|
|
11856
12174
|
const pathExt = (deps.pathExt ?? (() => process.env.PATHEXT ?? ""))();
|
|
11857
12175
|
const byRoute = candidateExtensions(command, pathExt);
|
|
11858
|
-
const dirs =
|
|
11859
|
-
const base =
|
|
12176
|
+
const dirs = path14.win32.basename(command) !== command ? [path14.win32.dirname(path14.win32.resolve(command))] : (await (deps.resolvePath ?? augmentedPath)() || "").split(path14.win32.delimiter).map((d) => d.trim().replace(/^"|"$/g, "")).filter(Boolean);
|
|
12177
|
+
const base = path14.win32.basename(command);
|
|
11860
12178
|
const found = [];
|
|
11861
12179
|
for (const route of ROUTES) {
|
|
11862
12180
|
outer: for (const ext of byRoute[route]) {
|
|
11863
12181
|
for (const dir of dirs) {
|
|
11864
|
-
const candidate =
|
|
12182
|
+
const candidate = path14.win32.join(dir, `${base}${ext}`);
|
|
11865
12183
|
if (exists(candidate)) {
|
|
11866
12184
|
found.push({ file: candidate, route });
|
|
11867
12185
|
break outer;
|
|
@@ -11907,10 +12225,10 @@ async function planShellLaunch(command, args, deps = {}) {
|
|
|
11907
12225
|
}
|
|
11908
12226
|
|
|
11909
12227
|
// src/utils/processTree.ts
|
|
11910
|
-
var
|
|
12228
|
+
var import_child_process4 = require("child_process");
|
|
11911
12229
|
var HARD_KILL_DELAY_MS = 5e3;
|
|
11912
12230
|
function defaultExecFile2(file, args, cb) {
|
|
11913
|
-
(0,
|
|
12231
|
+
(0, import_child_process4.execFile)(file, args, (err) => cb(err ?? null));
|
|
11914
12232
|
}
|
|
11915
12233
|
function killTree(proc, deps = {}) {
|
|
11916
12234
|
if (!proc || proc.killed || typeof proc.exitCode === "number") return;
|
|
@@ -11946,7 +12264,7 @@ function killTree(proc, deps = {}) {
|
|
|
11946
12264
|
}
|
|
11947
12265
|
|
|
11948
12266
|
// src/services/ModelDiscovery.ts
|
|
11949
|
-
var execAsync3 = (0, import_util3.promisify)(
|
|
12267
|
+
var execAsync3 = (0, import_util3.promisify)(import_child_process5.exec);
|
|
11950
12268
|
var defaultExec2 = async (command, options) => {
|
|
11951
12269
|
const PATH = await augmentedPath();
|
|
11952
12270
|
const { stdout } = await execAsync3(command, { ...options, maxBuffer: 32 * 1024 * 1024, env: withPath(process.env, PATH) });
|
|
@@ -11956,16 +12274,16 @@ var defaultAppServerClient = async (command, args, options) => {
|
|
|
11956
12274
|
const PATH = await augmentedPath();
|
|
11957
12275
|
const launch = await planDirectLaunch(command, args).catch(() => null);
|
|
11958
12276
|
if (!launch) return null;
|
|
11959
|
-
return new Promise((
|
|
12277
|
+
return new Promise((resolve7) => {
|
|
11960
12278
|
let child;
|
|
11961
12279
|
try {
|
|
11962
|
-
child = (0,
|
|
12280
|
+
child = (0, import_child_process5.spawn)(launch.file, launch.args, {
|
|
11963
12281
|
env: withPath(process.env, PATH),
|
|
11964
12282
|
stdio: ["pipe", "pipe", "ignore"],
|
|
11965
12283
|
windowsVerbatimArguments: launch.verbatim
|
|
11966
12284
|
});
|
|
11967
12285
|
} catch {
|
|
11968
|
-
|
|
12286
|
+
resolve7(null);
|
|
11969
12287
|
return;
|
|
11970
12288
|
}
|
|
11971
12289
|
let settled = false;
|
|
@@ -11974,7 +12292,7 @@ var defaultAppServerClient = async (command, args, options) => {
|
|
|
11974
12292
|
settled = true;
|
|
11975
12293
|
clearTimeout(timer);
|
|
11976
12294
|
killTree(child);
|
|
11977
|
-
|
|
12295
|
+
resolve7(value);
|
|
11978
12296
|
};
|
|
11979
12297
|
const timer = setTimeout(() => finish(null), options?.timeout ?? 2e4);
|
|
11980
12298
|
child.on("error", () => finish(null));
|
|
@@ -12041,7 +12359,7 @@ function geminiDiscoveryCacheKey(apiKey) {
|
|
|
12041
12359
|
return {
|
|
12042
12360
|
key: `gemini-models:${hash}`,
|
|
12043
12361
|
dir,
|
|
12044
|
-
file:
|
|
12362
|
+
file: path15.join(dir, `gemini_models_${hash}.json`)
|
|
12045
12363
|
};
|
|
12046
12364
|
}
|
|
12047
12365
|
function readGeminiCache(apiKey) {
|
|
@@ -12564,8 +12882,8 @@ var ModelDiscovery = class {
|
|
|
12564
12882
|
};
|
|
12565
12883
|
|
|
12566
12884
|
// src/services/ModelCatalog.ts
|
|
12567
|
-
var
|
|
12568
|
-
var
|
|
12885
|
+
var fs7 = __toESM(require("fs"));
|
|
12886
|
+
var path16 = __toESM(require("path"));
|
|
12569
12887
|
var CACHE_KEY = "model-catalog";
|
|
12570
12888
|
var CACHE_TTL_MS = 60 * 60 * 1e3;
|
|
12571
12889
|
function storageKey(baseUrl) {
|
|
@@ -12617,17 +12935,17 @@ var ModelCatalog = class {
|
|
|
12617
12935
|
const url = (baseUrl || "https://openrouter.ai/api/v1").replace(/\/+$/, "");
|
|
12618
12936
|
const key = storageKey(url);
|
|
12619
12937
|
const cacheDir = globalCacheDir();
|
|
12620
|
-
const cacheFile =
|
|
12621
|
-
if (
|
|
12938
|
+
const cacheFile = path16.join(cacheDir, `${key.replace(/[:/]/g, "_")}.json`);
|
|
12939
|
+
if (fs7.existsSync(cacheFile)) fs7.rmSync(cacheFile);
|
|
12622
12940
|
} catch {
|
|
12623
12941
|
}
|
|
12624
12942
|
}
|
|
12625
12943
|
static readCache(key) {
|
|
12626
12944
|
try {
|
|
12627
12945
|
const cacheDir = globalCacheDir();
|
|
12628
|
-
const cacheFile =
|
|
12629
|
-
if (!
|
|
12630
|
-
const raw =
|
|
12946
|
+
const cacheFile = path16.join(cacheDir, `${key.replace(/[:/]/g, "_")}.json`);
|
|
12947
|
+
if (!fs7.existsSync(cacheFile)) return null;
|
|
12948
|
+
const raw = fs7.readFileSync(cacheFile, "utf8");
|
|
12631
12949
|
const data = JSON.parse(raw);
|
|
12632
12950
|
if (Date.now() - data.cachedAt > CACHE_TTL_MS) return null;
|
|
12633
12951
|
return JSON.stringify(data.models);
|
|
@@ -12638,9 +12956,9 @@ var ModelCatalog = class {
|
|
|
12638
12956
|
static writeCache(key, models) {
|
|
12639
12957
|
try {
|
|
12640
12958
|
const cacheDir = globalCacheDir();
|
|
12641
|
-
|
|
12642
|
-
const cacheFile =
|
|
12643
|
-
|
|
12959
|
+
fs7.mkdirSync(cacheDir, { recursive: true });
|
|
12960
|
+
const cacheFile = path16.join(cacheDir, `${key.replace(/[:/]/g, "_")}.json`);
|
|
12961
|
+
fs7.writeFileSync(
|
|
12644
12962
|
cacheFile,
|
|
12645
12963
|
JSON.stringify({ models, cachedAt: Date.now() }, null, 2)
|
|
12646
12964
|
);
|
|
@@ -12907,9 +13225,9 @@ var ModelResolver = class {
|
|
|
12907
13225
|
};
|
|
12908
13226
|
|
|
12909
13227
|
// src/services/RunnerInstallation.ts
|
|
12910
|
-
var
|
|
13228
|
+
var import_child_process6 = require("child_process");
|
|
12911
13229
|
var import_util4 = require("util");
|
|
12912
|
-
var execAsync4 = (0, import_util4.promisify)(
|
|
13230
|
+
var execAsync4 = (0, import_util4.promisify)(import_child_process6.exec);
|
|
12913
13231
|
var defaultExec3 = async (command, options) => {
|
|
12914
13232
|
const PATH = await augmentedPath();
|
|
12915
13233
|
const { stdout } = await execAsync4(command, { ...options, env: withPath(process.env, PATH) });
|
|
@@ -13016,72 +13334,12 @@ var RunnerInstallation = class {
|
|
|
13016
13334
|
}
|
|
13017
13335
|
};
|
|
13018
13336
|
|
|
13019
|
-
// src/services/settingsEnvAllowlist.ts
|
|
13020
|
-
var ORDEWELL_SETTABLE_ENV = [
|
|
13021
|
-
"AI_PROVIDER",
|
|
13022
|
-
"ORCHESTRATOR_MODEL",
|
|
13023
|
-
"ORDEWELL_SUBAGENT_MODEL",
|
|
13024
|
-
"ORDEWELL_PLANNER_EFFORT",
|
|
13025
|
-
"ORDEWELL_RESEARCH_ENABLED",
|
|
13026
|
-
"ORDEWELL_MAX_PARALLEL",
|
|
13027
|
-
"ORDEWELL_CONFLICT_REPAIR_ATTEMPTS"
|
|
13028
|
-
];
|
|
13029
|
-
var SETTINGS_ENV_REFUSED = [
|
|
13030
|
-
// Approval posture and pre-approved scope.
|
|
13031
|
-
"ORDEWELL_AUTONOMOUS_MODE",
|
|
13032
|
-
"ORDEWELL_APPROVAL_MODE",
|
|
13033
|
-
"ORDEWELL_APPROVAL_ALLOW",
|
|
13034
|
-
// Where settings are persisted.
|
|
13035
|
-
"ORDEWELL_SETTINGS_PATH",
|
|
13036
|
-
// Runtime options and dynamic loaders.
|
|
13037
|
-
"NODE_OPTIONS",
|
|
13038
|
-
"NODE_REPL_EXTERNAL_MODULE",
|
|
13039
|
-
"NODE_PATH",
|
|
13040
|
-
"ELECTRON_RUN_AS_NODE",
|
|
13041
|
-
"LD_PRELOAD",
|
|
13042
|
-
"LD_LIBRARY_PATH",
|
|
13043
|
-
"LD_AUDIT",
|
|
13044
|
-
"DYLD_INSERT_LIBRARIES",
|
|
13045
|
-
"DYLD_LIBRARY_PATH",
|
|
13046
|
-
"DYLD_FRAMEWORK_PATH",
|
|
13047
|
-
// Executable resolution.
|
|
13048
|
-
"PATH",
|
|
13049
|
-
"PATHEXT",
|
|
13050
|
-
"NODE"
|
|
13051
|
-
];
|
|
13052
|
-
var REFUSED = new Set(SETTINGS_ENV_REFUSED);
|
|
13053
|
-
function providerEnvKeys() {
|
|
13054
|
-
const keys = [];
|
|
13055
|
-
for (const meta of Object.values(ALL_PROVIDERS)) {
|
|
13056
|
-
if (meta.apiKeyEnvVar) keys.push(meta.apiKeyEnvVar);
|
|
13057
|
-
if (meta.baseUrlEnvVar) keys.push(meta.baseUrlEnvVar);
|
|
13058
|
-
for (const detect of meta.detectEnvVars) keys.push(detect);
|
|
13059
|
-
}
|
|
13060
|
-
return keys;
|
|
13061
|
-
}
|
|
13062
|
-
var PROVIDER_CREDENTIAL_ENV = new Set(providerEnvKeys());
|
|
13063
|
-
var SETTINGS_ENV_ALLOWLIST = new Set(
|
|
13064
|
-
[...providerEnvKeys(), ...ORDEWELL_SETTABLE_ENV].filter((key) => !REFUSED.has(key))
|
|
13065
|
-
);
|
|
13066
|
-
function admitSettingsEnv(env) {
|
|
13067
|
-
const accepted = {};
|
|
13068
|
-
const rejected = [];
|
|
13069
|
-
for (const [key, value] of Object.entries(env)) {
|
|
13070
|
-
if (REFUSED.has(key) || !SETTINGS_ENV_ALLOWLIST.has(key) || typeof value !== "string") {
|
|
13071
|
-
rejected.push(key);
|
|
13072
|
-
continue;
|
|
13073
|
-
}
|
|
13074
|
-
accepted[key] = value;
|
|
13075
|
-
}
|
|
13076
|
-
return { accepted, rejected };
|
|
13077
|
-
}
|
|
13078
|
-
|
|
13079
13337
|
// src/services/harness/CliAgentAiService.ts
|
|
13080
|
-
var
|
|
13338
|
+
var import_child_process7 = require("child_process");
|
|
13081
13339
|
|
|
13082
13340
|
// src/utils/workspace.ts
|
|
13083
|
-
var
|
|
13084
|
-
var
|
|
13341
|
+
var fs8 = __toESM(require("fs"));
|
|
13342
|
+
var path17 = __toESM(require("path"));
|
|
13085
13343
|
var WorkspaceNotFoundError = class extends Error {
|
|
13086
13344
|
constructor(workspace) {
|
|
13087
13345
|
super(`Workspace "${workspace}" does not exist.`);
|
|
@@ -13092,7 +13350,7 @@ var WorkspaceNotFoundError = class extends Error {
|
|
|
13092
13350
|
};
|
|
13093
13351
|
function defaultIsDirectory(candidate) {
|
|
13094
13352
|
try {
|
|
13095
|
-
return
|
|
13353
|
+
return fs8.statSync(candidate).isDirectory();
|
|
13096
13354
|
} catch {
|
|
13097
13355
|
return false;
|
|
13098
13356
|
}
|
|
@@ -13128,8 +13386,8 @@ var PROJECT_MARKER_FILES = [
|
|
|
13128
13386
|
"CMakeLists.txt"
|
|
13129
13387
|
];
|
|
13130
13388
|
function assertWorkspaceIsProject(workspace, deps = {}) {
|
|
13131
|
-
const realpath = deps.realpath ?? ((candidate) =>
|
|
13132
|
-
const exists = deps.exists ?? ((candidate) =>
|
|
13389
|
+
const realpath = deps.realpath ?? ((candidate) => fs8.realpathSync(candidate));
|
|
13390
|
+
const exists = deps.exists ?? ((candidate) => fs8.existsSync(candidate));
|
|
13133
13391
|
let resolved;
|
|
13134
13392
|
try {
|
|
13135
13393
|
resolved = realpath(workspace);
|
|
@@ -13137,7 +13395,7 @@ function assertWorkspaceIsProject(workspace, deps = {}) {
|
|
|
13137
13395
|
throw new WorkspaceNotAProjectError(workspace);
|
|
13138
13396
|
}
|
|
13139
13397
|
const hasMarker = [...PROJECT_MARKER_DIRS, ...PROJECT_MARKER_FILES].some(
|
|
13140
|
-
(marker2) => exists(
|
|
13398
|
+
(marker2) => exists(path17.join(resolved, marker2))
|
|
13141
13399
|
);
|
|
13142
13400
|
if (!hasMarker) throw new WorkspaceNotAProjectError(workspace);
|
|
13143
13401
|
}
|
|
@@ -13205,13 +13463,14 @@ var StdioAgentAdapter = class {
|
|
|
13205
13463
|
}
|
|
13206
13464
|
async start(opts) {
|
|
13207
13465
|
assertWorkspaceExists(opts.cwd, { isDirectory: this.deps.isDirectory });
|
|
13208
|
-
this.processEnded = new Promise((
|
|
13209
|
-
this.markEnded =
|
|
13466
|
+
this.processEnded = new Promise((resolve7) => {
|
|
13467
|
+
this.markEnded = resolve7;
|
|
13210
13468
|
});
|
|
13211
13469
|
const spec = this.spawnSpec(opts);
|
|
13212
13470
|
const resolvePath = this.deps.resolvePath ?? augmentedPath;
|
|
13213
13471
|
const PATH = await resolvePath();
|
|
13214
|
-
|
|
13472
|
+
const workspace = await (this.deps.workspaceEnv ?? workspaceEnvOf)(opts.cwd);
|
|
13473
|
+
this.spawnEnv = withPath(process.env, PATH, { ...workspace, ...spec.env });
|
|
13215
13474
|
const launch = await planDirectLaunch(spec.command, spec.args, {
|
|
13216
13475
|
platform: this.deps.platform,
|
|
13217
13476
|
resolvePath
|
|
@@ -13257,7 +13516,7 @@ ${err.message}`).slice(-STDERR_TAIL_CHARS);
|
|
|
13257
13516
|
onEvent({ type: "error", message: this.exitMessage() });
|
|
13258
13517
|
return;
|
|
13259
13518
|
}
|
|
13260
|
-
await new Promise((
|
|
13519
|
+
await new Promise((resolve7) => {
|
|
13261
13520
|
let settled = false;
|
|
13262
13521
|
const finish = () => {
|
|
13263
13522
|
if (settled) return;
|
|
@@ -13268,7 +13527,7 @@ ${err.message}`).slice(-STDERR_TAIL_CHARS);
|
|
|
13268
13527
|
signal?.removeEventListener("abort", onAbort);
|
|
13269
13528
|
this.process?.removeListener("exit", onExit);
|
|
13270
13529
|
this.process?.removeListener("error", onExit);
|
|
13271
|
-
|
|
13530
|
+
resolve7();
|
|
13272
13531
|
};
|
|
13273
13532
|
const emit = (event) => {
|
|
13274
13533
|
onEvent(event);
|
|
@@ -13483,14 +13742,14 @@ function codexSandboxUnavailableMessage() {
|
|
|
13483
13742
|
].join("\n");
|
|
13484
13743
|
}
|
|
13485
13744
|
function runProbe(deps, cwd, env, flags) {
|
|
13486
|
-
return new Promise((
|
|
13745
|
+
return new Promise((resolve7) => {
|
|
13487
13746
|
let output = "";
|
|
13488
13747
|
let settled = false;
|
|
13489
13748
|
const finish = (code) => {
|
|
13490
13749
|
if (settled) return;
|
|
13491
13750
|
settled = true;
|
|
13492
13751
|
clearTimeout(timer);
|
|
13493
|
-
|
|
13752
|
+
resolve7({ code, output });
|
|
13494
13753
|
};
|
|
13495
13754
|
const timer = setTimeout(() => finish(null), PROBE_TIMEOUT_MS);
|
|
13496
13755
|
timer.unref?.();
|
|
@@ -13573,8 +13832,8 @@ var CodexAdapter = class extends StdioAgentAdapter {
|
|
|
13573
13832
|
async handshake(opts) {
|
|
13574
13833
|
this.sandbox = await probeCodexSandbox(this.deps, opts.cwd, this.spawnEnv);
|
|
13575
13834
|
if (this.sandbox === "unavailable") throw new Error(codexSandboxUnavailableMessage());
|
|
13576
|
-
const ready = new Promise((
|
|
13577
|
-
this.settleHandshake =
|
|
13835
|
+
const ready = new Promise((resolve7) => {
|
|
13836
|
+
this.settleHandshake = resolve7;
|
|
13578
13837
|
});
|
|
13579
13838
|
this.writeLine({
|
|
13580
13839
|
jsonrpc: "2.0",
|
|
@@ -13587,8 +13846,8 @@ var CodexAdapter = class extends StdioAgentAdapter {
|
|
|
13587
13846
|
// A binary that rejects `app-server` outright dies immediately; waiting
|
|
13588
13847
|
// out the timeout would turn a one-line diagnostic into a 30s stall.
|
|
13589
13848
|
this.processEnded.then(() => false),
|
|
13590
|
-
new Promise((
|
|
13591
|
-
const t = setTimeout(() =>
|
|
13849
|
+
new Promise((resolve7) => {
|
|
13850
|
+
const t = setTimeout(() => resolve7(false), HANDSHAKE_TIMEOUT_MS);
|
|
13592
13851
|
t.unref?.();
|
|
13593
13852
|
})
|
|
13594
13853
|
]);
|
|
@@ -13865,12 +14124,12 @@ function describeError(err) {
|
|
|
13865
14124
|
return lines.join(": ");
|
|
13866
14125
|
}
|
|
13867
14126
|
function delay(ms, signal) {
|
|
13868
|
-
return new Promise((
|
|
13869
|
-
const timer = setTimeout(
|
|
14127
|
+
return new Promise((resolve7) => {
|
|
14128
|
+
const timer = setTimeout(resolve7, ms);
|
|
13870
14129
|
timer.unref?.();
|
|
13871
14130
|
signal?.addEventListener("abort", () => {
|
|
13872
14131
|
clearTimeout(timer);
|
|
13873
|
-
|
|
14132
|
+
resolve7();
|
|
13874
14133
|
}, { once: true });
|
|
13875
14134
|
});
|
|
13876
14135
|
}
|
|
@@ -13904,17 +14163,17 @@ var OpenCodeAdapter = class {
|
|
|
13904
14163
|
throw new ExecutableNotFoundError("opencode", PATH);
|
|
13905
14164
|
}
|
|
13906
14165
|
this.process = this.deps.spawn(launch.file, launch.args, {
|
|
13907
|
-
env: withPath(process.env, PATH),
|
|
14166
|
+
env: withPath(process.env, PATH, await (this.deps.workspaceEnv ?? workspaceEnvOf)(opts.cwd)),
|
|
13908
14167
|
stdio: ["pipe", "pipe", "pipe"],
|
|
13909
14168
|
cwd: opts.cwd,
|
|
13910
14169
|
windowsVerbatimArguments: launch.verbatim
|
|
13911
14170
|
});
|
|
13912
|
-
const banner = new Promise((
|
|
14171
|
+
const banner = new Promise((resolve7) => {
|
|
13913
14172
|
let seen = "";
|
|
13914
14173
|
const scan = (chunk) => {
|
|
13915
14174
|
seen += chunk.toString();
|
|
13916
14175
|
const match = seen.match(/https?:\/\/[^\s]+/);
|
|
13917
|
-
if (match)
|
|
14176
|
+
if (match) resolve7(match[0].replace(/[.,)]$/, ""));
|
|
13918
14177
|
};
|
|
13919
14178
|
this.process.stdout?.on("data", scan);
|
|
13920
14179
|
this.process.stderr?.on("data", (chunk) => {
|
|
@@ -13923,15 +14182,15 @@ var OpenCodeAdapter = class {
|
|
|
13923
14182
|
});
|
|
13924
14183
|
this.process.on("exit", () => {
|
|
13925
14184
|
this.exited = true;
|
|
13926
|
-
|
|
14185
|
+
resolve7(null);
|
|
13927
14186
|
});
|
|
13928
14187
|
this.process.on("error", (err) => {
|
|
13929
14188
|
this.exited = true;
|
|
13930
14189
|
this.stderrTail = (this.stderrTail + `
|
|
13931
14190
|
${err.message}`).slice(-STDERR_TAIL_CHARS2);
|
|
13932
|
-
|
|
14191
|
+
resolve7(null);
|
|
13933
14192
|
});
|
|
13934
|
-
const timer = setTimeout(() =>
|
|
14193
|
+
const timer = setTimeout(() => resolve7(null), SERVER_READY_TIMEOUT_MS);
|
|
13935
14194
|
timer.unref?.();
|
|
13936
14195
|
});
|
|
13937
14196
|
this.baseUrl = await banner;
|
|
@@ -13964,8 +14223,8 @@ ${this.stderrTail.trim()}` : ""}`);
|
|
|
13964
14223
|
const streamAbort = new AbortController();
|
|
13965
14224
|
let connected = () => {
|
|
13966
14225
|
};
|
|
13967
|
-
const streamReady = new Promise((
|
|
13968
|
-
connected =
|
|
14226
|
+
const streamReady = new Promise((resolve7) => {
|
|
14227
|
+
connected = resolve7;
|
|
13969
14228
|
});
|
|
13970
14229
|
const live = this.streamEvents(
|
|
13971
14230
|
streamAbort.signal,
|
|
@@ -14169,14 +14428,14 @@ ${part.text}` : part.text });
|
|
|
14169
14428
|
}
|
|
14170
14429
|
}
|
|
14171
14430
|
}
|
|
14172
|
-
async json(method,
|
|
14173
|
-
const response = await this.deps.fetch(`${this.baseUrl}${
|
|
14431
|
+
async json(method, path26, body, signal) {
|
|
14432
|
+
const response = await this.deps.fetch(`${this.baseUrl}${path26}`, {
|
|
14174
14433
|
method,
|
|
14175
14434
|
headers: { "content-type": "application/json" },
|
|
14176
14435
|
...body === void 0 ? {} : { body: JSON.stringify(body) },
|
|
14177
14436
|
...signal ? { signal } : {}
|
|
14178
14437
|
});
|
|
14179
|
-
if (!response.ok) throw new Error(`${method} ${
|
|
14438
|
+
if (!response.ok) throw new Error(`${method} ${path26} failed: ${response.status} ${response.statusText}`);
|
|
14180
14439
|
return await response.json().catch(() => null);
|
|
14181
14440
|
}
|
|
14182
14441
|
nativeSessionId() {
|
|
@@ -14269,7 +14528,7 @@ var CliAgentAiService = class {
|
|
|
14269
14528
|
if (!runner) throw new Error(`${config.aiProvider} is not a coding-agent planner.`);
|
|
14270
14529
|
this.runner = runner;
|
|
14271
14530
|
this.processDeps = {
|
|
14272
|
-
spawn: deps.spawn ??
|
|
14531
|
+
spawn: deps.spawn ?? import_child_process7.spawn,
|
|
14273
14532
|
fetch: deps.fetch ?? globalThis.fetch,
|
|
14274
14533
|
resolvePath: deps.resolvePath,
|
|
14275
14534
|
platform: deps.platform,
|
|
@@ -14630,8 +14889,8 @@ var CliAgentAiService = class {
|
|
|
14630
14889
|
this.lastNativeSessionId = previousSessionId;
|
|
14631
14890
|
}
|
|
14632
14891
|
}
|
|
14633
|
-
async researchAndPlan(userDescription, runners, modelsByRunner,
|
|
14634
|
-
const contextStr = await collectResearchContext(
|
|
14892
|
+
async researchAndPlan(userDescription, runners, modelsByRunner, fs15, onProgress, _fetcher, runnerModes, modes = DEFAULT_PLANNER_MODES, signal) {
|
|
14893
|
+
const contextStr = await collectResearchContext(fs15, runners);
|
|
14635
14894
|
const researchLog = [
|
|
14636
14895
|
{ id: `up-${Date.now()}`, type: "user_prompt", content: userDescription, timestamp: (/* @__PURE__ */ new Date()).toISOString() }
|
|
14637
14896
|
];
|
|
@@ -15530,26 +15789,26 @@ function retargetTaskRunner(task, runner, catalog) {
|
|
|
15530
15789
|
}
|
|
15531
15790
|
|
|
15532
15791
|
// src/services/SkillsService.ts
|
|
15533
|
-
var
|
|
15534
|
-
var
|
|
15792
|
+
var fs9 = __toESM(require("fs"));
|
|
15793
|
+
var path19 = __toESM(require("path"));
|
|
15535
15794
|
|
|
15536
15795
|
// src/services/builtinSkills.ts
|
|
15537
|
-
var
|
|
15796
|
+
var path18 = __toESM(require("path"));
|
|
15538
15797
|
var import_url = require("url");
|
|
15539
15798
|
var import_meta = {};
|
|
15540
15799
|
function builtinSkillsDir() {
|
|
15541
|
-
const dir = typeof __dirname !== "undefined" && __dirname ? __dirname :
|
|
15800
|
+
const dir = typeof __dirname !== "undefined" && __dirname ? __dirname : path18.dirname((0, import_url.fileURLToPath)(
|
|
15542
15801
|
// @ts-expect-error - import.meta is only valid under an ESM module setting
|
|
15543
15802
|
import_meta.url
|
|
15544
15803
|
));
|
|
15545
|
-
return
|
|
15804
|
+
return path18.join(dir, "..", "skills");
|
|
15546
15805
|
}
|
|
15547
15806
|
|
|
15548
15807
|
// src/services/SkillsService.ts
|
|
15549
15808
|
var BUILTIN_SKILL_NAMES = ["grilling", "to-spec", "improve-codebase-architecture"];
|
|
15550
15809
|
var RETIRED_BUILTIN_SKILL_NAMES = ["grill-me"];
|
|
15551
15810
|
function parseSkillFile(filePath, name, source) {
|
|
15552
|
-
const raw =
|
|
15811
|
+
const raw = fs9.readFileSync(filePath, "utf8");
|
|
15553
15812
|
const frontmatter = {};
|
|
15554
15813
|
let content = raw;
|
|
15555
15814
|
if (raw.startsWith("---\n")) {
|
|
@@ -15578,23 +15837,23 @@ function parseSkillFile(filePath, name, source) {
|
|
|
15578
15837
|
return { name, description: metadata.description, metadata, content, path: filePath, source };
|
|
15579
15838
|
}
|
|
15580
15839
|
function copyDirSync2(src, dest) {
|
|
15581
|
-
|
|
15582
|
-
const entries =
|
|
15840
|
+
fs9.mkdirSync(dest, { recursive: true });
|
|
15841
|
+
const entries = fs9.readdirSync(src, { withFileTypes: true });
|
|
15583
15842
|
for (const entry of entries) {
|
|
15584
|
-
const srcPath =
|
|
15585
|
-
const destPath =
|
|
15843
|
+
const srcPath = path19.join(src, entry.name);
|
|
15844
|
+
const destPath = path19.join(dest, entry.name);
|
|
15586
15845
|
if (entry.isDirectory()) copyDirSync2(srcPath, destPath);
|
|
15587
|
-
else
|
|
15846
|
+
else fs9.copyFileSync(srcPath, destPath);
|
|
15588
15847
|
}
|
|
15589
15848
|
}
|
|
15590
15849
|
function readDir(dir, source) {
|
|
15591
|
-
if (!
|
|
15592
|
-
const entries =
|
|
15850
|
+
if (!fs9.existsSync(dir)) return [];
|
|
15851
|
+
const entries = fs9.readdirSync(dir, { withFileTypes: true });
|
|
15593
15852
|
const skills = [];
|
|
15594
15853
|
for (const entry of entries) {
|
|
15595
15854
|
if (!entry.isDirectory()) continue;
|
|
15596
|
-
const skillFile =
|
|
15597
|
-
if (!
|
|
15855
|
+
const skillFile = path19.join(dir, entry.name, "SKILL.md");
|
|
15856
|
+
if (!fs9.existsSync(skillFile)) continue;
|
|
15598
15857
|
const parsed = parseSkillFile(skillFile, entry.name, source);
|
|
15599
15858
|
if (parsed) skills.push(parsed);
|
|
15600
15859
|
}
|
|
@@ -15606,22 +15865,22 @@ var SkillsService = class {
|
|
|
15606
15865
|
}
|
|
15607
15866
|
workspaceRoot;
|
|
15608
15867
|
localDir() {
|
|
15609
|
-
return this.workspaceRoot ?
|
|
15868
|
+
return this.workspaceRoot ? path19.join(this.workspaceRoot, ".ordewell", "skills") : void 0;
|
|
15610
15869
|
}
|
|
15611
15870
|
globalDir() {
|
|
15612
|
-
return
|
|
15871
|
+
return path19.join(globalDataDir(), "skills");
|
|
15613
15872
|
}
|
|
15614
15873
|
seed(name) {
|
|
15615
|
-
const dest =
|
|
15616
|
-
if (
|
|
15874
|
+
const dest = path19.join(this.globalDir(), name);
|
|
15875
|
+
if (fs9.existsSync(dest)) return false;
|
|
15617
15876
|
let srcDir;
|
|
15618
15877
|
try {
|
|
15619
|
-
srcDir =
|
|
15878
|
+
srcDir = path19.join(builtinSkillsDir(), name);
|
|
15620
15879
|
} catch (err) {
|
|
15621
15880
|
console.warn(`Could not locate built-in skills dir: ${String(err)}`);
|
|
15622
15881
|
return false;
|
|
15623
15882
|
}
|
|
15624
|
-
if (!
|
|
15883
|
+
if (!fs9.existsSync(srcDir)) {
|
|
15625
15884
|
console.warn(`Built-in skill not found in package: ${name}`);
|
|
15626
15885
|
return false;
|
|
15627
15886
|
}
|
|
@@ -15635,14 +15894,14 @@ var SkillsService = class {
|
|
|
15635
15894
|
isUnmodifiedRetiredSeed(dir, expectedName) {
|
|
15636
15895
|
let entries;
|
|
15637
15896
|
try {
|
|
15638
|
-
entries =
|
|
15897
|
+
entries = fs9.readdirSync(dir, { withFileTypes: true });
|
|
15639
15898
|
} catch {
|
|
15640
15899
|
return false;
|
|
15641
15900
|
}
|
|
15642
15901
|
if (entries.length !== 1 || entries[0].name !== "SKILL.md" || !entries[0].isFile()) return false;
|
|
15643
15902
|
let raw;
|
|
15644
15903
|
try {
|
|
15645
|
-
raw =
|
|
15904
|
+
raw = fs9.readFileSync(path19.join(dir, "SKILL.md"), "utf8");
|
|
15646
15905
|
} catch {
|
|
15647
15906
|
return false;
|
|
15648
15907
|
}
|
|
@@ -15659,11 +15918,11 @@ var SkillsService = class {
|
|
|
15659
15918
|
}
|
|
15660
15919
|
pruneRetired() {
|
|
15661
15920
|
for (const name of RETIRED_BUILTIN_SKILL_NAMES) {
|
|
15662
|
-
const dest =
|
|
15663
|
-
if (!
|
|
15921
|
+
const dest = path19.join(this.globalDir(), name);
|
|
15922
|
+
if (!fs9.existsSync(dest)) continue;
|
|
15664
15923
|
if (!this.isUnmodifiedRetiredSeed(dest, name)) continue;
|
|
15665
15924
|
try {
|
|
15666
|
-
|
|
15925
|
+
fs9.rmSync(dest, { recursive: true, force: true });
|
|
15667
15926
|
} catch (err) {
|
|
15668
15927
|
console.warn(`Could not prune retired skill ${name}: ${String(err)}`);
|
|
15669
15928
|
}
|
|
@@ -15674,11 +15933,11 @@ var SkillsService = class {
|
|
|
15674
15933
|
if (BUILTIN_SKILL_NAMES.includes(name)) this.seed(name);
|
|
15675
15934
|
const localDir = this.localDir();
|
|
15676
15935
|
if (localDir) {
|
|
15677
|
-
const localFile =
|
|
15678
|
-
if (
|
|
15936
|
+
const localFile = path19.join(localDir, name, "SKILL.md");
|
|
15937
|
+
if (fs9.existsSync(localFile)) return parseSkillFile(localFile, name, "local");
|
|
15679
15938
|
}
|
|
15680
|
-
const globalFile =
|
|
15681
|
-
if (
|
|
15939
|
+
const globalFile = path19.join(this.globalDir(), name, "SKILL.md");
|
|
15940
|
+
if (fs9.existsSync(globalFile)) return parseSkillFile(globalFile, name, "global");
|
|
15682
15941
|
return void 0;
|
|
15683
15942
|
}
|
|
15684
15943
|
listSkills() {
|
|
@@ -15759,8 +16018,8 @@ function truncateCheckpointSummary(summary) {
|
|
|
15759
16018
|
}
|
|
15760
16019
|
|
|
15761
16020
|
// src/utils/sessionStore.ts
|
|
15762
|
-
var
|
|
15763
|
-
var
|
|
16021
|
+
var fs10 = __toESM(require("fs"));
|
|
16022
|
+
var path20 = __toESM(require("path"));
|
|
15764
16023
|
|
|
15765
16024
|
// src/utils/sessionId.ts
|
|
15766
16025
|
var import_crypto2 = require("crypto");
|
|
@@ -15771,7 +16030,7 @@ function mintSessionId() {
|
|
|
15771
16030
|
// src/utils/sessionStore.ts
|
|
15772
16031
|
var SESSIONS_SUBDIR = "sessions";
|
|
15773
16032
|
function getSessionsDir(baseDir) {
|
|
15774
|
-
return
|
|
16033
|
+
return path20.join(getStateDir(baseDir), SESSIONS_SUBDIR);
|
|
15775
16034
|
}
|
|
15776
16035
|
var REJECTION_MESSAGE = "This session was created with an older version of Ordewell. Please re-plan.";
|
|
15777
16036
|
function hasScalarRunner(parsed) {
|
|
@@ -15793,11 +16052,11 @@ function legacySessionFilename(session) {
|
|
|
15793
16052
|
return `${filenameStem(session)}_${session.id.slice(0, 8)}.json`;
|
|
15794
16053
|
}
|
|
15795
16054
|
function dropLegacyFile(sessionsDir, meta) {
|
|
15796
|
-
const legacy =
|
|
15797
|
-
if (legacy ===
|
|
16055
|
+
const legacy = path20.join(sessionsDir, legacySessionFilename(meta));
|
|
16056
|
+
if (legacy === path20.join(sessionsDir, sessionFilename(meta)) || !fs10.existsSync(legacy)) return;
|
|
15798
16057
|
try {
|
|
15799
|
-
const held = JSON.parse(
|
|
15800
|
-
if (held === meta.id)
|
|
16058
|
+
const held = JSON.parse(fs10.readFileSync(legacy, "utf-8")).meta?.id;
|
|
16059
|
+
if (held === meta.id) fs10.unlinkSync(legacy);
|
|
15801
16060
|
} catch {
|
|
15802
16061
|
}
|
|
15803
16062
|
}
|
|
@@ -15820,18 +16079,18 @@ function saveSession(plan, goal, baseDir, id) {
|
|
|
15820
16079
|
plan: JSON.parse(JSON.stringify(plan))
|
|
15821
16080
|
};
|
|
15822
16081
|
const filename = sessionFilename(meta);
|
|
15823
|
-
const filePath =
|
|
15824
|
-
|
|
16082
|
+
const filePath = path20.join(sessionsDir, filename);
|
|
16083
|
+
fs10.writeFileSync(filePath, JSON.stringify(session, null, 2));
|
|
15825
16084
|
dropLegacyFile(sessionsDir, meta);
|
|
15826
16085
|
return meta;
|
|
15827
16086
|
}
|
|
15828
16087
|
function listSessions(baseDir, logger = defaultLogger) {
|
|
15829
16088
|
const sessionsDir = getSessionsDir(baseDir);
|
|
15830
|
-
if (!
|
|
15831
|
-
const files =
|
|
16089
|
+
if (!fs10.existsSync(sessionsDir)) return [];
|
|
16090
|
+
const files = fs10.readdirSync(sessionsDir).filter((f) => f.endsWith(".json"));
|
|
15832
16091
|
return files.map((f) => {
|
|
15833
16092
|
try {
|
|
15834
|
-
const raw =
|
|
16093
|
+
const raw = fs10.readFileSync(path20.join(sessionsDir, f), "utf-8");
|
|
15835
16094
|
const parsed = JSON.parse(raw);
|
|
15836
16095
|
if (hasScalarRunner(parsed)) {
|
|
15837
16096
|
logger.warn("sessionStore", `session file ${f}: ${REJECTION_MESSAGE}`);
|
|
@@ -15846,10 +16105,10 @@ function listSessions(baseDir, logger = defaultLogger) {
|
|
|
15846
16105
|
}).filter((m) => m !== null).sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime());
|
|
15847
16106
|
}
|
|
15848
16107
|
function findSessionFile(sessionsDir, sessionId, logger) {
|
|
15849
|
-
const files =
|
|
16108
|
+
const files = fs10.readdirSync(sessionsDir).filter((f) => f.endsWith(".json"));
|
|
15850
16109
|
for (const f of files) {
|
|
15851
16110
|
try {
|
|
15852
|
-
const raw =
|
|
16111
|
+
const raw = fs10.readFileSync(path20.join(sessionsDir, f), "utf-8");
|
|
15853
16112
|
const session = JSON.parse(raw);
|
|
15854
16113
|
if (session.meta.id === sessionId) return f;
|
|
15855
16114
|
} catch (err) {
|
|
@@ -15860,11 +16119,11 @@ function findSessionFile(sessionsDir, sessionId, logger) {
|
|
|
15860
16119
|
}
|
|
15861
16120
|
function loadSession(sessionId, baseDir, logger = defaultLogger) {
|
|
15862
16121
|
const sessionsDir = getSessionsDir(baseDir);
|
|
15863
|
-
if (!
|
|
16122
|
+
if (!fs10.existsSync(sessionsDir)) return null;
|
|
15864
16123
|
try {
|
|
15865
16124
|
const file = findSessionFile(sessionsDir, sessionId, logger);
|
|
15866
16125
|
if (file) {
|
|
15867
|
-
const raw =
|
|
16126
|
+
const raw = fs10.readFileSync(path20.join(sessionsDir, file), "utf-8");
|
|
15868
16127
|
const parsed = JSON.parse(raw);
|
|
15869
16128
|
if (hasScalarRunner(parsed)) {
|
|
15870
16129
|
logger.warn("sessionStore", `session file ${file}: ${REJECTION_MESSAGE}`);
|
|
@@ -15895,11 +16154,11 @@ function getLatestSession(baseDir, logger = defaultLogger) {
|
|
|
15895
16154
|
}
|
|
15896
16155
|
function deleteSession(sessionId, baseDir, logger = defaultLogger) {
|
|
15897
16156
|
const sessionsDir = getSessionsDir(baseDir);
|
|
15898
|
-
if (!
|
|
16157
|
+
if (!fs10.existsSync(sessionsDir)) return false;
|
|
15899
16158
|
try {
|
|
15900
16159
|
const file = findSessionFile(sessionsDir, sessionId, logger);
|
|
15901
16160
|
if (file) {
|
|
15902
|
-
|
|
16161
|
+
fs10.unlinkSync(path20.join(sessionsDir, file));
|
|
15903
16162
|
return true;
|
|
15904
16163
|
}
|
|
15905
16164
|
} catch (err) {
|
|
@@ -16580,6 +16839,10 @@ var Session = class {
|
|
|
16580
16839
|
}
|
|
16581
16840
|
await this.orchestrator.runTask(taskId);
|
|
16582
16841
|
}
|
|
16842
|
+
/** Start whatever the scheduler can now fit — after the parallel limit was raised mid-run, say. */
|
|
16843
|
+
async reschedule() {
|
|
16844
|
+
await this.orchestrator.tick();
|
|
16845
|
+
}
|
|
16583
16846
|
async retryTask(taskId) {
|
|
16584
16847
|
await this.orchestrator.retryTask(taskId);
|
|
16585
16848
|
this.persist();
|
|
@@ -17337,7 +17600,7 @@ function buildRunnerInvocation(opts) {
|
|
|
17337
17600
|
}
|
|
17338
17601
|
|
|
17339
17602
|
// src/services/HeadlessRunner.ts
|
|
17340
|
-
var
|
|
17603
|
+
var import_child_process8 = require("child_process");
|
|
17341
17604
|
|
|
17342
17605
|
// src/services/AbstractRunner.ts
|
|
17343
17606
|
var import_events = require("events");
|
|
@@ -17392,7 +17655,7 @@ var AbstractRunner = class {
|
|
|
17392
17655
|
function defaultHasScriptCmd() {
|
|
17393
17656
|
if (process.platform === "win32") return false;
|
|
17394
17657
|
try {
|
|
17395
|
-
(0,
|
|
17658
|
+
(0, import_child_process8.execSync)("which script", { stdio: "ignore" });
|
|
17396
17659
|
return true;
|
|
17397
17660
|
} catch {
|
|
17398
17661
|
return false;
|
|
@@ -17482,7 +17745,7 @@ var HeadlessRunner = class extends AbstractRunner {
|
|
|
17482
17745
|
defaultInteractive = false;
|
|
17483
17746
|
constructor(deps = {}) {
|
|
17484
17747
|
super();
|
|
17485
|
-
this.spawnImpl = deps.spawnImpl ??
|
|
17748
|
+
this.spawnImpl = deps.spawnImpl ?? import_child_process8.spawn;
|
|
17486
17749
|
this.hasScriptCmd = deps.hasScriptCmd ?? defaultHasScriptCmd;
|
|
17487
17750
|
this.resolvePath = deps.resolvePath ?? augmentedPath;
|
|
17488
17751
|
this.launchDeps = { resolvePath: this.resolvePath, ...deps.launchDeps };
|
|
@@ -17522,7 +17785,7 @@ var HeadlessRunner = class extends AbstractRunner {
|
|
|
17522
17785
|
return {
|
|
17523
17786
|
launch: await planDirectLaunch(command, args, this.launchDeps),
|
|
17524
17787
|
resolvedPath,
|
|
17525
|
-
env: invocation.env,
|
|
17788
|
+
env: { ...opts.env, ...invocation.env },
|
|
17526
17789
|
pty,
|
|
17527
17790
|
submitPromptKey: invocation.submitPromptKey
|
|
17528
17791
|
};
|
|
@@ -17541,14 +17804,14 @@ var HeadlessRunner = class extends AbstractRunner {
|
|
|
17541
17804
|
};
|
|
17542
17805
|
|
|
17543
17806
|
// src/services/TmuxRunner.ts
|
|
17544
|
-
var
|
|
17807
|
+
var import_child_process10 = require("child_process");
|
|
17545
17808
|
var import_util5 = require("util");
|
|
17546
17809
|
var import_os = require("os");
|
|
17547
17810
|
var import_path = require("path");
|
|
17548
17811
|
var import_fs2 = require("fs");
|
|
17549
17812
|
|
|
17550
17813
|
// src/utils/tmux.ts
|
|
17551
|
-
var
|
|
17814
|
+
var import_child_process9 = require("child_process");
|
|
17552
17815
|
function tmuxSessionName(port) {
|
|
17553
17816
|
return `ordewell-${port}`;
|
|
17554
17817
|
}
|
|
@@ -17561,7 +17824,7 @@ function tmuxWindowName(taskId, planSessionId) {
|
|
|
17561
17824
|
return `t-${scope}${slug(taskId).slice(0, 16)}`;
|
|
17562
17825
|
}
|
|
17563
17826
|
var defaultProbe = () => {
|
|
17564
|
-
(0,
|
|
17827
|
+
(0, import_child_process9.execSync)(process.platform === "win32" ? "where tmux" : "which tmux", { stdio: "ignore" });
|
|
17565
17828
|
};
|
|
17566
17829
|
function hasTmux(probe = defaultProbe) {
|
|
17567
17830
|
try {
|
|
@@ -17573,7 +17836,7 @@ function hasTmux(probe = defaultProbe) {
|
|
|
17573
17836
|
}
|
|
17574
17837
|
var defaultHasBin = (bin) => {
|
|
17575
17838
|
try {
|
|
17576
|
-
(0,
|
|
17839
|
+
(0, import_child_process9.execSync)(`${process.platform === "win32" ? "where" : "which"} ${bin}`, { stdio: "ignore" });
|
|
17577
17840
|
return true;
|
|
17578
17841
|
} catch {
|
|
17579
17842
|
return false;
|
|
@@ -17596,7 +17859,7 @@ function clipboardCopyCommand(hasBin = defaultHasBin, platform = process.platfor
|
|
|
17596
17859
|
|
|
17597
17860
|
// src/services/TmuxRunner.ts
|
|
17598
17861
|
var EXIT_RE = /<<<ORDEWELL_TMUX_EXIT:(\d+)>>>/;
|
|
17599
|
-
var execFileAsync2 = (0, import_util5.promisify)(
|
|
17862
|
+
var execFileAsync2 = (0, import_util5.promisify)(import_child_process10.execFile);
|
|
17600
17863
|
var defaultExecFile3 = async (command, args) => {
|
|
17601
17864
|
const { stdout, stderr } = await execFileAsync2(command, args);
|
|
17602
17865
|
return { stdout: String(stdout), stderr: String(stderr) };
|
|
@@ -17834,7 +18097,7 @@ var TmuxRunner = class extends AbstractRunner {
|
|
|
17834
18097
|
(0, import_path.join)(this.logDir, `${id}.log`),
|
|
17835
18098
|
this.socket
|
|
17836
18099
|
);
|
|
17837
|
-
invocation.env = { ...invocation.env, PATH: await this.resolvePath() };
|
|
18100
|
+
invocation.env = { ...opts.env, ...invocation.env, PATH: await this.resolvePath() };
|
|
17838
18101
|
await session.start(invocation.command, invocation.args, opts.cwd, invocation.env);
|
|
17839
18102
|
if (invocation.submitPromptKey) session.write("\r");
|
|
17840
18103
|
this.registerSession(id, session);
|
|
@@ -17914,18 +18177,18 @@ function tokensMatch(presented, expected) {
|
|
|
17914
18177
|
}
|
|
17915
18178
|
|
|
17916
18179
|
// src/plugins/RunnerRegistry.ts
|
|
17917
|
-
var
|
|
17918
|
-
var
|
|
17919
|
-
var
|
|
17920
|
-
var
|
|
18180
|
+
var fs12 = __toESM(require("fs"));
|
|
18181
|
+
var path23 = __toESM(require("path"));
|
|
18182
|
+
var os6 = __toESM(require("os"));
|
|
18183
|
+
var import_child_process11 = require("child_process");
|
|
17921
18184
|
var import_crypto4 = require("crypto");
|
|
17922
18185
|
|
|
17923
18186
|
// src/plugins/FsPluginStore.ts
|
|
17924
|
-
var
|
|
17925
|
-
var
|
|
18187
|
+
var fs11 = __toESM(require("fs"));
|
|
18188
|
+
var path22 = __toESM(require("path"));
|
|
17926
18189
|
|
|
17927
18190
|
// src/plugins/pluginNames.ts
|
|
17928
|
-
var
|
|
18191
|
+
var path21 = __toESM(require("path"));
|
|
17929
18192
|
var PLUGIN_NAME_PATTERN = /^[A-Za-z0-9](?:[A-Za-z0-9._-]*[A-Za-z0-9])?$/;
|
|
17930
18193
|
var MAX_PLUGIN_NAME_LENGTH = 64;
|
|
17931
18194
|
function isPlainPluginName(name) {
|
|
@@ -17940,10 +18203,10 @@ function assertPlainPluginName(name) {
|
|
|
17940
18203
|
}
|
|
17941
18204
|
function resolvePluginInstallDir(pluginsDir, name) {
|
|
17942
18205
|
assertPlainPluginName(name);
|
|
17943
|
-
const root =
|
|
17944
|
-
const dest =
|
|
17945
|
-
const relative5 =
|
|
17946
|
-
if (relative5 !== name || relative5 === "" ||
|
|
18206
|
+
const root = path21.resolve(pluginsDir);
|
|
18207
|
+
const dest = path21.resolve(root, name);
|
|
18208
|
+
const relative5 = path21.relative(root, dest);
|
|
18209
|
+
if (relative5 !== name || relative5 === "" || path21.isAbsolute(relative5)) {
|
|
17947
18210
|
throw new Error(`Plugin install path escapes the plugins directory: ${JSON.stringify(name)}`);
|
|
17948
18211
|
}
|
|
17949
18212
|
return dest;
|
|
@@ -17958,13 +18221,13 @@ function isValidManifest(obj) {
|
|
|
17958
18221
|
|
|
17959
18222
|
// src/plugins/FsPluginStore.ts
|
|
17960
18223
|
function userPluginsDir() {
|
|
17961
|
-
return
|
|
18224
|
+
return path22.join(globalDataDir(), "plugins");
|
|
17962
18225
|
}
|
|
17963
18226
|
function loadManifestFromDir(dir) {
|
|
17964
|
-
const manifestPath =
|
|
17965
|
-
if (!
|
|
18227
|
+
const manifestPath = path22.join(dir, "manifest.json");
|
|
18228
|
+
if (!fs11.existsSync(manifestPath)) return null;
|
|
17966
18229
|
try {
|
|
17967
|
-
const raw =
|
|
18230
|
+
const raw = fs11.readFileSync(manifestPath, "utf8");
|
|
17968
18231
|
const parsed = JSON.parse(raw);
|
|
17969
18232
|
if (!isValidManifest(parsed)) return null;
|
|
17970
18233
|
return parsed;
|
|
@@ -17974,29 +18237,29 @@ function loadManifestFromDir(dir) {
|
|
|
17974
18237
|
}
|
|
17975
18238
|
function copyDirSync3(src, dest) {
|
|
17976
18239
|
ensureDir(dest);
|
|
17977
|
-
const entries =
|
|
18240
|
+
const entries = fs11.readdirSync(src, { withFileTypes: true });
|
|
17978
18241
|
for (const entry of entries) {
|
|
17979
|
-
const srcPath =
|
|
17980
|
-
const destPath =
|
|
18242
|
+
const srcPath = path22.join(src, entry.name);
|
|
18243
|
+
const destPath = path22.join(dest, entry.name);
|
|
17981
18244
|
if (entry.isDirectory()) {
|
|
17982
18245
|
copyDirSync3(srcPath, destPath);
|
|
17983
18246
|
} else {
|
|
17984
|
-
|
|
18247
|
+
fs11.copyFileSync(srcPath, destPath);
|
|
17985
18248
|
}
|
|
17986
18249
|
}
|
|
17987
18250
|
}
|
|
17988
18251
|
function rmDirSync(dir) {
|
|
17989
|
-
if (!
|
|
17990
|
-
const entries =
|
|
18252
|
+
if (!fs11.existsSync(dir)) return;
|
|
18253
|
+
const entries = fs11.readdirSync(dir, { withFileTypes: true });
|
|
17991
18254
|
for (const entry of entries) {
|
|
17992
|
-
const p =
|
|
18255
|
+
const p = path22.join(dir, entry.name);
|
|
17993
18256
|
if (entry.isDirectory()) {
|
|
17994
18257
|
rmDirSync(p);
|
|
17995
18258
|
} else {
|
|
17996
|
-
|
|
18259
|
+
fs11.unlinkSync(p);
|
|
17997
18260
|
}
|
|
17998
18261
|
}
|
|
17999
|
-
|
|
18262
|
+
fs11.rmdirSync(dir);
|
|
18000
18263
|
}
|
|
18001
18264
|
var FsPluginStore = class {
|
|
18002
18265
|
getUserPluginsDir() {
|
|
@@ -18004,10 +18267,10 @@ var FsPluginStore = class {
|
|
|
18004
18267
|
}
|
|
18005
18268
|
listUserPluginDirs() {
|
|
18006
18269
|
const dir = userPluginsDir();
|
|
18007
|
-
if (!
|
|
18270
|
+
if (!fs11.existsSync(dir)) return [];
|
|
18008
18271
|
let entries;
|
|
18009
18272
|
try {
|
|
18010
|
-
entries =
|
|
18273
|
+
entries = fs11.readdirSync(dir, { withFileTypes: true });
|
|
18011
18274
|
} catch {
|
|
18012
18275
|
return [];
|
|
18013
18276
|
}
|
|
@@ -18015,7 +18278,7 @@ var FsPluginStore = class {
|
|
|
18015
18278
|
}
|
|
18016
18279
|
listDir(dir) {
|
|
18017
18280
|
try {
|
|
18018
|
-
return
|
|
18281
|
+
return fs11.readdirSync(dir);
|
|
18019
18282
|
} catch {
|
|
18020
18283
|
return [];
|
|
18021
18284
|
}
|
|
@@ -18033,20 +18296,20 @@ var FsPluginStore = class {
|
|
|
18033
18296
|
ensureDir(dir);
|
|
18034
18297
|
}
|
|
18035
18298
|
writeFile(filePath, content) {
|
|
18036
|
-
|
|
18299
|
+
fs11.writeFileSync(filePath, content);
|
|
18037
18300
|
}
|
|
18038
18301
|
readFile(filePath) {
|
|
18039
18302
|
try {
|
|
18040
|
-
return
|
|
18303
|
+
return fs11.readFileSync(filePath, "utf8");
|
|
18041
18304
|
} catch {
|
|
18042
18305
|
return null;
|
|
18043
18306
|
}
|
|
18044
18307
|
}
|
|
18045
|
-
dirExists(
|
|
18046
|
-
return
|
|
18308
|
+
dirExists(path26) {
|
|
18309
|
+
return fs11.existsSync(path26) && fs11.statSync(path26).isDirectory();
|
|
18047
18310
|
}
|
|
18048
|
-
exists(
|
|
18049
|
-
return
|
|
18311
|
+
exists(path26) {
|
|
18312
|
+
return fs11.existsSync(path26);
|
|
18050
18313
|
}
|
|
18051
18314
|
};
|
|
18052
18315
|
|
|
@@ -18073,7 +18336,15 @@ var CLAUDE_CODE_MANIFEST = {
|
|
|
18073
18336
|
"{{/if}}",
|
|
18074
18337
|
"{{prompt}}"
|
|
18075
18338
|
],
|
|
18076
|
-
promptInArgs: true
|
|
18339
|
+
promptInArgs: true,
|
|
18340
|
+
// Interactive sessions show these even with --dangerously-skip-permissions:
|
|
18341
|
+
// a folder Claude Code has never been trusted in (a fresh clone, or a repo
|
|
18342
|
+
// only ever planned in headlessly), and the first Bypass Permissions run.
|
|
18343
|
+
blockingPrompts: [
|
|
18344
|
+
{ phrase: "Is this a project you created or one you trust", asks: "whether to trust the task's folder" },
|
|
18345
|
+
{ phrase: "Do you trust the files in this folder", asks: "whether to trust the task's folder" },
|
|
18346
|
+
{ phrase: "running in Bypass Permissions mode", asks: "you to accept Bypass Permissions mode" }
|
|
18347
|
+
]
|
|
18077
18348
|
},
|
|
18078
18349
|
features: {
|
|
18079
18350
|
modelSelection: true,
|
|
@@ -18417,7 +18688,7 @@ function isReservedRunnerName(name) {
|
|
|
18417
18688
|
return RESERVED_RUNNER_NAMES.has(name.toLowerCase());
|
|
18418
18689
|
}
|
|
18419
18690
|
function gitClone(url, destDir) {
|
|
18420
|
-
(0,
|
|
18691
|
+
(0, import_child_process11.execFileSync)("git", ["clone", "--depth", "1", "--", url, destDir], {
|
|
18421
18692
|
stdio: "pipe",
|
|
18422
18693
|
timeout: 3e4,
|
|
18423
18694
|
// A repository that demands credentials should fail, not hang on a prompt.
|
|
@@ -18478,7 +18749,7 @@ var RunnerRegistry = class {
|
|
|
18478
18749
|
return entry?.source === "builtin";
|
|
18479
18750
|
}
|
|
18480
18751
|
installFromPath(sourcePath) {
|
|
18481
|
-
const absPath =
|
|
18752
|
+
const absPath = path23.resolve(sourcePath);
|
|
18482
18753
|
if (!this.store.dirExists(absPath)) {
|
|
18483
18754
|
throw new Error(`Plugin source must be a directory: ${absPath}`);
|
|
18484
18755
|
}
|
|
@@ -18490,7 +18761,7 @@ var RunnerRegistry = class {
|
|
|
18490
18761
|
}
|
|
18491
18762
|
installFromGit(url) {
|
|
18492
18763
|
const validated = assertInstallablePluginUrl(url);
|
|
18493
|
-
const tmpDir =
|
|
18764
|
+
const tmpDir = path23.join(os6.tmpdir(), `ordewell-plugin-${(0, import_crypto4.randomBytes)(12).toString("hex")}`);
|
|
18494
18765
|
this.store.ensureDir(tmpDir);
|
|
18495
18766
|
try {
|
|
18496
18767
|
try {
|
|
@@ -18503,7 +18774,7 @@ var RunnerRegistry = class {
|
|
|
18503
18774
|
const hasManifest = contents.some((f) => f === "manifest.json");
|
|
18504
18775
|
let sourceDir = tmpDir;
|
|
18505
18776
|
if (!hasManifest) {
|
|
18506
|
-
const onlyEntry = contents.length === 1 ?
|
|
18777
|
+
const onlyEntry = contents.length === 1 ? path23.join(tmpDir, contents[0]) : void 0;
|
|
18507
18778
|
if (!onlyEntry || !this.store.dirExists(onlyEntry)) {
|
|
18508
18779
|
throw new Error(`No manifest.json found in repository: ${validated.toString()}`);
|
|
18509
18780
|
}
|
|
@@ -18551,7 +18822,7 @@ var RunnerRegistry = class {
|
|
|
18551
18822
|
}
|
|
18552
18823
|
createSkeleton(name, outputDir) {
|
|
18553
18824
|
assertPlainPluginName(name);
|
|
18554
|
-
const dir =
|
|
18825
|
+
const dir = path23.resolve(outputDir, name);
|
|
18555
18826
|
this.store.ensureDir(dir);
|
|
18556
18827
|
const manifest = {
|
|
18557
18828
|
name,
|
|
@@ -18583,8 +18854,8 @@ var RunnerRegistry = class {
|
|
|
18583
18854
|
]
|
|
18584
18855
|
}
|
|
18585
18856
|
};
|
|
18586
|
-
this.store.writeFile(
|
|
18587
|
-
const runSh =
|
|
18857
|
+
this.store.writeFile(path23.join(dir, "manifest.json"), JSON.stringify(manifest, null, 2) + "\n");
|
|
18858
|
+
const runSh = path23.join(dir, "run.sh");
|
|
18588
18859
|
const scriptContent = `#!/usr/bin/env bash
|
|
18589
18860
|
# Custom runner script for ${name}
|
|
18590
18861
|
# Receives:
|
|
@@ -18610,30 +18881,30 @@ echo "Running ${name} with prompt: $prompt"
|
|
|
18610
18881
|
};
|
|
18611
18882
|
function fsChmodSync(fpath, mode) {
|
|
18612
18883
|
try {
|
|
18613
|
-
|
|
18884
|
+
fs12.chmodSync(fpath, mode);
|
|
18614
18885
|
} catch {
|
|
18615
18886
|
}
|
|
18616
18887
|
}
|
|
18617
18888
|
|
|
18618
18889
|
// src/utils/stateStore.ts
|
|
18619
|
-
var
|
|
18620
|
-
var
|
|
18890
|
+
var fs13 = __toESM(require("fs"));
|
|
18891
|
+
var path24 = __toESM(require("path"));
|
|
18621
18892
|
var STATE_FILE = "state.json";
|
|
18622
18893
|
function getStatePath(baseDir) {
|
|
18623
|
-
return
|
|
18894
|
+
return path24.join(getStateDir(baseDir), STATE_FILE);
|
|
18624
18895
|
}
|
|
18625
18896
|
function saveState(plan, baseDir) {
|
|
18626
18897
|
const statePath = getStatePath(baseDir ?? process.cwd());
|
|
18627
|
-
ensureDir(
|
|
18898
|
+
ensureDir(path24.dirname(statePath));
|
|
18628
18899
|
plan.lastUpdated = (/* @__PURE__ */ new Date()).toISOString();
|
|
18629
|
-
|
|
18900
|
+
fs13.writeFileSync(statePath, JSON.stringify(plan, null, 2));
|
|
18630
18901
|
}
|
|
18631
18902
|
function loadState(baseDir, logger = defaultLogger) {
|
|
18632
18903
|
const dir = baseDir ?? process.cwd();
|
|
18633
18904
|
const statePath = getStatePath(dir);
|
|
18634
|
-
if (!
|
|
18905
|
+
if (!fs13.existsSync(statePath)) return null;
|
|
18635
18906
|
try {
|
|
18636
|
-
const raw =
|
|
18907
|
+
const raw = fs13.readFileSync(statePath, "utf-8");
|
|
18637
18908
|
const parsed = JSON.parse(raw);
|
|
18638
18909
|
if (!Array.isArray(parsed.runners)) {
|
|
18639
18910
|
logger.warn("stateStore", `rejecting state from ${statePath}: old format (scalar "runner" instead of "runners" array). Please re-plan.`);
|
|
@@ -18650,16 +18921,16 @@ function loadState(baseDir, logger = defaultLogger) {
|
|
|
18650
18921
|
function clearState(baseDir) {
|
|
18651
18922
|
const dir = baseDir ?? process.cwd();
|
|
18652
18923
|
const statePath = getStatePath(dir);
|
|
18653
|
-
if (
|
|
18924
|
+
if (fs13.existsSync(statePath)) fs13.unlinkSync(statePath);
|
|
18654
18925
|
}
|
|
18655
18926
|
function stateExists(baseDir) {
|
|
18656
18927
|
const dir = baseDir ?? process.cwd();
|
|
18657
|
-
return
|
|
18928
|
+
return fs13.existsSync(getStatePath(dir));
|
|
18658
18929
|
}
|
|
18659
18930
|
|
|
18660
18931
|
// src/services/SettingsService.ts
|
|
18661
|
-
var
|
|
18662
|
-
var
|
|
18932
|
+
var fs14 = __toESM(require("fs"));
|
|
18933
|
+
var path25 = __toESM(require("path"));
|
|
18663
18934
|
var DEFAULTS = {
|
|
18664
18935
|
tdd: { enabled: true },
|
|
18665
18936
|
verification: { enabled: false }
|
|
@@ -18667,7 +18938,7 @@ var DEFAULTS = {
|
|
|
18667
18938
|
function getSettingsPath() {
|
|
18668
18939
|
const override = process.env.ORDEWELL_SETTINGS_PATH;
|
|
18669
18940
|
if (override && override.trim()) return override.trim();
|
|
18670
|
-
return
|
|
18941
|
+
return path25.join(globalDataDir(), "settings.json");
|
|
18671
18942
|
}
|
|
18672
18943
|
function readPlannerModels(raw) {
|
|
18673
18944
|
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return void 0;
|
|
@@ -18700,7 +18971,7 @@ var SettingsService = class {
|
|
|
18700
18971
|
}
|
|
18701
18972
|
fileMtimeMs() {
|
|
18702
18973
|
try {
|
|
18703
|
-
return
|
|
18974
|
+
return fs14.statSync(this.filePath).mtimeMs;
|
|
18704
18975
|
} catch {
|
|
18705
18976
|
return null;
|
|
18706
18977
|
}
|
|
@@ -18772,8 +19043,8 @@ var SettingsService = class {
|
|
|
18772
19043
|
load() {
|
|
18773
19044
|
migrateOldConfigDir();
|
|
18774
19045
|
try {
|
|
18775
|
-
if (
|
|
18776
|
-
const raw = JSON.parse(
|
|
19046
|
+
if (fs14.existsSync(this.filePath)) {
|
|
19047
|
+
const raw = JSON.parse(fs14.readFileSync(this.filePath, "utf-8"));
|
|
18777
19048
|
const settings = {
|
|
18778
19049
|
tdd: { enabled: raw.tdd?.enabled ?? DEFAULTS.tdd.enabled },
|
|
18779
19050
|
verification: { enabled: raw.verification?.enabled ?? DEFAULTS.verification.enabled }
|
|
@@ -18795,11 +19066,11 @@ var SettingsService = class {
|
|
|
18795
19066
|
return { ...DEFAULTS, tdd: { ...DEFAULTS.tdd }, verification: { ...DEFAULTS.verification } };
|
|
18796
19067
|
}
|
|
18797
19068
|
persist() {
|
|
18798
|
-
const dir =
|
|
18799
|
-
if (!
|
|
18800
|
-
|
|
19069
|
+
const dir = path25.dirname(this.filePath);
|
|
19070
|
+
if (!fs14.existsSync(dir)) {
|
|
19071
|
+
fs14.mkdirSync(dir, { recursive: true });
|
|
18801
19072
|
}
|
|
18802
|
-
|
|
19073
|
+
fs14.writeFileSync(this.filePath, JSON.stringify(this.cache, null, 2));
|
|
18803
19074
|
this.cachedMtimeMs = this.fileMtimeMs();
|
|
18804
19075
|
}
|
|
18805
19076
|
};
|
|
@@ -18866,6 +19137,7 @@ var PlannerModelMemory = class {
|
|
|
18866
19137
|
ConversationEditError,
|
|
18867
19138
|
DAEMON_SUBPROTOCOL,
|
|
18868
19139
|
DAEMON_TOKEN_SUBPROTOCOL_PREFIX,
|
|
19140
|
+
DEFAULT_MAX_PARALLEL,
|
|
18869
19141
|
DENY_ALL,
|
|
18870
19142
|
EmbeddedNewlineError,
|
|
18871
19143
|
EnvConfig,
|
|
@@ -19048,6 +19320,7 @@ var PlannerModelMemory = class {
|
|
|
19048
19320
|
modifyValidationFeedback,
|
|
19049
19321
|
normalizeAgentArgs,
|
|
19050
19322
|
normalizeGeminiModel,
|
|
19323
|
+
parseMaxParallel,
|
|
19051
19324
|
parsePartialPlan,
|
|
19052
19325
|
parsePlanJson,
|
|
19053
19326
|
parseTaskOpsJson,
|