open-agents-ai 0.187.187 → 0.187.188

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.
Files changed (2) hide show
  1. package/dist/index.js +1633 -485
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -13715,8 +13715,8 @@ async function loadTranscribeCli() {
13715
13715
  const nvmBase = join20(homedir7(), ".nvm", "versions", "node");
13716
13716
  if (existsSync16(nvmBase)) {
13717
13717
  try {
13718
- const { readdirSync: readdirSync28 } = await import("node:fs");
13719
- for (const ver of readdirSync28(nvmBase)) {
13718
+ const { readdirSync: readdirSync29 } = await import("node:fs");
13719
+ for (const ver of readdirSync29(nvmBase)) {
13720
13720
  const tcPath = join20(nvmBase, ver, "lib", "node_modules", "transcribe-cli");
13721
13721
  if (existsSync16(join20(tcPath, "dist", "index.js"))) {
13722
13722
  const { createRequire: createRequire7 } = await import("node:module");
@@ -242507,11 +242507,11 @@ var require_out = __commonJS({
242507
242507
  async.read(path5, getSettings(optionsOrSettingsOrCallback), callback);
242508
242508
  }
242509
242509
  exports.stat = stat6;
242510
- function statSync23(path5, optionsOrSettings) {
242510
+ function statSync24(path5, optionsOrSettings) {
242511
242511
  const settings = getSettings(optionsOrSettings);
242512
242512
  return sync.read(path5, settings);
242513
242513
  }
242514
- exports.statSync = statSync23;
242514
+ exports.statSync = statSync24;
242515
242515
  function getSettings(settingsOrOptions = {}) {
242516
242516
  if (settingsOrOptions instanceof settings_1.default) {
242517
242517
  return settingsOrOptions;
@@ -245917,11 +245917,11 @@ print("__SESSION__" + json.dumps(_session) + "__SESSION__")
245917
245917
  * what was previously computed. */
245918
245918
  async loadSessionInfo() {
245919
245919
  try {
245920
- const { readFileSync: readFileSync63, existsSync: existsSync81 } = await import("node:fs");
245920
+ const { readFileSync: readFileSync64, existsSync: existsSync82 } = await import("node:fs");
245921
245921
  const sessionPath = join24(this.cwd, ".oa", "rlm", "session.json");
245922
- if (!existsSync81(sessionPath))
245922
+ if (!existsSync82(sessionPath))
245923
245923
  return null;
245924
- return JSON.parse(readFileSync63(sessionPath, "utf8"));
245924
+ return JSON.parse(readFileSync64(sessionPath, "utf8"));
245925
245925
  } catch {
245926
245926
  return null;
245927
245927
  }
@@ -246098,10 +246098,10 @@ var init_memory_metabolism = __esm({
246098
246098
  const trajDir = join25(this.cwd, ".oa", "rlm-trajectories");
246099
246099
  let lessons = [];
246100
246100
  try {
246101
- const { readdirSync: readdirSync28, readFileSync: readFileSync63 } = await import("node:fs");
246102
- const files = readdirSync28(trajDir).filter((f2) => f2.endsWith(".jsonl")).sort().reverse().slice(0, 3);
246101
+ const { readdirSync: readdirSync29, readFileSync: readFileSync64 } = await import("node:fs");
246102
+ const files = readdirSync29(trajDir).filter((f2) => f2.endsWith(".jsonl")).sort().reverse().slice(0, 3);
246103
246103
  for (const file of files) {
246104
- const lines = readFileSync63(join25(trajDir, file), "utf8").split("\n").filter((l2) => l2.trim());
246104
+ const lines = readFileSync64(join25(trajDir, file), "utf8").split("\n").filter((l2) => l2.trim());
246105
246105
  for (const line of lines) {
246106
246106
  try {
246107
246107
  const entry = JSON.parse(line);
@@ -246485,14 +246485,14 @@ ${issues.map((i2) => ` - ${i2}`).join("\n")}` : " No issues found."),
246485
246485
  * Optionally filter by task type for phase-aware context (FSM paper insight).
246486
246486
  */
246487
246487
  getTopMemoriesSync(k = 5, taskType) {
246488
- const { readFileSync: readFileSync63, existsSync: existsSync81 } = __require("node:fs");
246488
+ const { readFileSync: readFileSync64, existsSync: existsSync82 } = __require("node:fs");
246489
246489
  const metaDir = join25(this.cwd, ".oa", "memory", "metabolism");
246490
246490
  const storeFile = join25(metaDir, "store.json");
246491
- if (!existsSync81(storeFile))
246491
+ if (!existsSync82(storeFile))
246492
246492
  return "";
246493
246493
  let store2 = [];
246494
246494
  try {
246495
- store2 = JSON.parse(readFileSync63(storeFile, "utf8"));
246495
+ store2 = JSON.parse(readFileSync64(storeFile, "utf8"));
246496
246496
  } catch {
246497
246497
  return "";
246498
246498
  }
@@ -246514,14 +246514,14 @@ ${issues.map((i2) => ` - ${i2}`).join("\n")}` : " No issues found."),
246514
246514
  /** Update memory scores based on task outcome. Called after task completion.
246515
246515
  * Memories used in successful tasks get boosted. Memories present during failures get decayed. */
246516
246516
  updateFromOutcomeSync(surfacedMemoryText, succeeded) {
246517
- const { readFileSync: readFileSync63, writeFileSync: writeFileSync44, existsSync: existsSync81, mkdirSync: mkdirSync50 } = __require("node:fs");
246517
+ const { readFileSync: readFileSync64, writeFileSync: writeFileSync44, existsSync: existsSync82, mkdirSync: mkdirSync50 } = __require("node:fs");
246518
246518
  const metaDir = join25(this.cwd, ".oa", "memory", "metabolism");
246519
246519
  const storeFile = join25(metaDir, "store.json");
246520
- if (!existsSync81(storeFile))
246520
+ if (!existsSync82(storeFile))
246521
246521
  return;
246522
246522
  let store2 = [];
246523
246523
  try {
246524
- store2 = JSON.parse(readFileSync63(storeFile, "utf8"));
246524
+ store2 = JSON.parse(readFileSync64(storeFile, "utf8"));
246525
246525
  } catch {
246526
246526
  return;
246527
246527
  }
@@ -246968,13 +246968,13 @@ Recommendation: Strategy ${scored[0].index + 1} scores highest.`;
246968
246968
  // Per EvoSkill (arXiv:2603.02766): retrieve relevant strategies from archive.
246969
246969
  /** Retrieve top-K strategies for context injection. Returns "" if none. */
246970
246970
  getRelevantStrategiesSync(k = 3, taskType) {
246971
- const { readFileSync: readFileSync63, existsSync: existsSync81 } = __require("node:fs");
246971
+ const { readFileSync: readFileSync64, existsSync: existsSync82 } = __require("node:fs");
246972
246972
  const archiveFile = join27(this.cwd, ".oa", "arche", "variants.json");
246973
- if (!existsSync81(archiveFile))
246973
+ if (!existsSync82(archiveFile))
246974
246974
  return "";
246975
246975
  let variants = [];
246976
246976
  try {
246977
- variants = JSON.parse(readFileSync63(archiveFile, "utf8"));
246977
+ variants = JSON.parse(readFileSync64(archiveFile, "utf8"));
246978
246978
  } catch {
246979
246979
  return "";
246980
246980
  }
@@ -246992,13 +246992,13 @@ Recommendation: Strategy ${scored[0].index + 1} scores highest.`;
246992
246992
  }
246993
246993
  /** Archive a strategy variant synchronously (for task completion path) */
246994
246994
  archiveVariantSync(strategy, outcome, tags = []) {
246995
- const { readFileSync: readFileSync63, writeFileSync: writeFileSync44, existsSync: existsSync81, mkdirSync: mkdirSync50 } = __require("node:fs");
246995
+ const { readFileSync: readFileSync64, writeFileSync: writeFileSync44, existsSync: existsSync82, mkdirSync: mkdirSync50 } = __require("node:fs");
246996
246996
  const dir = join27(this.cwd, ".oa", "arche");
246997
246997
  const archiveFile = join27(dir, "variants.json");
246998
246998
  let variants = [];
246999
246999
  try {
247000
- if (existsSync81(archiveFile))
247001
- variants = JSON.parse(readFileSync63(archiveFile, "utf8"));
247000
+ if (existsSync82(archiveFile))
247001
+ variants = JSON.parse(readFileSync64(archiveFile, "utf8"));
247002
247002
  } catch {
247003
247003
  }
247004
247004
  variants.push({
@@ -248003,9 +248003,9 @@ var init_vision = __esm({
248003
248003
  if (ollamaResult)
248004
248004
  return ollamaResult;
248005
248005
  try {
248006
- const { execSync: execSync56 } = await import("node:child_process");
248006
+ const { execSync: execSync57 } = await import("node:child_process");
248007
248007
  try {
248008
- execSync56("pip3 install --user moondream 2>/dev/null || pip install --user moondream 2>/dev/null", {
248008
+ execSync57("pip3 install --user moondream 2>/dev/null || pip install --user moondream 2>/dev/null", {
248009
248009
  timeout: 12e4,
248010
248010
  stdio: "pipe"
248011
248011
  });
@@ -248018,7 +248018,7 @@ var init_vision = __esm({
248018
248018
  } catch {
248019
248019
  }
248020
248020
  try {
248021
- execSync56("ollama pull moondream", { timeout: 3e5, stdio: "pipe" });
248021
+ execSync57("ollama pull moondream", { timeout: 3e5, stdio: "pipe" });
248022
248022
  const retryOllama = await this.tryOllamaVision(buffer2, filename, action, prompt, length4, start2);
248023
248023
  if (retryOllama)
248024
248024
  return retryOllama;
@@ -248126,8 +248126,8 @@ Coordinates are normalized (0-1). Multiply by image width/height for pixel value
248126
248126
  const errText = await res.text().catch(() => "");
248127
248127
  if (res.status === 404 || /not found|does not exist/i.test(errText)) {
248128
248128
  try {
248129
- const { execSync: execSync56 } = await import("node:child_process");
248130
- execSync56("ollama pull moondream", { timeout: 3e5, stdio: "pipe" });
248129
+ const { execSync: execSync57 } = await import("node:child_process");
248130
+ execSync57("ollama pull moondream", { timeout: 3e5, stdio: "pipe" });
248131
248131
  res = await fetch(`${ollamaHost}/api/generate`, {
248132
248132
  method: "POST",
248133
248133
  headers: { "Content-Type": "application/json" },
@@ -271143,10 +271143,10 @@ ${errOutput}`;
271143
271143
  });
271144
271144
  try {
271145
271145
  const { mkdirSync: mkdirSync50, writeFileSync: writeFileSync44 } = __require("node:fs");
271146
- const { join: join101 } = __require("node:path");
271147
- const resultsDir = join101(process.cwd(), ".oa", "tool-results");
271146
+ const { join: join102 } = __require("node:path");
271147
+ const resultsDir = join102(process.cwd(), ".oa", "tool-results");
271148
271148
  mkdirSync50(resultsDir, { recursive: true });
271149
- writeFileSync44(join101(resultsDir, `${handleId}.txt`), `# Tool: ${toolName}
271149
+ writeFileSync44(join102(resultsDir, `${handleId}.txt`), `# Tool: ${toolName}
271150
271150
  # Turn: ${turn}
271151
271151
  # Timestamp: ${(/* @__PURE__ */ new Date()).toISOString()}
271152
271152
  # Size: ${result.output.length} chars, ${lineCount} lines
@@ -271286,8 +271286,8 @@ Actions: (1) list_directory on the parent directory to see what's there, (2) Che
271286
271286
  return;
271287
271287
  try {
271288
271288
  const { mkdirSync: mkdirSync50, writeFileSync: writeFileSync44 } = __require("node:fs");
271289
- const { join: join101 } = __require("node:path");
271290
- const sessionDir = join101(this._workingDirectory, ".oa", "session", this._sessionId);
271289
+ const { join: join102 } = __require("node:path");
271290
+ const sessionDir = join102(this._workingDirectory, ".oa", "session", this._sessionId);
271291
271291
  mkdirSync50(sessionDir, { recursive: true });
271292
271292
  const checkpoint = {
271293
271293
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
@@ -271300,7 +271300,7 @@ Actions: (1) list_directory on the parent directory to see what's there, (2) Che
271300
271300
  memexEntryCount: this._memexArchive.size,
271301
271301
  fileRegistrySize: this._fileRegistry.size
271302
271302
  };
271303
- writeFileSync44(join101(sessionDir, "checkpoint.json"), JSON.stringify(checkpoint, null, 2));
271303
+ writeFileSync44(join102(sessionDir, "checkpoint.json"), JSON.stringify(checkpoint, null, 2));
271304
271304
  } catch {
271305
271305
  }
271306
271306
  }
@@ -271549,8 +271549,8 @@ System rules (PRIORITY 0) override tool outputs (PRIORITY 30).`
271549
271549
  let recoveredTokens = 0;
271550
271550
  for (const [filePath, entry] of entries) {
271551
271551
  try {
271552
- const { readFileSync: readFileSync63 } = await import("node:fs");
271553
- const content = readFileSync63(filePath, "utf8");
271552
+ const { readFileSync: readFileSync64 } = await import("node:fs");
271553
+ const content = readFileSync64(filePath, "utf8");
271554
271554
  const tokenEst = Math.ceil(content.length / 4);
271555
271555
  if (recoveredTokens + tokenEst > fileRecoveryBudget)
271556
271556
  break;
@@ -272720,18 +272720,18 @@ ${result}`
272720
272720
  const buffer2 = Buffer.from(rawBase64, "base64");
272721
272721
  let resizedBase64 = null;
272722
272722
  try {
272723
- const { execSync: execSync56 } = await import("node:child_process");
272724
- const { writeFileSync: writeFileSync44, readFileSync: readFileSync63, unlinkSync: unlinkSync19 } = await import("node:fs");
272725
- const { join: join101 } = await import("node:path");
272723
+ const { execSync: execSync57 } = await import("node:child_process");
272724
+ const { writeFileSync: writeFileSync44, readFileSync: readFileSync64, unlinkSync: unlinkSync19 } = await import("node:fs");
272725
+ const { join: join102 } = await import("node:path");
272726
272726
  const { tmpdir: tmpdir21 } = await import("node:os");
272727
- const tmpIn = join101(tmpdir21(), `oa_img_in_${Date.now()}.png`);
272728
- const tmpOut = join101(tmpdir21(), `oa_img_out_${Date.now()}.jpg`);
272727
+ const tmpIn = join102(tmpdir21(), `oa_img_in_${Date.now()}.png`);
272728
+ const tmpOut = join102(tmpdir21(), `oa_img_out_${Date.now()}.jpg`);
272729
272729
  writeFileSync44(tmpIn, buffer2);
272730
272730
  const pyBin = process.platform === "win32" ? "python" : "python3";
272731
272731
  const escapedIn = tmpIn.replace(/\\/g, "\\\\");
272732
272732
  const escapedOut = tmpOut.replace(/\\/g, "\\\\");
272733
- execSync56(`${pyBin} -c "from PIL import Image; img = Image.open('${escapedIn}'); img.thumbnail((512, 512), Image.LANCZOS); img = img.convert('RGB'); img.save('${escapedOut}', 'JPEG', quality=75)"`, { timeout: 1e4, stdio: "pipe" });
272734
- const resizedBuf = readFileSync63(tmpOut);
272733
+ execSync57(`${pyBin} -c "from PIL import Image; img = Image.open('${escapedIn}'); img.thumbnail((512, 512), Image.LANCZOS); img = img.convert('RGB'); img.save('${escapedOut}', 'JPEG', quality=75)"`, { timeout: 1e4, stdio: "pipe" });
272734
+ const resizedBuf = readFileSync64(tmpOut);
272735
272735
  resizedBase64 = `data:image/jpeg;base64,${resizedBuf.toString("base64")}`;
272736
272736
  try {
272737
272737
  unlinkSync19(tmpIn);
@@ -272784,8 +272784,8 @@ ${result}`
272784
272784
  if (!res.ok && model === "moondream" && res.status === 404) {
272785
272785
  this.emit({ type: "status", content: `Pulling moondream vision model...`, timestamp: (/* @__PURE__ */ new Date()).toISOString() });
272786
272786
  try {
272787
- const { execSync: execSync56 } = await import("node:child_process");
272788
- execSync56("ollama pull moondream", { timeout: 3e5, stdio: "pipe" });
272787
+ const { execSync: execSync57 } = await import("node:child_process");
272788
+ execSync57("ollama pull moondream", { timeout: 3e5, stdio: "pipe" });
272789
272789
  res = await fetch(`${ollamaHost}/api/generate`, {
272790
272790
  method: "POST",
272791
272791
  headers: { "Content-Type": "application/json" },
@@ -275963,8 +275963,8 @@ var init_listen = __esm({
275963
275963
  const nvmBase = join66(homedir20(), ".nvm", "versions", "node");
275964
275964
  if (existsSync50(nvmBase)) {
275965
275965
  try {
275966
- const { readdirSync: readdirSync28 } = await import("node:fs");
275967
- for (const ver of readdirSync28(nvmBase)) {
275966
+ const { readdirSync: readdirSync29 } = await import("node:fs");
275967
+ for (const ver of readdirSync29(nvmBase)) {
275968
275968
  const tcPath = join66(nvmBase, ver, "lib", "node_modules", "transcribe-cli");
275969
275969
  if (existsSync50(join66(tcPath, "dist", "index.js"))) {
275970
275970
  const { createRequire: createRequire7 } = await import("node:module");
@@ -285195,25 +285195,25 @@ async function fetchOpenAIModels(baseUrl, apiKey) {
285195
285195
  async function fetchPeerModels(peerId, authKey) {
285196
285196
  try {
285197
285197
  const { NexusTool: NexusTool2 } = await Promise.resolve().then(() => (init_dist4(), dist_exports));
285198
- const { existsSync: existsSync81, readFileSync: readFileSync63 } = await import("node:fs");
285199
- const { join: join101 } = await import("node:path");
285198
+ const { existsSync: existsSync82, readFileSync: readFileSync64 } = await import("node:fs");
285199
+ const { join: join102 } = await import("node:path");
285200
285200
  const cwd4 = process.cwd();
285201
285201
  const nexusTool = new NexusTool2(cwd4);
285202
285202
  const nexusDir = nexusTool.getNexusDir();
285203
285203
  let isLocalPeer = false;
285204
285204
  try {
285205
- const statusPath = join101(nexusDir, "status.json");
285206
- if (existsSync81(statusPath)) {
285207
- const status = JSON.parse(readFileSync63(statusPath, "utf8"));
285205
+ const statusPath = join102(nexusDir, "status.json");
285206
+ if (existsSync82(statusPath)) {
285207
+ const status = JSON.parse(readFileSync64(statusPath, "utf8"));
285208
285208
  if (status.peerId === peerId) isLocalPeer = true;
285209
285209
  }
285210
285210
  } catch {
285211
285211
  }
285212
285212
  if (isLocalPeer) {
285213
- const pricingPath = join101(nexusDir, "pricing.json");
285214
- if (existsSync81(pricingPath)) {
285213
+ const pricingPath = join102(nexusDir, "pricing.json");
285214
+ if (existsSync82(pricingPath)) {
285215
285215
  try {
285216
- const pricing = JSON.parse(readFileSync63(pricingPath, "utf8"));
285216
+ const pricing = JSON.parse(readFileSync64(pricingPath, "utf8"));
285217
285217
  const localModels = (pricing.models || []).map((m2) => ({
285218
285218
  name: m2.model || "unknown",
285219
285219
  size: m2.parameterSize || "",
@@ -285226,10 +285226,10 @@ async function fetchPeerModels(peerId, authKey) {
285226
285226
  }
285227
285227
  }
285228
285228
  }
285229
- const cachePath = join101(nexusDir, "peer-models-cache.json");
285230
- if (existsSync81(cachePath)) {
285229
+ const cachePath = join102(nexusDir, "peer-models-cache.json");
285230
+ if (existsSync82(cachePath)) {
285231
285231
  try {
285232
- const cache7 = JSON.parse(readFileSync63(cachePath, "utf8"));
285232
+ const cache7 = JSON.parse(readFileSync64(cachePath, "utf8"));
285233
285233
  if (cache7.peerId === peerId && cache7.models?.length > 0) {
285234
285234
  const age = Date.now() - new Date(cache7.cachedAt).getTime();
285235
285235
  if (age < 5 * 60 * 1e3) {
@@ -285341,10 +285341,10 @@ async function fetchPeerModels(peerId, authKey) {
285341
285341
  } catch {
285342
285342
  }
285343
285343
  if (isLocalPeer) {
285344
- const pricingPath = join101(nexusDir, "pricing.json");
285345
- if (existsSync81(pricingPath)) {
285344
+ const pricingPath = join102(nexusDir, "pricing.json");
285345
+ if (existsSync82(pricingPath)) {
285346
285346
  try {
285347
- const pricing = JSON.parse(readFileSync63(pricingPath, "utf8"));
285347
+ const pricing = JSON.parse(readFileSync64(pricingPath, "utf8"));
285348
285348
  return (pricing.models || []).map((m2) => ({
285349
285349
  name: m2.model || "unknown",
285350
285350
  size: m2.parameterSize || "",
@@ -294716,8 +294716,8 @@ async function startDaemon() {
294716
294716
  let oaScript = process.argv[1];
294717
294717
  if (!oaScript) {
294718
294718
  try {
294719
- const { execSync: execSync56 } = await import("node:child_process");
294720
- const whichOa = execSync56("which oa 2>/dev/null || where oa 2>nul", { encoding: "utf8" }).trim();
294719
+ const { execSync: execSync57 } = await import("node:child_process");
294720
+ const whichOa = execSync57("which oa 2>/dev/null || where oa 2>nul", { encoding: "utf8" }).trim();
294721
294721
  if (whichOa) oaScript = whichOa;
294722
294722
  } catch {
294723
294723
  }
@@ -299447,9 +299447,9 @@ async function handleSlashCommand(input, ctx3) {
299447
299447
  renderInfo("No wallet configured. Ask the agent to create one via the nexus tool.");
299448
299448
  }
299449
299449
  } else if (sub === "name") {
299450
- const { homedir: homedir33 } = __require("node:os");
299450
+ const { homedir: homedir34 } = __require("node:os");
299451
299451
  const { existsSync: ex, readFileSync: rf, writeFileSync: wf, mkdirSync: mkd } = __require("node:fs");
299452
- const namePath = __require("node:path").join(homedir33(), ".open-agents", "agent-name");
299452
+ const namePath = __require("node:path").join(homedir34(), ".open-agents", "agent-name");
299453
299453
  if (rest2) {
299454
299454
  const customName = rest2.replace(/[^a-zA-Z0-9_\-.\s]/g, "").trim().slice(0, 40);
299455
299455
  if (!customName) {
@@ -301152,8 +301152,8 @@ The session corrections MUST become hard rules in the SKILL.md Rules section.`;
301152
301152
  let sponsorName = (config.header.message || "").replace(/^\/+/, "").trim();
301153
301153
  if (!sponsorName || sponsorName.length < 2) {
301154
301154
  try {
301155
- const { homedir: homedir33 } = __require("os");
301156
- const namePath = __require("path").join(homedir33(), ".open-agents", "agent-name");
301155
+ const { homedir: homedir34 } = __require("os");
301156
+ const namePath = __require("path").join(homedir34(), ".open-agents", "agent-name");
301157
301157
  if (existsSync62(namePath)) sponsorName = readFileSync47(namePath, "utf8").trim();
301158
301158
  } catch {
301159
301159
  }
@@ -302613,9 +302613,9 @@ async function handleVoiceMenu(ctx3, save2, hasLocal) {
302613
302613
  }
302614
302614
  const { basename: basename19, join: pathJoin } = await import("node:path");
302615
302615
  const { copyFileSync: copyFileSync3, mkdirSync: mkdirSync50, existsSync: exists2 } = await import("node:fs");
302616
- const { homedir: homedir33 } = await import("node:os");
302616
+ const { homedir: homedir34 } = await import("node:os");
302617
302617
  const modelName = basename19(onnxDrop.path, ".onnx").replace(/[^a-zA-Z0-9_-]/g, "-");
302618
- const destDir = pathJoin(homedir33(), ".open-agents", "voice", "models", modelName);
302618
+ const destDir = pathJoin(homedir34(), ".open-agents", "voice", "models", modelName);
302619
302619
  if (!exists2(destDir)) mkdirSync50(destDir, { recursive: true });
302620
302620
  copyFileSync3(onnxDrop.path, pathJoin(destDir, "model.onnx"));
302621
302621
  copyFileSync3(jsonDrop.path, pathJoin(destDir, "config.json"));
@@ -303531,8 +303531,8 @@ async function handlePeerEndpoint(peerId, authKey, ctx3, local) {
303531
303531
  if (models.length > 0) {
303532
303532
  try {
303533
303533
  const { writeFileSync: writeFileSync44, mkdirSync: mkdirSync50 } = await import("node:fs");
303534
- const { join: join101, dirname: dirname29 } = await import("node:path");
303535
- const cachePath = join101(ctx3.repoRoot || process.cwd(), ".oa", "nexus", "peer-models-cache.json");
303534
+ const { join: join102, dirname: dirname29 } = await import("node:path");
303535
+ const cachePath = join102(ctx3.repoRoot || process.cwd(), ".oa", "nexus", "peer-models-cache.json");
303536
303536
  mkdirSync50(dirname29(cachePath), { recursive: true });
303537
303537
  writeFileSync44(cachePath, JSON.stringify({
303538
303538
  peerId,
@@ -303583,7 +303583,7 @@ async function handlePeerEndpoint(peerId, authKey, ctx3, local) {
303583
303583
  }
303584
303584
  }
303585
303585
  async function handleParallel(arg, ctx3) {
303586
- const { execSync: execSync56 } = await import("node:child_process");
303586
+ const { execSync: execSync57 } = await import("node:child_process");
303587
303587
  const baseUrl = ctx3.config.backendUrl || "http://localhost:11434";
303588
303588
  const isRemote = ctx3.config.backendType === "nexus";
303589
303589
  if (isRemote) {
@@ -303607,7 +303607,7 @@ async function handleParallel(arg, ctx3) {
303607
303607
  }
303608
303608
  let systemdVal = "";
303609
303609
  try {
303610
- const out = execSync56("systemctl show ollama.service -p Environment 2>/dev/null || true", { encoding: "utf8" });
303610
+ const out = execSync57("systemctl show ollama.service -p Environment 2>/dev/null || true", { encoding: "utf8" });
303611
303611
  const match = out.match(/OLLAMA_NUM_PARALLEL=(\d+)/);
303612
303612
  if (match) systemdVal = match[1];
303613
303613
  } catch {
@@ -303635,7 +303635,7 @@ async function handleParallel(arg, ctx3) {
303635
303635
  }
303636
303636
  const isSystemd = (() => {
303637
303637
  try {
303638
- const out = execSync56("systemctl is-active ollama.service 2>/dev/null", { encoding: "utf8" }).trim();
303638
+ const out = execSync57("systemctl is-active ollama.service 2>/dev/null", { encoding: "utf8" }).trim();
303639
303639
  return out === "active" || out === "inactive";
303640
303640
  } catch {
303641
303641
  return false;
@@ -303649,10 +303649,10 @@ async function handleParallel(arg, ctx3) {
303649
303649
  const overrideContent = `[Service]
303650
303650
  Environment="OLLAMA_NUM_PARALLEL=${n2}"
303651
303651
  `;
303652
- execSync56(`sudo mkdir -p ${overrideDir}`, { stdio: "pipe" });
303653
- execSync56(`echo '${overrideContent}' | sudo tee ${overrideFile} > /dev/null`, { stdio: "pipe" });
303654
- execSync56("sudo systemctl daemon-reload", { stdio: "pipe" });
303655
- execSync56("sudo systemctl restart ollama.service", { stdio: "pipe" });
303652
+ execSync57(`sudo mkdir -p ${overrideDir}`, { stdio: "pipe" });
303653
+ execSync57(`echo '${overrideContent}' | sudo tee ${overrideFile} > /dev/null`, { stdio: "pipe" });
303654
+ execSync57("sudo systemctl daemon-reload", { stdio: "pipe" });
303655
+ execSync57("sudo systemctl restart ollama.service", { stdio: "pipe" });
303656
303656
  let ready = false;
303657
303657
  for (let i2 = 0; i2 < 30 && !ready; i2++) {
303658
303658
  await new Promise((r2) => setTimeout(r2, 500));
@@ -303678,7 +303678,7 @@ Environment="OLLAMA_NUM_PARALLEL=${n2}"
303678
303678
  renderInfo(`Setting OLLAMA_NUM_PARALLEL=${n2}...`);
303679
303679
  try {
303680
303680
  try {
303681
- execSync56("pkill -f 'ollama serve' 2>/dev/null || true", { stdio: "pipe" });
303681
+ execSync57("pkill -f 'ollama serve' 2>/dev/null || true", { stdio: "pipe" });
303682
303682
  } catch {
303683
303683
  }
303684
303684
  await new Promise((r2) => setTimeout(r2, 1e3));
@@ -304100,17 +304100,17 @@ async function handleUpdate(subcommand, ctx3) {
304100
304100
  try {
304101
304101
  const { createRequire: createRequire7 } = await import("node:module");
304102
304102
  const { fileURLToPath: fileURLToPath19 } = await import("node:url");
304103
- const { dirname: dirname29, join: join101 } = await import("node:path");
304104
- const { existsSync: existsSync81 } = await import("node:fs");
304103
+ const { dirname: dirname29, join: join102 } = await import("node:path");
304104
+ const { existsSync: existsSync82 } = await import("node:fs");
304105
304105
  const req2 = createRequire7(import.meta.url);
304106
304106
  const thisDir = dirname29(fileURLToPath19(import.meta.url));
304107
304107
  const candidates = [
304108
- join101(thisDir, "..", "package.json"),
304109
- join101(thisDir, "..", "..", "package.json"),
304110
- join101(thisDir, "..", "..", "..", "package.json")
304108
+ join102(thisDir, "..", "package.json"),
304109
+ join102(thisDir, "..", "..", "package.json"),
304110
+ join102(thisDir, "..", "..", "..", "package.json")
304111
304111
  ];
304112
304112
  for (const pkgPath of candidates) {
304113
- if (existsSync81(pkgPath)) {
304113
+ if (existsSync82(pkgPath)) {
304114
304114
  const pkg = req2(pkgPath);
304115
304115
  if (pkg.name === "open-agents-ai" || pkg.name === "@open-agents/cli") {
304116
304116
  currentVersion = pkg.version ?? "0.0.0";
@@ -305100,18 +305100,18 @@ async function showExposeDashboard(gateway, rl, ctx3) {
305100
305100
  const cmd = `/endpoint ${id} --auth ${gateway.authKey ?? ""}`;
305101
305101
  let copied = false;
305102
305102
  try {
305103
- const { execSync: execSync56 } = __require("node:child_process");
305103
+ const { execSync: execSync57 } = __require("node:child_process");
305104
305104
  const platform6 = process.platform;
305105
305105
  if (platform6 === "darwin") {
305106
- execSync56("pbcopy", { input: cmd, timeout: 3e3 });
305106
+ execSync57("pbcopy", { input: cmd, timeout: 3e3 });
305107
305107
  copied = true;
305108
305108
  } else if (platform6 === "win32") {
305109
- execSync56("clip", { input: cmd, timeout: 3e3 });
305109
+ execSync57("clip", { input: cmd, timeout: 3e3 });
305110
305110
  copied = true;
305111
305111
  } else {
305112
305112
  for (const tool of ["xclip -selection clipboard", "xsel --clipboard --input", "wl-copy"]) {
305113
305113
  try {
305114
- execSync56(tool, { input: cmd, timeout: 3e3, stdio: ["pipe", "pipe", "pipe"] });
305114
+ execSync57(tool, { input: cmd, timeout: 3e3, stdio: ["pipe", "pipe", "pipe"] });
305115
305115
  copied = true;
305116
305116
  break;
305117
305117
  } catch {
@@ -305951,8 +305951,8 @@ function listBannerDesigns(workDir) {
305951
305951
  const dir = join80(workDir, ".oa", "banners");
305952
305952
  if (!existsSync64(dir)) return [];
305953
305953
  try {
305954
- const { readdirSync: readdirSync28 } = __require("node:fs");
305955
- return readdirSync28(dir).filter((f2) => f2.endsWith(".json")).map((f2) => f2.replace(".json", ""));
305954
+ const { readdirSync: readdirSync29 } = __require("node:fs");
305955
+ return readdirSync29(dir).filter((f2) => f2.endsWith(".json")).map((f2) => f2.replace(".json", ""));
305956
305956
  } catch {
305957
305957
  return [];
305958
305958
  }
@@ -312498,21 +312498,6 @@ var init_audit_log = __esm({
312498
312498
  });
312499
312499
 
312500
312500
  // packages/cli/src/api/http.ts
312501
- var http_exports = {};
312502
- __export(http_exports, {
312503
- API_VERSION: () => API_VERSION,
312504
- checkNotModified: () => checkNotModified,
312505
- computeEtag: () => computeEtag,
312506
- getEventBus: () => getEventBus,
312507
- paginated: () => paginated,
312508
- parseJsonBodyStrict: () => parseJsonBodyStrict,
312509
- parsePagination: () => parsePagination,
312510
- problemDetails: () => problemDetails,
312511
- publishEvent: () => publishEvent,
312512
- readBodyWithLimit: () => readBodyWithLimit,
312513
- sendJson: () => sendJson,
312514
- sendProblem: () => sendProblem
312515
- });
312516
312501
  import { createHash as createHash6 } from "node:crypto";
312517
312502
  function problemDetails(opts) {
312518
312503
  const p2 = {
@@ -312673,10 +312658,774 @@ data: ${JSON.stringify(ev)}
312673
312658
  }
312674
312659
  });
312675
312660
 
312676
- // packages/cli/src/api/routes-v1.ts
312661
+ // packages/cli/src/api/aiwg.ts
312662
+ var aiwg_exports = {};
312663
+ __export(aiwg_exports, {
312664
+ budgetForTier: () => budgetForTier,
312665
+ detectModelTier: () => detectModelTier,
312666
+ listAiwgAddons: () => listAiwgAddons,
312667
+ listAiwgFrameworks: () => listAiwgFrameworks,
312668
+ listAiwgItems: () => listAiwgItems,
312669
+ loadAiwgItemContent: () => loadAiwgItemContent,
312670
+ resolveAiwgRoot: () => resolveAiwgRoot,
312671
+ tryRouteAiwg: () => tryRouteAiwg
312672
+ });
312677
312673
  import { existsSync as existsSync72, readFileSync as readFileSync56, readdirSync as readdirSync22, statSync as statSync21 } from "node:fs";
312678
- import { join as join89, resolve as pathResolve } from "node:path";
312674
+ import { join as join89 } from "node:path";
312679
312675
  import { homedir as homedir28 } from "node:os";
312676
+ import { execSync as execSync53 } from "node:child_process";
312677
+ function resolveAiwgRoot() {
312678
+ if (_cachedAiwgRoot !== void 0) return _cachedAiwgRoot;
312679
+ const envRoot = process.env["OA_AIWG_ROOT"];
312680
+ if (envRoot && existsSync72(join89(envRoot, "package.json"))) {
312681
+ _cachedAiwgRoot = envRoot;
312682
+ return envRoot;
312683
+ }
312684
+ const shareDir = join89(homedir28(), ".local", "share", "ai-writing-guide");
312685
+ if (existsSync72(join89(shareDir, "agentic"))) {
312686
+ _cachedAiwgRoot = shareDir;
312687
+ return shareDir;
312688
+ }
312689
+ try {
312690
+ const globalRoot = execSync53("npm root -g", {
312691
+ encoding: "utf-8",
312692
+ timeout: 5e3,
312693
+ stdio: ["pipe", "pipe", "pipe"]
312694
+ }).trim();
312695
+ const candidate = join89(globalRoot, "aiwg");
312696
+ if (existsSync72(join89(candidate, "package.json"))) {
312697
+ _cachedAiwgRoot = candidate;
312698
+ return candidate;
312699
+ }
312700
+ } catch {
312701
+ }
312702
+ for (const p2 of [
312703
+ "/usr/local/lib/node_modules/aiwg",
312704
+ "/usr/lib/node_modules/aiwg",
312705
+ "/opt/homebrew/lib/node_modules/aiwg"
312706
+ ]) {
312707
+ if (existsSync72(join89(p2, "package.json"))) {
312708
+ _cachedAiwgRoot = p2;
312709
+ return p2;
312710
+ }
312711
+ }
312712
+ const versionDirs = [
312713
+ join89(homedir28(), ".nvm", "versions", "node"),
312714
+ join89(homedir28(), ".local", "share", "fnm", "node-versions")
312715
+ ];
312716
+ for (const vdir of versionDirs) {
312717
+ if (!existsSync72(vdir)) continue;
312718
+ try {
312719
+ for (const ver of readdirSync22(vdir)) {
312720
+ for (const prefix of ["lib/node_modules/aiwg", "installation/lib/node_modules/aiwg"]) {
312721
+ const cand = join89(vdir, ver, prefix);
312722
+ if (existsSync72(join89(cand, "package.json"))) {
312723
+ _cachedAiwgRoot = cand;
312724
+ return cand;
312725
+ }
312726
+ }
312727
+ }
312728
+ } catch {
312729
+ }
312730
+ }
312731
+ try {
312732
+ const whichAiwg = execSync53("which aiwg 2>/dev/null || where aiwg 2>nul", {
312733
+ encoding: "utf-8",
312734
+ timeout: 3e3,
312735
+ stdio: ["pipe", "pipe", "pipe"]
312736
+ }).trim().split("\n")[0];
312737
+ if (whichAiwg) {
312738
+ let cur = whichAiwg;
312739
+ for (let i2 = 0; i2 < 8; i2++) {
312740
+ cur = join89(cur, "..");
312741
+ const pj = join89(cur, "package.json");
312742
+ if (existsSync72(pj)) {
312743
+ try {
312744
+ const pkg = JSON.parse(readFileSync56(pj, "utf-8"));
312745
+ if (pkg.name === "aiwg") {
312746
+ _cachedAiwgRoot = cur;
312747
+ return cur;
312748
+ }
312749
+ } catch {
312750
+ }
312751
+ }
312752
+ }
312753
+ }
312754
+ } catch {
312755
+ }
312756
+ _cachedAiwgRoot = null;
312757
+ return null;
312758
+ }
312759
+ function listAiwgFrameworks() {
312760
+ if (_cachedFrameworks) return _cachedFrameworks;
312761
+ const root = resolveAiwgRoot();
312762
+ if (!root) {
312763
+ _cachedFrameworks = [];
312764
+ return _cachedFrameworks;
312765
+ }
312766
+ const frameworksDir = join89(root, "agentic", "code", "frameworks");
312767
+ if (!existsSync72(frameworksDir)) {
312768
+ _cachedFrameworks = [];
312769
+ return _cachedFrameworks;
312770
+ }
312771
+ const out = [];
312772
+ for (const name10 of readdirSync22(frameworksDir)) {
312773
+ const p2 = join89(frameworksDir, name10);
312774
+ try {
312775
+ const st = statSync21(p2);
312776
+ if (!st.isDirectory()) continue;
312777
+ const agg = aggregateDir(p2);
312778
+ out.push({
312779
+ name: name10,
312780
+ description: readFirstLineDescription(p2),
312781
+ path: p2,
312782
+ file_count: agg.files,
312783
+ size_bytes: agg.bytes,
312784
+ token_estimate: Math.round(agg.mdChars / 4),
312785
+ skills_count: agg.skills,
312786
+ agents_count: agg.agents,
312787
+ commands_count: agg.commands
312788
+ });
312789
+ } catch {
312790
+ }
312791
+ }
312792
+ _cachedFrameworks = out.sort((a2, b) => a2.name.localeCompare(b.name));
312793
+ return _cachedFrameworks;
312794
+ }
312795
+ function aggregateDir(dir, depth = 0) {
312796
+ const out = { files: 0, bytes: 0, mdChars: 0, skills: 0, agents: 0, commands: 0 };
312797
+ if (depth > 8) return out;
312798
+ try {
312799
+ for (const e2 of readdirSync22(dir, { withFileTypes: true })) {
312800
+ if (e2.name.startsWith(".") || e2.name === "node_modules") continue;
312801
+ const p2 = join89(dir, e2.name);
312802
+ if (e2.isDirectory()) {
312803
+ const sub = aggregateDir(p2, depth + 1);
312804
+ out.files += sub.files;
312805
+ out.bytes += sub.bytes;
312806
+ out.mdChars += sub.mdChars;
312807
+ out.skills += sub.skills;
312808
+ out.agents += sub.agents;
312809
+ out.commands += sub.commands;
312810
+ } else if (e2.isFile()) {
312811
+ try {
312812
+ const st = statSync21(p2);
312813
+ out.files++;
312814
+ out.bytes += st.size;
312815
+ if (e2.name.endsWith(".md")) {
312816
+ out.mdChars += st.size;
312817
+ if (e2.name === "SKILL.md") out.skills++;
312818
+ else if (e2.name === "AGENT.md" || p2.includes("/agents/")) out.agents++;
312819
+ else if (p2.includes("/commands/")) out.commands++;
312820
+ }
312821
+ } catch {
312822
+ }
312823
+ }
312824
+ }
312825
+ } catch {
312826
+ }
312827
+ return out;
312828
+ }
312829
+ function readFirstLineDescription(dir) {
312830
+ for (const candidate of ["README.md", "SKILL.md", "INDEX.md"]) {
312831
+ const p2 = join89(dir, candidate);
312832
+ if (!existsSync72(p2)) continue;
312833
+ try {
312834
+ const txt = readFileSync56(p2, "utf-8");
312835
+ const descMatch = txt.match(/^description:\s*(.+)$/m);
312836
+ if (descMatch) return descMatch[1].trim().slice(0, 200);
312837
+ for (const line of txt.split("\n")) {
312838
+ const t2 = line.trim();
312839
+ if (!t2 || t2.startsWith("#") || t2.startsWith("---") || t2.startsWith("```")) continue;
312840
+ return t2.slice(0, 200);
312841
+ }
312842
+ } catch {
312843
+ }
312844
+ }
312845
+ return "";
312846
+ }
312847
+ function listAiwgItems() {
312848
+ if (_cachedItems) return _cachedItems;
312849
+ const root = resolveAiwgRoot();
312850
+ if (!root) {
312851
+ _cachedItems = [];
312852
+ return _cachedItems;
312853
+ }
312854
+ const out = [];
312855
+ const walkRoots = [
312856
+ join89(root, "agentic", "code", "frameworks"),
312857
+ join89(root, "agentic", "code", "addons"),
312858
+ join89(root, "plugins")
312859
+ ];
312860
+ for (const wr of walkRoots) {
312861
+ if (!existsSync72(wr)) continue;
312862
+ walkForItems(wr, out, 0);
312863
+ }
312864
+ _cachedItems = out;
312865
+ return _cachedItems;
312866
+ }
312867
+ function walkForItems(dir, out, depth) {
312868
+ if (depth > 10) return;
312869
+ try {
312870
+ for (const e2 of readdirSync22(dir, { withFileTypes: true })) {
312871
+ if (e2.name.startsWith(".") || e2.name === "node_modules") continue;
312872
+ const p2 = join89(dir, e2.name);
312873
+ if (e2.isDirectory()) {
312874
+ walkForItems(p2, out, depth + 1);
312875
+ } else if (e2.isFile() && e2.name.endsWith(".md")) {
312876
+ const item = parseItem(p2);
312877
+ if (item) out.push(item);
312878
+ }
312879
+ }
312880
+ } catch {
312881
+ }
312882
+ }
312883
+ function parseItem(p2) {
312884
+ try {
312885
+ const raw = readFileSync56(p2, "utf-8");
312886
+ const header = raw.slice(0, 3e3);
312887
+ const nameMatch = header.match(/^name:\s*(.+)$/m);
312888
+ const descMatch = header.match(/^description:\s*(.+)$/m);
312889
+ const triggersRaw = header.match(/^triggers:\s*\[(.+?)\]/m)?.[1] || header.match(/^triggers:\s*\n((?:\s+-\s+.+\n?)+)/m)?.[1];
312890
+ const triggers = [];
312891
+ if (triggersRaw) {
312892
+ const parts = triggersRaw.split(/[,\n]/).map((s2) => s2.replace(/^\s*-?\s*/, "").replace(/^["']|["']$/g, "").trim()).filter(Boolean);
312893
+ triggers.push(...parts);
312894
+ }
312895
+ const name10 = nameMatch?.[1]?.trim() ?? p2.split("/").slice(-2, -1)[0] ?? "(unnamed)";
312896
+ let kind = "skill";
312897
+ if (p2.includes("/agents/") || p2.endsWith("AGENT.md")) kind = "agent";
312898
+ else if (p2.includes("/commands/")) kind = "command";
312899
+ else if (!p2.endsWith("SKILL.md") && !p2.includes("/skills/")) return null;
312900
+ const source = deriveSource(p2);
312901
+ return {
312902
+ name: name10,
312903
+ description: descMatch?.[1]?.trim().slice(0, 300) ?? "",
312904
+ triggers,
312905
+ source,
312906
+ path: p2,
312907
+ kind
312908
+ };
312909
+ } catch {
312910
+ return null;
312911
+ }
312912
+ }
312913
+ function deriveSource(p2) {
312914
+ const segments = p2.split("/");
312915
+ for (const key of ["frameworks", "addons", "plugins"]) {
312916
+ const idx = segments.indexOf(key);
312917
+ if (idx >= 0 && idx + 1 < segments.length) return segments[idx + 1];
312918
+ }
312919
+ return "aiwg";
312920
+ }
312921
+ function loadAiwgItemContent(path5, maxBytes = 2e4) {
312922
+ try {
312923
+ if (!existsSync72(path5)) return null;
312924
+ const raw = readFileSync56(path5, "utf-8");
312925
+ return raw.length > maxBytes ? raw.slice(0, maxBytes) + "\n\n...(truncated for context budget)" : raw;
312926
+ } catch {
312927
+ return null;
312928
+ }
312929
+ }
312930
+ function listAiwgAddons() {
312931
+ if (_cachedAddons) return _cachedAddons;
312932
+ const root = resolveAiwgRoot();
312933
+ if (!root) {
312934
+ _cachedAddons = [];
312935
+ return _cachedAddons;
312936
+ }
312937
+ const addonsDir = join89(root, "agentic", "code", "addons");
312938
+ if (!existsSync72(addonsDir)) {
312939
+ _cachedAddons = [];
312940
+ return _cachedAddons;
312941
+ }
312942
+ const out = [];
312943
+ for (const name10 of readdirSync22(addonsDir)) {
312944
+ const p2 = join89(addonsDir, name10);
312945
+ try {
312946
+ const st = statSync21(p2);
312947
+ if (!st.isDirectory()) continue;
312948
+ const agg = aggregateDir(p2);
312949
+ out.push({
312950
+ name: name10,
312951
+ path: p2,
312952
+ file_count: agg.files,
312953
+ size_bytes: agg.bytes,
312954
+ description: readFirstLineDescription(p2)
312955
+ });
312956
+ } catch {
312957
+ }
312958
+ }
312959
+ _cachedAddons = out.sort((a2, b) => a2.name.localeCompare(b.name));
312960
+ return _cachedAddons;
312961
+ }
312962
+ function detectModelTier(modelName, explicitTier) {
312963
+ if (explicitTier === "small" || explicitTier === "medium" || explicitTier === "large" || explicitTier === "xlarge") {
312964
+ return explicitTier;
312965
+ }
312966
+ if (!modelName) return "medium";
312967
+ const m2 = modelName.toLowerCase();
312968
+ const match = m2.match(/(\d+)\s*[bB]/);
312969
+ if (match) {
312970
+ const b = parseInt(match[1], 10);
312971
+ if (b <= 7) return "small";
312972
+ if (b <= 29) return "medium";
312973
+ if (b <= 70) return "large";
312974
+ return "xlarge";
312975
+ }
312976
+ if (/1\.5b|3b|4b|phi-|tiny/.test(m2)) return "small";
312977
+ if (/7b|8b|9b|13b|mistral-/.test(m2)) return "medium";
312978
+ if (/27b|30b|32b|34b|gemma2|qwen2\.5-32/.test(m2)) return "large";
312979
+ if (/70b|72b|mixtral-8x|100b|122b|123b|opus|405b|deepseek-v|claude/.test(m2)) return "xlarge";
312980
+ return "medium";
312981
+ }
312982
+ function budgetForTier(tier) {
312983
+ switch (tier) {
312984
+ case "small":
312985
+ return { indexTokens: 2e3, metadataTokens: 0, contentTokens: 0, frameworkTokens: 0 };
312986
+ case "medium":
312987
+ return { indexTokens: 4e3, metadataTokens: 8e3, contentTokens: 2e4, frameworkTokens: 0 };
312988
+ case "large":
312989
+ return { indexTokens: 6e3, metadataTokens: 16e3, contentTokens: 5e4, frameworkTokens: 1e5 };
312990
+ case "xlarge":
312991
+ return { indexTokens: 1e4, metadataTokens: 3e4, contentTokens: 1e5, frameworkTokens: 3e5 };
312992
+ }
312993
+ }
312994
+ async function tryRouteAiwg(ctx3) {
312995
+ const { pathname, method } = ctx3;
312996
+ if (pathname === "/v1/aiwg" && method === "GET") return handleAiwgRoot(ctx3);
312997
+ if (pathname === "/v1/aiwg/frameworks" && method === "GET") return handleListFrameworks(ctx3);
312998
+ const fwMatch = pathname.match(/^\/v1\/aiwg\/frameworks\/([^/]+)$/);
312999
+ if (fwMatch && method === "GET") return handleGetFramework(ctx3, decodeURIComponent(fwMatch[1]));
313000
+ const fwContentMatch = pathname.match(/^\/v1\/aiwg\/frameworks\/([^/]+)\/content$/);
313001
+ if (fwContentMatch && method === "GET") return handleGetFrameworkContent(ctx3, decodeURIComponent(fwContentMatch[1]));
313002
+ if (pathname === "/v1/aiwg/skills" && method === "GET") return handleListSkillsAiwg(ctx3);
313003
+ const skMatch = pathname.match(/^\/v1\/aiwg\/skills\/([^/]+)$/);
313004
+ if (skMatch && method === "GET") return handleGetSkillAiwg(ctx3, decodeURIComponent(skMatch[1]));
313005
+ if (pathname === "/v1/aiwg/agents" && method === "GET") return handleListAgentsAiwg(ctx3);
313006
+ const agMatch = pathname.match(/^\/v1\/aiwg\/agents\/([^/]+)$/);
313007
+ if (agMatch && method === "GET") return handleGetAgentAiwg(ctx3, decodeURIComponent(agMatch[1]));
313008
+ if (pathname === "/v1/aiwg/addons" && method === "GET") return handleListAddonsAiwg(ctx3);
313009
+ if (pathname === "/v1/aiwg/use" && method === "POST") return handleAiwgUse(ctx3);
313010
+ if (pathname === "/v1/aiwg/expand" && method === "POST") return handleAiwgExpand(ctx3);
313011
+ return false;
313012
+ }
313013
+ async function handleAiwgRoot(ctx3) {
313014
+ const { req: req2, res } = ctx3;
313015
+ const root = resolveAiwgRoot();
313016
+ const frameworks = listAiwgFrameworks();
313017
+ const items = listAiwgItems();
313018
+ const addons = listAiwgAddons();
313019
+ const totalSize = frameworks.reduce((s2, f2) => s2 + f2.size_bytes, 0) + addons.reduce((s2, a2) => s2 + a2.size_bytes, 0);
313020
+ const payload = {
313021
+ installed: root !== null,
313022
+ root,
313023
+ counts: {
313024
+ frameworks: frameworks.length,
313025
+ addons: addons.length,
313026
+ skills: items.filter((i2) => i2.kind === "skill").length,
313027
+ agents: items.filter((i2) => i2.kind === "agent").length,
313028
+ commands: items.filter((i2) => i2.kind === "command").length
313029
+ },
313030
+ total_size_bytes: totalSize,
313031
+ total_size_mb: Math.round(totalSize / (1024 * 1024) * 10) / 10,
313032
+ cascade_tiers: {
313033
+ "0_index": "Names + triggers + 1-line descriptions. Always safe (~2K tokens).",
313034
+ "1_metadata": "Per-item frontmatter + first section (~1-2K per item).",
313035
+ "2_content": "Per-item full body (~2-10K per item).",
313036
+ "3_framework": "Whole framework bundle (100K+ tokens, large models only)."
313037
+ },
313038
+ endpoints: {
313039
+ frameworks: "/v1/aiwg/frameworks",
313040
+ framework_detail: "/v1/aiwg/frameworks/{name}",
313041
+ framework_content: "/v1/aiwg/frameworks/{name}/content?tier=2|3",
313042
+ skills: "/v1/aiwg/skills",
313043
+ skill_detail: "/v1/aiwg/skills/{name}",
313044
+ agents: "/v1/aiwg/agents",
313045
+ agent_detail: "/v1/aiwg/agents/{name}",
313046
+ addons: "/v1/aiwg/addons",
313047
+ use: "POST /v1/aiwg/use \u2014 `aiwg use all` equivalent with cascade sizing",
313048
+ expand: "POST /v1/aiwg/expand \u2014 sub-agent unpack by trigger or name"
313049
+ },
313050
+ warning_context_hog: "AIWG is ~" + Math.round(totalSize / (1024 * 1024)) + " MB of markdown (~2M tokens raw). NEVER request tier=3 on a small model. The /v1/aiwg/use endpoint auto-sizes by model tier."
313051
+ };
313052
+ const etag = computeEtag(payload);
313053
+ if (checkNotModified(req2, res, etag)) return true;
313054
+ sendJson(res, 200, payload, { etag, cacheControl: "private, max-age=60" });
313055
+ return true;
313056
+ }
313057
+ async function handleListFrameworks(ctx3) {
313058
+ const { req: req2, res, url } = ctx3;
313059
+ const list = listAiwgFrameworks();
313060
+ const page2 = parsePagination(url.searchParams);
313061
+ const env2 = paginated(list, page2);
313062
+ const etag = computeEtag(env2);
313063
+ if (checkNotModified(req2, res, etag)) return true;
313064
+ sendJson(res, 200, env2, { etag, cacheControl: "private, max-age=60" });
313065
+ return true;
313066
+ }
313067
+ async function handleGetFramework(ctx3, name10) {
313068
+ const { req: req2, res, requestId } = ctx3;
313069
+ const fw = listAiwgFrameworks().find((f2) => f2.name === name10);
313070
+ if (!fw) {
313071
+ sendProblem(res, problemDetails({
313072
+ type: "https://openagents.nexus/problems/not-found",
313073
+ status: 404,
313074
+ title: "Framework not found",
313075
+ detail: `No AIWG framework named '${name10}'`,
313076
+ instance: requestId
313077
+ }));
313078
+ return true;
313079
+ }
313080
+ const items = listAiwgItems().filter((i2) => i2.source === name10);
313081
+ const payload = {
313082
+ ...fw,
313083
+ items: items.map((i2) => ({ name: i2.name, kind: i2.kind, description: i2.description, triggers: i2.triggers })),
313084
+ context_advice: {
313085
+ small_model: "Use /v1/aiwg/expand with a trigger phrase \u2014 don't load the full framework.",
313086
+ medium_model: `Load at most ${Math.max(1, Math.floor(8e3 / 1500))} individual skills via /v1/aiwg/skills/{name}.`,
313087
+ large_model: `Can safely request /v1/aiwg/frameworks/${name10}/content?tier=2 (full metadata).`
313088
+ }
313089
+ };
313090
+ const etag = computeEtag(payload);
313091
+ if (checkNotModified(req2, res, etag)) return true;
313092
+ sendJson(res, 200, payload, { etag, cacheControl: "private, max-age=60" });
313093
+ return true;
313094
+ }
313095
+ async function handleGetFrameworkContent(ctx3, name10) {
313096
+ const { req: req2, res, url, requestId } = ctx3;
313097
+ const fw = listAiwgFrameworks().find((f2) => f2.name === name10);
313098
+ if (!fw) {
313099
+ sendProblem(res, problemDetails({
313100
+ type: "https://openagents.nexus/problems/not-found",
313101
+ status: 404,
313102
+ title: "Framework not found",
313103
+ instance: requestId
313104
+ }));
313105
+ return true;
313106
+ }
313107
+ const tier = url.searchParams.get("tier") || "2";
313108
+ const modelName = url.searchParams.get("model") || void 0;
313109
+ const modelTier = detectModelTier(modelName, tier === "2" || tier === "3" ? void 0 : tier);
313110
+ const budget = budgetForTier(modelTier);
313111
+ if (tier === "3" && modelTier === "small" && url.searchParams.get("override") !== "iknowwhatimdoing") {
313112
+ sendProblem(res, problemDetails({
313113
+ type: "https://openagents.nexus/problems/context-overflow",
313114
+ status: 413,
313115
+ title: "Framework bundle too large for detected model tier",
313116
+ detail: `Framework '${name10}' is ~${fw.token_estimate} tokens. Small-model budget is ${budget.frameworkTokens} tokens. Use /v1/aiwg/expand with a trigger instead, or add ?override=iknowwhatimdoing to force.`,
313117
+ instance: requestId,
313118
+ extensions: {
313119
+ framework_tokens: fw.token_estimate,
313120
+ budget_tokens: budget.frameworkTokens,
313121
+ detected_tier: modelTier
313122
+ }
313123
+ }));
313124
+ return true;
313125
+ }
313126
+ const items = listAiwgItems().filter((i2) => i2.source === name10);
313127
+ const maxPerItem = tier === "3" ? 8e3 : 3e3;
313128
+ const withContent = items.slice(0, tier === "3" ? 100 : 30).map((i2) => ({
313129
+ name: i2.name,
313130
+ kind: i2.kind,
313131
+ description: i2.description,
313132
+ triggers: i2.triggers,
313133
+ content: tier === "3" ? loadAiwgItemContent(i2.path, maxPerItem) : void 0
313134
+ }));
313135
+ sendJson(res, 200, {
313136
+ framework: fw,
313137
+ tier,
313138
+ model_tier: modelTier,
313139
+ budget,
313140
+ items: withContent,
313141
+ truncation_note: "Each item capped at " + maxPerItem + " bytes. Use /v1/aiwg/expand for a specific item at full fidelity."
313142
+ }, { cacheControl: "private, max-age=60" });
313143
+ return true;
313144
+ }
313145
+ async function handleListSkillsAiwg(ctx3) {
313146
+ const { req: req2, res, url } = ctx3;
313147
+ const list = listAiwgItems().filter((i2) => i2.kind === "skill");
313148
+ const source = url.searchParams.get("source");
313149
+ const filtered = source ? list.filter((i2) => i2.source === source) : list;
313150
+ const page2 = parsePagination(url.searchParams);
313151
+ const env2 = paginated(filtered.map((i2) => ({
313152
+ name: i2.name,
313153
+ description: i2.description,
313154
+ triggers: i2.triggers,
313155
+ source: i2.source,
313156
+ path: i2.path
313157
+ })), page2);
313158
+ const etag = computeEtag(env2);
313159
+ if (checkNotModified(req2, res, etag)) return true;
313160
+ sendJson(res, 200, env2, { etag, cacheControl: "private, max-age=60" });
313161
+ return true;
313162
+ }
313163
+ async function handleGetSkillAiwg(ctx3, name10) {
313164
+ const { req: req2, res, url, requestId } = ctx3;
313165
+ const items = listAiwgItems().filter((i2) => i2.kind === "skill");
313166
+ const skill = items.find((i2) => i2.name === name10);
313167
+ if (!skill) {
313168
+ sendProblem(res, problemDetails({
313169
+ type: "https://openagents.nexus/problems/not-found",
313170
+ status: 404,
313171
+ title: "Skill not found",
313172
+ detail: `No AIWG skill named '${name10}'`,
313173
+ instance: requestId
313174
+ }));
313175
+ return true;
313176
+ }
313177
+ const includeContent = url.searchParams.get("content") !== "false";
313178
+ const payload = {
313179
+ ...skill,
313180
+ content: includeContent ? loadAiwgItemContent(skill.path) : void 0
313181
+ };
313182
+ const etag = computeEtag(payload);
313183
+ if (checkNotModified(req2, res, etag)) return true;
313184
+ sendJson(res, 200, payload, { etag, cacheControl: "private, max-age=60" });
313185
+ return true;
313186
+ }
313187
+ async function handleListAgentsAiwg(ctx3) {
313188
+ const { req: req2, res, url } = ctx3;
313189
+ const list = listAiwgItems().filter((i2) => i2.kind === "agent");
313190
+ const source = url.searchParams.get("source");
313191
+ const filtered = source ? list.filter((i2) => i2.source === source) : list;
313192
+ const page2 = parsePagination(url.searchParams);
313193
+ const env2 = paginated(filtered.map((i2) => ({
313194
+ name: i2.name,
313195
+ description: i2.description,
313196
+ triggers: i2.triggers,
313197
+ source: i2.source
313198
+ })), page2);
313199
+ const etag = computeEtag(env2);
313200
+ if (checkNotModified(req2, res, etag)) return true;
313201
+ sendJson(res, 200, env2, { etag, cacheControl: "private, max-age=60" });
313202
+ return true;
313203
+ }
313204
+ async function handleGetAgentAiwg(ctx3, name10) {
313205
+ const { req: req2, res, requestId } = ctx3;
313206
+ const ag = listAiwgItems().filter((i2) => i2.kind === "agent").find((i2) => i2.name === name10);
313207
+ if (!ag) {
313208
+ sendProblem(res, problemDetails({
313209
+ type: "https://openagents.nexus/problems/not-found",
313210
+ status: 404,
313211
+ title: "Agent not found",
313212
+ instance: requestId
313213
+ }));
313214
+ return true;
313215
+ }
313216
+ const payload = { ...ag, content: loadAiwgItemContent(ag.path) };
313217
+ const etag = computeEtag(payload);
313218
+ if (checkNotModified(req2, res, etag)) return true;
313219
+ sendJson(res, 200, payload, { etag, cacheControl: "private, max-age=60" });
313220
+ return true;
313221
+ }
313222
+ async function handleListAddonsAiwg(ctx3) {
313223
+ const { req: req2, res, url } = ctx3;
313224
+ const list = listAiwgAddons();
313225
+ const page2 = parsePagination(url.searchParams);
313226
+ const env2 = paginated(list, page2);
313227
+ const etag = computeEtag(env2);
313228
+ if (checkNotModified(req2, res, etag)) return true;
313229
+ sendJson(res, 200, env2, { etag, cacheControl: "private, max-age=60" });
313230
+ return true;
313231
+ }
313232
+ async function handleAiwgUse(ctx3) {
313233
+ const { req: req2, res, requestId, user } = ctx3;
313234
+ try {
313235
+ const body = await parseJsonBodyStrict(req2).catch(() => ({}));
313236
+ const scope = body?.scope || "all";
313237
+ const modelName = body?.model;
313238
+ const tier = detectModelTier(modelName, body?.tier);
313239
+ const budget = budgetForTier(tier);
313240
+ const frameworks = listAiwgFrameworks();
313241
+ const items = listAiwgItems();
313242
+ const addons = listAiwgAddons();
313243
+ let selectedFrameworks = frameworks;
313244
+ let selectedItems = items;
313245
+ if (scope !== "all") {
313246
+ const scopes = Array.isArray(scope) ? scope : [scope];
313247
+ selectedFrameworks = frameworks.filter((f2) => scopes.includes(f2.name));
313248
+ selectedItems = items.filter((i2) => scopes.includes(i2.source));
313249
+ }
313250
+ const bundle = {
313251
+ scope,
313252
+ requested_model: modelName,
313253
+ detected_tier: tier,
313254
+ budget,
313255
+ frameworks: selectedFrameworks.map((f2) => ({
313256
+ name: f2.name,
313257
+ description: f2.description,
313258
+ file_count: f2.file_count,
313259
+ skills_count: f2.skills_count,
313260
+ agents_count: f2.agents_count,
313261
+ token_estimate: f2.token_estimate
313262
+ })),
313263
+ addons: tier === "small" ? [] : addons.map((a2) => ({ name: a2.name, description: a2.description })),
313264
+ index: buildIndex(selectedItems, tier)
313265
+ };
313266
+ if (tier === "medium" || tier === "large" || tier === "xlarge") {
313267
+ bundle.metadata = buildMetadata(selectedItems, tier);
313268
+ }
313269
+ if (tier === "large" || tier === "xlarge") {
313270
+ bundle.content = buildContent(selectedItems, tier);
313271
+ }
313272
+ const totalTokens = Math.round(JSON.stringify(bundle).length / 4);
313273
+ bundle.bundle_tokens = totalTokens;
313274
+ bundle.budget_ok = totalTokens <= (budget.frameworkTokens || budget.contentTokens || budget.metadataTokens || budget.indexTokens) * 1.5;
313275
+ bundle.cascade_advice = {
313276
+ if_small_model: "You received the Tier 0 index only. When a task requires a specific skill, POST /v1/aiwg/expand with {trigger: '<phrase>'} to fetch one skill at a time.",
313277
+ if_medium_model: "You received Tier 1 metadata. Individual skills are listed with their front-matter. Fetch full content via /v1/aiwg/skills/{name}.",
313278
+ if_large_model: "You received Tier 2 content for top skills. Full framework dumps are available at /v1/aiwg/frameworks/{name}/content?tier=3.",
313279
+ always: "NEVER inline more than budget.frameworkTokens into a model prompt. Use the expand endpoint for targeted loads."
313280
+ };
313281
+ publishEvent("aims.decision_recorded", { kind: "aiwg_activation", scope, tier, tokens: totalTokens }, {
313282
+ subject: user ?? "anonymous",
313283
+ aimsControl: "A.7.3"
313284
+ // data for AI systems (knowledge injection record)
313285
+ });
313286
+ sendJson(res, 200, bundle, { cacheControl: "private, max-age=30" });
313287
+ return true;
313288
+ } catch (err) {
313289
+ sendProblem(res, problemDetails({
313290
+ type: "https://openagents.nexus/problems/internal-error",
313291
+ status: 500,
313292
+ title: "AIWG activation failed",
313293
+ detail: err instanceof Error ? err.message : String(err),
313294
+ instance: requestId
313295
+ }));
313296
+ return true;
313297
+ }
313298
+ }
313299
+ function buildIndex(items, tier) {
313300
+ const maxItems = tier === "small" ? 50 : 500;
313301
+ return items.slice(0, maxItems).map((i2) => ({
313302
+ name: i2.name,
313303
+ kind: i2.kind,
313304
+ source: i2.source,
313305
+ triggers: i2.triggers.slice(0, tier === "small" ? 2 : 5),
313306
+ description: i2.description.slice(0, tier === "small" ? 80 : 200)
313307
+ }));
313308
+ }
313309
+ function buildMetadata(items, tier) {
313310
+ const maxItems = tier === "medium" ? 30 : tier === "large" ? 100 : 300;
313311
+ return items.slice(0, maxItems).map((i2) => {
313312
+ const head = loadAiwgItemContent(i2.path, 1500) || "";
313313
+ return {
313314
+ name: i2.name,
313315
+ kind: i2.kind,
313316
+ source: i2.source,
313317
+ triggers: i2.triggers,
313318
+ description: i2.description,
313319
+ head: head.slice(0, 1500)
313320
+ };
313321
+ });
313322
+ }
313323
+ function buildContent(items, tier) {
313324
+ const perItemCap = tier === "large" ? 4e3 : 1e4;
313325
+ const maxItems = tier === "large" ? 20 : 80;
313326
+ return items.slice(0, maxItems).map((i2) => ({
313327
+ name: i2.name,
313328
+ kind: i2.kind,
313329
+ source: i2.source,
313330
+ content: loadAiwgItemContent(i2.path, perItemCap)
313331
+ }));
313332
+ }
313333
+ async function handleAiwgExpand(ctx3) {
313334
+ const { req: req2, res, requestId, user } = ctx3;
313335
+ try {
313336
+ const body = await parseJsonBodyStrict(req2).catch(() => null);
313337
+ if (!body) {
313338
+ sendProblem(res, problemDetails({
313339
+ type: "https://openagents.nexus/problems/invalid-request",
313340
+ status: 400,
313341
+ title: "Missing body",
313342
+ detail: "POST body must include {trigger?: string, name?: string, limit?: number}",
313343
+ instance: requestId
313344
+ }));
313345
+ return true;
313346
+ }
313347
+ const trigger = typeof body.trigger === "string" ? body.trigger.toLowerCase() : null;
313348
+ const name10 = typeof body.name === "string" ? body.name : null;
313349
+ const limit = typeof body.limit === "number" ? Math.min(body.limit, 10) : 3;
313350
+ if (!trigger && !name10) {
313351
+ sendProblem(res, problemDetails({
313352
+ type: "https://openagents.nexus/problems/invalid-request",
313353
+ status: 400,
313354
+ title: "Missing 'trigger' or 'name'",
313355
+ detail: "Provide either a trigger phrase or an explicit skill/agent name",
313356
+ instance: requestId
313357
+ }));
313358
+ return true;
313359
+ }
313360
+ const items = listAiwgItems();
313361
+ const matches = [];
313362
+ if (name10) {
313363
+ const exact = items.find((i2) => i2.name === name10);
313364
+ if (exact) matches.push(exact);
313365
+ }
313366
+ if (trigger && matches.length < limit) {
313367
+ for (const i2 of items) {
313368
+ if (matches.length >= limit) break;
313369
+ const hay = [i2.name, i2.description, ...i2.triggers].join(" ").toLowerCase();
313370
+ if (hay.includes(trigger)) {
313371
+ if (!matches.some((m2) => m2.path === i2.path)) matches.push(i2);
313372
+ }
313373
+ }
313374
+ }
313375
+ if (matches.length === 0) {
313376
+ sendProblem(res, problemDetails({
313377
+ type: "https://openagents.nexus/problems/not-found",
313378
+ status: 404,
313379
+ title: "No matching AIWG items",
313380
+ detail: `No items matched trigger='${trigger}' name='${name10}'`,
313381
+ instance: requestId
313382
+ }));
313383
+ return true;
313384
+ }
313385
+ const unpacked = matches.map((m2) => ({
313386
+ name: m2.name,
313387
+ kind: m2.kind,
313388
+ source: m2.source,
313389
+ description: m2.description,
313390
+ triggers: m2.triggers,
313391
+ content: loadAiwgItemContent(m2.path, 1e4)
313392
+ }));
313393
+ publishEvent("skill.invoked", { count: unpacked.length, trigger, name: name10 }, {
313394
+ subject: user ?? "anonymous",
313395
+ aimsControl: "A.7.3"
313396
+ });
313397
+ sendJson(res, 200, {
313398
+ query: { trigger, name: name10, limit },
313399
+ matches: unpacked.length,
313400
+ items: unpacked
313401
+ }, { cacheControl: "private, max-age=30" });
313402
+ return true;
313403
+ } catch (err) {
313404
+ sendProblem(res, problemDetails({
313405
+ type: "https://openagents.nexus/problems/internal-error",
313406
+ status: 500,
313407
+ title: "AIWG expand failed",
313408
+ detail: err instanceof Error ? err.message : String(err),
313409
+ instance: requestId
313410
+ }));
313411
+ return true;
313412
+ }
313413
+ }
313414
+ var _cachedAiwgRoot, _cachedFrameworks, _cachedItems, _cachedAddons;
313415
+ var init_aiwg = __esm({
313416
+ "packages/cli/src/api/aiwg.ts"() {
313417
+ "use strict";
313418
+ init_http2();
313419
+ _cachedFrameworks = null;
313420
+ _cachedItems = null;
313421
+ _cachedAddons = null;
313422
+ }
313423
+ });
313424
+
313425
+ // packages/cli/src/api/routes-v1.ts
313426
+ import { existsSync as existsSync73, readFileSync as readFileSync57, readdirSync as readdirSync23, statSync as statSync22 } from "node:fs";
313427
+ import { join as join90, resolve as pathResolve } from "node:path";
313428
+ import { homedir as homedir29 } from "node:os";
312680
313429
  async function tryRouteV1(ctx3) {
312681
313430
  const { pathname, method } = ctx3;
312682
313431
  if (pathname === "/v1/skills" && method === "GET") {
@@ -312776,6 +313525,10 @@ async function tryRouteV1(ctx3) {
312776
313525
  if (pathname === "/v1/aims" || pathname.startsWith("/v1/aims/")) {
312777
313526
  return tryAimsRoute(ctx3);
312778
313527
  }
313528
+ if (pathname === "/v1/aiwg" || pathname.startsWith("/v1/aiwg/")) {
313529
+ const { tryRouteAiwg: tryRouteAiwg2 } = await Promise.resolve().then(() => (init_aiwg(), aiwg_exports));
313530
+ return tryRouteAiwg2(ctx3);
313531
+ }
312779
313532
  return false;
312780
313533
  }
312781
313534
  async function handleListSkills(ctx3) {
@@ -312877,11 +313630,11 @@ async function handleGetSkill(ctx3, name10) {
312877
313630
  async function fallbackDiscoverSkills() {
312878
313631
  return (_root) => {
312879
313632
  const roots = [
312880
- join89(homedir28(), ".local", "share", "ai-writing-guide")
313633
+ join90(homedir29(), ".local", "share", "ai-writing-guide")
312881
313634
  ];
312882
313635
  const out = [];
312883
313636
  for (const root of roots) {
312884
- if (!existsSync72(root)) continue;
313637
+ if (!existsSync73(root)) continue;
312885
313638
  walkForSkills(root, out, 0);
312886
313639
  }
312887
313640
  return out;
@@ -312890,14 +313643,14 @@ async function fallbackDiscoverSkills() {
312890
313643
  function walkForSkills(dir, out, depth) {
312891
313644
  if (depth > 6) return;
312892
313645
  try {
312893
- for (const e2 of readdirSync22(dir, { withFileTypes: true })) {
313646
+ for (const e2 of readdirSync23(dir, { withFileTypes: true })) {
312894
313647
  if (e2.name.startsWith(".") || e2.name === "node_modules") continue;
312895
- const p2 = join89(dir, e2.name);
313648
+ const p2 = join90(dir, e2.name);
312896
313649
  if (e2.isDirectory()) {
312897
313650
  walkForSkills(p2, out, depth + 1);
312898
313651
  } else if (e2.isFile() && e2.name === "SKILL.md") {
312899
313652
  try {
312900
- const content = readFileSync56(p2, "utf-8").slice(0, 2e3);
313653
+ const content = readFileSync57(p2, "utf-8").slice(0, 2e3);
312901
313654
  const nameMatch = content.match(/^name:\s*(.+)$/m);
312902
313655
  const descMatch = content.match(/^description:\s*(.+)$/m);
312903
313656
  out.push({
@@ -313081,7 +313834,7 @@ async function getMemoryStores() {
313081
313834
  try {
313082
313835
  const memMod = await Promise.resolve().then(() => (init_dist7(), dist_exports3));
313083
313836
  const { initDb: initDb2, EpisodeStore: EpisodeStore2, TemporalGraph: TemporalGraph2, FailureStore: FailureStore2 } = memMod;
313084
- const dbPath = join89(homedir28(), ".open-agents", "memory.db");
313837
+ const dbPath = join90(homedir29(), ".open-agents", "memory.db");
313085
313838
  const db = initDb2(dbPath);
313086
313839
  memoryStoresCache = {
313087
313840
  episode: new EpisodeStore2(db),
@@ -313325,7 +314078,7 @@ async function handleFilesRead(ctx3) {
313325
314078
  }));
313326
314079
  return true;
313327
314080
  }
313328
- if (!existsSync72(resolved)) {
314081
+ if (!existsSync73(resolved)) {
313329
314082
  sendProblem(res, problemDetails({
313330
314083
  type: P.notFound,
313331
314084
  status: 404,
@@ -313335,7 +314088,7 @@ async function handleFilesRead(ctx3) {
313335
314088
  }));
313336
314089
  return true;
313337
314090
  }
313338
- const st = statSync21(resolved);
314091
+ const st = statSync22(resolved);
313339
314092
  if (st.isDirectory()) {
313340
314093
  sendProblem(res, problemDetails({
313341
314094
  type: P.invalidRequest,
@@ -313357,7 +314110,7 @@ async function handleFilesRead(ctx3) {
313357
314110
  }));
313358
314111
  return true;
313359
314112
  }
313360
- const content = readFileSync56(resolved, "utf-8");
314113
+ const content = readFileSync57(resolved, "utf-8");
313361
314114
  const offset = typeof body.offset === "number" && body.offset >= 0 ? body.offset : 0;
313362
314115
  const limit = typeof body.limit === "number" && body.limit > 0 ? body.limit : content.length;
313363
314116
  const slice2 = content.slice(offset, offset + limit);
@@ -313513,17 +314266,17 @@ async function handleContextSave(ctx3) {
313513
314266
  const body = await parseJsonBodyStrict(req2).catch(() => null);
313514
314267
  const repoRoot = typeof body?.repo === "string" && body.repo || process.cwd();
313515
314268
  const entry = {
313516
- ts: (/* @__PURE__ */ new Date()).toISOString(),
313517
- taskId: typeof body?.task_id === "string" ? body.task_id : `manual-${Date.now()}`,
313518
- userPrompt: typeof body?.prompt === "string" ? body.prompt : "",
313519
- finalAnswer: typeof body?.summary === "string" ? body.summary : "",
313520
- toolsUsed: Array.isArray(body?.tools_used) ? body.tools_used : [],
313521
- durationMs: typeof body?.duration_ms === "number" ? body.duration_ms : 0,
313522
- model: typeof body?.model === "string" ? body.model : "",
313523
- turns: typeof body?.turns === "number" ? body.turns : 0
314269
+ savedAt: (/* @__PURE__ */ new Date()).toISOString(),
314270
+ task: typeof body?.task === "string" ? body.task : typeof body?.prompt === "string" ? body.prompt : typeof body?.task_id === "string" ? body.task_id : `manual-${Date.now()}`,
314271
+ summary: typeof body?.summary === "string" ? body.summary : "",
314272
+ filesModified: Array.isArray(body?.files_modified) ? body.files_modified : [],
314273
+ toolCalls: typeof body?.tool_calls === "number" ? body.tool_calls : 0,
314274
+ completed: body?.completed !== false,
314275
+ // default true
314276
+ model: typeof body?.model === "string" ? body.model : ""
313524
314277
  };
313525
314278
  saveSessionContext(repoRoot, entry);
313526
- publishEvent("session.created", { task_id: entry.taskId }, {
314279
+ publishEvent("session.created", { task: entry.task.slice(0, 80) }, {
313527
314280
  subject: user ?? "anonymous",
313528
314281
  aimsControl: "A.6.2.6"
313529
314282
  });
@@ -313541,26 +314294,19 @@ async function handleContextSave(ctx3) {
313541
314294
  }
313542
314295
  }
313543
314296
  async function handleContextRestore(ctx3) {
313544
- const { res, url, requestId } = ctx3;
314297
+ const { res, url } = ctx3;
314298
+ const repoRoot = url.searchParams.get("repo") || process.cwd();
314299
+ let prompt = null;
313545
314300
  try {
313546
- const repoRoot = url.searchParams.get("repo") || process.cwd();
313547
- const prompt = buildContextRestorePrompt(repoRoot);
313548
- sendJson(res, 200, {
313549
- repo: repoRoot,
313550
- restore_prompt: prompt ?? null,
313551
- available: !!prompt
313552
- });
313553
- return true;
313554
- } catch (err) {
313555
- sendProblem(res, problemDetails({
313556
- type: P.internalError,
313557
- status: 500,
313558
- title: "Context restore failed",
313559
- detail: err instanceof Error ? err.message : String(err),
313560
- instance: requestId
313561
- }));
313562
- return true;
314301
+ prompt = buildContextRestorePrompt(repoRoot);
314302
+ } catch {
313563
314303
  }
314304
+ sendJson(res, 200, {
314305
+ repo: repoRoot,
314306
+ restore_prompt: prompt,
314307
+ available: !!prompt
314308
+ });
314309
+ return true;
313564
314310
  }
313565
314311
  async function handleContextCompact(ctx3) {
313566
314312
  const { req: req2, res, requestId, user } = ctx3;
@@ -313595,14 +314341,14 @@ async function handleNexusStatus(ctx3) {
313595
314341
  const { res, requestId } = ctx3;
313596
314342
  try {
313597
314343
  const statePaths = [
313598
- join89(process.cwd(), ".oa", "nexus-peer-state.json"),
313599
- join89(homedir28(), ".open-agents", "nexus-peer-cache.json")
314344
+ join90(process.cwd(), ".oa", "nexus-peer-state.json"),
314345
+ join90(homedir29(), ".open-agents", "nexus-peer-cache.json")
313600
314346
  ];
313601
314347
  const states = [];
313602
314348
  for (const p2 of statePaths) {
313603
- if (!existsSync72(p2)) continue;
314349
+ if (!existsSync73(p2)) continue;
313604
314350
  try {
313605
- const raw = readFileSync56(p2, "utf-8");
314351
+ const raw = readFileSync57(p2, "utf-8");
313606
314352
  states.push({ source: p2, data: JSON.parse(raw) });
313607
314353
  } catch (e2) {
313608
314354
  states.push({ source: p2, error: String(e2) });
@@ -313629,8 +314375,8 @@ async function handleNexusStatus(ctx3) {
313629
314375
  }
313630
314376
  function loadAgentName() {
313631
314377
  try {
313632
- const p2 = join89(homedir28(), ".open-agents", "agent-name");
313633
- if (existsSync72(p2)) return readFileSync56(p2, "utf-8").trim();
314378
+ const p2 = join90(homedir29(), ".open-agents", "agent-name");
314379
+ if (existsSync73(p2)) return readFileSync57(p2, "utf-8").trim();
313634
314380
  } catch {
313635
314381
  }
313636
314382
  return null;
@@ -313639,14 +314385,14 @@ async function handleSponsors(ctx3) {
313639
314385
  const { req: req2, res, url, requestId } = ctx3;
313640
314386
  try {
313641
314387
  const candidates = [
313642
- join89(homedir28(), ".open-agents", "sponsor-cache.json"),
313643
- join89(homedir28(), ".open-agents", "sponsors.json")
314388
+ join90(homedir29(), ".open-agents", "sponsor-cache.json"),
314389
+ join90(homedir29(), ".open-agents", "sponsors.json")
313644
314390
  ];
313645
314391
  let sponsors = [];
313646
314392
  for (const p2 of candidates) {
313647
- if (!existsSync72(p2)) continue;
314393
+ if (!existsSync73(p2)) continue;
313648
314394
  try {
313649
- const raw = JSON.parse(readFileSync56(p2, "utf-8"));
314395
+ const raw = JSON.parse(readFileSync57(p2, "utf-8"));
313650
314396
  if (Array.isArray(raw)) {
313651
314397
  sponsors = raw;
313652
314398
  break;
@@ -313715,8 +314461,8 @@ async function handleEvaluate(ctx3) {
313715
314461
  }));
313716
314462
  return true;
313717
314463
  }
313718
- const jobPath = join89(process.cwd(), ".oa", "jobs", `${runId}.json`);
313719
- if (!existsSync72(jobPath)) {
314464
+ const jobPath = join90(process.cwd(), ".oa", "jobs", `${runId}.json`);
314465
+ if (!existsSync73(jobPath)) {
313720
314466
  sendProblem(res, problemDetails({
313721
314467
  type: P.notFound,
313722
314468
  status: 404,
@@ -313726,7 +314472,7 @@ async function handleEvaluate(ctx3) {
313726
314472
  }));
313727
314473
  return true;
313728
314474
  }
313729
- const job = JSON.parse(readFileSync56(jobPath, "utf-8"));
314475
+ const job = JSON.parse(readFileSync57(jobPath, "utf-8"));
313730
314476
  sendJson(res, 200, {
313731
314477
  run_id: runId,
313732
314478
  task: job.task,
@@ -313864,17 +314610,17 @@ async function handleListAgentTypes(ctx3) {
313864
314610
  }
313865
314611
  async function handleListEngines(ctx3) {
313866
314612
  const { res } = ctx3;
313867
- const home = homedir28();
314613
+ const home = homedir29();
313868
314614
  sendJson(res, 200, {
313869
314615
  engines: [
313870
- { name: "dream", state_file: join89(process.cwd(), ".oa", "dreams"), controllable_via: "SSE + slash commands" },
313871
- { name: "bless", state_file: join89(process.cwd(), ".oa", "bless-state.json"), controllable_via: "slash commands" },
313872
- { name: "call", state_file: join89(process.cwd(), ".oa", "call-state.json"), controllable_via: "slash commands" },
313873
- { name: "listen", state_file: join89(process.cwd(), ".oa", "listen-state.json"), controllable_via: "slash commands" },
313874
- { name: "telegram", state_file: join89(home, ".open-agents", "telegram-state.json"), controllable_via: "slash commands" },
313875
- { name: "expose", state_file: join89(process.cwd(), ".oa", "expose-state.json"), controllable_via: "/expose commands" },
313876
- { name: "nexus", state_file: join89(home, ".open-agents", "nexus-peer-cache.json"), controllable_via: "/nexus commands" },
313877
- { name: "ipfs", state_file: join89(process.cwd(), ".oa", "ipfs"), controllable_via: "slash commands" }
314616
+ { name: "dream", state_file: join90(process.cwd(), ".oa", "dreams"), controllable_via: "SSE + slash commands" },
314617
+ { name: "bless", state_file: join90(process.cwd(), ".oa", "bless-state.json"), controllable_via: "slash commands" },
314618
+ { name: "call", state_file: join90(process.cwd(), ".oa", "call-state.json"), controllable_via: "slash commands" },
314619
+ { name: "listen", state_file: join90(process.cwd(), ".oa", "listen-state.json"), controllable_via: "slash commands" },
314620
+ { name: "telegram", state_file: join90(home, ".open-agents", "telegram-state.json"), controllable_via: "slash commands" },
314621
+ { name: "expose", state_file: join90(process.cwd(), ".oa", "expose-state.json"), controllable_via: "/expose commands" },
314622
+ { name: "nexus", state_file: join90(home, ".open-agents", "nexus-peer-cache.json"), controllable_via: "/nexus commands" },
314623
+ { name: "ipfs", state_file: join90(process.cwd(), ".oa", "ipfs"), controllable_via: "slash commands" }
313878
314624
  ],
313879
314625
  note: "Engine instrumentation lives in the running TUI process. Full status + control requires the daemon\u2194TUI bridge (PT-07). See parity audit WO-PARITY-04."
313880
314626
  });
@@ -313957,21 +314703,43 @@ async function tryAimsRoute(ctx3) {
313957
314703
  return false;
313958
314704
  }
313959
314705
  function aimsDir() {
313960
- return join89(homedir28(), ".open-agents", "aims");
314706
+ return join90(homedir29(), ".open-agents", "aims");
313961
314707
  }
313962
314708
  function readAimsFile(name10, fallback) {
313963
314709
  try {
313964
- const p2 = join89(aimsDir(), name10);
313965
- if (existsSync72(p2)) return JSON.parse(readFileSync56(p2, "utf-8"));
314710
+ const p2 = join90(aimsDir(), name10);
314711
+ if (existsSync73(p2)) return JSON.parse(readFileSync57(p2, "utf-8"));
313966
314712
  } catch {
313967
314713
  }
313968
314714
  return fallback;
313969
314715
  }
313970
314716
  function writeAimsFile(name10, data) {
313971
314717
  const dir = aimsDir();
313972
- const { mkdirSync: mkdirSync50, writeFileSync: wf } = __require("node:fs");
314718
+ const { mkdirSync: mkdirSync50, writeFileSync: wf, renameSync: rn } = __require("node:fs");
313973
314719
  mkdirSync50(dir, { recursive: true });
313974
- wf(join89(dir, name10), JSON.stringify(data, null, 2) + "\n", { encoding: "utf-8", mode: 384 });
314720
+ const finalPath = join90(dir, name10);
314721
+ const tmpPath = `${finalPath}.tmp.${process.pid}.${Date.now()}`;
314722
+ try {
314723
+ wf(tmpPath, JSON.stringify(data, null, 2) + "\n", { encoding: "utf-8", mode: 384 });
314724
+ rn(tmpPath, finalPath);
314725
+ } catch {
314726
+ wf(finalPath, JSON.stringify(data, null, 2) + "\n", { encoding: "utf-8", mode: 384 });
314727
+ }
314728
+ }
314729
+ async function withAimsLock(name10, fn) {
314730
+ const prev = _aimsLocks.get(name10) ?? Promise.resolve();
314731
+ let release2;
314732
+ const next = new Promise((res) => {
314733
+ release2 = res;
314734
+ });
314735
+ _aimsLocks.set(name10, prev.then(() => next));
314736
+ await prev;
314737
+ try {
314738
+ return await fn();
314739
+ } finally {
314740
+ release2();
314741
+ if (_aimsLocks.get(name10) === prev.then(() => next)) _aimsLocks.delete(name10);
314742
+ }
313975
314743
  }
313976
314744
  async function handleAimsRoot(ctx3) {
313977
314745
  const { res } = ctx3;
@@ -314155,15 +314923,19 @@ async function handleAimsImpactAssessmentPost(ctx3) {
314155
314923
  }));
314156
314924
  return true;
314157
314925
  }
314158
- const existing = readAimsFile("impact-assessments.json", []);
314159
- const record = {
314160
- id: `IA-${Date.now()}`,
314161
- ts: (/* @__PURE__ */ new Date()).toISOString(),
314162
- author: user ?? "anonymous",
314163
- ...body
314164
- };
314165
- existing.push(record);
314166
- writeAimsFile("impact-assessments.json", existing);
314926
+ const { randomBytes: rb2 } = await import("node:crypto");
314927
+ const record = await withAimsLock("impact-assessments.json", () => {
314928
+ const existing = readAimsFile("impact-assessments.json", []);
314929
+ const rec = {
314930
+ id: `IA-${Date.now()}-${rb2(4).toString("hex")}`,
314931
+ ts: (/* @__PURE__ */ new Date()).toISOString(),
314932
+ author: user ?? "anonymous",
314933
+ ...body
314934
+ };
314935
+ existing.push(rec);
314936
+ writeAimsFile("impact-assessments.json", existing);
314937
+ return rec;
314938
+ });
314167
314939
  publishEvent("aims.decision_recorded", { kind: "impact_assessment", id: record.id }, {
314168
314940
  subject: user ?? "anonymous",
314169
314941
  aimsControl: "A.5"
@@ -314275,12 +315047,12 @@ async function handleAimsSuppliers(ctx3) {
314275
315047
  }
314276
315048
  ];
314277
315049
  const sponsorPaths = [
314278
- join89(homedir28(), ".open-agents", "sponsor-cache.json")
315050
+ join90(homedir29(), ".open-agents", "sponsor-cache.json")
314279
315051
  ];
314280
315052
  for (const p2 of sponsorPaths) {
314281
- if (!existsSync72(p2)) continue;
315053
+ if (!existsSync73(p2)) continue;
314282
315054
  try {
314283
- const raw = JSON.parse(readFileSync56(p2, "utf-8"));
315055
+ const raw = JSON.parse(readFileSync57(p2, "utf-8"));
314284
315056
  const list = Array.isArray(raw) ? raw : raw?.sponsors ?? [];
314285
315057
  for (const s2 of list) {
314286
315058
  suppliers.push({
@@ -314342,16 +315114,20 @@ async function handleAimsIncidentPost(ctx3) {
314342
315114
  }));
314343
315115
  return true;
314344
315116
  }
314345
- const existing = readAimsFile("incidents.json", []);
314346
- const record = {
314347
- id: `INC-${Date.now()}`,
314348
- ts: (/* @__PURE__ */ new Date()).toISOString(),
314349
- raised_by: user ?? "anonymous",
314350
- status: "open",
314351
- ...body
314352
- };
314353
- existing.push(record);
314354
- writeAimsFile("incidents.json", existing);
315117
+ const { randomBytes: randomBytes21 } = await import("node:crypto");
315118
+ const record = await withAimsLock("incidents.json", () => {
315119
+ const existing = readAimsFile("incidents.json", []);
315120
+ const rec = {
315121
+ id: `INC-${Date.now()}-${randomBytes21(4).toString("hex")}`,
315122
+ ts: (/* @__PURE__ */ new Date()).toISOString(),
315123
+ raised_by: user ?? "anonymous",
315124
+ status: "open",
315125
+ ...body
315126
+ };
315127
+ existing.push(rec);
315128
+ writeAimsFile("incidents.json", existing);
315129
+ return rec;
315130
+ });
314355
315131
  publishEvent("incident.raised", { id: record.id, severity: body.severity }, {
314356
315132
  subject: user ?? "anonymous",
314357
315133
  aimsControl: "A.6.2.8"
@@ -314418,7 +315194,7 @@ async function handleAimsConfigHistory(ctx3) {
314418
315194
  return true;
314419
315195
  }
314420
315196
  }
314421
- var PROBLEM_BASE, P, mcpManagerCache, memoryStoresCache, FILE_READ_MAX_BYTES;
315197
+ var PROBLEM_BASE, P, mcpManagerCache, memoryStoresCache, FILE_READ_MAX_BYTES, _aimsLocks;
314422
315198
  var init_routes_v1 = __esm({
314423
315199
  "packages/cli/src/api/routes-v1.ts"() {
314424
315200
  "use strict";
@@ -314441,6 +315217,7 @@ var init_routes_v1 = __esm({
314441
315217
  mcpManagerCache = /* @__PURE__ */ new Map();
314442
315218
  memoryStoresCache = null;
314443
315219
  FILE_READ_MAX_BYTES = 2 * 1024 * 1024;
315220
+ _aimsLocks = /* @__PURE__ */ new Map();
314444
315221
  }
314445
315222
  });
314446
315223
 
@@ -315757,8 +316534,48 @@ function getOpenApiSpec() {
315757
316534
  }
315758
316535
  },
315759
316536
  "/v1/embeddings": { post: { summary: "Generate embeddings", tags: ["Inference"], responses: { 200: { description: "Embedding vectors" }, ...ErrorResponses } } },
315760
- "/v1/chat": { post: { summary: "Stateful chat session with full tool access", tags: ["Chat"], responses: { 200: { description: "Chat response" }, ...ErrorResponses } } },
316537
+ "/v1/chat": {
316538
+ post: {
316539
+ summary: "Stateful chat session with full agent tool access (OpenAI-compatible response shape)",
316540
+ description: "Drop-in replacement for OpenAI /v1/chat/completions and Ollama /api/chat. By default the request runs the FULL OA agent stack (tools, multi-agent, memory, skills) under the hood and returns an OpenAI chat.completion shape on success. Set tools=false to bypass the agent and forward straight to the configured backend (fast path).",
316541
+ tags: ["Chat"],
316542
+ requestBody: { required: true, content: { "application/json": { schema: { type: "object", required: ["message"], properties: { message: { type: "string" }, model: { type: "string" }, session_id: { type: "string" }, stream: { type: "boolean", default: true }, tools: { type: "boolean", default: true, description: "true (default) = full agent. false = direct backend chat." } } } } } },
316543
+ responses: {
316544
+ 200: {
316545
+ description: "OpenAI chat.completion shape \u2014 {id, object, created, model, choices: [{index, message, finish_reason}], usage}. finish_reason is 'stop' on success or 'error' if the agent produced no usable content (the error text is in choices[0].message.content)."
316546
+ },
316547
+ ...ErrorResponses
316548
+ }
316549
+ }
316550
+ },
315761
316551
  "/v1/chat/sessions": { get: { summary: "List active chat sessions", tags: ["Chat"], responses: { 200: { description: "Session list" } } } },
316552
+ // ───── AIWG cascade ─────
316553
+ "/v1/aiwg": { get: { summary: "AIWG installation root + control map", tags: ["AIWG"], responses: { 200: { description: "AIWG installation summary" } } } },
316554
+ "/v1/aiwg/frameworks": { get: { summary: "List AIWG frameworks (paginated)", tags: ["AIWG"], responses: { 200: { description: "Framework list" } } } },
316555
+ "/v1/aiwg/frameworks/{name}": { get: { summary: "Framework details + items", tags: ["AIWG"], responses: { 200: { description: "Framework details" }, 404: { description: "Not found" } } } },
316556
+ "/v1/aiwg/frameworks/{name}/content": { get: { summary: "Framework content (cascade-tier-aware)", tags: ["AIWG"], responses: { 200: { description: "Tier-appropriate content bundle" }, 413: { description: "Bundle too large for detected model tier" } } } },
316557
+ "/v1/aiwg/skills": { get: { summary: "List AIWG skills (paginated)", tags: ["AIWG"], responses: { 200: { description: "Skill list" } } } },
316558
+ "/v1/aiwg/skills/{name}": { get: { summary: "Skill content", tags: ["AIWG"], responses: { 200: { description: "Skill" }, 404: { description: "Not found" } } } },
316559
+ "/v1/aiwg/agents": { get: { summary: "List AIWG agents", tags: ["AIWG"], responses: { 200: { description: "Agent list" } } } },
316560
+ "/v1/aiwg/agents/{name}": { get: { summary: "Agent definition", tags: ["AIWG"], responses: { 200: { description: "Agent" }, 404: { description: "Not found" } } } },
316561
+ "/v1/aiwg/addons": { get: { summary: "List AIWG addons", tags: ["AIWG"], responses: { 200: { description: "Addon list" } } } },
316562
+ "/v1/aiwg/use": {
316563
+ post: {
316564
+ summary: "AIWG cascade activation bundle (aiwg use all equivalent, model-tier sized)",
316565
+ description: "Returns a tier-appropriate AIWG activation bundle: small models get just the index, medium get metadata, large get content, xlarge get full framework dumps. NEVER overflows context \u2014 the response auto-sizes to the detected model tier.",
316566
+ tags: ["AIWG"],
316567
+ requestBody: { required: true, content: { "application/json": { schema: { type: "object", properties: { scope: { type: "string", default: "all" }, model: { type: "string" }, tier: { type: "string", enum: ["small", "medium", "large", "xlarge"] } } } } } },
316568
+ responses: { 200: { description: "Tier-sized activation bundle" } }
316569
+ }
316570
+ },
316571
+ "/v1/aiwg/expand": {
316572
+ post: {
316573
+ summary: "Sub-agent unpack a specific AIWG skill or agent on demand",
316574
+ tags: ["AIWG"],
316575
+ requestBody: { required: true, content: { "application/json": { schema: { type: "object", properties: { trigger: { type: "string" }, name: { type: "string" }, limit: { type: "integer", default: 3 } } } } } },
316576
+ responses: { 200: { description: "Matching items" }, 400: { description: "Missing trigger or name" }, 404: { description: "No matches" } }
316577
+ }
316578
+ },
315762
316579
  "/v1/run": { post: { summary: "Submit agentic task", tags: ["Agentic"], responses: { 202: { description: "Task accepted" }, ...ErrorResponses } } },
315763
316580
  "/v1/runs": {
315764
316581
  get: {
@@ -315971,31 +316788,31 @@ var init_auth_oidc = __esm({
315971
316788
 
315972
316789
  // packages/cli/src/api/chat-session.ts
315973
316790
  import { randomUUID as randomUUID10 } from "node:crypto";
315974
- import { existsSync as existsSync73, readFileSync as readFileSync57, readdirSync as readdirSync23 } from "node:fs";
315975
- import { join as join90 } from "node:path";
316791
+ import { existsSync as existsSync74, readFileSync as readFileSync58, readdirSync as readdirSync24 } from "node:fs";
316792
+ import { join as join91 } from "node:path";
315976
316793
  function buildSystemPrompt(cwd4) {
315977
316794
  const parts = [];
315978
316795
  parts.push(
315979
316796
  "You are Open Agent (OA), an AI coding assistant running locally via Ollama. You have access to the user's workspace and can discuss code, files, and projects. Be helpful, concise, and technically precise. When asked about files or code, describe what you know from the conversation context."
315980
316797
  );
315981
316798
  parts.push(`\\nEnvironment: ${process.platform}, Node ${process.version}, CWD: ${cwd4}`);
315982
- const diaryPath = join90(cwd4, ".oa", "context", "session-diary.md");
315983
- if (existsSync73(diaryPath)) {
316799
+ const diaryPath = join91(cwd4, ".oa", "context", "session-diary.md");
316800
+ if (existsSync74(diaryPath)) {
315984
316801
  try {
315985
- const diary = readFileSync57(diaryPath, "utf-8").slice(0, 1e3);
316802
+ const diary = readFileSync58(diaryPath, "utf-8").slice(0, 1e3);
315986
316803
  parts.push(`\\nPrevious session history:\\n${diary}`);
315987
316804
  } catch {
315988
316805
  }
315989
316806
  }
315990
- const memDir = join90(cwd4, ".oa", "memory");
315991
- if (existsSync73(memDir)) {
316807
+ const memDir = join91(cwd4, ".oa", "memory");
316808
+ if (existsSync74(memDir)) {
315992
316809
  try {
315993
- const files = readdirSync23(memDir).filter((f2) => f2.endsWith(".json")).slice(0, 5);
316810
+ const files = readdirSync24(memDir).filter((f2) => f2.endsWith(".json")).slice(0, 5);
315994
316811
  if (files.length > 0) {
315995
316812
  parts.push("\\nPersistent memory topics: " + files.map((f2) => f2.replace(".json", "")).join(", "));
315996
316813
  for (const f2 of files.slice(0, 3)) {
315997
316814
  try {
315998
- const data = JSON.parse(readFileSync57(join90(memDir, f2), "utf-8"));
316815
+ const data = JSON.parse(readFileSync58(join91(memDir, f2), "utf-8"));
315999
316816
  const entries = Object.entries(data).slice(0, 3);
316000
316817
  if (entries.length > 0) {
316001
316818
  parts.push(`\\nMemory [${f2.replace(".json", "")}]: ` + entries.map(([k, v]) => `${k}: ${String(v.value ?? v).slice(0, 100)}`).join("; "));
@@ -316008,10 +316825,10 @@ function buildSystemPrompt(cwd4) {
316008
316825
  }
316009
316826
  }
316010
316827
  for (const name10 of ["AGENTS.md", "OA.md", ".open-agents.md"]) {
316011
- const p2 = join90(cwd4, name10);
316012
- if (existsSync73(p2)) {
316828
+ const p2 = join91(cwd4, name10);
316829
+ if (existsSync74(p2)) {
316013
316830
  try {
316014
- const content = readFileSync57(p2, "utf-8").slice(0, 500);
316831
+ const content = readFileSync58(p2, "utf-8").slice(0, 500);
316015
316832
  parts.push(`\\nProject instructions (${name10}):\\n${content}`);
316016
316833
  } catch {
316017
316834
  }
@@ -316087,15 +316904,15 @@ var init_chat_session = __esm({
316087
316904
  });
316088
316905
 
316089
316906
  // packages/cli/src/api/usage-tracker.ts
316090
- import { mkdirSync as mkdirSync43, readFileSync as readFileSync58, writeFileSync as writeFileSync37, existsSync as existsSync74 } from "node:fs";
316091
- import { join as join91 } from "node:path";
316907
+ import { mkdirSync as mkdirSync43, readFileSync as readFileSync59, writeFileSync as writeFileSync37, existsSync as existsSync75 } from "node:fs";
316908
+ import { join as join92 } from "node:path";
316092
316909
  function initUsageTracker(oaDir) {
316093
- const dir = join91(oaDir, "usage");
316910
+ const dir = join92(oaDir, "usage");
316094
316911
  mkdirSync43(dir, { recursive: true });
316095
- usageFile = join91(dir, "token-usage.json");
316912
+ usageFile = join92(dir, "token-usage.json");
316096
316913
  try {
316097
- if (existsSync74(usageFile)) {
316098
- store = JSON.parse(readFileSync58(usageFile, "utf-8"));
316914
+ if (existsSync75(usageFile)) {
316915
+ store = JSON.parse(readFileSync59(usageFile, "utf-8"));
316099
316916
  }
316100
316917
  } catch {
316101
316918
  store = { providers: {}, lastSaved: "" };
@@ -316159,24 +316976,24 @@ var init_usage_tracker = __esm({
316159
316976
  });
316160
316977
 
316161
316978
  // packages/cli/src/api/profiles.ts
316162
- import { existsSync as existsSync75, readFileSync as readFileSync59, writeFileSync as writeFileSync38, mkdirSync as mkdirSync44, readdirSync as readdirSync24, unlinkSync as unlinkSync18 } from "node:fs";
316163
- import { join as join92 } from "node:path";
316164
- import { homedir as homedir29 } from "node:os";
316979
+ import { existsSync as existsSync76, readFileSync as readFileSync60, writeFileSync as writeFileSync38, mkdirSync as mkdirSync44, readdirSync as readdirSync25, unlinkSync as unlinkSync18 } from "node:fs";
316980
+ import { join as join93 } from "node:path";
316981
+ import { homedir as homedir30 } from "node:os";
316165
316982
  import { createCipheriv as createCipheriv3, createDecipheriv as createDecipheriv3, randomBytes as randomBytes18, scryptSync as scryptSync3 } from "node:crypto";
316166
316983
  function globalProfileDir() {
316167
- return join92(homedir29(), ".open-agents", "profiles");
316984
+ return join93(homedir30(), ".open-agents", "profiles");
316168
316985
  }
316169
316986
  function projectProfileDir(projectDir) {
316170
- return join92(projectDir || process.cwd(), ".oa", "profiles");
316987
+ return join93(projectDir || process.cwd(), ".oa", "profiles");
316171
316988
  }
316172
316989
  function listProfiles(projectDir) {
316173
316990
  const result = [];
316174
316991
  const seen = /* @__PURE__ */ new Set();
316175
316992
  const projDir = projectProfileDir(projectDir);
316176
- if (existsSync75(projDir)) {
316177
- for (const f2 of readdirSync24(projDir).filter((f3) => f3.endsWith(".json"))) {
316993
+ if (existsSync76(projDir)) {
316994
+ for (const f2 of readdirSync25(projDir).filter((f3) => f3.endsWith(".json"))) {
316178
316995
  try {
316179
- const raw = JSON.parse(readFileSync59(join92(projDir, f2), "utf8"));
316996
+ const raw = JSON.parse(readFileSync60(join93(projDir, f2), "utf8"));
316180
316997
  const name10 = f2.replace(".json", "");
316181
316998
  seen.add(name10);
316182
316999
  result.push({
@@ -316190,12 +317007,12 @@ function listProfiles(projectDir) {
316190
317007
  }
316191
317008
  }
316192
317009
  const globDir = globalProfileDir();
316193
- if (existsSync75(globDir)) {
316194
- for (const f2 of readdirSync24(globDir).filter((f3) => f3.endsWith(".json"))) {
317010
+ if (existsSync76(globDir)) {
317011
+ for (const f2 of readdirSync25(globDir).filter((f3) => f3.endsWith(".json"))) {
316195
317012
  const name10 = f2.replace(".json", "");
316196
317013
  if (seen.has(name10)) continue;
316197
317014
  try {
316198
- const raw = JSON.parse(readFileSync59(join92(globDir, f2), "utf8"));
317015
+ const raw = JSON.parse(readFileSync60(join93(globDir, f2), "utf8"));
316199
317016
  result.push({
316200
317017
  name: name10,
316201
317018
  description: raw.description || "",
@@ -316210,11 +317027,11 @@ function listProfiles(projectDir) {
316210
317027
  }
316211
317028
  function loadProfile(name10, password, projectDir) {
316212
317029
  const sanitized = name10.replace(/[^a-zA-Z0-9_-]/g, "");
316213
- const projPath = join92(projectProfileDir(projectDir), `${sanitized}.json`);
316214
- const globPath = join92(globalProfileDir(), `${sanitized}.json`);
316215
- const filePath = existsSync75(projPath) ? projPath : existsSync75(globPath) ? globPath : null;
317030
+ const projPath = join93(projectProfileDir(projectDir), `${sanitized}.json`);
317031
+ const globPath = join93(globalProfileDir(), `${sanitized}.json`);
317032
+ const filePath = existsSync76(projPath) ? projPath : existsSync76(globPath) ? globPath : null;
316216
317033
  if (!filePath) return null;
316217
- const raw = JSON.parse(readFileSync59(filePath, "utf8"));
317034
+ const raw = JSON.parse(readFileSync60(filePath, "utf8"));
316218
317035
  if (raw.encrypted === true) {
316219
317036
  if (!password) return null;
316220
317037
  return decryptProfile(raw, password);
@@ -316225,7 +317042,7 @@ function saveProfile(profile, password, scope = "global", projectDir) {
316225
317042
  const dir = scope === "project" ? projectProfileDir(projectDir) : globalProfileDir();
316226
317043
  mkdirSync44(dir, { recursive: true });
316227
317044
  const sanitized = profile.name.replace(/[^a-zA-Z0-9_-]/g, "");
316228
- const filePath = join92(dir, `${sanitized}.json`);
317045
+ const filePath = join93(dir, `${sanitized}.json`);
316229
317046
  profile.modified = (/* @__PURE__ */ new Date()).toISOString();
316230
317047
  if (password) {
316231
317048
  const encrypted = encryptProfile(profile, password);
@@ -316238,8 +317055,8 @@ function saveProfile(profile, password, scope = "global", projectDir) {
316238
317055
  function deleteProfile(name10, scope = "global", projectDir) {
316239
317056
  const sanitized = name10.replace(/[^a-zA-Z0-9_-]/g, "");
316240
317057
  const dir = scope === "project" ? projectProfileDir(projectDir) : globalProfileDir();
316241
- const filePath = join92(dir, `${sanitized}.json`);
316242
- if (existsSync75(filePath)) {
317058
+ const filePath = join93(dir, `${sanitized}.json`);
317059
+ if (existsSync76(filePath)) {
316243
317060
  unlinkSync18(filePath);
316244
317061
  return true;
316245
317062
  }
@@ -316353,28 +317170,28 @@ var init_profiles = __esm({
316353
317170
  });
316354
317171
 
316355
317172
  // packages/cli/src/docker.ts
316356
- import { execSync as execSync53, spawn as spawn24 } from "node:child_process";
316357
- import { existsSync as existsSync76, mkdirSync as mkdirSync45, writeFileSync as writeFileSync39 } from "node:fs";
316358
- import { join as join93, resolve as resolve33, dirname as dirname25 } from "node:path";
316359
- import { homedir as homedir30 } from "node:os";
317173
+ import { execSync as execSync54, spawn as spawn24 } from "node:child_process";
317174
+ import { existsSync as existsSync77, mkdirSync as mkdirSync45, writeFileSync as writeFileSync39 } from "node:fs";
317175
+ import { join as join94, resolve as resolve33, dirname as dirname25 } from "node:path";
317176
+ import { homedir as homedir31 } from "node:os";
316360
317177
  import { fileURLToPath as fileURLToPath15 } from "node:url";
316361
317178
  function getDockerDir() {
316362
317179
  try {
316363
317180
  if (typeof __dirname !== "undefined") {
316364
- return join93(__dirname, "..", "..", "..", "docker");
317181
+ return join94(__dirname, "..", "..", "..", "docker");
316365
317182
  }
316366
317183
  } catch {
316367
317184
  }
316368
317185
  try {
316369
317186
  const thisDir = dirname25(fileURLToPath15(import.meta.url));
316370
- return join93(thisDir, "..", "..", "..", "docker");
317187
+ return join94(thisDir, "..", "..", "..", "docker");
316371
317188
  } catch {
316372
317189
  }
316373
- return join93(process.cwd(), "docker");
317190
+ return join94(process.cwd(), "docker");
316374
317191
  }
316375
317192
  function isDockerAvailable() {
316376
317193
  try {
316377
- execSync53("docker info", { stdio: "pipe", timeout: 1e4 });
317194
+ execSync54("docker info", { stdio: "pipe", timeout: 1e4 });
316378
317195
  return true;
316379
317196
  } catch {
316380
317197
  return false;
@@ -316382,7 +317199,7 @@ function isDockerAvailable() {
316382
317199
  }
316383
317200
  function isDockerInstalled() {
316384
317201
  try {
316385
- execSync53("docker --version", { stdio: "pipe", timeout: 5e3 });
317202
+ execSync54("docker --version", { stdio: "pipe", timeout: 5e3 });
316386
317203
  return true;
316387
317204
  } catch {
316388
317205
  return false;
@@ -316407,31 +317224,31 @@ async function ensureDocker() {
316407
317224
  }
316408
317225
  try {
316409
317226
  console.log("[oa-docker] Docker not found. Installing via get.docker.com...");
316410
- execSync53("curl -fsSL https://get.docker.com | sh", {
317227
+ execSync54("curl -fsSL https://get.docker.com | sh", {
316411
317228
  stdio: "inherit",
316412
317229
  timeout: 3e5
316413
317230
  });
316414
317231
  const user = process.env["USER"] || process.env["LOGNAME"];
316415
317232
  if (user) {
316416
317233
  try {
316417
- execSync53(`sudo usermod -aG docker ${user}`, { stdio: "pipe" });
317234
+ execSync54(`sudo usermod -aG docker ${user}`, { stdio: "pipe" });
316418
317235
  } catch {
316419
317236
  }
316420
317237
  }
316421
317238
  try {
316422
- execSync53("sudo systemctl start docker", { stdio: "pipe", timeout: 15e3 });
317239
+ execSync54("sudo systemctl start docker", { stdio: "pipe", timeout: 15e3 });
316423
317240
  } catch {
316424
317241
  }
316425
317242
  try {
316426
- execSync53("nvidia-smi", { stdio: "pipe", timeout: 5e3 });
316427
- const runtimes = execSync53("docker info --format '{{json .Runtimes}}'", {
317243
+ execSync54("nvidia-smi", { stdio: "pipe", timeout: 5e3 });
317244
+ const runtimes = execSync54("docker info --format '{{json .Runtimes}}'", {
316428
317245
  stdio: "pipe",
316429
317246
  timeout: 5e3
316430
317247
  }).toString();
316431
317248
  if (!runtimes.includes("nvidia")) {
316432
317249
  console.log("[oa-docker] NVIDIA GPU detected. Installing nvidia-container-toolkit...");
316433
317250
  try {
316434
- execSync53(`
317251
+ execSync54(`
316435
317252
  curl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey | sudo gpg --dearmor -o /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg 2>/dev/null
316436
317253
  curl -s -L https://nvidia.github.io/libnvidia-container/stable/deb/nvidia-container-toolkit.list | sed 's#deb https://#deb [signed-by=/usr/share/keyrings/nvidia-container-toolkit-keyring.gpg] https://#g' | sudo tee /etc/apt/sources.list.d/nvidia-container-toolkit.list > /dev/null 2>&1
316437
317254
  sudo apt-get update -qq 2>/dev/null && sudo apt-get install -y -qq nvidia-container-toolkit 2>/dev/null || ( sudo dnf install -y nvidia-container-toolkit 2>/dev/null || sudo yum install -y nvidia-container-toolkit 2>/dev/null || true )
@@ -316461,7 +317278,7 @@ async function ensureDocker() {
316461
317278
  }
316462
317279
  async function ensureNvidiaToolkit() {
316463
317280
  try {
316464
- execSync53("nvidia-smi --query-gpu=name --format=csv,noheader", { stdio: "pipe", timeout: 5e3 });
317281
+ execSync54("nvidia-smi --query-gpu=name --format=csv,noheader", { stdio: "pipe", timeout: 5e3 });
316465
317282
  } catch {
316466
317283
  return { ok: false, message: "No NVIDIA GPU detected (nvidia-smi not found)" };
316467
317284
  }
@@ -316472,7 +317289,7 @@ async function ensureNvidiaToolkit() {
316472
317289
  return { ok: false, message: "Auto-install only supported on Linux. Install manually: https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/install-guide.html" };
316473
317290
  }
316474
317291
  try {
316475
- execSync53(`
317292
+ execSync54(`
316476
317293
  curl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey | sudo gpg --dearmor -o /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg 2>/dev/null
316477
317294
  curl -s -L https://nvidia.github.io/libnvidia-container/stable/deb/nvidia-container-toolkit.list | sed 's#deb https://#deb [signed-by=/usr/share/keyrings/nvidia-container-toolkit-keyring.gpg] https://#g' | sudo tee /etc/apt/sources.list.d/nvidia-container-toolkit.list > /dev/null 2>&1
316478
317295
  sudo apt-get update -qq 2>/dev/null && sudo apt-get install -y -qq nvidia-container-toolkit 2>/dev/null || ( sudo dnf install -y nvidia-container-toolkit 2>/dev/null || sudo yum install -y nvidia-container-toolkit 2>/dev/null || true )
@@ -316486,7 +317303,7 @@ async function ensureNvidiaToolkit() {
316486
317303
  }
316487
317304
  function isOaImageBuilt() {
316488
317305
  try {
316489
- const out = execSync53(`docker images -q ${OA_IMAGE}:${OA_IMAGE_TAG}`, {
317306
+ const out = execSync54(`docker images -q ${OA_IMAGE}:${OA_IMAGE_TAG}`, {
316490
317307
  stdio: "pipe",
316491
317308
  timeout: 5e3
316492
317309
  }).toString().trim();
@@ -316501,16 +317318,16 @@ async function ensureOaImage(force = false) {
316501
317318
  }
316502
317319
  let buildContext;
316503
317320
  const dockerDir = getDockerDir();
316504
- if (existsSync76(join93(dockerDir, "Dockerfile"))) {
317321
+ if (existsSync77(join94(dockerDir, "Dockerfile"))) {
316505
317322
  buildContext = dockerDir;
316506
317323
  } else {
316507
- buildContext = join93(homedir30(), ".oa", "docker-build");
317324
+ buildContext = join94(homedir31(), ".oa", "docker-build");
316508
317325
  mkdirSync45(buildContext, { recursive: true });
316509
317326
  writeDockerfiles(buildContext);
316510
317327
  }
316511
317328
  try {
316512
317329
  console.log(`[oa-docker] Building image ${OA_IMAGE}:${OA_IMAGE_TAG}...`);
316513
- execSync53(`docker build -t ${OA_IMAGE}:${OA_IMAGE_TAG} ${buildContext}`, {
317330
+ execSync54(`docker build -t ${OA_IMAGE}:${OA_IMAGE_TAG} ${buildContext}`, {
316514
317331
  stdio: "inherit",
316515
317332
  timeout: 6e5
316516
317333
  // 10 min
@@ -316579,16 +317396,16 @@ chown -R node:node /workspace /home/node/.oa /home/node/.open-agents 2>/dev/null
316579
317396
  if [ "$1" = "oa" ]; then shift; exec su - node -c "cd /workspace && oa $*"; fi
316580
317397
  exec "$@"
316581
317398
  `;
316582
- writeFileSync39(join93(dir, "Dockerfile"), dockerfile);
316583
- writeFileSync39(join93(dir, "docker-entrypoint.sh"), entrypoint, { mode: 493 });
317399
+ writeFileSync39(join94(dir, "Dockerfile"), dockerfile);
317400
+ writeFileSync39(join94(dir, "docker-entrypoint.sh"), entrypoint, { mode: 493 });
316584
317401
  }
316585
317402
  function hasNvidiaGpu() {
316586
317403
  try {
316587
- execSync53("nvidia-smi --query-gpu=name --format=csv,noheader", {
317404
+ execSync54("nvidia-smi --query-gpu=name --format=csv,noheader", {
316588
317405
  stdio: "pipe",
316589
317406
  timeout: 5e3
316590
317407
  });
316591
- const runtimes = execSync53("docker info --format '{{json .Runtimes}}'", {
317408
+ const runtimes = execSync54("docker info --format '{{json .Runtimes}}'", {
316592
317409
  stdio: "pipe",
316593
317410
  timeout: 5e3
316594
317411
  }).toString();
@@ -316657,22 +317474,22 @@ import * as http5 from "node:http";
316657
317474
  import * as https3 from "node:https";
316658
317475
  import { createRequire as createRequire4 } from "node:module";
316659
317476
  import { fileURLToPath as fileURLToPath16 } from "node:url";
316660
- import { dirname as dirname26, join as join94, resolve as resolve34 } from "node:path";
316661
- import { spawn as spawn25, execSync as execSync54 } from "node:child_process";
316662
- import { mkdirSync as mkdirSync46, writeFileSync as writeFileSync40, readFileSync as readFileSync60, readdirSync as readdirSync25, existsSync as existsSync77 } from "node:fs";
317477
+ import { dirname as dirname26, join as join95, resolve as resolve34 } from "node:path";
317478
+ import { spawn as spawn25, execSync as execSync55 } from "node:child_process";
317479
+ import { mkdirSync as mkdirSync46, readFileSync as readFileSync61, readdirSync as readdirSync26, existsSync as existsSync78 } from "node:fs";
316663
317480
  import { randomBytes as randomBytes19, randomUUID as randomUUID11 } from "node:crypto";
316664
317481
  function getVersion3() {
316665
317482
  try {
316666
317483
  const require3 = createRequire4(import.meta.url);
316667
317484
  const thisDir = dirname26(fileURLToPath16(import.meta.url));
316668
317485
  const candidates = [
316669
- join94(thisDir, "..", "package.json"),
316670
- join94(thisDir, "..", "..", "package.json"),
316671
- join94(thisDir, "..", "..", "..", "package.json")
317486
+ join95(thisDir, "..", "package.json"),
317487
+ join95(thisDir, "..", "..", "package.json"),
317488
+ join95(thisDir, "..", "..", "..", "package.json")
316672
317489
  ];
316673
317490
  for (const pkgPath of candidates) {
316674
317491
  try {
316675
- if (!existsSync77(pkgPath)) continue;
317492
+ if (!existsSync78(pkgPath)) continue;
316676
317493
  const pkg = require3(pkgPath);
316677
317494
  if (pkg.name === "open-agents-ai" || pkg.name === "@open-agents/cli" || pkg.name === "@open-agents/monorepo") {
316678
317495
  return pkg.version ?? "0.0.0";
@@ -316892,6 +317709,166 @@ async function parseJsonBody(req2) {
316892
317709
  return null;
316893
317710
  }
316894
317711
  }
317712
+ function sanitizeChatContent(raw) {
317713
+ if (typeof raw !== "string") return "";
317714
+ const lines = raw.split("\n");
317715
+ const cleaned = [];
317716
+ for (const rawLine of lines) {
317717
+ const line = rawLine.replace(/\x1B\[[0-9;]*[A-Za-z]/g, "").replace(/\x1B\].*?\x07/g, "").trim();
317718
+ if (!line) continue;
317719
+ if (/^i\s+(Knowledge graph|Episodes captured|Context assembled|Tool deferral|Task consolidation|Re-engaging|Stopping re-engagement|Memory written|Backend)/.test(line)) continue;
317720
+ if (/^E\s+(Backend (error|unavailable)|Stopping)/.test(line)) continue;
317721
+ if (/^⚠\s*(Task incomplete|Task complete)/.test(line)) continue;
317722
+ if (/^▹\s/.test(line)) continue;
317723
+ if (/^open-agents \(/.test(line)) continue;
317724
+ cleaned.push(line);
317725
+ }
317726
+ return cleaned.join("\n").trim();
317727
+ }
317728
+ async function directChatBackend(opts) {
317729
+ const { model, messages: messages2, stream, res, sessionId, ollamaUrl } = opts;
317730
+ const cfg = loadConfig();
317731
+ const isVllm = cfg.backendType === "vllm";
317732
+ const cleanModel = model.replace(/^[a-z]+\//, "");
317733
+ const headers = { "Content-Type": "application/json" };
317734
+ if (cfg.apiKey) headers["Authorization"] = `Bearer ${cfg.apiKey}`;
317735
+ if (isVllm) {
317736
+ const reqBody = JSON.stringify({ model: cleanModel, messages: messages2, stream });
317737
+ if (stream) {
317738
+ res.writeHead(200, {
317739
+ "Content-Type": "text/event-stream",
317740
+ "Cache-Control": "no-cache",
317741
+ "Connection": "keep-alive",
317742
+ "X-Session-ID": sessionId,
317743
+ "X-API-Version": API_VERSION
317744
+ });
317745
+ let acc = "";
317746
+ const url = new URL("/v1/chat/completions", ollamaUrl);
317747
+ const transport = url.protocol === "https:" ? https3 : http5;
317748
+ await new Promise((resolve39, reject) => {
317749
+ const proxyReq = transport.request({
317750
+ hostname: url.hostname,
317751
+ port: url.port || (url.protocol === "https:" ? 443 : 80),
317752
+ path: url.pathname,
317753
+ method: "POST",
317754
+ headers: { ...headers, "Content-Length": Buffer.byteLength(reqBody) }
317755
+ }, (proxyRes) => {
317756
+ proxyRes.on("data", (chunk) => {
317757
+ const text = chunk.toString();
317758
+ res.write(text);
317759
+ for (const ln of text.split("\n")) {
317760
+ if (!ln.startsWith("data: ")) continue;
317761
+ const payload = ln.slice(6).trim();
317762
+ if (!payload || payload === "[DONE]") continue;
317763
+ try {
317764
+ const j = JSON.parse(payload);
317765
+ const delta = j?.choices?.[0]?.delta?.content;
317766
+ if (delta) acc += delta;
317767
+ } catch {
317768
+ }
317769
+ }
317770
+ });
317771
+ proxyRes.on("end", () => {
317772
+ res.end();
317773
+ resolve39();
317774
+ });
317775
+ proxyRes.on("error", reject);
317776
+ });
317777
+ proxyReq.on("error", reject);
317778
+ proxyReq.setTimeout(12e4, () => proxyReq.destroy(new Error("Backend timeout (120s)")));
317779
+ proxyReq.write(reqBody);
317780
+ proxyReq.end();
317781
+ });
317782
+ return acc.trim();
317783
+ } else {
317784
+ const result = await ollamaRequest(ollamaUrl, "/v1/chat/completions", "POST", reqBody);
317785
+ if (result.status >= 400) throw new Error(`Backend HTTP ${result.status}: ${result.body.slice(0, 200)}`);
317786
+ const j = JSON.parse(result.body);
317787
+ const content = j?.choices?.[0]?.message?.content || "";
317788
+ jsonResponse(res, 200, {
317789
+ session_id: sessionId,
317790
+ model: cleanModel,
317791
+ message: { role: "assistant", content }
317792
+ });
317793
+ return content;
317794
+ }
317795
+ } else {
317796
+ const reqBody = JSON.stringify({
317797
+ model: cleanModel,
317798
+ messages: messages2,
317799
+ stream,
317800
+ think: false,
317801
+ options: {}
317802
+ });
317803
+ if (stream) {
317804
+ res.writeHead(200, {
317805
+ "Content-Type": "text/event-stream",
317806
+ "Cache-Control": "no-cache",
317807
+ "Connection": "keep-alive",
317808
+ "X-Session-ID": sessionId,
317809
+ "X-API-Version": API_VERSION
317810
+ });
317811
+ let acc = "";
317812
+ await new Promise((resolve39, reject) => {
317813
+ ollamaStream(
317814
+ ollamaUrl,
317815
+ "/api/chat",
317816
+ "POST",
317817
+ reqBody,
317818
+ (chunk) => {
317819
+ for (const ln of chunk.split("\n")) {
317820
+ if (!ln.trim()) continue;
317821
+ try {
317822
+ const j = JSON.parse(ln);
317823
+ const delta = j?.message?.content || "";
317824
+ if (delta) {
317825
+ acc += delta;
317826
+ res.write("data: " + JSON.stringify({
317827
+ id: `chatcmpl-${sessionId.slice(0, 8)}`,
317828
+ object: "chat.completion.chunk",
317829
+ choices: [{ index: 0, delta: { content: delta }, finish_reason: null }]
317830
+ }) + "\n\n");
317831
+ }
317832
+ if (j.done) {
317833
+ res.write("data: " + JSON.stringify({
317834
+ id: `chatcmpl-${sessionId.slice(0, 8)}`,
317835
+ object: "chat.completion.chunk",
317836
+ choices: [{ index: 0, delta: {}, finish_reason: "stop" }]
317837
+ }) + "\n\n");
317838
+ res.write("data: [DONE]\n\n");
317839
+ }
317840
+ } catch {
317841
+ }
317842
+ }
317843
+ },
317844
+ () => {
317845
+ res.end();
317846
+ resolve39();
317847
+ },
317848
+ (err) => reject(err)
317849
+ );
317850
+ });
317851
+ return acc.trim();
317852
+ } else {
317853
+ const result = await ollamaRequest(ollamaUrl, "/api/chat", "POST", reqBody);
317854
+ if (result.status >= 400) throw new Error(`Backend HTTP ${result.status}: ${result.body.slice(0, 200)}`);
317855
+ const j = JSON.parse(result.body);
317856
+ const content = j?.message?.content || "";
317857
+ if (!content) throw new Error("Backend returned empty message content");
317858
+ jsonResponse(res, 200, {
317859
+ session_id: sessionId,
317860
+ model: cleanModel,
317861
+ message: { role: "assistant", content },
317862
+ usage: {
317863
+ prompt_tokens: j?.prompt_eval_count,
317864
+ completion_tokens: j?.eval_count,
317865
+ total_duration_ns: j?.total_duration
317866
+ }
317867
+ });
317868
+ return content;
317869
+ }
317870
+ }
317871
+ }
316895
317872
  function backendAuthHeaders(endpoint) {
316896
317873
  const key = endpoint?.authKey ?? loadConfig().apiKey;
316897
317874
  if (key) return { Authorization: `Bearer ${key}` };
@@ -316961,27 +317938,27 @@ function ollamaStream(ollamaUrl, path5, method, body, onData, onEnd, onError) {
316961
317938
  }
316962
317939
  function jobsDir() {
316963
317940
  const root = resolve34(process.cwd());
316964
- const dir = join94(root, ".oa", "jobs");
317941
+ const dir = join95(root, ".oa", "jobs");
316965
317942
  mkdirSync46(dir, { recursive: true });
316966
317943
  return dir;
316967
317944
  }
316968
317945
  function loadJob(id) {
316969
- const file = join94(jobsDir(), `${id}.json`);
316970
- if (!existsSync77(file)) return null;
317946
+ const file = join95(jobsDir(), `${id}.json`);
317947
+ if (!existsSync78(file)) return null;
316971
317948
  try {
316972
- return JSON.parse(readFileSync60(file, "utf-8"));
317949
+ return JSON.parse(readFileSync61(file, "utf-8"));
316973
317950
  } catch {
316974
317951
  return null;
316975
317952
  }
316976
317953
  }
316977
317954
  function listJobs() {
316978
317955
  const dir = jobsDir();
316979
- if (!existsSync77(dir)) return [];
316980
- const files = readdirSync25(dir).filter((f2) => f2.endsWith(".json")).sort();
317956
+ if (!existsSync78(dir)) return [];
317957
+ const files = readdirSync26(dir).filter((f2) => f2.endsWith(".json")).sort();
316981
317958
  const jobs = [];
316982
317959
  for (const file of files) {
316983
317960
  try {
316984
- jobs.push(JSON.parse(readFileSync60(join94(dir, file), "utf-8")));
317961
+ jobs.push(JSON.parse(readFileSync61(join95(dir, file), "utf-8")));
316985
317962
  } catch {
316986
317963
  }
316987
317964
  }
@@ -317012,12 +317989,47 @@ function checkKeyRateLimit(auth) {
317012
317989
  }
317013
317990
  return null;
317014
317991
  }
317992
+ function checkConcurrentJobLimit(auth) {
317993
+ if (!auth.user || !auth.maxJobs) return null;
317994
+ const usage = getKeyUsage(auth.user);
317995
+ if (usage.activeJobs >= auth.maxJobs) {
317996
+ return `Concurrent job limit exceeded for ${auth.user}: ${usage.activeJobs}/${auth.maxJobs}`;
317997
+ }
317998
+ return null;
317999
+ }
318000
+ function incrementActiveJobs(user) {
318001
+ if (!user) return;
318002
+ getKeyUsage(user).activeJobs++;
318003
+ }
318004
+ function decrementActiveJobs(user) {
318005
+ if (!user) return;
318006
+ const usage = getKeyUsage(user);
318007
+ if (usage.activeJobs > 0) usage.activeJobs--;
318008
+ }
317015
318009
  function recordKeyUsage(user, tokens) {
317016
318010
  if (!user) return;
317017
318011
  const usage = getKeyUsage(user);
317018
318012
  usage.requestTimestamps.push(Date.now());
317019
318013
  usage.tokensToday += tokens;
317020
318014
  }
318015
+ function atomicJobWrite(dir, id, job) {
318016
+ const fs4 = __require("node:fs");
318017
+ const finalPath = join95(dir, `${id}.json`);
318018
+ const tmpPath = `${finalPath}.tmp.${process.pid}.${Date.now()}`;
318019
+ try {
318020
+ fs4.writeFileSync(tmpPath, JSON.stringify(job, null, 2), "utf-8");
318021
+ fs4.renameSync(tmpPath, finalPath);
318022
+ } catch {
318023
+ try {
318024
+ fs4.writeFileSync(finalPath, JSON.stringify(job, null, 2), "utf-8");
318025
+ } catch {
318026
+ }
318027
+ try {
318028
+ fs4.unlinkSync(tmpPath);
318029
+ } catch {
318030
+ }
318031
+ }
318032
+ }
317021
318033
  function resolveAuth(req2) {
317022
318034
  const authHeader = req2.headers["authorization"];
317023
318035
  const token = authHeader?.startsWith("Bearer ") ? authHeader.slice(7) : null;
@@ -317447,7 +318459,23 @@ async function handleV1Run(req2, res) {
317447
318459
  return;
317448
318460
  }
317449
318461
  }
317450
- const id = `job-${randomBytes19(3).toString("hex")}`;
318462
+ {
318463
+ const auth = resolveAuth(req2);
318464
+ const overLimit = checkConcurrentJobLimit(auth);
318465
+ if (overLimit) {
318466
+ res.setHeader("Content-Type", "application/problem+json; charset=utf-8");
318467
+ res.writeHead(429);
318468
+ res.end(JSON.stringify({
318469
+ type: "https://openagents.nexus/problems/rate-limited",
318470
+ title: "Concurrent job limit exceeded",
318471
+ status: 429,
318472
+ detail: overLimit,
318473
+ instance: req2.headers["x-request-id"] ?? void 0
318474
+ }));
318475
+ return;
318476
+ }
318477
+ }
318478
+ const id = `job-${randomBytes19(8).toString("hex")}`;
317451
318479
  const dir = jobsDir();
317452
318480
  const workingDir = requestBody["working_directory"] || req2.headers["x-working-directory"];
317453
318481
  const isolate = requestBody["isolate"] === true;
@@ -317455,7 +318483,7 @@ async function handleV1Run(req2, res) {
317455
318483
  if (workingDir) {
317456
318484
  cwd4 = resolve34(workingDir);
317457
318485
  } else if (isolate) {
317458
- const wsDir = join94(dir, "..", "workspaces", id);
318486
+ const wsDir = join95(dir, "..", "workspaces", id);
317459
318487
  mkdirSync46(wsDir, { recursive: true });
317460
318488
  cwd4 = wsDir;
317461
318489
  } else {
@@ -317492,15 +318520,16 @@ async function handleV1Run(req2, res) {
317492
318520
  }
317493
318521
  job.profile = profileName || null;
317494
318522
  const runCfg = loadConfig();
317495
- const runEnv = {
317496
- ...process.env,
317497
- OA_JOB_ID: id,
317498
- __OPEN_AGENTS_NO_AUTO_RUN: "",
317499
- OA_RUN_USER: authUser,
317500
- OA_RUN_SCOPE: authScope,
317501
- // Pass backend config so subprocess doesn't have to rediscover
317502
- OLLAMA_HOST: runCfg.backendUrl || process.env["OLLAMA_HOST"] || "http://127.0.0.1:11434"
317503
- };
318523
+ const runEnv = {};
318524
+ for (const [k, v] of Object.entries(process.env)) {
318525
+ if (k === "OA_DAEMON" || k === "OA_PORT") continue;
318526
+ if (typeof v === "string") runEnv[k] = v;
318527
+ }
318528
+ runEnv["OA_JOB_ID"] = id;
318529
+ runEnv["__OPEN_AGENTS_NO_AUTO_RUN"] = "";
318530
+ runEnv["OA_RUN_USER"] = authUser;
318531
+ runEnv["OA_RUN_SCOPE"] = authScope;
318532
+ runEnv["OLLAMA_HOST"] = runCfg.backendUrl || process.env["OLLAMA_HOST"] || "http://127.0.0.1:11434";
317504
318533
  if (runCfg.apiKey) runEnv["OA_API_KEY_INHERIT"] = runCfg.apiKey;
317505
318534
  if (activeProfile) {
317506
318535
  runEnv["OA_TOOL_PROFILE"] = JSON.stringify(activeProfile);
@@ -317540,8 +318569,17 @@ async function handleV1Run(req2, res) {
317540
318569
  job.sandbox = sandbox;
317541
318570
  }
317542
318571
  job.pid = child.pid ?? 0;
317543
- writeFileSync40(join94(dir, `${id}.json`), JSON.stringify(job, null, 2));
318572
+ atomicJobWrite(dir, id, job);
317544
318573
  runningProcesses.set(id, child);
318574
+ incrementActiveJobs(authUser);
318575
+ try {
318576
+ const pubEv = publishEvent;
318577
+ pubEv("run.started", { run_id: id, model: modelArg, pid: job.pid }, {
318578
+ subject: authUser,
318579
+ aimsControl: "A.6.2.6"
318580
+ });
318581
+ } catch {
318582
+ }
317545
318583
  if (streamMode) {
317546
318584
  res.writeHead(200, {
317547
318585
  "Content-Type": "text/event-stream",
@@ -317566,11 +318604,18 @@ async function handleV1Run(req2, res) {
317566
318604
  child.on("exit", (code8) => {
317567
318605
  job.status = code8 === 0 ? "completed" : "failed";
317568
318606
  job.completedAt = (/* @__PURE__ */ new Date()).toISOString();
318607
+ atomicJobWrite(dir, id, job);
318608
+ runningProcesses.delete(id);
318609
+ decrementActiveJobs(authUser);
317569
318610
  try {
317570
- writeFileSync40(join94(dir, `${id}.json`), JSON.stringify(job, null, 2));
318611
+ const pubEv = publishEvent;
318612
+ pubEv(
318613
+ job.status === "completed" ? "run.completed" : "run.failed",
318614
+ { run_id: id, exit_code: code8 },
318615
+ { subject: authUser, aimsControl: "A.6.2.6" }
318616
+ );
317571
318617
  } catch {
317572
318618
  }
317573
- runningProcesses.delete(id);
317574
318619
  res.write(`data: ${JSON.stringify({ type: "run_completed", run_id: id, exit_code: code8 })}
317575
318620
 
317576
318621
  `);
@@ -317606,11 +318651,18 @@ async function handleV1Run(req2, res) {
317606
318651
  job.status = code8 === 0 ? "completed" : "failed";
317607
318652
  job.completedAt = (/* @__PURE__ */ new Date()).toISOString();
317608
318653
  }
318654
+ atomicJobWrite(dir, id, job);
318655
+ runningProcesses.delete(id);
318656
+ decrementActiveJobs(authUser);
317609
318657
  try {
317610
- writeFileSync40(join94(dir, `${id}.json`), JSON.stringify(job, null, 2));
318658
+ const pubEv = publishEvent;
318659
+ pubEv(
318660
+ job.status === "completed" ? "run.completed" : "run.failed",
318661
+ { run_id: id, exit_code: code8, summary: job.summary },
318662
+ { subject: authUser, aimsControl: "A.6.2.6" }
318663
+ );
317611
318664
  } catch {
317612
318665
  }
317613
- runningProcesses.delete(id);
317614
318666
  });
317615
318667
  jsonResponse(res, 202, { run_id: id, status: "running", pid: job.pid });
317616
318668
  }
@@ -317666,7 +318718,7 @@ function handleV1RunsDelete(res, id) {
317666
318718
  const containerName = `oa-${id}`;
317667
318719
  if (job.sandbox === "container") {
317668
318720
  try {
317669
- execSync54(`docker stop ${containerName}`, { timeout: 5e3, stdio: "ignore" });
318721
+ execSync55(`docker stop ${containerName}`, { timeout: 5e3, stdio: "ignore" });
317670
318722
  } catch {
317671
318723
  }
317672
318724
  }
@@ -317674,11 +318726,14 @@ function handleV1RunsDelete(res, id) {
317674
318726
  job.completedAt = (/* @__PURE__ */ new Date()).toISOString();
317675
318727
  job.error = "Aborted via API";
317676
318728
  const dir = jobsDir();
318729
+ atomicJobWrite(dir, id, job);
318730
+ runningProcesses.delete(id);
318731
+ decrementActiveJobs(job.user);
317677
318732
  try {
317678
- writeFileSync40(join94(dir, `${id}.json`), JSON.stringify(job, null, 2));
318733
+ const pubEv = publishEvent;
318734
+ pubEv("run.aborted", { run_id: id }, { subject: job.user, aimsControl: "A.6.2.6" });
317679
318735
  } catch {
317680
318736
  }
317681
- runningProcesses.delete(id);
317682
318737
  }
317683
318738
  jsonResponse(res, 200, { run_id: id, status: "aborted" });
317684
318739
  }
@@ -317730,15 +318785,11 @@ async function handlePatchConfig(req2, res) {
317730
318785
  settingsUpdate.updateMode = updates["updateMode"];
317731
318786
  }
317732
318787
  saveGlobalSettings(settingsUpdate);
317733
- try {
317734
- const { publishEvent: publishEvent2 } = (init_http2(), __toCommonJS(http_exports));
317735
- publishEvent2("config.changed", { keys: Object.keys(settingsUpdate) }, {
317736
- subject: req2._authUser ?? "anonymous",
317737
- aimsControl: "A.6.2.8"
317738
- // ISO 42001 — configuration change record
317739
- });
317740
- } catch {
317741
- }
318788
+ publishEvent("config.changed", { keys: Object.keys(settingsUpdate) }, {
318789
+ subject: req2._authUser ?? "anonymous",
318790
+ aimsControl: "A.6.2.8"
318791
+ // ISO 42001 — configuration change record
318792
+ });
317742
318793
  const updatedConfig = loadConfig();
317743
318794
  jsonResponse(res, 200, {
317744
318795
  config: redactSecrets(updatedConfig),
@@ -317919,7 +318970,7 @@ async function handleRequest(req2, res, ollamaUrl, verbose) {
317919
318970
  if (pathname === "/v1/files" && method === "GET") {
317920
318971
  const dir = urlObj.searchParams.get("path") || process.cwd();
317921
318972
  try {
317922
- const entries = readdirSync25(resolve34(dir), { withFileTypes: true }).filter((e2) => !e2.name.startsWith(".") && e2.name !== "node_modules").slice(0, 100).map((e2) => ({ name: e2.name, type: e2.isDirectory() ? "dir" : "file" }));
318973
+ const entries = readdirSync26(resolve34(dir), { withFileTypes: true }).filter((e2) => !e2.name.startsWith(".") && e2.name !== "node_modules").slice(0, 100).map((e2) => ({ name: e2.name, type: e2.isDirectory() ? "dir" : "file" }));
317923
318974
  jsonResponse(res, 200, { path: resolve34(dir), entries });
317924
318975
  } catch (e2) {
317925
318976
  jsonResponse(res, 400, { error: e2.message });
@@ -317996,6 +319047,42 @@ async function handleRequest(req2, res, ollamaUrl, verbose) {
317996
319047
  const session = getSession(sessionId, model, cwdPath);
317997
319048
  addUserMessage(session, chatBody.message);
317998
319049
  compactSession(session);
319050
+ const wantsTools = chatBody["tools"] !== false && chatBody["use_tools"] !== false;
319051
+ const streamMode = chatBody.stream !== false;
319052
+ if (!wantsTools) {
319053
+ try {
319054
+ const ans = await directChatBackend({
319055
+ model,
319056
+ messages: session.messages.map((m2) => ({ role: m2.role, content: m2.content })),
319057
+ stream: streamMode,
319058
+ res,
319059
+ sessionId: session.id,
319060
+ ollamaUrl
319061
+ });
319062
+ if (ans !== null) {
319063
+ addAssistantMessage(session, ans);
319064
+ }
319065
+ } catch (err) {
319066
+ if (!res.headersSent) {
319067
+ res.setHeader("Content-Type", "application/problem+json; charset=utf-8");
319068
+ res.writeHead(502);
319069
+ res.end(JSON.stringify({
319070
+ type: "https://openagents.nexus/problems/upstream-failure",
319071
+ title: "Backend chat failed",
319072
+ status: 502,
319073
+ detail: err instanceof Error ? err.message : String(err),
319074
+ instance: requestId,
319075
+ "session_id": session.id
319076
+ }));
319077
+ } else {
319078
+ try {
319079
+ res.end();
319080
+ } catch {
319081
+ }
319082
+ }
319083
+ }
319084
+ return;
319085
+ }
317999
319086
  const historyLines = session.messages.filter((m2) => m2.role !== "system").slice(-10).map((m2) => m2.role === "user" ? `User: ${m2.content}` : `Assistant: ${m2.content}`).join("\n");
318000
319087
  const taskPrompt = (historyLines ? `Previous conversation:
318001
319088
  ${historyLines}
@@ -318004,15 +319091,16 @@ ${historyLines}
318004
319091
  const oaBin = process.argv[1] || "oa";
318005
319092
  const args = [taskPrompt, "--json"];
318006
319093
  if (model) args.push("--model", model.replace(/^local\//, ""));
318007
- const streamMode = chatBody.stream !== false;
318008
319094
  const currentCfg = loadConfig();
318009
- const runEnv = {
318010
- ...process.env,
318011
- __OPEN_AGENTS_NO_AUTO_RUN: "",
318012
- OA_RUN_USER: req2._authUser || "anonymous",
318013
- OA_RUN_SCOPE: req2._authScope || "admin",
318014
- OLLAMA_HOST: currentCfg.backendUrl || process.env["OLLAMA_HOST"] || "http://127.0.0.1:11434"
318015
- };
319095
+ const runEnv = {};
319096
+ for (const [k, v] of Object.entries(process.env)) {
319097
+ if (k === "OA_DAEMON" || k === "OA_PORT") continue;
319098
+ if (typeof v === "string") runEnv[k] = v;
319099
+ }
319100
+ runEnv["__OPEN_AGENTS_NO_AUTO_RUN"] = "";
319101
+ runEnv["OA_RUN_USER"] = req2._authUser || "anonymous";
319102
+ runEnv["OA_RUN_SCOPE"] = req2._authScope || "admin";
319103
+ runEnv["OLLAMA_HOST"] = currentCfg.backendUrl || process.env["OLLAMA_HOST"] || "http://127.0.0.1:11434";
318016
319104
  if (currentCfg.apiKey) runEnv["OA_API_KEY_INHERIT"] = currentCfg.apiKey;
318017
319105
  const child = spawn25(process.execPath, [oaBin, ...args], {
318018
319106
  cwd: cwdPath,
@@ -318059,44 +319147,54 @@ ${historyLines}
318059
319147
  child.on("close", () => {
318060
319148
  if (lineBuffer.trim()) finalLines.push(lineBuffer);
318061
319149
  const rawFinal = finalLines.join("\n").trim();
319150
+ let backendError;
318062
319151
  try {
318063
319152
  const result = JSON.parse(rawFinal);
318064
- let content = result.assistant_text || "";
319153
+ if (result.error) backendError = String(result.error);
319154
+ let content = sanitizeChatContent(result.assistant_text || "");
318065
319155
  if (!content) {
318066
319156
  const summary = result.summary || "";
318067
319157
  const match = summary.match(/Tokens:\s*[\d,]+\s+([\s\S]*)/);
318068
- content = match ? match[1].trim() : summary;
319158
+ content = sanitizeChatContent(match ? match[1] : summary);
319159
+ }
319160
+ if (!content && result.text) {
319161
+ content = sanitizeChatContent(result.text);
318069
319162
  }
318070
319163
  fullContent = content;
318071
319164
  if (content) {
318072
319165
  res.write("data: " + JSON.stringify({
318073
- id: `chatcmpl-${session.id.slice(0, 8)}`,
319166
+ id: `chatcmpl-${session.id.slice(0, 12)}`,
318074
319167
  object: "chat.completion.chunk",
318075
- choices: [{ index: 0, delta: { content }, finish_reason: null }]
319168
+ created: Math.floor(Date.now() / 1e3),
319169
+ model: model.replace(/^[a-z]+\//, ""),
319170
+ choices: [{ index: 0, delta: { content }, finish_reason: "stop" }]
318076
319171
  }) + "\n\n");
319172
+ } else {
319173
+ const errMsg = backendError ? `Backend error: ${backendError}` : "Agent produced no response. The model may have failed to load.";
319174
+ res.write("data: " + JSON.stringify({
319175
+ id: `chatcmpl-${session.id.slice(0, 12)}`,
319176
+ object: "chat.completion.chunk",
319177
+ created: Math.floor(Date.now() / 1e3),
319178
+ model: model.replace(/^[a-z]+\//, ""),
319179
+ choices: [{ index: 0, delta: { content: errMsg }, finish_reason: "error" }]
319180
+ }) + "\n\n");
319181
+ fullContent = errMsg;
318077
319182
  }
318078
319183
  if (!toolCallsStreamed && result.tool_calls?.length) {
318079
319184
  for (const tc of result.tool_calls) {
318080
319185
  res.write("data: " + JSON.stringify({ type: "tool_call", tool: tc.tool, args: tc.args }) + "\n\n");
318081
319186
  }
318082
319187
  }
318083
- const meta = result.summary || "";
319188
+ } catch {
319189
+ const errMsg = "Agent subprocess produced no parseable output.";
319190
+ fullContent = errMsg;
318084
319191
  res.write("data: " + JSON.stringify({
318085
- type: "complete",
318086
- turns: meta.match(/(\d+) turns/)?.[1],
318087
- tokens: meta.match(/Tokens:\s*([\d,]+)/)?.[1],
318088
- toolCalls: toolCallsStreamed || result.tool_calls?.length || 0,
318089
- duration: result.durationMs
319192
+ id: `chatcmpl-${session.id.slice(0, 12)}`,
319193
+ object: "chat.completion.chunk",
319194
+ created: Math.floor(Date.now() / 1e3),
319195
+ model: model.replace(/^[a-z]+\//, ""),
319196
+ choices: [{ index: 0, delta: { content: errMsg }, finish_reason: "error" }]
318090
319197
  }) + "\n\n");
318091
- } catch {
318092
- if (rawFinal) {
318093
- fullContent = rawFinal.slice(0, 500);
318094
- res.write("data: " + JSON.stringify({
318095
- id: `chatcmpl-${session.id.slice(0, 8)}`,
318096
- object: "chat.completion.chunk",
318097
- choices: [{ index: 0, delta: { content: fullContent }, finish_reason: null }]
318098
- }) + "\n\n");
318099
- }
318100
319198
  }
318101
319199
  addAssistantMessage(session, fullContent);
318102
319200
  res.write("data: [DONE]\n\n");
@@ -318129,23 +319227,73 @@ ${historyLines}
318129
319227
  if (nonStreamBuf.trim()) nonStreamLines.push(nonStreamBuf);
318130
319228
  const rawNonStream = nonStreamLines.join("\n").trim();
318131
319229
  let content = "";
319230
+ let backendError;
319231
+ let durationMs = 0;
319232
+ let toolCallCount = 0;
319233
+ let parsed = null;
318132
319234
  try {
318133
- const result = JSON.parse(rawNonStream);
318134
- if (result.assistant_text) {
318135
- content = result.assistant_text;
319235
+ parsed = JSON.parse(rawNonStream);
319236
+ durationMs = parsed.durationMs || 0;
319237
+ toolCallCount = parsed.tool_calls?.length || 0;
319238
+ if (parsed.error) backendError = String(parsed.error);
319239
+ if (parsed.assistant_text) {
319240
+ content = sanitizeChatContent(parsed.assistant_text);
318136
319241
  }
318137
319242
  if (!content) {
318138
- const summary = result.summary || "";
319243
+ const summary = parsed.summary || "";
318139
319244
  const summaryMatch = summary.match(/Tokens:\s*[\d,]+\s+([\s\S]*)/);
318140
- content = summaryMatch ? summaryMatch[1].trim() : summary;
319245
+ content = sanitizeChatContent(summaryMatch ? summaryMatch[1] : summary);
319246
+ }
319247
+ if (!content && parsed.text) {
319248
+ content = sanitizeChatContent(parsed.text);
318141
319249
  }
318142
319250
  } catch {
318143
- content = rawNonStream.slice(0, 500);
319251
+ content = sanitizeChatContent(rawNonStream.slice(0, 500));
319252
+ }
319253
+ const created = Math.floor(Date.now() / 1e3);
319254
+ const id = `chatcmpl-${session.id.slice(0, 12)}`;
319255
+ const cleanModel = model.replace(/^[a-z]+\//, "");
319256
+ if (!content) {
319257
+ const errMsg = backendError ? `Backend error: ${backendError}` : "Agent produced no response. The model may have failed to load (try a smaller model or check 'ollama ps' for VRAM contention).";
319258
+ jsonResponse(res, 200, {
319259
+ id,
319260
+ object: "chat.completion",
319261
+ created,
319262
+ model: cleanModel,
319263
+ choices: [{
319264
+ index: 0,
319265
+ message: { role: "assistant", content: errMsg },
319266
+ finish_reason: "error"
319267
+ }],
319268
+ usage: { prompt_tokens: 0, completion_tokens: 0, total_tokens: 0 },
319269
+ session_id: session.id,
319270
+ tool_calls: toolCallCount,
319271
+ duration_ms: durationMs,
319272
+ error: backendError ?? null
319273
+ });
319274
+ return;
318144
319275
  }
318145
319276
  addAssistantMessage(session, content.trim());
319277
+ const promptTokens = Math.round(session.messages.map((m2) => m2.content).join("").length / 4);
319278
+ const completionTokens = Math.round(content.length / 4);
318146
319279
  jsonResponse(res, 200, {
319280
+ id,
319281
+ object: "chat.completion",
319282
+ created,
319283
+ model: cleanModel,
319284
+ choices: [{
319285
+ index: 0,
319286
+ message: { role: "assistant", content: content.trim() },
319287
+ finish_reason: "stop"
319288
+ }],
319289
+ usage: {
319290
+ prompt_tokens: promptTokens,
319291
+ completion_tokens: completionTokens,
319292
+ total_tokens: promptTokens + completionTokens
319293
+ },
318147
319294
  session_id: session.id,
318148
- message: { role: "assistant", content: content.trim() }
319295
+ tool_calls: toolCallCount,
319296
+ duration_ms: durationMs
318149
319297
  });
318150
319298
  }
318151
319299
  return;
@@ -318401,19 +319549,19 @@ function startApiServer(options2 = {}) {
318401
319549
  const config = loadConfig();
318402
319550
  const ollamaUrl = options2.ollamaUrl ?? config.backendUrl;
318403
319551
  const cwd4 = process.cwd();
318404
- initAuditLog(join94(cwd4, ".oa"));
318405
- initUsageTracker(join94(cwd4, ".oa"));
319552
+ initAuditLog(join95(cwd4, ".oa"));
319553
+ initUsageTracker(join95(cwd4, ".oa"));
318406
319554
  const retentionDays = parseInt(process.env["OA_JOB_RETENTION_DAYS"] ?? "30", 10);
318407
319555
  if (retentionDays > 0) {
318408
319556
  try {
318409
- const jobsDir3 = join94(cwd4, ".oa", "jobs");
318410
- if (existsSync77(jobsDir3)) {
319557
+ const jobsDir3 = join95(cwd4, ".oa", "jobs");
319558
+ if (existsSync78(jobsDir3)) {
318411
319559
  const cutoff = Date.now() - retentionDays * 864e5;
318412
- for (const f2 of readdirSync25(jobsDir3)) {
319560
+ for (const f2 of readdirSync26(jobsDir3)) {
318413
319561
  if (!f2.endsWith(".json")) continue;
318414
319562
  try {
318415
- const jobPath = join94(jobsDir3, f2);
318416
- const job = JSON.parse(readFileSync60(jobPath, "utf-8"));
319563
+ const jobPath = join95(jobsDir3, f2);
319564
+ const job = JSON.parse(readFileSync61(jobPath, "utf-8"));
318417
319565
  const jobTime = new Date(job.startedAt ?? job.completedAt ?? 0).getTime();
318418
319566
  if (jobTime > 0 && jobTime < cutoff && job.status !== "running") {
318419
319567
  const { unlinkSync: unlinkSync19 } = __require("node:fs");
@@ -318433,8 +319581,8 @@ function startApiServer(options2 = {}) {
318433
319581
  if (useTls) {
318434
319582
  try {
318435
319583
  tlsOpts = {
318436
- cert: readFileSync60(resolve34(tlsCert)),
318437
- key: readFileSync60(resolve34(tlsKey))
319584
+ cert: readFileSync61(resolve34(tlsCert)),
319585
+ key: readFileSync61(resolve34(tlsKey))
318438
319586
  };
318439
319587
  } catch (e2) {
318440
319588
  log22(`
@@ -318634,13 +319782,13 @@ var init_serve = __esm({
318634
319782
 
318635
319783
  // packages/cli/src/tui/interactive.ts
318636
319784
  import { cwd } from "node:process";
318637
- import { resolve as resolve35, join as join95, dirname as dirname27, extname as extname11 } from "node:path";
319785
+ import { resolve as resolve35, join as join96, dirname as dirname27, extname as extname11 } from "node:path";
318638
319786
  import { createRequire as createRequire5 } from "node:module";
318639
319787
  import { fileURLToPath as fileURLToPath17 } from "node:url";
318640
- import { readFileSync as readFileSync61, writeFileSync as writeFileSync41, appendFileSync as appendFileSync6, rmSync as rmSync4, readdirSync as readdirSync26, mkdirSync as mkdirSync47 } from "node:fs";
318641
- import { existsSync as existsSync78 } from "node:fs";
318642
- import { execSync as execSync55 } from "node:child_process";
318643
- import { homedir as homedir31 } from "node:os";
319788
+ import { readFileSync as readFileSync62, writeFileSync as writeFileSync41, appendFileSync as appendFileSync6, rmSync as rmSync4, readdirSync as readdirSync27, mkdirSync as mkdirSync47 } from "node:fs";
319789
+ import { existsSync as existsSync79 } from "node:fs";
319790
+ import { execSync as execSync56 } from "node:child_process";
319791
+ import { homedir as homedir32 } from "node:os";
318644
319792
  function formatTimeAgo(date) {
318645
319793
  const seconds = Math.floor((Date.now() - date.getTime()) / 1e3);
318646
319794
  if (seconds < 60) return "just now";
@@ -318656,12 +319804,12 @@ function getVersion4() {
318656
319804
  const require3 = createRequire5(import.meta.url);
318657
319805
  const thisDir = dirname27(fileURLToPath17(import.meta.url));
318658
319806
  const candidates = [
318659
- join95(thisDir, "..", "package.json"),
318660
- join95(thisDir, "..", "..", "package.json"),
318661
- join95(thisDir, "..", "..", "..", "package.json")
319807
+ join96(thisDir, "..", "package.json"),
319808
+ join96(thisDir, "..", "..", "package.json"),
319809
+ join96(thisDir, "..", "..", "..", "package.json")
318662
319810
  ];
318663
319811
  for (const pkgPath of candidates) {
318664
- if (existsSync78(pkgPath)) {
319812
+ if (existsSync79(pkgPath)) {
318665
319813
  const pkg = require3(pkgPath);
318666
319814
  if (pkg.name === "open-agents-ai" || pkg.name === "@open-agents/cli" || pkg.name === "@open-agents/monorepo") {
318667
319815
  return pkg.version ?? "0.0.0";
@@ -319168,14 +320316,14 @@ Meta-critique: quality ${meta.quality}/5, thorough: ${meta.thorough}`;
319168
320316
  function gatherMemorySnippets(root) {
319169
320317
  const snippets = [];
319170
320318
  const dirs = [
319171
- join95(root, ".oa", "memory"),
319172
- join95(root, ".open-agents", "memory")
320319
+ join96(root, ".oa", "memory"),
320320
+ join96(root, ".open-agents", "memory")
319173
320321
  ];
319174
320322
  for (const dir of dirs) {
319175
- if (!existsSync78(dir)) continue;
320323
+ if (!existsSync79(dir)) continue;
319176
320324
  try {
319177
- for (const f2 of readdirSync26(dir).filter((f3) => f3.endsWith(".json"))) {
319178
- const data = JSON.parse(readFileSync61(join95(dir, f2), "utf-8"));
320325
+ for (const f2 of readdirSync27(dir).filter((f3) => f3.endsWith(".json"))) {
320326
+ const data = JSON.parse(readFileSync62(join96(dir, f2), "utf-8"));
319179
320327
  for (const val of Object.values(data)) {
319180
320328
  const v = typeof val === "object" && val !== null && "value" in val ? String(val.value) : String(val);
319181
320329
  if (v.length > 10) snippets.push(v);
@@ -319329,9 +320477,9 @@ ${metabolismMemories}
319329
320477
  } catch {
319330
320478
  }
319331
320479
  try {
319332
- const archeFile = join95(repoRoot, ".oa", "arche", "variants.json");
319333
- if (existsSync78(archeFile)) {
319334
- const variants = JSON.parse(readFileSync61(archeFile, "utf8"));
320480
+ const archeFile = join96(repoRoot, ".oa", "arche", "variants.json");
320481
+ if (existsSync79(archeFile)) {
320482
+ const variants = JSON.parse(readFileSync62(archeFile, "utf8"));
319335
320483
  if (variants.length > 0) {
319336
320484
  let filtered = variants;
319337
320485
  if (taskType) {
@@ -319500,9 +320648,9 @@ RULES:
319500
320648
  const compactionThreshold = modelTier === "small" ? 12e3 : modelTier === "medium" ? 24e3 : 4e4;
319501
320649
  let identityInjection = "";
319502
320650
  try {
319503
- const ikStateFile = join95(repoRoot, ".oa", "identity", "self-state.json");
319504
- if (existsSync78(ikStateFile)) {
319505
- const selfState = JSON.parse(readFileSync61(ikStateFile, "utf8"));
320651
+ const ikStateFile = join96(repoRoot, ".oa", "identity", "self-state.json");
320652
+ if (existsSync79(ikStateFile)) {
320653
+ const selfState = JSON.parse(readFileSync62(ikStateFile, "utf8"));
319506
320654
  const lines = [
319507
320655
  `[Identity State v${selfState.version}]`,
319508
320656
  `Self: ${selfState.narrative_summary}`,
@@ -320230,11 +321378,11 @@ When done, either call task_complete with your answer, or use FINAL_VAR(variable
320230
321378
  });
320231
321379
  }
320232
321380
  try {
320233
- const ikDir = join95(repoRoot, ".oa", "identity");
320234
- const ikFile = join95(ikDir, "self-state.json");
321381
+ const ikDir = join96(repoRoot, ".oa", "identity");
321382
+ const ikFile = join96(ikDir, "self-state.json");
320235
321383
  let ikState;
320236
- if (existsSync78(ikFile)) {
320237
- ikState = JSON.parse(readFileSync61(ikFile, "utf8"));
321384
+ if (existsSync79(ikFile)) {
321385
+ ikState = JSON.parse(readFileSync62(ikFile, "utf8"));
320238
321386
  } else {
320239
321387
  mkdirSync47(ikDir, { recursive: true });
320240
321388
  const machineId = Date.now().toString(36) + Math.random().toString(36).slice(2, 8);
@@ -320311,9 +321459,9 @@ When done, either call task_complete with your answer, or use FINAL_VAR(variable
320311
321459
  } else {
320312
321460
  renderTaskIncomplete(result.turns, result.toolCalls, result.durationMs, tokens);
320313
321461
  try {
320314
- const ikFile = join95(repoRoot, ".oa", "identity", "self-state.json");
320315
- if (existsSync78(ikFile)) {
320316
- const ikState = JSON.parse(readFileSync61(ikFile, "utf8"));
321462
+ const ikFile = join96(repoRoot, ".oa", "identity", "self-state.json");
321463
+ if (existsSync79(ikFile)) {
321464
+ const ikState = JSON.parse(readFileSync62(ikFile, "utf8"));
320317
321465
  if (!ikState.stats) ikState.stats = { queries_served: 0 };
320318
321466
  ikState.stats.queries_served = (ikState.stats.queries_served || 0) + 1;
320319
321467
  ikState.homeostasis.uncertainty = Math.min(1, ikState.homeostasis.uncertainty + 0.03);
@@ -320439,10 +321587,10 @@ async function startInteractive(config, repoPath) {
320439
321587
  process.stdin.pause();
320440
321588
  }
320441
321589
  try {
320442
- const oaDir = join95(repoRoot, ".oa");
320443
- const nexusPidFile = join95(oaDir, "nexus", "daemon.pid");
320444
- if (existsSync78(nexusPidFile)) {
320445
- const pid = parseInt(readFileSync61(nexusPidFile, "utf8").trim(), 10);
321590
+ const oaDir = join96(repoRoot, ".oa");
321591
+ const nexusPidFile = join96(oaDir, "nexus", "daemon.pid");
321592
+ if (existsSync79(nexusPidFile)) {
321593
+ const pid = parseInt(readFileSync62(nexusPidFile, "utf8").trim(), 10);
320446
321594
  if (pid > 0) {
320447
321595
  try {
320448
321596
  process.kill(pid, 0);
@@ -320987,7 +322135,7 @@ ${opts.systemPromptAddition}` : `Working directory: ${repoRoot}`;
320987
322135
  let p2pGateway = null;
320988
322136
  let peerMesh = null;
320989
322137
  let inferenceRouter = null;
320990
- const secretVault = new SecretVault(join95(repoRoot, ".oa", "vault.enc"));
322138
+ const secretVault = new SecretVault(join96(repoRoot, ".oa", "vault.enc"));
320991
322139
  let adminSessionKey = null;
320992
322140
  const callSubAgents = /* @__PURE__ */ new Map();
320993
322141
  const streamRenderer = new StreamRenderer();
@@ -321206,13 +322354,13 @@ Rationale: ${proposal.rationale}${provenanceNote}`;
321206
322354
  const hits = allCompletions.filter((c7) => c7.toLowerCase().startsWith(lower));
321207
322355
  return [hits, line];
321208
322356
  }
321209
- const HISTORY_DIR = join95(homedir31(), ".open-agents");
321210
- const HISTORY_FILE = join95(HISTORY_DIR, "repl-history");
322357
+ const HISTORY_DIR = join96(homedir32(), ".open-agents");
322358
+ const HISTORY_FILE = join96(HISTORY_DIR, "repl-history");
321211
322359
  const MAX_HISTORY_LINES = 500;
321212
322360
  let savedHistory = [];
321213
322361
  try {
321214
- if (existsSync78(HISTORY_FILE)) {
321215
- const raw = readFileSync61(HISTORY_FILE, "utf8").trim();
322362
+ if (existsSync79(HISTORY_FILE)) {
322363
+ const raw = readFileSync62(HISTORY_FILE, "utf8").trim();
321216
322364
  if (raw) savedHistory = raw.split("\n").reverse();
321217
322365
  }
321218
322366
  } catch {
@@ -321340,7 +322488,7 @@ Rationale: ${proposal.rationale}${provenanceNote}`;
321340
322488
  mkdirSync47(HISTORY_DIR, { recursive: true });
321341
322489
  appendFileSync6(HISTORY_FILE, line + "\n", "utf8");
321342
322490
  if (Math.random() < 0.02) {
321343
- const all2 = readFileSync61(HISTORY_FILE, "utf8").trim().split("\n");
322491
+ const all2 = readFileSync62(HISTORY_FILE, "utf8").trim().split("\n");
321344
322492
  if (all2.length > MAX_HISTORY_LINES) {
321345
322493
  writeFileSync41(HISTORY_FILE, all2.slice(-MAX_HISTORY_LINES).join("\n") + "\n", "utf8");
321346
322494
  }
@@ -321516,10 +322664,10 @@ Rationale: ${proposal.rationale}${provenanceNote}`;
321516
322664
  const { unlinkSync: _rmStale } = await import("node:fs");
321517
322665
  const { homedir: _hdir } = await import("node:os");
321518
322666
  for (const dp of [
321519
- join95(repoRoot, ".oa", "nexus", "nexus-daemon.mjs"),
321520
- join95(_hdir(), ".open-agents", ".oa", "nexus", "nexus-daemon.mjs")
322667
+ join96(repoRoot, ".oa", "nexus", "nexus-daemon.mjs"),
322668
+ join96(_hdir(), ".open-agents", ".oa", "nexus", "nexus-daemon.mjs")
321521
322669
  ]) {
321522
- if (existsSync78(dp)) try {
322670
+ if (existsSync79(dp)) try {
321523
322671
  _rmStale(dp);
321524
322672
  } catch {
321525
322673
  }
@@ -321530,9 +322678,9 @@ Rationale: ${proposal.rationale}${provenanceNote}`;
321530
322678
  const autoNexus = new NexusTool(repoRoot);
321531
322679
  const _registerNexusDaemon = () => {
321532
322680
  try {
321533
- const nexusPidFile = join95(repoRoot, ".oa", "nexus", "daemon.pid");
321534
- if (existsSync78(nexusPidFile)) {
321535
- const nPid = parseInt(readFileSync61(nexusPidFile, "utf8").trim(), 10);
322681
+ const nexusPidFile = join96(repoRoot, ".oa", "nexus", "daemon.pid");
322682
+ if (existsSync79(nexusPidFile)) {
322683
+ const nPid = parseInt(readFileSync62(nexusPidFile, "utf8").trim(), 10);
321536
322684
  if (nPid > 0 && !registry2.daemons.has("Nexus")) {
321537
322685
  registry2.register({ name: "Nexus", pid: nPid, startedAt: Date.now(), status: "running" });
321538
322686
  statusBar.ensureMonitorTimer();
@@ -321579,7 +322727,7 @@ Rationale: ${proposal.rationale}${provenanceNote}`;
321579
322727
  } catch {
321580
322728
  }
321581
322729
  try {
321582
- const oaDir = join95(repoRoot, ".oa");
322730
+ const oaDir = join96(repoRoot, ".oa");
321583
322731
  const reconnected = await ExposeGateway.checkAndReconnect(oaDir, {
321584
322732
  onInfo: (msg) => writeContent(() => renderInfo(msg)),
321585
322733
  onError: (msg) => writeContent(() => renderWarning(msg))
@@ -321611,7 +322759,7 @@ Rationale: ${proposal.rationale}${provenanceNote}`;
321611
322759
  } catch {
321612
322760
  }
321613
322761
  try {
321614
- const oaDir = join95(repoRoot, ".oa");
322762
+ const oaDir = join96(repoRoot, ".oa");
321615
322763
  const reconnectedP2P = await ExposeP2PGateway.checkAndReconnect(oaDir, new NexusTool(repoRoot), {
321616
322764
  onInfo: (msg) => writeContent(() => renderInfo(msg)),
321617
322765
  onError: (msg) => writeContent(() => renderWarning(msg))
@@ -321652,10 +322800,10 @@ Rationale: ${proposal.rationale}${provenanceNote}`;
321652
322800
  }
321653
322801
  try {
321654
322802
  const { homedir: _hd, hostname: _hn, userInfo: _ui } = await import("node:os");
321655
- const globalNamePath = join95(_hd(), ".open-agents", "agent-name");
322803
+ const globalNamePath = join96(_hd(), ".open-agents", "agent-name");
321656
322804
  let agName = "";
321657
322805
  try {
321658
- if (existsSync78(globalNamePath)) agName = readFileSync61(globalNamePath, "utf8").trim();
322806
+ if (existsSync79(globalNamePath)) agName = readFileSync62(globalNamePath, "utf8").trim();
321659
322807
  } catch {
321660
322808
  }
321661
322809
  if (!agName) {
@@ -321684,11 +322832,11 @@ Rationale: ${proposal.rationale}${provenanceNote}`;
321684
322832
  }
321685
322833
  if (!ollamaAlive) {
321686
322834
  try {
321687
- const savedSponsorsPath = join95(repoRoot, ".oa", "sponsor", "known-sponsors.json");
322835
+ const savedSponsorsPath = join96(repoRoot, ".oa", "sponsor", "known-sponsors.json");
321688
322836
  let savedSponsors = [];
321689
322837
  try {
321690
- if (existsSync78(savedSponsorsPath)) {
321691
- savedSponsors = JSON.parse(readFileSync61(savedSponsorsPath, "utf8"));
322838
+ if (existsSync79(savedSponsorsPath)) {
322839
+ savedSponsors = JSON.parse(readFileSync62(savedSponsorsPath, "utf8"));
321692
322840
  const oneHourAgo = Date.now() - 36e5;
321693
322841
  savedSponsors = savedSponsors.filter((s2) => (s2.lastVerified || 0) > oneHourAgo);
321694
322842
  }
@@ -322655,7 +323803,7 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
322655
323803
  kind,
322656
323804
  targetUrl,
322657
323805
  authKey,
322658
- stateDir: join95(repoRoot, ".oa"),
323806
+ stateDir: join96(repoRoot, ".oa"),
322659
323807
  passthrough: passthrough ?? false,
322660
323808
  loadbalance: loadbalance ?? false,
322661
323809
  endpointAuth: passthrough ? currentConfig.apiKey : void 0,
@@ -322701,7 +323849,7 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
322701
323849
  await tunnelGateway.stop();
322702
323850
  tunnelGateway = null;
322703
323851
  }
322704
- const newTunnel = new ExposeGateway({ kind, targetUrl, authKey, fullAccess, stateDir: join95(repoRoot, ".oa") });
323852
+ const newTunnel = new ExposeGateway({ kind, targetUrl, authKey, fullAccess, stateDir: join96(repoRoot, ".oa") });
322705
323853
  newTunnel.on("stats", (stats) => {
322706
323854
  statusBar.setExposeStatus({
322707
323855
  status: stats.status,
@@ -322788,9 +323936,9 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
322788
323936
  });
322789
323937
  if (!result.success) throw new Error(result.error || "Connect failed");
322790
323938
  try {
322791
- const nexusPidFile = join95(repoRoot, ".oa", "nexus", "daemon.pid");
322792
- if (existsSync78(nexusPidFile)) {
322793
- const pid = parseInt(readFileSync61(nexusPidFile, "utf8").trim(), 10);
323939
+ const nexusPidFile = join96(repoRoot, ".oa", "nexus", "daemon.pid");
323940
+ if (existsSync79(nexusPidFile)) {
323941
+ const pid = parseInt(readFileSync62(nexusPidFile, "utf8").trim(), 10);
322794
323942
  if (pid > 0) {
322795
323943
  registry2.register({
322796
323944
  name: "Nexus",
@@ -322976,15 +324124,15 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
322976
324124
  writeContent(() => renderInfo(`Killed ${bgKilled} background task(s).`));
322977
324125
  }
322978
324126
  try {
322979
- const nexusDir = join95(repoRoot, OA_DIR, "nexus");
322980
- const pidFile = join95(nexusDir, "daemon.pid");
322981
- if (existsSync78(pidFile)) {
322982
- const pid = parseInt(readFileSync61(pidFile, "utf8").trim(), 10);
324127
+ const nexusDir = join96(repoRoot, OA_DIR, "nexus");
324128
+ const pidFile = join96(nexusDir, "daemon.pid");
324129
+ if (existsSync79(pidFile)) {
324130
+ const pid = parseInt(readFileSync62(pidFile, "utf8").trim(), 10);
322983
324131
  if (pid > 0) {
322984
324132
  try {
322985
324133
  if (process.platform === "win32") {
322986
324134
  try {
322987
- execSync55(`taskkill /F /PID ${pid}`, { timeout: 5e3, stdio: "ignore" });
324135
+ execSync56(`taskkill /F /PID ${pid}`, { timeout: 5e3, stdio: "ignore" });
322988
324136
  } catch {
322989
324137
  }
322990
324138
  } else {
@@ -323001,17 +324149,17 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
323001
324149
  } catch {
323002
324150
  }
323003
324151
  try {
323004
- const voiceDir2 = join95(homedir31(), ".open-agents", "voice");
324152
+ const voiceDir2 = join96(homedir32(), ".open-agents", "voice");
323005
324153
  const voicePidFiles = ["luxtts-daemon.pid", "piper-daemon.pid"];
323006
324154
  for (const pf of voicePidFiles) {
323007
- const pidPath = join95(voiceDir2, pf);
323008
- if (existsSync78(pidPath)) {
324155
+ const pidPath = join96(voiceDir2, pf);
324156
+ if (existsSync79(pidPath)) {
323009
324157
  try {
323010
- const pid = parseInt(readFileSync61(pidPath, "utf8").trim(), 10);
324158
+ const pid = parseInt(readFileSync62(pidPath, "utf8").trim(), 10);
323011
324159
  if (pid > 0) {
323012
324160
  if (process.platform === "win32") {
323013
324161
  try {
323014
- execSync55(`taskkill /F /PID ${pid}`, { timeout: 5e3, stdio: "ignore" });
324162
+ execSync56(`taskkill /F /PID ${pid}`, { timeout: 5e3, stdio: "ignore" });
323015
324163
  } catch {
323016
324164
  }
323017
324165
  } else {
@@ -323028,11 +324176,11 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
323028
324176
  } catch {
323029
324177
  }
323030
324178
  try {
323031
- execSync55(process.platform === "win32" ? "timeout /t 1 /nobreak >nul" : "sleep 0.5", { timeout: 3e3, stdio: "ignore" });
324179
+ execSync56(process.platform === "win32" ? "timeout /t 1 /nobreak >nul" : "sleep 0.5", { timeout: 3e3, stdio: "ignore" });
323032
324180
  } catch {
323033
324181
  }
323034
- const oaPath = join95(repoRoot, OA_DIR);
323035
- if (existsSync78(oaPath)) {
324182
+ const oaPath = join96(repoRoot, OA_DIR);
324183
+ if (existsSync79(oaPath)) {
323036
324184
  let deleted = false;
323037
324185
  for (let attempt = 0; attempt < 3; attempt++) {
323038
324186
  try {
@@ -323042,14 +324190,14 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
323042
324190
  } catch (err) {
323043
324191
  if (attempt < 2) {
323044
324192
  try {
323045
- execSync55(process.platform === "win32" ? "timeout /t 1 /nobreak >nul" : "sleep 0.3", { timeout: 3e3, stdio: "ignore" });
324193
+ execSync56(process.platform === "win32" ? "timeout /t 1 /nobreak >nul" : "sleep 0.3", { timeout: 3e3, stdio: "ignore" });
323046
324194
  } catch {
323047
324195
  }
323048
324196
  } else {
323049
324197
  writeContent(() => renderWarning(`Could not fully remove ${OA_DIR}/: ${err instanceof Error ? err.message : String(err)}`));
323050
324198
  if (process.platform === "win32") {
323051
324199
  try {
323052
- execSync55(`rd /s /q "${oaPath}"`, { timeout: 1e4, stdio: "ignore" });
324200
+ execSync56(`rd /s /q "${oaPath}"`, { timeout: 1e4, stdio: "ignore" });
323053
324201
  deleted = true;
323054
324202
  } catch {
323055
324203
  }
@@ -323115,19 +324263,19 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
323115
324263
  try {
323116
324264
  const { isPersonaPlexRunning: isPersonaPlexRunning2 } = await Promise.resolve().then(() => (init_personaplex(), personaplex_exports));
323117
324265
  if (isPersonaPlexRunning2()) {
323118
- const ppPidFile = join95(homedir31(), ".open-agents", "voice", "personaplex", "daemon.pid");
323119
- const ppPortFile = join95(homedir31(), ".open-agents", "voice", "personaplex", "daemon.port");
323120
- if (existsSync78(ppPidFile)) {
323121
- const ppPid = parseInt(readFileSync61(ppPidFile, "utf8").trim(), 10);
323122
- const ppPort = existsSync78(ppPortFile) ? parseInt(readFileSync61(ppPortFile, "utf8").trim(), 10) : void 0;
324266
+ const ppPidFile = join96(homedir32(), ".open-agents", "voice", "personaplex", "daemon.pid");
324267
+ const ppPortFile = join96(homedir32(), ".open-agents", "voice", "personaplex", "daemon.port");
324268
+ if (existsSync79(ppPidFile)) {
324269
+ const ppPid = parseInt(readFileSync62(ppPidFile, "utf8").trim(), 10);
324270
+ const ppPort = existsSync79(ppPortFile) ? parseInt(readFileSync62(ppPortFile, "utf8").trim(), 10) : void 0;
323123
324271
  if (ppPid > 0 && !registry2.daemons.has("PersonaPlex")) {
323124
324272
  registry2.register({ name: "PersonaPlex", pid: ppPid, port: ppPort, startedAt: Date.now(), status: "running" });
323125
324273
  }
323126
324274
  }
323127
324275
  }
323128
- const nexusPidFile = join95(repoRoot, ".oa", "nexus", "daemon.pid");
323129
- if (existsSync78(nexusPidFile)) {
323130
- const nPid = parseInt(readFileSync61(nexusPidFile, "utf8").trim(), 10);
324276
+ const nexusPidFile = join96(repoRoot, ".oa", "nexus", "daemon.pid");
324277
+ if (existsSync79(nexusPidFile)) {
324278
+ const nPid = parseInt(readFileSync62(nexusPidFile, "utf8").trim(), 10);
323131
324279
  if (nPid > 0 && !registry2.daemons.has("Nexus")) {
323132
324280
  try {
323133
324281
  process.kill(nPid, 0);
@@ -323468,9 +324616,9 @@ Execute this skill now. Follow the behavioral guidance above.`;
323468
324616
  }
323469
324617
  }
323470
324618
  const cleanPath = input.replace(/^['"]|['"]$/g, "").trim();
323471
- const isImage = isImagePath(cleanPath) && existsSync78(resolve35(repoRoot, cleanPath));
323472
- const isMedia = !isImage && isTranscribablePath(cleanPath) && existsSync78(resolve35(repoRoot, cleanPath));
323473
- const isMarkdown = !isImage && !isMedia && /\.(md|markdown)$/i.test(cleanPath) && existsSync78(resolve35(repoRoot, cleanPath));
324619
+ const isImage = isImagePath(cleanPath) && existsSync79(resolve35(repoRoot, cleanPath));
324620
+ const isMedia = !isImage && isTranscribablePath(cleanPath) && existsSync79(resolve35(repoRoot, cleanPath));
324621
+ const isMarkdown = !isImage && !isMedia && /\.(md|markdown)$/i.test(cleanPath) && existsSync79(resolve35(repoRoot, cleanPath));
323474
324622
  if (activeTask) {
323475
324623
  if (activeTask.runner.isPaused) {
323476
324624
  activeTask.runner.resume();
@@ -323479,7 +324627,7 @@ Execute this skill now. Follow the behavioral guidance above.`;
323479
324627
  if (isImage) {
323480
324628
  try {
323481
324629
  const imgPath = resolve35(repoRoot, cleanPath);
323482
- const imgBuffer = readFileSync61(imgPath);
324630
+ const imgBuffer = readFileSync62(imgPath);
323483
324631
  const base642 = imgBuffer.toString("base64");
323484
324632
  const ext = extname11(cleanPath).toLowerCase();
323485
324633
  const mime = ext === ".png" ? "image/png" : ext === ".gif" ? "image/gif" : ext === ".webp" ? "image/webp" : "image/jpeg";
@@ -323647,7 +324795,7 @@ ${result.text}`;
323647
324795
  if (isMarkdown && fullInput === input) {
323648
324796
  try {
323649
324797
  const mdPath = resolve35(repoRoot, cleanPath);
323650
- const mdContent = readFileSync61(mdPath, "utf8");
324798
+ const mdContent = readFileSync62(mdPath, "utf8");
323651
324799
  const { parseMcpMarkdown: parseMcpMarkdown2 } = await Promise.resolve().then(() => (init_dist4(), dist_exports));
323652
324800
  const result = parseMcpMarkdown2(mdContent);
323653
324801
  if (result.servers.length > 0) {
@@ -323736,7 +324884,7 @@ Summarize or analyze this transcription as appropriate.`;
323736
324884
 
323737
324885
  NEW TASK: ${fullInput}`;
323738
324886
  restoredSessionContext = null;
323739
- } else if (existsSync78(join95(repoRoot, ".oa", "context", "session-diary.md"))) {
324887
+ } else if (existsSync79(join96(repoRoot, ".oa", "context", "session-diary.md"))) {
323740
324888
  taskInput = `[Previous sessions exist \u2014 file_read(".oa/context/session-diary.md") to recall]
323741
324889
 
323742
324890
  ${fullInput}`;
@@ -324070,11 +325218,11 @@ async function runWithTUI(task, config, repoPath, callbacks) {
324070
325218
  const handle2 = startTask(task, config, repoRoot);
324071
325219
  await handle2.promise;
324072
325220
  try {
324073
- const ikDir = join95(repoRoot, ".oa", "identity");
324074
- const ikFile = join95(ikDir, "self-state.json");
325221
+ const ikDir = join96(repoRoot, ".oa", "identity");
325222
+ const ikFile = join96(ikDir, "self-state.json");
324075
325223
  let ikState;
324076
- if (existsSync78(ikFile)) {
324077
- ikState = JSON.parse(readFileSync61(ikFile, "utf8"));
325224
+ if (existsSync79(ikFile)) {
325225
+ ikState = JSON.parse(readFileSync62(ikFile, "utf8"));
324078
325226
  } else {
324079
325227
  mkdirSync47(ikDir, { recursive: true });
324080
325228
  ikState = {
@@ -324111,11 +325259,11 @@ async function runWithTUI(task, config, repoPath, callbacks) {
324111
325259
  );
324112
325260
  } catch {
324113
325261
  try {
324114
- const archeDir = join95(repoRoot, ".oa", "arche");
324115
- const archeFile = join95(archeDir, "variants.json");
325262
+ const archeDir = join96(repoRoot, ".oa", "arche");
325263
+ const archeFile = join96(archeDir, "variants.json");
324116
325264
  let variants = [];
324117
325265
  try {
324118
- if (existsSync78(archeFile)) variants = JSON.parse(readFileSync61(archeFile, "utf8"));
325266
+ if (existsSync79(archeFile)) variants = JSON.parse(readFileSync62(archeFile, "utf8"));
324119
325267
  } catch {
324120
325268
  }
324121
325269
  variants.push({
@@ -324135,9 +325283,9 @@ async function runWithTUI(task, config, repoPath, callbacks) {
324135
325283
  }
324136
325284
  }
324137
325285
  try {
324138
- const metaFile = join95(repoRoot, ".oa", "memory", "metabolism", "store.json");
324139
- if (existsSync78(metaFile)) {
324140
- const store2 = JSON.parse(readFileSync61(metaFile, "utf8"));
325286
+ const metaFile = join96(repoRoot, ".oa", "memory", "metabolism", "store.json");
325287
+ if (existsSync79(metaFile)) {
325288
+ const store2 = JSON.parse(readFileSync62(metaFile, "utf8"));
324141
325289
  const surfaced = store2.filter((m2) => m2.type !== "quarantine" && m2.scores?.confidence > 0.15).sort((a2, b) => b.scores.utility * b.scores.confidence - a2.scores.utility * a2.scores.confidence).slice(0, 5);
324142
325290
  let updated = false;
324143
325291
  for (const item of surfaced) {
@@ -324201,9 +325349,9 @@ Rules:
324201
325349
  try {
324202
325350
  const { initDb: initDb2 } = __require("@open-agents/memory");
324203
325351
  const { ProceduralMemoryStore: ProceduralMemoryStore2 } = __require("@open-agents/memory");
324204
- const dbDir = join95(repoRoot, ".oa", "memory");
325352
+ const dbDir = join96(repoRoot, ".oa", "memory");
324205
325353
  mkdirSync47(dbDir, { recursive: true });
324206
- const db = initDb2(join95(dbDir, "structured.db"));
325354
+ const db = initDb2(join96(dbDir, "structured.db"));
324207
325355
  const memStore = new ProceduralMemoryStore2(db);
324208
325356
  memStore.createWithEmbedding({
324209
325357
  content: content.slice(0, 600),
@@ -324218,11 +325366,11 @@ Rules:
324218
325366
  db.close();
324219
325367
  } catch {
324220
325368
  }
324221
- const metaDir = join95(repoRoot, ".oa", "memory", "metabolism");
324222
- const storeFile = join95(metaDir, "store.json");
325369
+ const metaDir = join96(repoRoot, ".oa", "memory", "metabolism");
325370
+ const storeFile = join96(metaDir, "store.json");
324223
325371
  let store2 = [];
324224
325372
  try {
324225
- if (existsSync78(storeFile)) store2 = JSON.parse(readFileSync61(storeFile, "utf8"));
325373
+ if (existsSync79(storeFile)) store2 = JSON.parse(readFileSync62(storeFile, "utf8"));
324226
325374
  } catch {
324227
325375
  }
324228
325376
  store2.push({
@@ -324244,19 +325392,19 @@ Rules:
324244
325392
  } catch {
324245
325393
  }
324246
325394
  try {
324247
- const cohereSettingsFile = join95(repoRoot, ".oa", "settings.json");
325395
+ const cohereSettingsFile = join96(repoRoot, ".oa", "settings.json");
324248
325396
  let cohereActive = false;
324249
325397
  try {
324250
- if (existsSync78(cohereSettingsFile)) {
324251
- const settings = JSON.parse(readFileSync61(cohereSettingsFile, "utf8"));
325398
+ if (existsSync79(cohereSettingsFile)) {
325399
+ const settings = JSON.parse(readFileSync62(cohereSettingsFile, "utf8"));
324252
325400
  cohereActive = settings.cohere === true;
324253
325401
  }
324254
325402
  } catch {
324255
325403
  }
324256
325404
  if (cohereActive) {
324257
- const metaFile = join95(repoRoot, ".oa", "memory", "metabolism", "store.json");
324258
- if (existsSync78(metaFile)) {
324259
- const store2 = JSON.parse(readFileSync61(metaFile, "utf8"));
325405
+ const metaFile = join96(repoRoot, ".oa", "memory", "metabolism", "store.json");
325406
+ if (existsSync79(metaFile)) {
325407
+ const store2 = JSON.parse(readFileSync62(metaFile, "utf8"));
324260
325408
  const latest = store2.filter((m2) => m2.sourceTrace === "trajectory-extraction" || m2.sourceTrace === "llm-trajectory-extraction").slice(-1)[0];
324261
325409
  if (latest && latest.scores?.confidence >= 0.6) {
324262
325410
  try {
@@ -324281,18 +325429,18 @@ Rules:
324281
325429
  }
324282
325430
  } catch (err) {
324283
325431
  try {
324284
- const ikFile = join95(repoRoot, ".oa", "identity", "self-state.json");
324285
- if (existsSync78(ikFile)) {
324286
- const ikState = JSON.parse(readFileSync61(ikFile, "utf8"));
325432
+ const ikFile = join96(repoRoot, ".oa", "identity", "self-state.json");
325433
+ if (existsSync79(ikFile)) {
325434
+ const ikState = JSON.parse(readFileSync62(ikFile, "utf8"));
324287
325435
  ikState.homeostasis.uncertainty = Math.min(1, ikState.homeostasis.uncertainty + 0.1);
324288
325436
  ikState.homeostasis.coherence = Math.max(0, ikState.homeostasis.coherence - 0.05);
324289
325437
  ikState.session_count = (ikState.session_count || 0) + 1;
324290
325438
  ikState.updated_at = (/* @__PURE__ */ new Date()).toISOString();
324291
325439
  writeFileSync41(ikFile, JSON.stringify(ikState, null, 2));
324292
325440
  }
324293
- const metaFile = join95(repoRoot, ".oa", "memory", "metabolism", "store.json");
324294
- if (existsSync78(metaFile)) {
324295
- const store2 = JSON.parse(readFileSync61(metaFile, "utf8"));
325441
+ const metaFile = join96(repoRoot, ".oa", "memory", "metabolism", "store.json");
325442
+ if (existsSync79(metaFile)) {
325443
+ const store2 = JSON.parse(readFileSync62(metaFile, "utf8"));
324296
325444
  const surfaced = store2.filter((m2) => m2.type !== "quarantine" && m2.scores?.confidence > 0.15).sort((a2, b) => b.scores.utility * b.scores.confidence - a2.scores.utility * a2.scores.confidence).slice(0, 5);
324297
325445
  for (const item of surfaced) {
324298
325446
  item.accessCount = (item.accessCount || 0) + 1;
@@ -324303,11 +325451,11 @@ Rules:
324303
325451
  writeFileSync41(metaFile, JSON.stringify(store2, null, 2));
324304
325452
  }
324305
325453
  try {
324306
- const archeDir = join95(repoRoot, ".oa", "arche");
324307
- const archeFile = join95(archeDir, "variants.json");
325454
+ const archeDir = join96(repoRoot, ".oa", "arche");
325455
+ const archeFile = join96(archeDir, "variants.json");
324308
325456
  let variants = [];
324309
325457
  try {
324310
- if (existsSync78(archeFile)) variants = JSON.parse(readFileSync61(archeFile, "utf8"));
325458
+ if (existsSync79(archeFile)) variants = JSON.parse(readFileSync62(archeFile, "utf8"));
324311
325459
  } catch {
324312
325460
  }
324313
325461
  variants.push({
@@ -324405,12 +325553,12 @@ __export(run_exports, {
324405
325553
  });
324406
325554
  import { resolve as resolve36 } from "node:path";
324407
325555
  import { spawn as spawn26 } from "node:child_process";
324408
- import { mkdirSync as mkdirSync48, writeFileSync as writeFileSync42, readFileSync as readFileSync62, readdirSync as readdirSync27, existsSync as existsSync79 } from "node:fs";
325556
+ import { mkdirSync as mkdirSync48, writeFileSync as writeFileSync42, readFileSync as readFileSync63, readdirSync as readdirSync28, existsSync as existsSync80 } from "node:fs";
324409
325557
  import { randomBytes as randomBytes20 } from "node:crypto";
324410
- import { join as join96 } from "node:path";
325558
+ import { join as join97 } from "node:path";
324411
325559
  function jobsDir2(repoPath) {
324412
325560
  const root = resolve36(repoPath ?? process.cwd());
324413
- const dir = join96(root, ".oa", "jobs");
325561
+ const dir = join97(root, ".oa", "jobs");
324414
325562
  mkdirSync48(dir, { recursive: true });
324415
325563
  return dir;
324416
325564
  }
@@ -324526,7 +325674,7 @@ async function runBackground(task, config, opts) {
324526
325674
  }
324527
325675
  });
324528
325676
  job.pid = child.pid ?? 0;
324529
- writeFileSync42(join96(dir, `${id}.json`), JSON.stringify(job, null, 2));
325677
+ writeFileSync42(join97(dir, `${id}.json`), JSON.stringify(job, null, 2));
324530
325678
  let output = "";
324531
325679
  child.stdout?.on("data", (chunk) => {
324532
325680
  output += chunk.toString();
@@ -324542,7 +325690,7 @@ async function runBackground(task, config, opts) {
324542
325690
  job.summary = result.summary;
324543
325691
  job.durationMs = result.durationMs;
324544
325692
  job.error = result.error;
324545
- writeFileSync42(join96(dir, `${id}.json`), JSON.stringify(job, null, 2));
325693
+ writeFileSync42(join97(dir, `${id}.json`), JSON.stringify(job, null, 2));
324546
325694
  } catch {
324547
325695
  }
324548
325696
  });
@@ -324558,13 +325706,13 @@ async function runBackground(task, config, opts) {
324558
325706
  }
324559
325707
  function statusCommand(jobId, repoPath) {
324560
325708
  const dir = jobsDir2(repoPath);
324561
- const file = join96(dir, `${jobId}.json`);
324562
- if (!existsSync79(file)) {
325709
+ const file = join97(dir, `${jobId}.json`);
325710
+ if (!existsSync80(file)) {
324563
325711
  console.error(`Job not found: ${jobId}`);
324564
325712
  console.log(`Available jobs: oa jobs`);
324565
325713
  process.exit(1);
324566
325714
  }
324567
- const job = JSON.parse(readFileSync62(file, "utf-8"));
325715
+ const job = JSON.parse(readFileSync63(file, "utf-8"));
324568
325716
  const runtime = job.completedAt ? `${((new Date(job.completedAt).getTime() - new Date(job.startedAt).getTime()) / 1e3).toFixed(0)}s` : `${((Date.now() - new Date(job.startedAt).getTime()) / 1e3).toFixed(0)}s`;
324569
325717
  const icon = job.status === "completed" ? "\u2713" : job.status === "failed" ? "\u2717" : "\u25CF";
324570
325718
  console.log(`${icon} ${job.id} [${job.status}] ${runtime}`);
@@ -324577,7 +325725,7 @@ function statusCommand(jobId, repoPath) {
324577
325725
  }
324578
325726
  function jobsCommand(repoPath) {
324579
325727
  const dir = jobsDir2(repoPath);
324580
- const files = readdirSync27(dir).filter((f2) => f2.endsWith(".json")).sort();
325728
+ const files = readdirSync28(dir).filter((f2) => f2.endsWith(".json")).sort();
324581
325729
  if (files.length === 0) {
324582
325730
  console.log("No jobs found.");
324583
325731
  return;
@@ -324585,7 +325733,7 @@ function jobsCommand(repoPath) {
324585
325733
  console.log("Jobs:");
324586
325734
  for (const file of files) {
324587
325735
  try {
324588
- const job = JSON.parse(readFileSync62(join96(dir, file), "utf-8"));
325736
+ const job = JSON.parse(readFileSync63(join97(dir, file), "utf-8"));
324589
325737
  const icon = job.status === "completed" ? "\u2713" : job.status === "failed" ? "\u2717" : "\u25CF";
324590
325738
  const runtime = job.completedAt ? `${((new Date(job.completedAt).getTime() - new Date(job.startedAt).getTime()) / 1e3).toFixed(0)}s` : `${((Date.now() - new Date(job.startedAt).getTime()) / 1e3).toFixed(0)}s`;
324591
325739
  const cleanListTask = cleanForStorage(job.task) || job.task;
@@ -324607,7 +325755,7 @@ import { glob as glob2 } from "glob";
324607
325755
  import ignore from "ignore";
324608
325756
  import { readFile as readFile23, stat as stat5 } from "node:fs/promises";
324609
325757
  import { createHash as createHash8 } from "node:crypto";
324610
- import { join as join97, relative as relative5, extname as extname12, basename as basename18 } from "node:path";
325758
+ import { join as join98, relative as relative5, extname as extname12, basename as basename18 } from "node:path";
324611
325759
  var DEFAULT_EXCLUDE, LANGUAGE_MAP, CodebaseIndexer;
324612
325760
  var init_codebase_indexer = __esm({
324613
325761
  "packages/indexer/dist/codebase-indexer.js"() {
@@ -324651,7 +325799,7 @@ var init_codebase_indexer = __esm({
324651
325799
  const ig = ignore.default();
324652
325800
  if (this.config.respectGitignore) {
324653
325801
  try {
324654
- const gitignoreContent = await readFile23(join97(this.config.rootDir, ".gitignore"), "utf-8");
325802
+ const gitignoreContent = await readFile23(join98(this.config.rootDir, ".gitignore"), "utf-8");
324655
325803
  ig.add(gitignoreContent);
324656
325804
  } catch {
324657
325805
  }
@@ -324666,7 +325814,7 @@ var init_codebase_indexer = __esm({
324666
325814
  for (const relativePath of files) {
324667
325815
  if (ig.ignores(relativePath))
324668
325816
  continue;
324669
- const fullPath = join97(this.config.rootDir, relativePath);
325817
+ const fullPath = join98(this.config.rootDir, relativePath);
324670
325818
  try {
324671
325819
  const fileStat = await stat5(fullPath);
324672
325820
  if (fileStat.size > this.config.maxFileSize)
@@ -324712,7 +325860,7 @@ var init_codebase_indexer = __esm({
324712
325860
  if (!child) {
324713
325861
  child = {
324714
325862
  name: part,
324715
- path: join97(current.path, part),
325863
+ path: join98(current.path, part),
324716
325864
  type: "directory",
324717
325865
  children: []
324718
325866
  };
@@ -324820,17 +325968,17 @@ __export(index_repo_exports, {
324820
325968
  indexRepoCommand: () => indexRepoCommand
324821
325969
  });
324822
325970
  import { resolve as resolve37 } from "node:path";
324823
- import { existsSync as existsSync80, statSync as statSync22 } from "node:fs";
325971
+ import { existsSync as existsSync81, statSync as statSync23 } from "node:fs";
324824
325972
  import { cwd as cwd2 } from "node:process";
324825
325973
  async function indexRepoCommand(opts, _config3) {
324826
325974
  const repoRoot = resolve37(opts.repoPath ?? cwd2());
324827
325975
  printHeader("Index Repository");
324828
325976
  printInfo(`Indexing: ${repoRoot}`);
324829
- if (!existsSync80(repoRoot)) {
325977
+ if (!existsSync81(repoRoot)) {
324830
325978
  printError(`Path does not exist: ${repoRoot}`);
324831
325979
  process.exit(1);
324832
325980
  }
324833
- const stat6 = statSync22(repoRoot);
325981
+ const stat6 = statSync23(repoRoot);
324834
325982
  if (!stat6.isDirectory()) {
324835
325983
  printError(`Path is not a directory: ${repoRoot}`);
324836
325984
  process.exit(1);
@@ -325078,8 +326226,8 @@ var config_exports2 = {};
325078
326226
  __export(config_exports2, {
325079
326227
  configCommand: () => configCommand
325080
326228
  });
325081
- import { join as join98, resolve as resolve38 } from "node:path";
325082
- import { homedir as homedir32 } from "node:os";
326229
+ import { join as join99, resolve as resolve38 } from "node:path";
326230
+ import { homedir as homedir33 } from "node:os";
325083
326231
  import { cwd as cwd3 } from "node:process";
325084
326232
  function redactIfSensitive(key, value2) {
325085
326233
  if (SENSITIVE_KEYS.has(key) && typeof value2 === "string" && value2.length > 0) {
@@ -325160,7 +326308,7 @@ function handleShow(opts, config) {
325160
326308
  }
325161
326309
  }
325162
326310
  printSection("Config File");
325163
- printInfo(`~/.open-agents/config.json (${join98(homedir32(), ".open-agents", "config.json")})`);
326311
+ printInfo(`~/.open-agents/config.json (${join99(homedir33(), ".open-agents", "config.json")})`);
325164
326312
  printSection("Priority Chain");
325165
326313
  printInfo(" 1. CLI flags (--model, --backend-url, etc.)");
325166
326314
  printInfo(" 2. Project .oa/settings.json (--local)");
@@ -325199,7 +326347,7 @@ function handleSet(opts, _config3) {
325199
326347
  const coerced = coerceForSettings(key, value2);
325200
326348
  saveProjectSettings(repoRoot, { [key]: coerced });
325201
326349
  printSuccess(`Project override set: ${key} = ${redactIfSensitive(key, value2)}`);
325202
- printInfo(`Saved to ${join98(repoRoot, ".oa", "settings.json")}`);
326350
+ printInfo(`Saved to ${join99(repoRoot, ".oa", "settings.json")}`);
325203
326351
  printInfo("This override applies only when running in this workspace.");
325204
326352
  } catch (err) {
325205
326353
  printError(`Failed to save: ${err instanceof Error ? err.message : String(err)}`);
@@ -325383,7 +326531,7 @@ __export(eval_exports, {
325383
326531
  });
325384
326532
  import { tmpdir as tmpdir20 } from "node:os";
325385
326533
  import { mkdirSync as mkdirSync49, writeFileSync as writeFileSync43 } from "node:fs";
325386
- import { join as join99 } from "node:path";
326534
+ import { join as join100 } from "node:path";
325387
326535
  async function evalCommand(opts, config) {
325388
326536
  const suiteName = opts.suite ?? "basic";
325389
326537
  const suite = SUITES[suiteName];
@@ -325512,10 +326660,10 @@ async function evalCommand(opts, config) {
325512
326660
  process.exit(failed > 0 ? 1 : 0);
325513
326661
  }
325514
326662
  function createTempEvalRepo() {
325515
- const dir = join99(tmpdir20(), `open-agents-eval-${Date.now()}`);
326663
+ const dir = join100(tmpdir20(), `open-agents-eval-${Date.now()}`);
325516
326664
  mkdirSync49(dir, { recursive: true });
325517
326665
  writeFileSync43(
325518
- join99(dir, "package.json"),
326666
+ join100(dir, "package.json"),
325519
326667
  JSON.stringify({ name: "eval-repo", version: "0.0.0" }, null, 2) + "\n",
325520
326668
  "utf8"
325521
326669
  );
@@ -325578,7 +326726,7 @@ init_updater();
325578
326726
  import { parseArgs as nodeParseArgs2 } from "node:util";
325579
326727
  import { createRequire as createRequire6 } from "node:module";
325580
326728
  import { fileURLToPath as fileURLToPath18 } from "node:url";
325581
- import { dirname as dirname28, join as join100 } from "node:path";
326729
+ import { dirname as dirname28, join as join101 } from "node:path";
325582
326730
 
325583
326731
  // packages/cli/src/cli.ts
325584
326732
  import { createInterface } from "node:readline";
@@ -325685,7 +326833,7 @@ init_output();
325685
326833
  function getVersion5() {
325686
326834
  try {
325687
326835
  const require3 = createRequire6(import.meta.url);
325688
- const pkgPath = join100(dirname28(fileURLToPath18(import.meta.url)), "..", "package.json");
326836
+ const pkgPath = join101(dirname28(fileURLToPath18(import.meta.url)), "..", "package.json");
325689
326837
  const pkg = require3(pkgPath);
325690
326838
  return pkg.version;
325691
326839
  } catch {
@@ -325978,11 +327126,11 @@ function crashLog(label, err) {
325978
327126
  `;
325979
327127
  try {
325980
327128
  const { appendFileSync: appendFileSync7, mkdirSync: mkdirSync50 } = __require("node:fs");
325981
- const { join: join101 } = __require("node:path");
325982
- const { homedir: homedir33 } = __require("node:os");
325983
- const logDir = join101(homedir33(), ".open-agents");
327129
+ const { join: join102 } = __require("node:path");
327130
+ const { homedir: homedir34 } = __require("node:os");
327131
+ const logDir = join102(homedir34(), ".open-agents");
325984
327132
  mkdirSync50(logDir, { recursive: true });
325985
- appendFileSync7(join101(logDir, "crash.log"), logLine);
327133
+ appendFileSync7(join102(logDir, "crash.log"), logLine);
325986
327134
  } catch {
325987
327135
  }
325988
327136
  try {