@elizaos/plugin-agent-orchestrator 0.3.12 → 0.3.13

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.
@@ -1 +1 @@
1
- {"version":3,"file":"agent-routes.d.ts","sourceRoot":"","sources":["../../src/api/agent-routes.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AASjE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAWhD;;;GAGG;AACH,wBAAsB,iBAAiB,CACrC,GAAG,EAAE,eAAe,EACpB,GAAG,EAAE,cAAc,EACnB,QAAQ,EAAE,MAAM,EAChB,GAAG,EAAE,YAAY,GAChB,OAAO,CAAC,OAAO,CAAC,CAwdlB"}
1
+ {"version":3,"file":"agent-routes.d.ts","sourceRoot":"","sources":["../../src/api/agent-routes.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAajE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAiMhD;;;GAGG;AACH,wBAAsB,iBAAiB,CACrC,GAAG,EAAE,eAAe,EACpB,GAAG,EAAE,cAAc,EACnB,QAAQ,EAAE,MAAM,EAChB,GAAG,EAAE,YAAY,GAChB,OAAO,CAAC,OAAO,CAAC,CAuelB"}
package/dist/index.js CHANGED
@@ -537,6 +537,7 @@ __export(exports_swarm_decision_loop, {
537
537
  handleBlocked: () => handleBlocked,
538
538
  handleAutonomousDecision: () => handleAutonomousDecision,
539
539
  executeDecision: () => executeDecision,
540
+ clearDeferredTurnCompleteTimers: () => clearDeferredTurnCompleteTimers,
540
541
  checkAllTasksComplete: () => checkAllTasksComplete,
541
542
  POST_SEND_COOLDOWN_MS: () => POST_SEND_COOLDOWN_MS
542
543
  });
@@ -554,6 +555,12 @@ function withTimeout(promise, ms, label) {
554
555
  });
555
556
  });
556
557
  }
558
+ function clearDeferredTurnCompleteTimers() {
559
+ for (const timer of deferredTurnCompleteTimers.values()) {
560
+ clearTimeout(timer);
561
+ }
562
+ deferredTurnCompleteTimers.clear();
563
+ }
557
564
  function toContextSummary(taskCtx) {
558
565
  return {
559
566
  sessionId: taskCtx.sessionId,
@@ -733,14 +740,23 @@ function checkAllTasksComplete(ctx) {
733
740
  };
734
741
  if (swarmCompleteCb) {
735
742
  ctx.log("checkAllTasksComplete: swarm complete callback is wired — calling synthesis");
736
- const taskSummaries = tasks.map((t) => ({
737
- sessionId: t.sessionId,
738
- label: t.label,
739
- agentType: t.agentType,
740
- originalTask: t.originalTask,
741
- status: t.status,
742
- completionSummary: t.completionSummary ?? ""
743
- }));
743
+ const taskSummaries = tasks.map((t) => {
744
+ const decisions = ctx.sharedDecisions.filter((sd) => sd.agentLabel === t.label).map((sd) => sd.summary);
745
+ const summaryParts = [];
746
+ if (decisions.length > 0)
747
+ summaryParts.push(decisions.join("; "));
748
+ if (t.completionSummary)
749
+ summaryParts.push(t.completionSummary);
750
+ return {
751
+ sessionId: t.sessionId,
752
+ label: t.label,
753
+ agentType: t.agentType,
754
+ originalTask: t.originalTask,
755
+ status: t.status,
756
+ completionSummary: summaryParts.join(`
757
+ `) || ""
758
+ };
759
+ });
744
760
  withTimeout(Promise.resolve().then(() => swarmCompleteCb({
745
761
  tasks: taskSummaries,
746
762
  total: tasks.length,
@@ -958,6 +974,8 @@ async function handleBlocked(ctx, sessionId, taskCtx, data) {
958
974
  }
959
975
  }
960
976
  async function handleTurnComplete(ctx, sessionId, taskCtx, data) {
977
+ if (taskCtx.status !== "active")
978
+ return;
961
979
  if (ctx.inFlightDecisions.has(sessionId)) {
962
980
  ctx.log(`Buffering turn-complete for ${sessionId} (in-flight decision running)`);
963
981
  ctx.pendingTurnComplete.set(sessionId, data);
@@ -966,10 +984,35 @@ async function handleTurnComplete(ctx, sessionId, taskCtx, data) {
966
984
  if (taskCtx.lastInputSentAt) {
967
985
  const elapsed = Date.now() - taskCtx.lastInputSentAt;
968
986
  if (elapsed < POST_SEND_COOLDOWN_MS) {
987
+ ctx.pendingTurnComplete.set(sessionId, data);
988
+ if (!deferredTurnCompleteTimers.has(sessionId)) {
989
+ const delayMs = POST_SEND_COOLDOWN_MS - elapsed + 50;
990
+ const timer = setTimeout(() => {
991
+ deferredTurnCompleteTimers.delete(sessionId);
992
+ const pendingData = ctx.pendingTurnComplete.get(sessionId);
993
+ if (!pendingData)
994
+ return;
995
+ const currentTask = ctx.tasks.get(sessionId);
996
+ if (!currentTask || currentTask.status !== "active") {
997
+ ctx.pendingTurnComplete.delete(sessionId);
998
+ return;
999
+ }
1000
+ handleTurnComplete(ctx, sessionId, currentTask, pendingData).catch((err) => {
1001
+ ctx.log(`Deferred turn-complete replay failed for ${sessionId}: ${err}`);
1002
+ });
1003
+ }, delayMs);
1004
+ deferredTurnCompleteTimers.set(sessionId, timer);
1005
+ }
969
1006
  ctx.log(`Suppressing turn-complete for "${taskCtx.label}" — ` + `${Math.round(elapsed / 1000)}s since last input (cooldown ${POST_SEND_COOLDOWN_MS / 1000}s)`);
970
1007
  return;
971
1008
  }
972
1009
  }
1010
+ const deferredTimer = deferredTurnCompleteTimers.get(sessionId);
1011
+ if (deferredTimer) {
1012
+ clearTimeout(deferredTimer);
1013
+ deferredTurnCompleteTimers.delete(sessionId);
1014
+ }
1015
+ ctx.pendingTurnComplete.delete(sessionId);
973
1016
  ctx.inFlightDecisions.add(sessionId);
974
1017
  try {
975
1018
  ctx.log(`Turn complete for "${taskCtx.label}" — assessing whether task is done`);
@@ -1219,10 +1262,11 @@ async function handleConfirmDecision(ctx, sessionId, taskCtx, promptText, recent
1219
1262
  await drainPendingBlocked(ctx, sessionId);
1220
1263
  }
1221
1264
  }
1222
- var DECISION_CB_TIMEOUT_MS = 30000, MAX_AUTO_RESPONSES = 10, POST_SEND_COOLDOWN_MS = 15000;
1265
+ var DECISION_CB_TIMEOUT_MS = 30000, MAX_AUTO_RESPONSES = 10, POST_SEND_COOLDOWN_MS = 15000, deferredTurnCompleteTimers;
1223
1266
  var init_swarm_decision_loop = __esm(() => {
1224
1267
  init_ansi_utils();
1225
1268
  init_swarm_event_triage();
1269
+ deferredTurnCompleteTimers = new Map;
1226
1270
  });
1227
1271
 
1228
1272
  // src/actions/finalize-workspace.ts
@@ -3177,6 +3221,7 @@ async function scanIdleSessions(ctx) {
3177
3221
  if (!session) {
3178
3222
  ctx.log(`Idle watchdog: "${taskCtx.label}" — PTY session no longer exists, marking as stopped`);
3179
3223
  taskCtx.status = "stopped";
3224
+ taskCtx.stoppedAt = now;
3180
3225
  taskCtx.decisions.push({
3181
3226
  timestamp: now,
3182
3227
  event: "idle_watchdog",
@@ -3220,6 +3265,7 @@ async function scanIdleSessions(ctx) {
3220
3265
  if (taskCtx.idleCheckCount >= MAX_IDLE_CHECKS) {
3221
3266
  ctx.log(`Idle watchdog: force-stopping "${taskCtx.label}" after ${MAX_IDLE_CHECKS} checks`);
3222
3267
  taskCtx.status = "stopped";
3268
+ taskCtx.stoppedAt = now;
3223
3269
  taskCtx.decisions.push({
3224
3270
  timestamp: now,
3225
3271
  event: "idle_watchdog",
@@ -3354,6 +3400,7 @@ async function handleIdleCheck(ctx, taskCtx, idleMinutes) {
3354
3400
  var UNREGISTERED_BUFFER_MS = 2000;
3355
3401
  var IDLE_SCAN_INTERVAL_MS = 60 * 1000;
3356
3402
  var PAUSE_TIMEOUT_MS = 30000;
3403
+ var STOPPED_RECOVERY_WINDOW_MS = 90000;
3357
3404
 
3358
3405
  class SwarmCoordinator {
3359
3406
  static serviceType = "SWARM_COORDINATOR";
@@ -3454,6 +3501,7 @@ class SwarmCoordinator {
3454
3501
  this.pendingDecisions.clear();
3455
3502
  this.inFlightDecisions.clear();
3456
3503
  this.pendingTurnComplete.clear();
3504
+ clearDeferredTurnCompleteTimers();
3457
3505
  this.lastBlockedPromptFingerprint.clear();
3458
3506
  this.pendingBlocked.clear();
3459
3507
  this.unregisteredBuffer.clear();
@@ -3635,8 +3683,21 @@ class SwarmCoordinator {
3635
3683
  }
3636
3684
  return;
3637
3685
  }
3686
+ let recoveredFromStopped = false;
3638
3687
  if (taskCtx.status === "stopped" || taskCtx.status === "error" || taskCtx.status === "completed") {
3639
- if (event !== "stopped" && event !== "error") {
3688
+ if (taskCtx.status === "stopped" && event === "task_complete") {
3689
+ const stoppedAt = taskCtx.stoppedAt ?? 0;
3690
+ const ageMs = Date.now() - stoppedAt;
3691
+ if (stoppedAt > 0 && ageMs <= STOPPED_RECOVERY_WINDOW_MS) {
3692
+ this.log(`Recovering "${taskCtx.label}" from stopped on late task_complete (${Math.round(ageMs / 1000)}s old)`);
3693
+ taskCtx.status = "active";
3694
+ recoveredFromStopped = true;
3695
+ } else {
3696
+ this.log(`Ignoring "${event}" for ${taskCtx.label} (status: stopped, age=${Math.round(ageMs / 1000)}s)`);
3697
+ return;
3698
+ }
3699
+ }
3700
+ if (!recoveredFromStopped && event !== "stopped" && event !== "error") {
3640
3701
  this.log(`Ignoring "${event}" for ${taskCtx.label} (status: ${taskCtx.status})`);
3641
3702
  return;
3642
3703
  }
@@ -3687,6 +3748,7 @@ class SwarmCoordinator {
3687
3748
  case "stopped":
3688
3749
  if (taskCtx.status !== "completed" && taskCtx.status !== "error") {
3689
3750
  taskCtx.status = "stopped";
3751
+ taskCtx.stoppedAt = Date.now();
3690
3752
  }
3691
3753
  this.inFlightDecisions.delete(sessionId);
3692
3754
  this.broadcast({
@@ -4774,7 +4836,9 @@ import {
4774
4836
  ModelType as ModelType5
4775
4837
  } from "@elizaos/core";
4776
4838
  // src/services/trajectory-feedback.ts
4839
+ import { logger as elizaLogger } from "@elizaos/core";
4777
4840
  var QUERY_TIMEOUT_MS = 5000;
4841
+ var SLOW_PATH_BUDGET_MS = 15000;
4778
4842
  function withTimeout2(promise, ms) {
4779
4843
  return Promise.race([
4780
4844
  promise,
@@ -4860,18 +4924,37 @@ async function queryPastExperience(runtime, options = {}) {
4860
4924
  if (!result.trajectories || result.trajectories.length === 0)
4861
4925
  return [];
4862
4926
  const experiences = [];
4927
+ const slowPathDeadline = Date.now() + SLOW_PATH_BUDGET_MS;
4863
4928
  const maxScans = Math.min(result.trajectories.length, maxTrajectories);
4864
4929
  for (let scanIdx = 0;scanIdx < maxScans; scanIdx++) {
4865
4930
  const summary = result.trajectories[scanIdx];
4866
- const detail = await withTimeout2(logger5.getTrajectoryDetail(summary.id), QUERY_TIMEOUT_MS).catch(() => null);
4867
- if (!detail?.steps)
4868
- continue;
4869
- const metadata = detail.metadata;
4931
+ const metadata = summary.metadata;
4932
+ const metadataInsights = Array.isArray(metadata?.insights) ? metadata.insights.filter((value) => typeof value === "string" && value.trim().length > 0).slice(0, 50) : [];
4870
4933
  const decisionType = metadata?.orchestrator?.decisionType ?? "unknown";
4871
4934
  const taskLabel = metadata?.orchestrator?.taskLabel ?? "";
4872
4935
  const trajectoryRepo = metadata?.orchestrator?.repo;
4873
4936
  if (repo && (!trajectoryRepo || trajectoryRepo !== repo))
4874
4937
  continue;
4938
+ if (metadataInsights.length > 0) {
4939
+ elizaLogger.debug(`[trajectory-feedback] Fast path: ${metadataInsights.length} insight(s) from metadata for ${summary.id}`);
4940
+ for (const insight of metadataInsights) {
4941
+ experiences.push({
4942
+ timestamp: summary.startTime,
4943
+ decisionType,
4944
+ taskLabel,
4945
+ insight
4946
+ });
4947
+ }
4948
+ continue;
4949
+ }
4950
+ if (Date.now() > slowPathDeadline) {
4951
+ elizaLogger.debug(`[trajectory-feedback] Slow path budget exhausted; stopping detail loads`);
4952
+ break;
4953
+ }
4954
+ elizaLogger.debug(`[trajectory-feedback] Slow path: loading full detail for ${summary.id} (no metadata insights)`);
4955
+ const detail = await withTimeout2(logger5.getTrajectoryDetail(summary.id), QUERY_TIMEOUT_MS).catch(() => null);
4956
+ if (!detail?.steps)
4957
+ continue;
4875
4958
  for (const step of detail.steps) {
4876
4959
  if (!step.llmCalls)
4877
4960
  continue;
@@ -4904,7 +4987,7 @@ async function queryPastExperience(runtime, options = {}) {
4904
4987
  }
4905
4988
  return Array.from(seen.values()).sort((a, b) => b.timestamp - a.timestamp).slice(0, maxEntries);
4906
4989
  } catch (err) {
4907
- console.error("[trajectory-feedback] Failed to query past experience:", err);
4990
+ elizaLogger.error(`[trajectory-feedback] Failed to query past experience: ${err}`);
4908
4991
  return [];
4909
4992
  }
4910
4993
  }
@@ -6593,8 +6676,143 @@ class CodingWorkspaceService {
6593
6676
  }
6594
6677
  }
6595
6678
  // src/api/agent-routes.ts
6679
+ import { access, readFile as readFile3, realpath, rm } from "node:fs/promises";
6680
+ import { createHash } from "node:crypto";
6596
6681
  import * as os4 from "node:os";
6597
6682
  import * as path6 from "node:path";
6683
+ import { execFile } from "node:child_process";
6684
+ import { promisify } from "node:util";
6685
+ var execFileAsync = promisify(execFile);
6686
+ var PREFLIGHT_DONE = new Set;
6687
+ var PREFLIGHT_INFLIGHT = new Map;
6688
+ function shouldAutoPreflight() {
6689
+ if (process.env.PARALLAX_BENCHMARK_PREFLIGHT_AUTO === "1")
6690
+ return true;
6691
+ return false;
6692
+ }
6693
+ function isPathInside(parent, candidate) {
6694
+ return candidate === parent || candidate.startsWith(`${parent}${path6.sep}`);
6695
+ }
6696
+ async function resolveSafeVenvPath(workdir, venvDirRaw) {
6697
+ const venvDir = venvDirRaw.trim();
6698
+ if (!venvDir) {
6699
+ throw new Error("PARALLAX_BENCHMARK_PREFLIGHT_VENV must be non-empty");
6700
+ }
6701
+ if (path6.isAbsolute(venvDir)) {
6702
+ throw new Error("PARALLAX_BENCHMARK_PREFLIGHT_VENV must be relative to workdir");
6703
+ }
6704
+ const normalized = path6.normalize(venvDir);
6705
+ if (normalized === "." || normalized === ".." || normalized.startsWith(`..${path6.sep}`)) {
6706
+ throw new Error("PARALLAX_BENCHMARK_PREFLIGHT_VENV must stay within workdir");
6707
+ }
6708
+ const workdirResolved = path6.resolve(workdir);
6709
+ const workdirReal = await realpath(workdirResolved);
6710
+ const resolved = path6.resolve(workdirReal, normalized);
6711
+ if (!isPathInside(workdirReal, resolved)) {
6712
+ throw new Error("PARALLAX_BENCHMARK_PREFLIGHT_VENV resolves outside workdir");
6713
+ }
6714
+ if (resolved === workdirReal) {
6715
+ throw new Error("PARALLAX_BENCHMARK_PREFLIGHT_VENV must not resolve to workdir root");
6716
+ }
6717
+ try {
6718
+ const resolvedReal = await realpath(resolved);
6719
+ if (!isPathInside(workdirReal, resolvedReal) || resolvedReal === workdirReal) {
6720
+ throw new Error("PARALLAX_BENCHMARK_PREFLIGHT_VENV resolves outside workdir");
6721
+ }
6722
+ } catch (err) {
6723
+ const maybeErr = err;
6724
+ if (maybeErr?.code !== "ENOENT")
6725
+ throw err;
6726
+ const parentReal = await realpath(path6.dirname(resolved));
6727
+ if (!isPathInside(workdirReal, parentReal)) {
6728
+ throw new Error("PARALLAX_BENCHMARK_PREFLIGHT_VENV parent resolves outside workdir");
6729
+ }
6730
+ }
6731
+ return resolved;
6732
+ }
6733
+ async function fileExists(filePath) {
6734
+ try {
6735
+ await access(filePath);
6736
+ return true;
6737
+ } catch {
6738
+ return false;
6739
+ }
6740
+ }
6741
+ async function resolveRequirementsPath(workdir) {
6742
+ const workdirReal = await realpath(path6.resolve(workdir));
6743
+ const candidates = [
6744
+ path6.join(workdir, "apps", "api", "requirements.txt"),
6745
+ path6.join(workdir, "requirements.txt")
6746
+ ];
6747
+ for (const candidate of candidates) {
6748
+ if (!await fileExists(candidate))
6749
+ continue;
6750
+ try {
6751
+ const candidateReal = await realpath(candidate);
6752
+ if (isPathInside(workdirReal, candidateReal))
6753
+ return candidateReal;
6754
+ } catch {}
6755
+ }
6756
+ return null;
6757
+ }
6758
+ async function fingerprintRequirementsFile(requirementsPath) {
6759
+ const file = await readFile3(requirementsPath);
6760
+ return createHash("sha256").update(file).digest("hex");
6761
+ }
6762
+ async function runBenchmarkPreflight(workdir) {
6763
+ if (!shouldAutoPreflight())
6764
+ return;
6765
+ const requirementsPath = await resolveRequirementsPath(workdir);
6766
+ if (!requirementsPath)
6767
+ return;
6768
+ const requirementsFingerprint = await fingerprintRequirementsFile(requirementsPath);
6769
+ const mode = process.env.PARALLAX_BENCHMARK_PREFLIGHT_MODE?.toLowerCase() === "warm" ? "warm" : "cold";
6770
+ const venvDir = process.env.PARALLAX_BENCHMARK_PREFLIGHT_VENV || ".benchmark-venv";
6771
+ const venvPath = await resolveSafeVenvPath(workdir, venvDir);
6772
+ const pythonInVenv = path6.join(venvPath, process.platform === "win32" ? "Scripts" : "bin", process.platform === "win32" ? "python.exe" : "python");
6773
+ const key = `${workdir}::${mode}::${venvPath}::${requirementsFingerprint}`;
6774
+ if (PREFLIGHT_DONE.has(key)) {
6775
+ if (await fileExists(pythonInVenv))
6776
+ return;
6777
+ PREFLIGHT_DONE.delete(key);
6778
+ }
6779
+ const existing = PREFLIGHT_INFLIGHT.get(key);
6780
+ if (existing) {
6781
+ await existing;
6782
+ return;
6783
+ }
6784
+ const run = (async () => {
6785
+ const pythonCommand = process.platform === "win32" ? "python" : "python3";
6786
+ if (mode === "cold") {
6787
+ await rm(venvPath, { recursive: true, force: true });
6788
+ }
6789
+ const hasVenv = await fileExists(pythonInVenv);
6790
+ if (!hasVenv) {
6791
+ await execFileAsync(pythonCommand, ["-m", "venv", venvPath], {
6792
+ cwd: workdir,
6793
+ timeout: 120000,
6794
+ maxBuffer: 8 * 1024 * 1024
6795
+ });
6796
+ }
6797
+ await execFileAsync(pythonInVenv, ["-m", "pip", "install", "--upgrade", "pip"], {
6798
+ cwd: workdir,
6799
+ timeout: 300000,
6800
+ maxBuffer: 8 * 1024 * 1024
6801
+ });
6802
+ await execFileAsync(pythonInVenv, ["-m", "pip", "install", "-r", requirementsPath], {
6803
+ cwd: workdir,
6804
+ timeout: 600000,
6805
+ maxBuffer: 16 * 1024 * 1024
6806
+ });
6807
+ PREFLIGHT_DONE.add(key);
6808
+ })();
6809
+ PREFLIGHT_INFLIGHT.set(key, run);
6810
+ try {
6811
+ await run;
6812
+ } finally {
6813
+ PREFLIGHT_INFLIGHT.delete(key);
6814
+ }
6815
+ }
6598
6816
  async function handleAgentRoutes(req, res, pathname, ctx) {
6599
6817
  const method = req.method?.toUpperCase();
6600
6818
  if (method === "GET" && pathname === "/api/coding-agents/preflight") {
@@ -6722,19 +6940,25 @@ async function handleAgentRoutes(req, res, pathname, ctx) {
6722
6940
  metadata
6723
6941
  } = body;
6724
6942
  const workspaceBaseDir = path6.join(os4.homedir(), ".milady", "workspaces");
6725
- const allowedPrefixes = [
6726
- path6.resolve(workspaceBaseDir),
6727
- path6.resolve(process.cwd())
6728
- ];
6943
+ const workspaceBaseDirResolved = path6.resolve(workspaceBaseDir);
6944
+ const cwdResolved = path6.resolve(process.cwd());
6945
+ const workspaceBaseDirReal = await realpath(workspaceBaseDirResolved).catch(() => workspaceBaseDirResolved);
6946
+ const cwdReal = await realpath(cwdResolved).catch(() => cwdResolved);
6947
+ const allowedPrefixes = [workspaceBaseDirReal, cwdReal];
6729
6948
  let workdir = rawWorkdir;
6730
6949
  if (workdir) {
6731
6950
  const resolved = path6.resolve(workdir);
6732
- const isAllowed = allowedPrefixes.some((prefix2) => resolved === prefix2 || resolved.startsWith(prefix2 + path6.sep));
6951
+ const resolvedReal = await realpath(resolved).catch(() => null);
6952
+ if (!resolvedReal) {
6953
+ sendError(res, "workdir must exist", 403);
6954
+ return true;
6955
+ }
6956
+ const isAllowed = allowedPrefixes.some((prefix2) => resolvedReal === prefix2 || resolvedReal.startsWith(prefix2 + path6.sep));
6733
6957
  if (!isAllowed) {
6734
6958
  sendError(res, "workdir must be within workspace base directory or cwd", 403);
6735
6959
  return true;
6736
6960
  }
6737
- workdir = resolved;
6961
+ workdir = resolvedReal;
6738
6962
  }
6739
6963
  const activeSessions = await ctx.ptyService.listSessions();
6740
6964
  const maxSessions = 8;
@@ -6742,6 +6966,13 @@ async function handleAgentRoutes(req, res, pathname, ctx) {
6742
6966
  sendError(res, `Concurrent session limit reached (${maxSessions})`, 429);
6743
6967
  return true;
6744
6968
  }
6969
+ if (workdir) {
6970
+ try {
6971
+ await runBenchmarkPreflight(workdir);
6972
+ } catch (preflightError) {
6973
+ console.warn(`[coding-agent] benchmark preflight failed for ${workdir}:`, preflightError);
6974
+ }
6975
+ }
6745
6976
  const credentials = {
6746
6977
  anthropicKey: ctx.runtime.getSetting("ANTHROPIC_API_KEY"),
6747
6978
  openaiKey: ctx.runtime.getSetting("OPENAI_API_KEY"),
@@ -7468,5 +7699,5 @@ export {
7468
7699
  CodingWorkspaceService
7469
7700
  };
7470
7701
 
7471
- //# debugId=4383E51DF7A1F4A764756E2164756E21
7702
+ //# debugId=A818F3DE8DCF48E664756E2164756E21
7472
7703
  //# sourceMappingURL=index.js.map