@elizaos/plugin-agent-orchestrator 0.4.1 → 0.4.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.
@@ -9,8 +9,14 @@
9
9
  */
10
10
  import { type HandlerCallback, type IAgentRuntime } from "@elizaos/core";
11
11
  import type { PTYService } from "../services/pty-service.js";
12
- /** Create a scratch sandbox directory for non-repo tasks */
13
- export declare function createScratchDir(): string;
12
+ /**
13
+ * Create a scratch sandbox directory for non-repo tasks.
14
+ *
15
+ * When `PARALLAX_CODING_DIRECTORY` is set (e.g. `~/Projects`), creates a
16
+ * named subdir like `~/Projects/todo-app/` derived from the task label.
17
+ * Otherwise falls back to `~/.milady/workspaces/{uuid}`.
18
+ */
19
+ export declare function createScratchDir(runtime?: IAgentRuntime, label?: string): string;
14
20
  /**
15
21
  * Generate a short semantic label from repo URL and/or task description.
16
22
  * e.g. "git-workspace-service-testbed/hello-mima" or "scratch/react-research"
@@ -1 +1 @@
1
- {"version":3,"file":"coding-task-helpers.d.ts","sourceRoot":"","sources":["../../src/actions/coding-task-helpers.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAMH,OAAO,EACL,KAAK,eAAe,EACpB,KAAK,aAAa,EAEnB,MAAM,eAAe,CAAC;AACvB,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAC;AAG7D,4DAA4D;AAC5D,wBAAgB,gBAAgB,IAAI,MAAM,CAMzC;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAC3B,IAAI,EAAE,MAAM,GAAG,SAAS,EACxB,IAAI,EAAE,MAAM,GAAG,SAAS,GACvB,MAAM,CA4BR;AAED;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CACnC,UAAU,EAAE,UAAU,EACtB,OAAO,EAAE,aAAa,EACtB,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,MAAM,EACb,UAAU,EAAE,MAAM,GAAG,IAAI,EACzB,QAAQ,CAAC,EAAE,eAAe,EAC1B,iBAAiB,UAAQ,GACxB,IAAI,CA6DN"}
1
+ {"version":3,"file":"coding-task-helpers.d.ts","sourceRoot":"","sources":["../../src/actions/coding-task-helpers.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAMH,OAAO,EACL,KAAK,eAAe,EACpB,KAAK,aAAa,EAEnB,MAAM,eAAe,CAAC;AACvB,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAC;AA+B7D;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAC9B,OAAO,CAAC,EAAE,aAAa,EACvB,KAAK,CAAC,EAAE,MAAM,GACb,MAAM,CA0BR;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAC3B,IAAI,EAAE,MAAM,GAAG,SAAS,EACxB,IAAI,EAAE,MAAM,GAAG,SAAS,GACvB,MAAM,CA4BR;AAED;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CACnC,UAAU,EAAE,UAAU,EACtB,OAAO,EAAE,aAAa,EACtB,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,MAAM,EACb,UAAU,EAAE,MAAM,GAAG,IAAI,EACzB,QAAQ,CAAC,EAAE,eAAe,EAC1B,iBAAiB,UAAQ,GACxB,IAAI,CAsEN"}
package/dist/index.js CHANGED
@@ -3241,6 +3241,7 @@ import * as os from "os";
3241
3241
  import * as path2 from "path";
3242
3242
  var MAX_ENTRIES = 150;
3243
3243
  var TRUNCATE_TO = 100;
3244
+ var MAX_FILE_SIZE_BYTES = 1048576;
3244
3245
 
3245
3246
  class WriteMutex {
3246
3247
  queue = [];
@@ -3280,6 +3281,13 @@ class SwarmHistory {
3280
3281
  await fs.appendFile(this.filePath, `${JSON.stringify(entry)}
3281
3282
  `, "utf-8");
3282
3283
  this.appendCount++;
3284
+ try {
3285
+ const stat2 = await fs.stat(this.filePath);
3286
+ if (stat2.size > MAX_FILE_SIZE_BYTES) {
3287
+ await this.truncateInner(TRUNCATE_TO);
3288
+ return;
3289
+ }
3290
+ } catch {}
3283
3291
  if (this.appendCount >= MAX_ENTRIES - TRUNCATE_TO) {
3284
3292
  const content = await fs.readFile(this.filePath, "utf-8");
3285
3293
  const lineCount = content.split(`
@@ -3339,10 +3347,16 @@ class SwarmHistory {
3339
3347
  return;
3340
3348
  }
3341
3349
  }
3342
- const kept = entries.slice(-maxEntries);
3343
- const content = kept.map((e) => JSON.stringify(e)).join(`
3350
+ let kept = entries.slice(-maxEntries);
3351
+ let content = kept.map((e) => JSON.stringify(e)).join(`
3352
+ `) + `
3353
+ `;
3354
+ while (Buffer.byteLength(content, "utf-8") > MAX_FILE_SIZE_BYTES && kept.length > 1) {
3355
+ kept = kept.slice(Math.max(1, Math.floor(kept.length * 0.2)));
3356
+ content = kept.map((e) => JSON.stringify(e)).join(`
3344
3357
  `) + `
3345
3358
  `;
3359
+ }
3346
3360
  await fs.writeFile(this.filePath, content, "utf-8");
3347
3361
  this.appendCount = 0;
3348
3362
  }
@@ -3586,9 +3600,30 @@ class SwarmCoordinator {
3586
3600
  constructor(runtime) {
3587
3601
  this.runtime = runtime;
3588
3602
  }
3603
+ scratchDecisionWired = false;
3589
3604
  setChatCallback(cb) {
3590
3605
  this.chatCallback = cb;
3591
3606
  this.log("Chat callback wired");
3607
+ this.wireScratchDecisionCallback();
3608
+ }
3609
+ wireScratchDecisionCallback() {
3610
+ if (this.scratchDecisionWired || !this.chatCallback)
3611
+ return;
3612
+ const wsService = this.runtime.getService("CODING_WORKSPACE_SERVICE");
3613
+ if (wsService?.setScratchDecisionCallback) {
3614
+ const chatCb = this.chatCallback;
3615
+ wsService.setScratchDecisionCallback(async (record) => {
3616
+ const ttlNote = record.expiresAt ? (() => {
3617
+ const remainMs = record.expiresAt - Date.now();
3618
+ const hours = Math.round(remainMs / (60 * 60 * 1000));
3619
+ return hours >= 1 ? `It will be automatically cleaned up in ~${hours} hour${hours === 1 ? "" : "s"}.` : `It will be automatically cleaned up shortly.`;
3620
+ })() : "It will be automatically cleaned up after the configured retention period.";
3621
+ await chatCb(`Task "${record.label}" finished. Code is at \`${record.path}\`.
3622
+ ` + `${ttlNote} To keep it, say "keep the workspace" or manage it in Settings → Coding Agents.`, "coding-agent");
3623
+ });
3624
+ this.scratchDecisionWired = true;
3625
+ this.log("Scratch decision callback wired");
3626
+ }
3592
3627
  }
3593
3628
  setWsBroadcast(cb) {
3594
3629
  this.wsBroadcast = cb;
@@ -3897,6 +3932,9 @@ class SwarmCoordinator {
3897
3932
  } catch {}
3898
3933
  }
3899
3934
  async handleSessionEvent(sessionId, event, data) {
3935
+ if (!this.scratchDecisionWired) {
3936
+ this.wireScratchDecisionCallback();
3937
+ }
3900
3938
  const tsMatch = sessionId.match(/^pty-(\d+)-/);
3901
3939
  if (tsMatch) {
3902
3940
  const sessionCreatedAt = Number(tsMatch[1]);
@@ -5270,15 +5308,55 @@ function formatAge(timestamp) {
5270
5308
  // src/actions/coding-task-helpers.ts
5271
5309
  import { randomUUID } from "node:crypto";
5272
5310
  import * as fs2 from "node:fs";
5273
- import * as os3 from "node:os";
5274
- import * as path4 from "node:path";
5311
+ import * as os4 from "node:os";
5312
+ import * as path5 from "node:path";
5275
5313
  import {
5276
5314
  logger as logger5
5277
5315
  } from "@elizaos/core";
5278
- function createScratchDir() {
5279
- const baseDir = path4.join(os3.homedir(), ".milady", "workspaces");
5316
+
5317
+ // src/services/config-env.ts
5318
+ import { readFileSync } from "node:fs";
5319
+ import * as os3 from "node:os";
5320
+ import * as path4 from "node:path";
5321
+ function readConfigEnvKey(key) {
5322
+ try {
5323
+ const configPath = path4.join(process.env.MILADY_STATE_DIR ?? process.env.ELIZA_STATE_DIR ?? path4.join(os3.homedir(), ".milady"), process.env.ELIZA_NAMESPACE === "milady" || !process.env.ELIZA_NAMESPACE ? "milady.json" : `${process.env.ELIZA_NAMESPACE}.json`);
5324
+ const raw = readFileSync(configPath, "utf-8");
5325
+ const config = JSON.parse(raw);
5326
+ const val = config?.env?.[key];
5327
+ return typeof val === "string" ? val : undefined;
5328
+ } catch {
5329
+ return;
5330
+ }
5331
+ }
5332
+
5333
+ // src/actions/coding-task-helpers.ts
5334
+ function sanitizeDirName(label) {
5335
+ return label.toLowerCase().replace(/[^a-z0-9-]/g, "-").replace(/-{2,}/g, "-").replace(/^-|-$/g, "").slice(0, 60) || "scratch";
5336
+ }
5337
+ function resolveNonColliding(baseDir, name) {
5338
+ let candidate = path5.join(baseDir, name);
5339
+ if (!fs2.existsSync(candidate))
5340
+ return candidate;
5341
+ for (let i = 2;i < 100; i++) {
5342
+ candidate = path5.join(baseDir, `${name}-${i}`);
5343
+ if (!fs2.existsSync(candidate))
5344
+ return candidate;
5345
+ }
5346
+ return path5.join(baseDir, `${name}-${randomUUID().slice(0, 8)}`);
5347
+ }
5348
+ function createScratchDir(runtime, label) {
5349
+ const codingDir = runtime?.getSetting("PARALLAX_CODING_DIRECTORY") ?? readConfigEnvKey("PARALLAX_CODING_DIRECTORY") ?? process.env.PARALLAX_CODING_DIRECTORY;
5350
+ if (codingDir?.trim()) {
5351
+ const resolved = codingDir.startsWith("~") ? path5.join(os4.homedir(), codingDir.slice(1)) : path5.resolve(codingDir);
5352
+ const dirName = label ? sanitizeDirName(label) : `scratch-${randomUUID().slice(0, 8)}`;
5353
+ const scratchDir2 = resolveNonColliding(resolved, dirName);
5354
+ fs2.mkdirSync(scratchDir2, { recursive: true });
5355
+ return scratchDir2;
5356
+ }
5357
+ const baseDir = path5.join(os4.homedir(), ".milady", "workspaces");
5280
5358
  const scratchId = randomUUID();
5281
- const scratchDir = path4.join(baseDir, scratchId);
5359
+ const scratchDir = path5.join(baseDir, scratchId);
5282
5360
  fs2.mkdirSync(scratchDir, { recursive: true });
5283
5361
  return scratchDir;
5284
5362
  }
@@ -5298,6 +5376,7 @@ function generateLabel(repo, task) {
5298
5376
  return parts.join("/");
5299
5377
  }
5300
5378
  function registerSessionEvents(ptyService, runtime, sessionId, label, scratchDir, callback, coordinatorActive = false) {
5379
+ let scratchRegistered = false;
5301
5380
  ptyService.onSessionEvent((sid, event, data) => {
5302
5381
  if (sid !== sessionId)
5303
5382
  return;
@@ -5327,10 +5406,15 @@ ${preview}` : `Agent "${label}" completed the task.`
5327
5406
  });
5328
5407
  }
5329
5408
  }
5330
- if ((event === "stopped" || event === "task_complete" || event === "error") && scratchDir) {
5409
+ if ((event === "stopped" || event === "task_complete" || event === "error") && scratchDir && !scratchRegistered) {
5410
+ logger5.info(`[scratch-lifecycle] Terminal event "${event}" for "${label}" — registering scratch workspace at ${scratchDir}`);
5331
5411
  const wsService = runtime.getService("CODING_WORKSPACE_SERVICE");
5332
- if (wsService) {
5333
- wsService.registerScratchWorkspace(sessionId, scratchDir, label, event).catch((err) => {
5412
+ if (!wsService) {
5413
+ logger5.warn(`[scratch-lifecycle] CODING_WORKSPACE_SERVICE not found cannot register scratch workspace`);
5414
+ } else {
5415
+ wsService.registerScratchWorkspace(sessionId, scratchDir, label, event).then(() => {
5416
+ scratchRegistered = true;
5417
+ }).catch((err) => {
5334
5418
  logger5.warn(`[START_CODING_TASK] Failed to register scratch workspace for "${label}": ${err}`);
5335
5419
  });
5336
5420
  }
@@ -5509,7 +5593,7 @@ async function handleMultiAgent(ctx, agentsParam) {
5509
5593
  branch = workspace.branch;
5510
5594
  wsService.setLabel(workspace.id, specLabel);
5511
5595
  } else {
5512
- workdir = createScratchDir();
5596
+ workdir = createScratchDir(runtime, specLabel);
5513
5597
  }
5514
5598
  if (specAgentType !== "shell" && specAgentType !== "pi") {
5515
5599
  const [preflight] = await ptyService.checkAvailableAgents([
@@ -6177,7 +6261,7 @@ var activeWorkspaceContextProvider = {
6177
6261
  try {
6178
6262
  sessions = await Promise.race([
6179
6263
  ptyService.listSessions(),
6180
- new Promise((resolve3) => setTimeout(() => resolve3([]), 2000))
6264
+ new Promise((resolve4) => setTimeout(() => resolve4([]), 2000))
6181
6265
  ]);
6182
6266
  } catch {
6183
6267
  sessions = [];
@@ -6261,8 +6345,8 @@ var activeWorkspaceContextProvider = {
6261
6345
  };
6262
6346
 
6263
6347
  // src/services/workspace-service.ts
6264
- import * as os4 from "node:os";
6265
- import * as path6 from "node:path";
6348
+ import * as os6 from "node:os";
6349
+ import * as path7 from "node:path";
6266
6350
  import * as fs4 from "node:fs/promises";
6267
6351
  import {
6268
6352
  CredentialService,
@@ -6477,12 +6561,18 @@ async function createPR(workspaceService, workspace, workspaceId, options, log)
6477
6561
 
6478
6562
  // src/services/workspace-lifecycle.ts
6479
6563
  import * as fs3 from "node:fs";
6480
- import * as path5 from "node:path";
6481
- async function removeScratchDir(dirPath, baseDir, log) {
6482
- const resolved = path5.resolve(dirPath);
6483
- const resolvedBase = path5.resolve(baseDir) + path5.sep;
6484
- if (!resolved.startsWith(resolvedBase) && resolved !== path5.resolve(baseDir)) {
6485
- console.warn(`[CodingWorkspaceService] Refusing to remove dir outside base: ${resolved}`);
6564
+ import * as os5 from "node:os";
6565
+ import * as path6 from "node:path";
6566
+ async function removeScratchDir(dirPath, baseDir, log, allowedDirs) {
6567
+ const resolved = path6.resolve(dirPath);
6568
+ const expandTilde = (p) => p.startsWith("~") ? path6.join(os5.homedir(), p.slice(1)) : p;
6569
+ const allAllowed = [baseDir, ...allowedDirs ?? []];
6570
+ const isAllowed = allAllowed.some((dir) => {
6571
+ const resolvedDir = path6.resolve(expandTilde(dir)) + path6.sep;
6572
+ return resolved.startsWith(resolvedDir) || resolved === path6.resolve(expandTilde(dir));
6573
+ });
6574
+ if (!isAllowed) {
6575
+ console.warn(`[CodingWorkspaceService] Refusing to remove dir outside allowed paths: ${resolved}`);
6486
6576
  return;
6487
6577
  }
6488
6578
  try {
@@ -6513,7 +6603,7 @@ async function gcOrphanedWorkspaces(baseDir, workspaceTtlMs, trackedWorkspaceIds
6513
6603
  skipped++;
6514
6604
  continue;
6515
6605
  }
6516
- const dirPath = path5.join(baseDir, entry.name);
6606
+ const dirPath = path6.join(baseDir, entry.name);
6517
6607
  try {
6518
6608
  const stat2 = await fs3.promises.stat(dirPath);
6519
6609
  const age = now - stat2.mtimeMs;
@@ -6549,10 +6639,11 @@ class CodingWorkspaceService {
6549
6639
  scratchCleanupTimers = new Map;
6550
6640
  eventCallbacks = [];
6551
6641
  authPromptCallback = null;
6642
+ scratchDecisionCallback = null;
6552
6643
  constructor(runtime, config = {}) {
6553
6644
  this.runtime = runtime;
6554
6645
  this.serviceConfig = {
6555
- baseDir: config.baseDir ?? path6.join(os4.homedir(), ".milady", "workspaces"),
6646
+ baseDir: config.baseDir ?? path7.join(os6.homedir(), ".milady", "workspaces"),
6556
6647
  branchPrefix: config.branchPrefix ?? "milady",
6557
6648
  debug: config.debug ?? false,
6558
6649
  workspaceTtlMs: config.workspaceTtlMs ?? 24 * 60 * 60 * 1000
@@ -6747,6 +6838,9 @@ class CodingWorkspaceService {
6747
6838
  setAuthPromptCallback(callback) {
6748
6839
  this.authPromptCallback = callback;
6749
6840
  }
6841
+ setScratchDecisionCallback(callback) {
6842
+ this.scratchDecisionCallback = callback;
6843
+ }
6750
6844
  async createIssue(repo, options) {
6751
6845
  return createIssue(this.getGitHubContext(), repo, options);
6752
6846
  }
@@ -6805,7 +6899,10 @@ class CodingWorkspaceService {
6805
6899
  }
6806
6900
  }
6807
6901
  async removeScratchDir(dirPath) {
6808
- return removeScratchDir(dirPath, this.serviceConfig.baseDir, (msg) => this.log(msg));
6902
+ const rawCodingDir = this.runtime.getSetting("PARALLAX_CODING_DIRECTORY") ?? this.readConfigEnvKey("PARALLAX_CODING_DIRECTORY") ?? process.env.PARALLAX_CODING_DIRECTORY;
6903
+ const codingDir = rawCodingDir?.trim() ? rawCodingDir.trim().startsWith("~") ? path7.join(os6.homedir(), rawCodingDir.trim().slice(1)) : path7.resolve(rawCodingDir.trim()) : undefined;
6904
+ const allowedDirs = codingDir ? [codingDir] : undefined;
6905
+ return removeScratchDir(dirPath, this.serviceConfig.baseDir, (msg) => this.log(msg), allowedDirs);
6809
6906
  }
6810
6907
  listScratchWorkspaces() {
6811
6908
  return Array.from(this.scratchBySession.values()).sort((a, b) => b.terminalAt - a.terminalAt);
@@ -6823,6 +6920,7 @@ class CodingWorkspaceService {
6823
6920
  status: "pending_decision"
6824
6921
  };
6825
6922
  const policy = this.getScratchRetentionPolicy();
6923
+ this.log(`Scratch retention policy: "${policy}" for "${label}"`);
6826
6924
  if (policy === "ephemeral") {
6827
6925
  await this.removeScratchDir(dirPath);
6828
6926
  this.scratchBySession.delete(sessionId);
@@ -6843,6 +6941,14 @@ class CodingWorkspaceService {
6843
6941
  const ttlMs = this.getScratchDecisionTtlMs();
6844
6942
  record.expiresAt = now + ttlMs;
6845
6943
  this.scheduleScratchCleanup(sessionId, ttlMs);
6944
+ if (this.scratchDecisionCallback) {
6945
+ this.log(`Firing scratch decision prompt for "${label}" at ${dirPath}`);
6946
+ this.scratchDecisionCallback(record).catch((err) => {
6947
+ console.warn(`[workspace] Failed to send scratch decision prompt: ${err}`);
6948
+ });
6949
+ } else {
6950
+ this.log(`No scratch decision callback wired — skipping prompt for "${label}"`);
6951
+ }
6846
6952
  } else {
6847
6953
  this.clearScratchCleanupTimer(sessionId);
6848
6954
  }
@@ -6898,14 +7004,22 @@ class CodingWorkspaceService {
6898
7004
  console.log(`[CodingWorkspaceService] ${message}`);
6899
7005
  }
6900
7006
  }
7007
+ readConfigEnvKey(key) {
7008
+ return readConfigEnvKey(key);
7009
+ }
6901
7010
  getScratchRetentionPolicy() {
6902
- const setting = this.runtime.getSetting("PARALLAX_SCRATCH_RETENTION") ?? process.env.PARALLAX_SCRATCH_RETENTION;
7011
+ const setting = this.runtime.getSetting("PARALLAX_SCRATCH_RETENTION") ?? this.readConfigEnvKey("PARALLAX_SCRATCH_RETENTION") ?? process.env.PARALLAX_SCRATCH_RETENTION;
6903
7012
  const normalized = setting?.trim().toLowerCase();
6904
7013
  if (normalized === "ephemeral")
6905
7014
  return "ephemeral";
6906
7015
  if (normalized === "persistent" || normalized === "keep") {
6907
7016
  return "persistent";
6908
7017
  }
7018
+ if (!normalized) {
7019
+ const codingDir = this.runtime.getSetting("PARALLAX_CODING_DIRECTORY") ?? this.readConfigEnvKey("PARALLAX_CODING_DIRECTORY") ?? process.env.PARALLAX_CODING_DIRECTORY;
7020
+ if (codingDir?.trim())
7021
+ return "persistent";
7022
+ }
6909
7023
  return "pending_decision";
6910
7024
  }
6911
7025
  getScratchDecisionTtlMs() {
@@ -6951,11 +7065,11 @@ class CodingWorkspaceService {
6951
7065
  return compact || `scratch-${Date.now().toString(36)}`;
6952
7066
  }
6953
7067
  async allocatePromotedPath(baseDir, baseName) {
6954
- const baseResolved = path6.resolve(baseDir);
7068
+ const baseResolved = path7.resolve(baseDir);
6955
7069
  for (let i = 0;i < 1000; i++) {
6956
7070
  const candidateName = i === 0 ? baseName : `${baseName}-${i}`;
6957
- const candidate = path6.resolve(baseResolved, candidateName);
6958
- if (candidate !== baseResolved && !candidate.startsWith(`${baseResolved}${path6.sep}`)) {
7071
+ const candidate = path7.resolve(baseResolved, candidateName);
7072
+ if (candidate !== baseResolved && !candidate.startsWith(`${baseResolved}${path7.sep}`)) {
6959
7073
  continue;
6960
7074
  }
6961
7075
  try {
@@ -6970,8 +7084,8 @@ class CodingWorkspaceService {
6970
7084
  // src/api/agent-routes.ts
6971
7085
  import { access as access2, readFile as readFile4, realpath, rm as rm2 } from "node:fs/promises";
6972
7086
  import { createHash } from "node:crypto";
6973
- import * as os5 from "node:os";
6974
- import * as path7 from "node:path";
7087
+ import * as os7 from "node:os";
7088
+ import * as path8 from "node:path";
6975
7089
  import { execFile } from "node:child_process";
6976
7090
  import { promisify } from "node:util";
6977
7091
  var execFileAsync = promisify(execFile);
@@ -6983,23 +7097,23 @@ function shouldAutoPreflight() {
6983
7097
  return false;
6984
7098
  }
6985
7099
  function isPathInside(parent, candidate) {
6986
- return candidate === parent || candidate.startsWith(`${parent}${path7.sep}`);
7100
+ return candidate === parent || candidate.startsWith(`${parent}${path8.sep}`);
6987
7101
  }
6988
7102
  async function resolveSafeVenvPath(workdir, venvDirRaw) {
6989
7103
  const venvDir = venvDirRaw.trim();
6990
7104
  if (!venvDir) {
6991
7105
  throw new Error("PARALLAX_BENCHMARK_PREFLIGHT_VENV must be non-empty");
6992
7106
  }
6993
- if (path7.isAbsolute(venvDir)) {
7107
+ if (path8.isAbsolute(venvDir)) {
6994
7108
  throw new Error("PARALLAX_BENCHMARK_PREFLIGHT_VENV must be relative to workdir");
6995
7109
  }
6996
- const normalized = path7.normalize(venvDir);
6997
- if (normalized === "." || normalized === ".." || normalized.startsWith(`..${path7.sep}`)) {
7110
+ const normalized = path8.normalize(venvDir);
7111
+ if (normalized === "." || normalized === ".." || normalized.startsWith(`..${path8.sep}`)) {
6998
7112
  throw new Error("PARALLAX_BENCHMARK_PREFLIGHT_VENV must stay within workdir");
6999
7113
  }
7000
- const workdirResolved = path7.resolve(workdir);
7114
+ const workdirResolved = path8.resolve(workdir);
7001
7115
  const workdirReal = await realpath(workdirResolved);
7002
- const resolved = path7.resolve(workdirReal, normalized);
7116
+ const resolved = path8.resolve(workdirReal, normalized);
7003
7117
  if (!isPathInside(workdirReal, resolved)) {
7004
7118
  throw new Error("PARALLAX_BENCHMARK_PREFLIGHT_VENV resolves outside workdir");
7005
7119
  }
@@ -7015,7 +7129,7 @@ async function resolveSafeVenvPath(workdir, venvDirRaw) {
7015
7129
  const maybeErr = err;
7016
7130
  if (maybeErr?.code !== "ENOENT")
7017
7131
  throw err;
7018
- const parentReal = await realpath(path7.dirname(resolved));
7132
+ const parentReal = await realpath(path8.dirname(resolved));
7019
7133
  if (!isPathInside(workdirReal, parentReal)) {
7020
7134
  throw new Error("PARALLAX_BENCHMARK_PREFLIGHT_VENV parent resolves outside workdir");
7021
7135
  }
@@ -7031,10 +7145,10 @@ async function fileExists(filePath) {
7031
7145
  }
7032
7146
  }
7033
7147
  async function resolveRequirementsPath(workdir) {
7034
- const workdirReal = await realpath(path7.resolve(workdir));
7148
+ const workdirReal = await realpath(path8.resolve(workdir));
7035
7149
  const candidates = [
7036
- path7.join(workdir, "apps", "api", "requirements.txt"),
7037
- path7.join(workdir, "requirements.txt")
7150
+ path8.join(workdir, "apps", "api", "requirements.txt"),
7151
+ path8.join(workdir, "requirements.txt")
7038
7152
  ];
7039
7153
  for (const candidate of candidates) {
7040
7154
  if (!await fileExists(candidate))
@@ -7061,7 +7175,7 @@ async function runBenchmarkPreflight(workdir) {
7061
7175
  const mode = process.env.PARALLAX_BENCHMARK_PREFLIGHT_MODE?.toLowerCase() === "warm" ? "warm" : "cold";
7062
7176
  const venvDir = process.env.PARALLAX_BENCHMARK_PREFLIGHT_VENV || ".benchmark-venv";
7063
7177
  const venvPath = await resolveSafeVenvPath(workdir, venvDir);
7064
- const pythonInVenv = path7.join(venvPath, process.platform === "win32" ? "Scripts" : "bin", process.platform === "win32" ? "python.exe" : "python");
7178
+ const pythonInVenv = path8.join(venvPath, process.platform === "win32" ? "Scripts" : "bin", process.platform === "win32" ? "python.exe" : "python");
7065
7179
  const key = `${workdir}::${mode}::${venvPath}::${requirementsFingerprint}`;
7066
7180
  if (PREFLIGHT_DONE.has(key)) {
7067
7181
  if (await fileExists(pythonInVenv))
@@ -7269,21 +7383,21 @@ async function handleAgentRoutes(req, res, pathname, ctx) {
7269
7383
  customCredentials,
7270
7384
  metadata
7271
7385
  } = body;
7272
- const workspaceBaseDir = path7.join(os5.homedir(), ".milady", "workspaces");
7273
- const workspaceBaseDirResolved = path7.resolve(workspaceBaseDir);
7274
- const cwdResolved = path7.resolve(process.cwd());
7386
+ const workspaceBaseDir = path8.join(os7.homedir(), ".milady", "workspaces");
7387
+ const workspaceBaseDirResolved = path8.resolve(workspaceBaseDir);
7388
+ const cwdResolved = path8.resolve(process.cwd());
7275
7389
  const workspaceBaseDirReal = await realpath(workspaceBaseDirResolved).catch(() => workspaceBaseDirResolved);
7276
7390
  const cwdReal = await realpath(cwdResolved).catch(() => cwdResolved);
7277
7391
  const allowedPrefixes = [workspaceBaseDirReal, cwdReal];
7278
7392
  let workdir = rawWorkdir;
7279
7393
  if (workdir) {
7280
- const resolved = path7.resolve(workdir);
7394
+ const resolved = path8.resolve(workdir);
7281
7395
  const resolvedReal = await realpath(resolved).catch(() => null);
7282
7396
  if (!resolvedReal) {
7283
7397
  sendError(res, "workdir must exist", 403);
7284
7398
  return true;
7285
7399
  }
7286
- const isAllowed = allowedPrefixes.some((prefix2) => resolvedReal === prefix2 || resolvedReal.startsWith(prefix2 + path7.sep));
7400
+ const isAllowed = allowedPrefixes.some((prefix2) => resolvedReal === prefix2 || resolvedReal.startsWith(prefix2 + path8.sep));
7287
7401
  if (!isAllowed) {
7288
7402
  sendError(res, "workdir must be within workspace base directory or cwd", 403);
7289
7403
  return true;
@@ -7927,7 +8041,7 @@ async function handleWorkspaceRoutes(req, res, pathname, ctx) {
7927
8041
  // src/api/routes.ts
7928
8042
  var MAX_BODY_SIZE = 1024 * 1024;
7929
8043
  async function parseBody(req) {
7930
- return new Promise((resolve6, reject) => {
8044
+ return new Promise((resolve7, reject) => {
7931
8045
  let body = "";
7932
8046
  let size = 0;
7933
8047
  req.on("data", (chunk) => {
@@ -7941,7 +8055,7 @@ async function parseBody(req) {
7941
8055
  });
7942
8056
  req.on("end", () => {
7943
8057
  try {
7944
- resolve6(body ? JSON.parse(body) : {});
8058
+ resolve7(body ? JSON.parse(body) : {});
7945
8059
  } catch {
7946
8060
  reject(new Error("Invalid JSON body"));
7947
8061
  }
@@ -8029,5 +8143,5 @@ export {
8029
8143
  CodingWorkspaceService
8030
8144
  };
8031
8145
 
8032
- //# debugId=C832944FABDC8E0964756E2164756E21
8146
+ //# debugId=CC31B3CD95C1813A64756E2164756E21
8033
8147
  //# sourceMappingURL=index.js.map