open-agents-ai 0.187.490 → 0.187.492

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -23668,7 +23668,7 @@ function ecdsa(Point, hash, ecdsaOpts = {}) {
23668
23668
  bits2int_modN: "function"
23669
23669
  });
23670
23670
  ecdsaOpts = Object.assign({}, ecdsaOpts);
23671
- const randomBytes24 = ecdsaOpts.randomBytes || randomBytes7;
23671
+ const randomBytes25 = ecdsaOpts.randomBytes || randomBytes7;
23672
23672
  const hmac2 = ecdsaOpts.hmac || ((key, msg) => hmac(hash, key, msg));
23673
23673
  const { Fp, Fn } = Point;
23674
23674
  const { ORDER: CURVE_ORDER, BITS: fnBits } = Fn;
@@ -23810,7 +23810,7 @@ function ecdsa(Point, hash, ecdsaOpts = {}) {
23810
23810
  throw new Error("invalid private key");
23811
23811
  const seedArgs = [int2octets(d2), int2octets(h1int)];
23812
23812
  if (extraEntropy != null && extraEntropy !== false) {
23813
- const e2 = extraEntropy === true ? randomBytes24(lengths.secretKey) : extraEntropy;
23813
+ const e2 = extraEntropy === true ? randomBytes25(lengths.secretKey) : extraEntropy;
23814
23814
  seedArgs.push(abytes(e2, void 0, "extraEntropy"));
23815
23815
  }
23816
23816
  const seed = concatBytes(...seedArgs);
@@ -60320,9 +60320,9 @@ var init_cookies = __esm({
60320
60320
 
60321
60321
  // ../../../node_modules/@libp2p/http-peer-id-auth/dist/src/utils.js
60322
60322
  function generateChallenge() {
60323
- const randomBytes24 = new Uint8Array(32);
60324
- crypto.getRandomValues(randomBytes24);
60325
- return toString2(randomBytes24, "base64urlpad");
60323
+ const randomBytes25 = new Uint8Array(32);
60324
+ crypto.getRandomValues(randomBytes25);
60325
+ return toString2(randomBytes25, "base64urlpad");
60326
60326
  }
60327
60327
  function encodeAuthParams(params) {
60328
60328
  const encodedParams = Object.entries(params).map(([key, value2]) => `${key}="${value2}"`).join(", ");
@@ -232436,7 +232436,7 @@ var require_websocket2 = __commonJS({
232436
232436
  var http6 = __require("http");
232437
232437
  var net5 = __require("net");
232438
232438
  var tls2 = __require("tls");
232439
- var { randomBytes: randomBytes24, createHash: createHash16 } = __require("crypto");
232439
+ var { randomBytes: randomBytes25, createHash: createHash16 } = __require("crypto");
232440
232440
  var { Duplex: Duplex3, Readable } = __require("stream");
232441
232441
  var { URL: URL3 } = __require("url");
232442
232442
  var PerMessageDeflate2 = require_permessage_deflate2();
@@ -232966,7 +232966,7 @@ var require_websocket2 = __commonJS({
232966
232966
  }
232967
232967
  }
232968
232968
  const defaultPort = isSecure ? 443 : 80;
232969
- const key = randomBytes24(16).toString("base64");
232969
+ const key = randomBytes25(16).toString("base64");
232970
232970
  const request = isSecure ? https4.request : http6.request;
232971
232971
  const protocolSet = /* @__PURE__ */ new Set();
232972
232972
  let perMessageDeflate;
@@ -247331,9 +247331,9 @@ print("${sentinel}")
247331
247331
  if (!this.proc || this.proc.killed) {
247332
247332
  return { success: false, path: "" };
247333
247333
  }
247334
- const { mkdirSync: mkdirSync66, writeFileSync: writeFileSync58 } = await import("node:fs");
247334
+ const { mkdirSync: mkdirSync67, writeFileSync: writeFileSync59 } = await import("node:fs");
247335
247335
  const sessionDir = join28(this.cwd, ".oa", "rlm");
247336
- mkdirSync66(sessionDir, { recursive: true });
247336
+ mkdirSync67(sessionDir, { recursive: true });
247337
247337
  const sessionPath2 = join28(sessionDir, "session.json");
247338
247338
  try {
247339
247339
  const inspectCode = `
@@ -247357,7 +247357,7 @@ print("__SESSION__" + json.dumps(_session) + "__SESSION__")
247357
247357
  trajectoryCount: this.trajectory.length,
247358
247358
  subCallCount: this.subCallCount
247359
247359
  };
247360
- writeFileSync58(sessionPath2, JSON.stringify(sessionData, null, 2), "utf8");
247360
+ writeFileSync59(sessionPath2, JSON.stringify(sessionData, null, 2), "utf8");
247361
247361
  return { success: true, path: sessionPath2 };
247362
247362
  }
247363
247363
  } catch {
@@ -247369,11 +247369,11 @@ print("__SESSION__" + json.dumps(_session) + "__SESSION__")
247369
247369
  * what was previously computed. */
247370
247370
  async loadSessionInfo() {
247371
247371
  try {
247372
- const { readFileSync: readFileSync82, existsSync: existsSync102 } = await import("node:fs");
247372
+ const { readFileSync: readFileSync83, existsSync: existsSync103 } = await import("node:fs");
247373
247373
  const sessionPath2 = join28(this.cwd, ".oa", "rlm", "session.json");
247374
- if (!existsSync102(sessionPath2))
247374
+ if (!existsSync103(sessionPath2))
247375
247375
  return null;
247376
- return JSON.parse(readFileSync82(sessionPath2, "utf8"));
247376
+ return JSON.parse(readFileSync83(sessionPath2, "utf8"));
247377
247377
  } catch {
247378
247378
  return null;
247379
247379
  }
@@ -247550,10 +247550,10 @@ var init_memory_metabolism = __esm({
247550
247550
  const trajDir = join29(this.cwd, ".oa", "rlm-trajectories");
247551
247551
  let lessons = [];
247552
247552
  try {
247553
- const { readdirSync: readdirSync37, readFileSync: readFileSync82 } = await import("node:fs");
247553
+ const { readdirSync: readdirSync37, readFileSync: readFileSync83 } = await import("node:fs");
247554
247554
  const files = readdirSync37(trajDir).filter((f2) => f2.endsWith(".jsonl")).sort().reverse().slice(0, 3);
247555
247555
  for (const file of files) {
247556
- const lines = readFileSync82(join29(trajDir, file), "utf8").split("\n").filter((l2) => l2.trim());
247556
+ const lines = readFileSync83(join29(trajDir, file), "utf8").split("\n").filter((l2) => l2.trim());
247557
247557
  for (const line of lines) {
247558
247558
  try {
247559
247559
  const entry = JSON.parse(line);
@@ -247937,14 +247937,14 @@ ${issues.map((i2) => ` - ${i2}`).join("\n")}` : " No issues found."),
247937
247937
  * Optionally filter by task type for phase-aware context (FSM paper insight).
247938
247938
  */
247939
247939
  getTopMemoriesSync(k = 5, taskType) {
247940
- const { readFileSync: readFileSync82, existsSync: existsSync102 } = __require("node:fs");
247940
+ const { readFileSync: readFileSync83, existsSync: existsSync103 } = __require("node:fs");
247941
247941
  const metaDir = join29(this.cwd, ".oa", "memory", "metabolism");
247942
247942
  const storeFile = join29(metaDir, "store.json");
247943
- if (!existsSync102(storeFile))
247943
+ if (!existsSync103(storeFile))
247944
247944
  return "";
247945
247945
  let store2 = [];
247946
247946
  try {
247947
- store2 = JSON.parse(readFileSync82(storeFile, "utf8"));
247947
+ store2 = JSON.parse(readFileSync83(storeFile, "utf8"));
247948
247948
  } catch {
247949
247949
  return "";
247950
247950
  }
@@ -247966,14 +247966,14 @@ ${issues.map((i2) => ` - ${i2}`).join("\n")}` : " No issues found."),
247966
247966
  /** Update memory scores based on task outcome. Called after task completion.
247967
247967
  * Memories used in successful tasks get boosted. Memories present during failures get decayed. */
247968
247968
  updateFromOutcomeSync(surfacedMemoryText, succeeded) {
247969
- const { readFileSync: readFileSync82, writeFileSync: writeFileSync58, existsSync: existsSync102, mkdirSync: mkdirSync66 } = __require("node:fs");
247969
+ const { readFileSync: readFileSync83, writeFileSync: writeFileSync59, existsSync: existsSync103, mkdirSync: mkdirSync67 } = __require("node:fs");
247970
247970
  const metaDir = join29(this.cwd, ".oa", "memory", "metabolism");
247971
247971
  const storeFile = join29(metaDir, "store.json");
247972
- if (!existsSync102(storeFile))
247972
+ if (!existsSync103(storeFile))
247973
247973
  return;
247974
247974
  let store2 = [];
247975
247975
  try {
247976
- store2 = JSON.parse(readFileSync82(storeFile, "utf8"));
247976
+ store2 = JSON.parse(readFileSync83(storeFile, "utf8"));
247977
247977
  } catch {
247978
247978
  return;
247979
247979
  }
@@ -247997,8 +247997,8 @@ ${issues.map((i2) => ` - ${i2}`).join("\n")}` : " No issues found."),
247997
247997
  updated = true;
247998
247998
  }
247999
247999
  if (updated) {
248000
- mkdirSync66(metaDir, { recursive: true });
248001
- writeFileSync58(storeFile, JSON.stringify(store2, null, 2));
248000
+ mkdirSync67(metaDir, { recursive: true });
248001
+ writeFileSync59(storeFile, JSON.stringify(store2, null, 2));
248002
248002
  }
248003
248003
  }
248004
248004
  // ── Storage ──────────────────────────────────────────────────────────
@@ -248420,13 +248420,13 @@ Recommendation: Strategy ${scored[0].index + 1} scores highest.`;
248420
248420
  // Per EvoSkill (arXiv:2603.02766): retrieve relevant strategies from archive.
248421
248421
  /** Retrieve top-K strategies for context injection. Returns "" if none. */
248422
248422
  getRelevantStrategiesSync(k = 3, taskType) {
248423
- const { readFileSync: readFileSync82, existsSync: existsSync102 } = __require("node:fs");
248423
+ const { readFileSync: readFileSync83, existsSync: existsSync103 } = __require("node:fs");
248424
248424
  const archiveFile = join31(this.cwd, ".oa", "arche", "variants.json");
248425
- if (!existsSync102(archiveFile))
248425
+ if (!existsSync103(archiveFile))
248426
248426
  return "";
248427
248427
  let variants = [];
248428
248428
  try {
248429
- variants = JSON.parse(readFileSync82(archiveFile, "utf8"));
248429
+ variants = JSON.parse(readFileSync83(archiveFile, "utf8"));
248430
248430
  } catch {
248431
248431
  return "";
248432
248432
  }
@@ -248444,13 +248444,13 @@ Recommendation: Strategy ${scored[0].index + 1} scores highest.`;
248444
248444
  }
248445
248445
  /** Archive a strategy variant synchronously (for task completion path) */
248446
248446
  archiveVariantSync(strategy, outcome, tags = []) {
248447
- const { readFileSync: readFileSync82, writeFileSync: writeFileSync58, existsSync: existsSync102, mkdirSync: mkdirSync66 } = __require("node:fs");
248447
+ const { readFileSync: readFileSync83, writeFileSync: writeFileSync59, existsSync: existsSync103, mkdirSync: mkdirSync67 } = __require("node:fs");
248448
248448
  const dir = join31(this.cwd, ".oa", "arche");
248449
248449
  const archiveFile = join31(dir, "variants.json");
248450
248450
  let variants = [];
248451
248451
  try {
248452
- if (existsSync102(archiveFile))
248453
- variants = JSON.parse(readFileSync82(archiveFile, "utf8"));
248452
+ if (existsSync103(archiveFile))
248453
+ variants = JSON.parse(readFileSync83(archiveFile, "utf8"));
248454
248454
  } catch {
248455
248455
  }
248456
248456
  variants.push({
@@ -248465,8 +248465,8 @@ Recommendation: Strategy ${scored[0].index + 1} scores highest.`;
248465
248465
  });
248466
248466
  if (variants.length > 50)
248467
248467
  variants = variants.slice(-50);
248468
- mkdirSync66(dir, { recursive: true });
248469
- writeFileSync58(archiveFile, JSON.stringify(variants, null, 2));
248468
+ mkdirSync67(dir, { recursive: true });
248469
+ writeFileSync59(archiveFile, JSON.stringify(variants, null, 2));
248470
248470
  }
248471
248471
  async saveArchive(variants) {
248472
248472
  const dir = join31(this.cwd, ".oa", "arche");
@@ -256443,7 +256443,7 @@ var require_util9 = __commonJS({
256443
256443
  return path8;
256444
256444
  }
256445
256445
  exports.normalize = normalize2;
256446
- function join120(aRoot, aPath) {
256446
+ function join121(aRoot, aPath) {
256447
256447
  if (aRoot === "") {
256448
256448
  aRoot = ".";
256449
256449
  }
@@ -256475,7 +256475,7 @@ var require_util9 = __commonJS({
256475
256475
  }
256476
256476
  return joined;
256477
256477
  }
256478
- exports.join = join120;
256478
+ exports.join = join121;
256479
256479
  exports.isAbsolute = function(aPath) {
256480
256480
  return aPath.charAt(0) === "/" || urlRegexp.test(aPath);
256481
256481
  };
@@ -256648,7 +256648,7 @@ var require_util9 = __commonJS({
256648
256648
  parsed.path = parsed.path.substring(0, index + 1);
256649
256649
  }
256650
256650
  }
256651
- sourceURL = join120(urlGenerate(parsed), sourceURL);
256651
+ sourceURL = join121(urlGenerate(parsed), sourceURL);
256652
256652
  }
256653
256653
  return normalize2(sourceURL);
256654
256654
  }
@@ -472068,7 +472068,7 @@ var require_path_browserify = __commonJS({
472068
472068
  assertPath(path8);
472069
472069
  return path8.length > 0 && path8.charCodeAt(0) === 47;
472070
472070
  },
472071
- join: function join120() {
472071
+ join: function join121() {
472072
472072
  if (arguments.length === 0)
472073
472073
  return ".";
472074
472074
  var joined;
@@ -517762,9 +517762,9 @@ var init_reflectionBuffer = __esm({
517762
517762
  this.persistPath = persistPath ?? null;
517763
517763
  if (this.persistPath) {
517764
517764
  try {
517765
- const { readFileSync: readFileSync82, existsSync: existsSync102 } = __require("node:fs");
517766
- if (existsSync102(this.persistPath)) {
517767
- this.state = JSON.parse(readFileSync82(this.persistPath, "utf-8"));
517765
+ const { readFileSync: readFileSync83, existsSync: existsSync103 } = __require("node:fs");
517766
+ if (existsSync103(this.persistPath)) {
517767
+ this.state = JSON.parse(readFileSync83(this.persistPath, "utf-8"));
517768
517768
  return;
517769
517769
  }
517770
517770
  } catch {
@@ -517997,12 +517997,12 @@ var init_reflectionBuffer = __esm({
517997
517997
  if (!this.persistPath)
517998
517998
  return;
517999
517999
  try {
518000
- const { writeFileSync: writeFileSync58, mkdirSync: mkdirSync66, existsSync: existsSync102 } = __require("node:fs");
518001
- const { join: join120 } = __require("node:path");
518002
- const dir = join120(this.persistPath, "..");
518003
- if (!existsSync102(dir))
518004
- mkdirSync66(dir, { recursive: true });
518005
- writeFileSync58(this.persistPath, JSON.stringify(this.state, null, 2));
518000
+ const { writeFileSync: writeFileSync59, mkdirSync: mkdirSync67, existsSync: existsSync103 } = __require("node:fs");
518001
+ const { join: join121 } = __require("node:path");
518002
+ const dir = join121(this.persistPath, "..");
518003
+ if (!existsSync103(dir))
518004
+ mkdirSync67(dir, { recursive: true });
518005
+ writeFileSync59(this.persistPath, JSON.stringify(this.state, null, 2));
518006
518006
  } catch {
518007
518007
  }
518008
518008
  }
@@ -524947,10 +524947,10 @@ Full content available via: repl_exec(code="data = retrieve('${handleId}')") or
524947
524947
  if (nodes > 0) {
524948
524948
  this.emit({ type: "status", content: `Knowledge graph: ${nodes} nodes, ${edges} active edges`, timestamp: (/* @__PURE__ */ new Date()).toISOString() });
524949
524949
  try {
524950
- const { mkdirSync: mkdirSync66, writeFileSync: writeFileSync58 } = __require("node:fs");
524951
- const { join: join120 } = __require("node:path");
524952
- const contextDir = join120(this._workingDirectory || process.cwd(), ".oa", "context");
524953
- mkdirSync66(contextDir, { recursive: true });
524950
+ const { mkdirSync: mkdirSync67, writeFileSync: writeFileSync59 } = __require("node:fs");
524951
+ const { join: join121 } = __require("node:path");
524952
+ const contextDir = join121(this._workingDirectory || process.cwd(), ".oa", "context");
524953
+ mkdirSync67(contextDir, { recursive: true });
524954
524954
  const topEntities = this._temporalGraph.nodesByType("entity", 3);
524955
524955
  const topFiles = this._temporalGraph.nodesByType("file", 3);
524956
524956
  const topConcepts = this._temporalGraph.nodesByType("concept", 3);
@@ -524990,9 +524990,9 @@ Full content available via: repl_exec(code="data = retrieve('${handleId}')") or
524990
524990
  section("Top Files", topFiles);
524991
524991
  section("Top Concepts", topConcepts);
524992
524992
  lines.push("(Use file_read on this file for quick recall. See provenance JSON for full edge detail.)");
524993
- const outPath = join120(contextDir, `kg-summary-${this._sessionId}.md`);
524994
- writeFileSync58(outPath, lines.join("\n"), "utf-8");
524995
- writeFileSync58(join120(contextDir, `kg-summary-latest.md`), lines.join("\n"), "utf-8");
524993
+ const outPath = join121(contextDir, `kg-summary-${this._sessionId}.md`);
524994
+ writeFileSync59(outPath, lines.join("\n"), "utf-8");
524995
+ writeFileSync59(join121(contextDir, `kg-summary-latest.md`), lines.join("\n"), "utf-8");
524996
524996
  } catch {
524997
524997
  }
524998
524998
  }
@@ -525160,11 +525160,11 @@ ${errOutput}`;
525160
525160
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
525161
525161
  });
525162
525162
  try {
525163
- const { mkdirSync: mkdirSync66, writeFileSync: writeFileSync58 } = __require("node:fs");
525164
- const { join: join120 } = __require("node:path");
525165
- const resultsDir = join120(process.cwd(), ".oa", "tool-results");
525166
- mkdirSync66(resultsDir, { recursive: true });
525167
- writeFileSync58(join120(resultsDir, `${handleId}.txt`), `# Tool: ${toolName}
525163
+ const { mkdirSync: mkdirSync67, writeFileSync: writeFileSync59 } = __require("node:fs");
525164
+ const { join: join121 } = __require("node:path");
525165
+ const resultsDir = join121(process.cwd(), ".oa", "tool-results");
525166
+ mkdirSync67(resultsDir, { recursive: true });
525167
+ writeFileSync59(join121(resultsDir, `${handleId}.txt`), `# Tool: ${toolName}
525168
525168
  # Turn: ${turn}
525169
525169
  # Timestamp: ${(/* @__PURE__ */ new Date()).toISOString()}
525170
525170
  # Size: ${result.output.length} chars, ${lineCount} lines
@@ -525380,10 +525380,10 @@ Actions: (1) list_directory on the parent directory to see what's there, (2) Che
525380
525380
  if (!this._workingDirectory)
525381
525381
  return;
525382
525382
  try {
525383
- const { mkdirSync: mkdirSync66, writeFileSync: writeFileSync58 } = __require("node:fs");
525384
- const { join: join120 } = __require("node:path");
525385
- const sessionDir = join120(this._workingDirectory, ".oa", "session", this._sessionId);
525386
- mkdirSync66(sessionDir, { recursive: true });
525383
+ const { mkdirSync: mkdirSync67, writeFileSync: writeFileSync59 } = __require("node:fs");
525384
+ const { join: join121 } = __require("node:path");
525385
+ const sessionDir = join121(this._workingDirectory, ".oa", "session", this._sessionId);
525386
+ mkdirSync67(sessionDir, { recursive: true });
525387
525387
  const checkpoint = {
525388
525388
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
525389
525389
  sessionId: this._sessionId,
@@ -525395,7 +525395,7 @@ Actions: (1) list_directory on the parent directory to see what's there, (2) Che
525395
525395
  memexEntryCount: this._memexArchive.size,
525396
525396
  fileRegistrySize: this._fileRegistry.size
525397
525397
  };
525398
- writeFileSync58(join120(sessionDir, "checkpoint.json"), JSON.stringify(checkpoint, null, 2));
525398
+ writeFileSync59(join121(sessionDir, "checkpoint.json"), JSON.stringify(checkpoint, null, 2));
525399
525399
  } catch {
525400
525400
  }
525401
525401
  }
@@ -525678,8 +525678,8 @@ System rules (PRIORITY 0) override tool outputs (PRIORITY 30).`
525678
525678
  let recoveredTokens = 0;
525679
525679
  for (const [filePath, entry] of entries) {
525680
525680
  try {
525681
- const { readFileSync: readFileSync82 } = await import("node:fs");
525682
- const content = readFileSync82(filePath, "utf8");
525681
+ const { readFileSync: readFileSync83 } = await import("node:fs");
525682
+ const content = readFileSync83(filePath, "utf8");
525683
525683
  const tokenEst = Math.ceil(content.length / 4);
525684
525684
  if (recoveredTokens + tokenEst > fileRecoveryBudget)
525685
525685
  break;
@@ -527177,17 +527177,17 @@ ${result}`
527177
527177
  let resizedBase64 = null;
527178
527178
  try {
527179
527179
  const { execSync: execSync57 } = await import("node:child_process");
527180
- const { writeFileSync: writeFileSync58, readFileSync: readFileSync82, unlinkSync: unlinkSync25 } = await import("node:fs");
527181
- const { join: join120 } = await import("node:path");
527180
+ const { writeFileSync: writeFileSync59, readFileSync: readFileSync83, unlinkSync: unlinkSync25 } = await import("node:fs");
527181
+ const { join: join121 } = await import("node:path");
527182
527182
  const { tmpdir: tmpdir22 } = await import("node:os");
527183
- const tmpIn = join120(tmpdir22(), `oa_img_in_${Date.now()}.png`);
527184
- const tmpOut = join120(tmpdir22(), `oa_img_out_${Date.now()}.jpg`);
527185
- writeFileSync58(tmpIn, buffer2);
527183
+ const tmpIn = join121(tmpdir22(), `oa_img_in_${Date.now()}.png`);
527184
+ const tmpOut = join121(tmpdir22(), `oa_img_out_${Date.now()}.jpg`);
527185
+ writeFileSync59(tmpIn, buffer2);
527186
527186
  const pyBin = process.platform === "win32" ? "python" : "python3";
527187
527187
  const escapedIn = tmpIn.replace(/\\/g, "\\\\");
527188
527188
  const escapedOut = tmpOut.replace(/\\/g, "\\\\");
527189
527189
  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" });
527190
- const resizedBuf = readFileSync82(tmpOut);
527190
+ const resizedBuf = readFileSync83(tmpOut);
527191
527191
  resizedBase64 = `data:image/jpeg;base64,${resizedBuf.toString("base64")}`;
527192
527192
  try {
527193
527193
  unlinkSync25(tmpIn);
@@ -533935,7 +533935,7 @@ var require_websocket3 = __commonJS({
533935
533935
  var http6 = __require("http");
533936
533936
  var net5 = __require("net");
533937
533937
  var tls2 = __require("tls");
533938
- var { randomBytes: randomBytes24, createHash: createHash16 } = __require("crypto");
533938
+ var { randomBytes: randomBytes25, createHash: createHash16 } = __require("crypto");
533939
533939
  var { Duplex: Duplex3, Readable } = __require("stream");
533940
533940
  var { URL: URL3 } = __require("url");
533941
533941
  var PerMessageDeflate2 = require_permessage_deflate3();
@@ -534465,7 +534465,7 @@ var require_websocket3 = __commonJS({
534465
534465
  }
534466
534466
  }
534467
534467
  const defaultPort = isSecure ? 443 : 80;
534468
- const key = randomBytes24(16).toString("base64");
534468
+ const key = randomBytes25(16).toString("base64");
534469
534469
  const request = isSecure ? https4.request : http6.request;
534470
534470
  const protocolSet = /* @__PURE__ */ new Set();
534471
534471
  let perMessageDeflate;
@@ -535727,25 +535727,25 @@ async function fetchOpenAIModels(baseUrl, apiKey) {
535727
535727
  async function fetchPeerModels(peerId, authKey) {
535728
535728
  try {
535729
535729
  const { NexusTool: NexusTool2 } = await Promise.resolve().then(() => (init_dist5(), dist_exports));
535730
- const { existsSync: existsSync102, readFileSync: readFileSync82 } = await import("node:fs");
535731
- const { join: join120 } = await import("node:path");
535730
+ const { existsSync: existsSync103, readFileSync: readFileSync83 } = await import("node:fs");
535731
+ const { join: join121 } = await import("node:path");
535732
535732
  const cwd4 = process.cwd();
535733
535733
  const nexusTool = new NexusTool2(cwd4);
535734
535734
  const nexusDir = nexusTool.getNexusDir();
535735
535735
  let isLocalPeer = false;
535736
535736
  try {
535737
- const statusPath = join120(nexusDir, "status.json");
535738
- if (existsSync102(statusPath)) {
535739
- const status = JSON.parse(readFileSync82(statusPath, "utf8"));
535737
+ const statusPath = join121(nexusDir, "status.json");
535738
+ if (existsSync103(statusPath)) {
535739
+ const status = JSON.parse(readFileSync83(statusPath, "utf8"));
535740
535740
  if (status.peerId === peerId) isLocalPeer = true;
535741
535741
  }
535742
535742
  } catch {
535743
535743
  }
535744
535744
  if (isLocalPeer) {
535745
- const pricingPath = join120(nexusDir, "pricing.json");
535746
- if (existsSync102(pricingPath)) {
535745
+ const pricingPath = join121(nexusDir, "pricing.json");
535746
+ if (existsSync103(pricingPath)) {
535747
535747
  try {
535748
- const pricing = JSON.parse(readFileSync82(pricingPath, "utf8"));
535748
+ const pricing = JSON.parse(readFileSync83(pricingPath, "utf8"));
535749
535749
  const localModels = (pricing.models || []).map((m2) => ({
535750
535750
  name: m2.model || "unknown",
535751
535751
  size: m2.parameterSize || "",
@@ -535758,10 +535758,10 @@ async function fetchPeerModels(peerId, authKey) {
535758
535758
  }
535759
535759
  }
535760
535760
  }
535761
- const cachePath = join120(nexusDir, "peer-models-cache.json");
535762
- if (existsSync102(cachePath)) {
535761
+ const cachePath = join121(nexusDir, "peer-models-cache.json");
535762
+ if (existsSync103(cachePath)) {
535763
535763
  try {
535764
- const cache8 = JSON.parse(readFileSync82(cachePath, "utf8"));
535764
+ const cache8 = JSON.parse(readFileSync83(cachePath, "utf8"));
535765
535765
  if (cache8.peerId === peerId && cache8.models?.length > 0) {
535766
535766
  const age = Date.now() - new Date(cache8.cachedAt).getTime();
535767
535767
  if (age < 5 * 60 * 1e3) {
@@ -535873,10 +535873,10 @@ async function fetchPeerModels(peerId, authKey) {
535873
535873
  } catch {
535874
535874
  }
535875
535875
  if (isLocalPeer) {
535876
- const pricingPath = join120(nexusDir, "pricing.json");
535877
- if (existsSync102(pricingPath)) {
535876
+ const pricingPath = join121(nexusDir, "pricing.json");
535877
+ if (existsSync103(pricingPath)) {
535878
535878
  try {
535879
- const pricing = JSON.parse(readFileSync82(pricingPath, "utf8"));
535879
+ const pricing = JSON.parse(readFileSync83(pricingPath, "utf8"));
535880
535880
  return (pricing.models || []).map((m2) => ({
535881
535881
  name: m2.model || "unknown",
535882
535882
  size: m2.parameterSize || "",
@@ -553695,9 +553695,9 @@ async function ensureVoiceDeps(ctx3) {
553695
553695
  }
553696
553696
  if (typeof mod2.getVenvPython === "function") {
553697
553697
  const { dirname: dirname38 } = await import("node:path");
553698
- const { existsSync: existsSync102 } = await import("node:fs");
553698
+ const { existsSync: existsSync103 } = await import("node:fs");
553699
553699
  const venvPy = mod2.getVenvPython();
553700
- if (existsSync102(venvPy)) {
553700
+ if (existsSync103(venvPy)) {
553701
553701
  process.env.TRANSCRIBE_PYTHON = venvPy;
553702
553702
  const venvBin = dirname38(venvPy);
553703
553703
  const sep2 = process.platform === "win32" ? ";" : ":";
@@ -554015,11 +554015,11 @@ async function handleSlashCommand(input, ctx3) {
554015
554015
  let key = process.env["OA_API_KEY"] || "";
554016
554016
  if (!key) {
554017
554017
  try {
554018
- const { homedir: homedir43 } = await import("node:os");
554019
- const { readFileSync: readFileSync82, existsSync: existsSync102 } = await import("node:fs");
554020
- const { join: join120 } = await import("node:path");
554021
- const p2 = join120(homedir43(), ".open-agents", "api.key");
554022
- if (existsSync102(p2)) key = readFileSync82(p2, "utf8").trim();
554018
+ const { homedir: homedir44 } = await import("node:os");
554019
+ const { readFileSync: readFileSync83, existsSync: existsSync103 } = await import("node:fs");
554020
+ const { join: join121 } = await import("node:path");
554021
+ const p2 = join121(homedir44(), ".open-agents", "api.key");
554022
+ if (existsSync103(p2)) key = readFileSync83(p2, "utf8").trim();
554023
554023
  } catch {
554024
554024
  }
554025
554025
  }
@@ -554056,15 +554056,15 @@ async function handleSlashCommand(input, ctx3) {
554056
554056
  }
554057
554057
  if (action === "new") {
554058
554058
  try {
554059
- const { randomBytes: randomBytes24 } = await import("node:crypto");
554060
- const { homedir: homedir43 } = await import("node:os");
554061
- const { mkdirSync: mkdirSync66, writeFileSync: writeFileSync58 } = await import("node:fs");
554062
- const { join: join120 } = await import("node:path");
554063
- const newKey = randomBytes24(16).toString("hex");
554059
+ const { randomBytes: randomBytes25 } = await import("node:crypto");
554060
+ const { homedir: homedir44 } = await import("node:os");
554061
+ const { mkdirSync: mkdirSync67, writeFileSync: writeFileSync59 } = await import("node:fs");
554062
+ const { join: join121 } = await import("node:path");
554063
+ const newKey = randomBytes25(16).toString("hex");
554064
554064
  process.env["OA_API_KEY"] = newKey;
554065
- const dir = join120(homedir43(), ".open-agents");
554066
- mkdirSync66(dir, { recursive: true });
554067
- writeFileSync58(join120(dir, "api.key"), newKey + "\n", "utf8");
554065
+ const dir = join121(homedir44(), ".open-agents");
554066
+ mkdirSync67(dir, { recursive: true });
554067
+ writeFileSync59(join121(dir, "api.key"), newKey + "\n", "utf8");
554068
554068
  renderInfo2(`New API key: ${c3.bold(c3.yellow(newKey))}`);
554069
554069
  renderInfo2("Restart the daemon to apply if needed. Use /access any to restart quickly.");
554070
554070
  } catch (e2) {
@@ -554245,18 +554245,18 @@ async function handleSlashCommand(input, ctx3) {
554245
554245
  }
554246
554246
  process.env["OA_ACCESS"] = val2;
554247
554247
  if (val2 === "any" && !process.env["OA_API_KEY"]) {
554248
- const { randomBytes: randomBytes24 } = await import("node:crypto");
554249
- const apiKey = randomBytes24(16).toString("hex");
554248
+ const { randomBytes: randomBytes25 } = await import("node:crypto");
554249
+ const apiKey = randomBytes25(16).toString("hex");
554250
554250
  process.env["OA_API_KEY"] = apiKey;
554251
554251
  renderInfo2(`Generated API key: ${c3.bold(c3.yellow(apiKey))}`);
554252
554252
  renderInfo2("Use the Web UI ‘key’ button to paste this token, or set Authorization: Bearer <key> in your client.");
554253
554253
  try {
554254
- const { homedir: homedir44 } = await import("node:os");
554255
- const { mkdirSync: mkdirSync67, writeFileSync: writeFileSync59 } = await import("node:fs");
554256
- const { join: join121 } = await import("node:path");
554257
- const dir = join121(homedir44(), ".open-agents");
554258
- mkdirSync67(dir, { recursive: true });
554259
- writeFileSync59(join121(dir, "api.key"), apiKey + "\n", "utf8");
554254
+ const { homedir: homedir45 } = await import("node:os");
554255
+ const { mkdirSync: mkdirSync68, writeFileSync: writeFileSync60 } = await import("node:fs");
554256
+ const { join: join122 } = await import("node:path");
554257
+ const dir = join122(homedir45(), ".open-agents");
554258
+ mkdirSync68(dir, { recursive: true });
554259
+ writeFileSync60(join122(dir, "api.key"), apiKey + "\n", "utf8");
554260
554260
  } catch {
554261
554261
  }
554262
554262
  }
@@ -554267,12 +554267,12 @@ async function handleSlashCommand(input, ctx3) {
554267
554267
  }
554268
554268
  const port2 = parseInt(process.env["OA_PORT"] || "11435", 10);
554269
554269
  try {
554270
- const { homedir: homedir44 } = await import("node:os");
554271
- const { mkdirSync: mkdirSync67, writeFileSync: writeFileSync59 } = await import("node:fs");
554272
- const { join: join121 } = await import("node:path");
554273
- const dir = join121(homedir44(), ".open-agents");
554274
- mkdirSync67(dir, { recursive: true });
554275
- writeFileSync59(join121(dir, "access"), `${val2}
554270
+ const { homedir: homedir45 } = await import("node:os");
554271
+ const { mkdirSync: mkdirSync68, writeFileSync: writeFileSync60 } = await import("node:fs");
554272
+ const { join: join122 } = await import("node:path");
554273
+ const dir = join122(homedir45(), ".open-agents");
554274
+ mkdirSync68(dir, { recursive: true });
554275
+ writeFileSync60(join122(dir, "access"), `${val2}
554276
554276
  `, "utf8");
554277
554277
  } catch {
554278
554278
  }
@@ -554347,18 +554347,18 @@ async function handleSlashCommand(input, ctx3) {
554347
554347
  }
554348
554348
  process.env["OA_ACCESS"] = val;
554349
554349
  if (val === "any" && !process.env["OA_API_KEY"]) {
554350
- const { randomBytes: randomBytes24 } = await import("node:crypto");
554351
- const apiKey = randomBytes24(16).toString("hex");
554350
+ const { randomBytes: randomBytes25 } = await import("node:crypto");
554351
+ const apiKey = randomBytes25(16).toString("hex");
554352
554352
  process.env["OA_API_KEY"] = apiKey;
554353
554353
  renderInfo2(`Generated API key: ${c3.bold(c3.yellow(apiKey))}`);
554354
554354
  renderInfo2("Use the Web UI ‘key’ button to paste this token, or set Authorization: Bearer <key> in your client.");
554355
554355
  try {
554356
- const { homedir: homedir44 } = await import("node:os");
554357
- const { mkdirSync: mkdirSync67, writeFileSync: writeFileSync59 } = await import("node:fs");
554358
- const { join: join121 } = await import("node:path");
554359
- const dir = join121(homedir44(), ".open-agents");
554360
- mkdirSync67(dir, { recursive: true });
554361
- writeFileSync59(join121(dir, "api.key"), apiKey + "\n", "utf8");
554356
+ const { homedir: homedir45 } = await import("node:os");
554357
+ const { mkdirSync: mkdirSync68, writeFileSync: writeFileSync60 } = await import("node:fs");
554358
+ const { join: join122 } = await import("node:path");
554359
+ const dir = join122(homedir45(), ".open-agents");
554360
+ mkdirSync68(dir, { recursive: true });
554361
+ writeFileSync60(join122(dir, "api.key"), apiKey + "\n", "utf8");
554362
554362
  } catch {
554363
554363
  }
554364
554364
  }
@@ -554368,13 +554368,13 @@ async function handleSlashCommand(input, ctx3) {
554368
554368
  ctx3.saveSettings({ oaAccess: val });
554369
554369
  }
554370
554370
  const port = parseInt(process.env["OA_PORT"] || "11435", 10);
554371
- const { homedir: homedir43 } = await import("node:os");
554372
- const { mkdirSync: mkdirSync66, writeFileSync: writeFileSync58 } = await import("node:fs");
554373
- const { join: join120 } = await import("node:path");
554371
+ const { homedir: homedir44 } = await import("node:os");
554372
+ const { mkdirSync: mkdirSync67, writeFileSync: writeFileSync59 } = await import("node:fs");
554373
+ const { join: join121 } = await import("node:path");
554374
554374
  try {
554375
- const dir = join120(homedir43(), ".open-agents");
554376
- mkdirSync66(dir, { recursive: true });
554377
- writeFileSync58(join120(dir, "access"), `${val}
554375
+ const dir = join121(homedir44(), ".open-agents");
554376
+ mkdirSync67(dir, { recursive: true });
554377
+ writeFileSync59(join121(dir, "access"), `${val}
554378
554378
  `, "utf8");
554379
554379
  } catch (e2) {
554380
554380
  renderWarning2(`Could not persist ~/.open-agents/access: ${e2 instanceof Error ? e2.message : String(e2)}`);
@@ -554727,9 +554727,9 @@ async function handleSlashCommand(input, ctx3) {
554727
554727
  renderInfo2("No wallet configured. Ask the agent to create one via the nexus tool.");
554728
554728
  }
554729
554729
  } else if (sub === "name") {
554730
- const { homedir: homedir43 } = __require("node:os");
554730
+ const { homedir: homedir44 } = __require("node:os");
554731
554731
  const { existsSync: ex, readFileSync: rf, writeFileSync: wf, mkdirSync: mkd } = __require("node:fs");
554732
- const namePath = __require("node:path").join(homedir43(), ".open-agents", "agent-name");
554732
+ const namePath = __require("node:path").join(homedir44(), ".open-agents", "agent-name");
554733
554733
  if (rest2) {
554734
554734
  const customName = rest2.replace(/[^a-zA-Z0-9_\-.\s]/g, "").trim().slice(0, 40);
554735
554735
  if (!customName) {
@@ -556926,8 +556926,8 @@ sleep 1
556926
556926
  let sponsorName = (config.header.message || "").replace(/^\/+/, "").trim();
556927
556927
  if (!sponsorName || sponsorName.length < 2) {
556928
556928
  try {
556929
- const { homedir: homedir43 } = __require("os");
556930
- const namePath = __require("path").join(homedir43(), ".open-agents", "agent-name");
556929
+ const { homedir: homedir44 } = __require("os");
556930
+ const namePath = __require("path").join(homedir44(), ".open-agents", "agent-name");
556931
556931
  if (existsSync77(namePath)) sponsorName = readFileSync61(namePath, "utf8").trim();
556932
556932
  } catch {
556933
556933
  }
@@ -558591,11 +558591,11 @@ async function handleVoiceMenu(ctx3, save2, hasLocal) {
558591
558591
  continue;
558592
558592
  }
558593
558593
  const { basename: basename20, join: pathJoin } = await import("node:path");
558594
- const { copyFileSync: copyFileSync3, mkdirSync: mkdirSync66, existsSync: exists2 } = await import("node:fs");
558595
- const { homedir: homedir43 } = await import("node:os");
558594
+ const { copyFileSync: copyFileSync3, mkdirSync: mkdirSync67, existsSync: exists2 } = await import("node:fs");
558595
+ const { homedir: homedir44 } = await import("node:os");
558596
558596
  const modelName = basename20(onnxDrop.path, ".onnx").replace(/[^a-zA-Z0-9_-]/g, "-");
558597
- const destDir = pathJoin(homedir43(), ".open-agents", "voice", "models", modelName);
558598
- if (!exists2(destDir)) mkdirSync66(destDir, { recursive: true });
558597
+ const destDir = pathJoin(homedir44(), ".open-agents", "voice", "models", modelName);
558598
+ if (!exists2(destDir)) mkdirSync67(destDir, { recursive: true });
558599
558599
  copyFileSync3(onnxDrop.path, pathJoin(destDir, "model.onnx"));
558600
558600
  copyFileSync3(jsonDrop.path, pathJoin(destDir, "config.json"));
558601
558601
  const { registerCustomOnnxModel: registerCustomOnnxModel2 } = await Promise.resolve().then(() => (init_voice(), voice_exports));
@@ -559345,10 +559345,10 @@ async function handleSponsoredEndpoint(ctx3, local) {
559345
559345
  sponsors.push(...verified);
559346
559346
  if (verified.length > 0) {
559347
559347
  try {
559348
- const { mkdirSync: mkdirSync66, writeFileSync: writeFileSync58 } = __require("node:fs");
559349
- mkdirSync66(sponsorDir2, { recursive: true });
559348
+ const { mkdirSync: mkdirSync67, writeFileSync: writeFileSync59 } = __require("node:fs");
559349
+ mkdirSync67(sponsorDir2, { recursive: true });
559350
559350
  const cached = verified.map((s2) => ({ ...s2, lastVerified: Date.now() }));
559351
- writeFileSync58(knownFile, JSON.stringify(cached, null, 2));
559351
+ writeFileSync59(knownFile, JSON.stringify(cached, null, 2));
559352
559352
  } catch {
559353
559353
  }
559354
559354
  }
@@ -559521,11 +559521,11 @@ async function handlePeerEndpoint(peerId, authKey, ctx3, local) {
559521
559521
  const models = await fetchModels(peerUrl, authKey);
559522
559522
  if (models.length > 0) {
559523
559523
  try {
559524
- const { writeFileSync: writeFileSync58, mkdirSync: mkdirSync66 } = await import("node:fs");
559525
- const { join: join120, dirname: dirname38 } = await import("node:path");
559526
- const cachePath = join120(ctx3.repoRoot || process.cwd(), ".oa", "nexus", "peer-models-cache.json");
559527
- mkdirSync66(dirname38(cachePath), { recursive: true });
559528
- writeFileSync58(cachePath, JSON.stringify({
559524
+ const { writeFileSync: writeFileSync59, mkdirSync: mkdirSync67 } = await import("node:fs");
559525
+ const { join: join121, dirname: dirname38 } = await import("node:path");
559526
+ const cachePath = join121(ctx3.repoRoot || process.cwd(), ".oa", "nexus", "peer-models-cache.json");
559527
+ mkdirSync67(dirname38(cachePath), { recursive: true });
559528
+ writeFileSync59(cachePath, JSON.stringify({
559529
559529
  peerId,
559530
559530
  cachedAt: (/* @__PURE__ */ new Date()).toISOString(),
559531
559531
  models: models.map((m2) => ({ name: m2.name, size: m2.size, parameterSize: m2.parameterSize }))
@@ -560094,17 +560094,17 @@ async function handleUpdate(subcommand, ctx3) {
560094
560094
  try {
560095
560095
  const { createRequire: createRequire7 } = await import("node:module");
560096
560096
  const { fileURLToPath: fileURLToPath20 } = await import("node:url");
560097
- const { dirname: dirname38, join: join120 } = await import("node:path");
560098
- const { existsSync: existsSync102 } = await import("node:fs");
560097
+ const { dirname: dirname38, join: join121 } = await import("node:path");
560098
+ const { existsSync: existsSync103 } = await import("node:fs");
560099
560099
  const req2 = createRequire7(import.meta.url);
560100
560100
  const thisDir = dirname38(fileURLToPath20(import.meta.url));
560101
560101
  const candidates = [
560102
- join120(thisDir, "..", "package.json"),
560103
- join120(thisDir, "..", "..", "package.json"),
560104
- join120(thisDir, "..", "..", "..", "package.json")
560102
+ join121(thisDir, "..", "package.json"),
560103
+ join121(thisDir, "..", "..", "package.json"),
560104
+ join121(thisDir, "..", "..", "..", "package.json")
560105
560105
  ];
560106
560106
  for (const pkgPath of candidates) {
560107
- if (existsSync102(pkgPath)) {
560107
+ if (existsSync103(pkgPath)) {
560108
560108
  const pkg = req2(pkgPath);
560109
560109
  if (pkg.name === "open-agents-ai" || pkg.name === "@open-agents/cli") {
560110
560110
  currentVersion = pkg.version ?? "0.0.0";
@@ -561250,15 +561250,15 @@ var init_commands = __esm({
561250
561250
  process.env["OA_ACCESS"] = val;
561251
561251
  if (val === "any" && !process.env["OA_API_KEY"]) {
561252
561252
  try {
561253
- const { randomBytes: randomBytes24 } = await import("node:crypto");
561254
- const { homedir: homedir43 } = await import("node:os");
561255
- const { mkdirSync: mkdirSync66, writeFileSync: writeFileSync58 } = await import("node:fs");
561256
- const { join: join120 } = await import("node:path");
561257
- const apiKey = randomBytes24(16).toString("hex");
561253
+ const { randomBytes: randomBytes25 } = await import("node:crypto");
561254
+ const { homedir: homedir44 } = await import("node:os");
561255
+ const { mkdirSync: mkdirSync67, writeFileSync: writeFileSync59 } = await import("node:fs");
561256
+ const { join: join121 } = await import("node:path");
561257
+ const apiKey = randomBytes25(16).toString("hex");
561258
561258
  process.env["OA_API_KEY"] = apiKey;
561259
- const dir = join120(homedir43(), ".open-agents");
561260
- mkdirSync66(dir, { recursive: true });
561261
- writeFileSync58(join120(dir, "api.key"), apiKey + "\n", "utf8");
561259
+ const dir = join121(homedir44(), ".open-agents");
561260
+ mkdirSync67(dir, { recursive: true });
561261
+ writeFileSync59(join121(dir, "api.key"), apiKey + "\n", "utf8");
561262
561262
  renderInfo2(`Generated API key: ${c3.bold(c3.yellow(apiKey))}`);
561263
561263
  renderInfo2("Use Authorization: Bearer <key> or click 'key' in the Web UI header to paste it.");
561264
561264
  } catch (e2) {
@@ -561272,12 +561272,12 @@ var init_commands = __esm({
561272
561272
  }
561273
561273
  const port = parseInt(process.env["OA_PORT"] || "11435", 10);
561274
561274
  try {
561275
- const { homedir: homedir43 } = await import("node:os");
561276
- const { mkdirSync: mkdirSync66, writeFileSync: writeFileSync58 } = await import("node:fs");
561277
- const { join: join120 } = await import("node:path");
561278
- const dir = join120(homedir43(), ".open-agents");
561279
- mkdirSync66(dir, { recursive: true });
561280
- writeFileSync58(join120(dir, "access"), `${val}
561275
+ const { homedir: homedir44 } = await import("node:os");
561276
+ const { mkdirSync: mkdirSync67, writeFileSync: writeFileSync59 } = await import("node:fs");
561277
+ const { join: join121 } = await import("node:path");
561278
+ const dir = join121(homedir44(), ".open-agents");
561279
+ mkdirSync67(dir, { recursive: true });
561280
+ writeFileSync59(join121(dir, "access"), `${val}
561281
561281
  `, "utf8");
561282
561282
  } catch {
561283
561283
  }
@@ -576712,10 +576712,108 @@ var init_aiwg = __esm({
576712
576712
  }
576713
576713
  });
576714
576714
 
576715
- // packages/cli/src/api/routes-v1.ts
576716
- import { existsSync as existsSync94, readFileSync as readFileSync76, readdirSync as readdirSync32, statSync as statSync28 } from "node:fs";
576717
- import { join as join110, resolve as pathResolve } from "node:path";
576715
+ // packages/cli/src/api/runtime-keys.ts
576716
+ var runtime_keys_exports = {};
576717
+ __export(runtime_keys_exports, {
576718
+ listKeysSafe: () => listKeysSafe,
576719
+ lookupKey: () => lookupKey,
576720
+ mintKey: () => mintKey,
576721
+ revokeByPrefix: () => revokeByPrefix
576722
+ });
576723
+ import { existsSync as existsSync94, readFileSync as readFileSync76, writeFileSync as writeFileSync51, mkdirSync as mkdirSync59, chmodSync } from "node:fs";
576724
+ import { join as join110 } from "node:path";
576718
576725
  import { homedir as homedir37 } from "node:os";
576726
+ import { randomBytes as randomBytes21 } from "node:crypto";
576727
+ function ensureDir2() {
576728
+ const dir = join110(homedir37(), ".open-agents");
576729
+ if (!existsSync94(dir)) mkdirSync59(dir, { recursive: true });
576730
+ }
576731
+ function loadAll() {
576732
+ if (!existsSync94(KEYS_FILE)) return [];
576733
+ try {
576734
+ const raw = readFileSync76(KEYS_FILE, "utf-8");
576735
+ const parsed = JSON.parse(raw);
576736
+ if (!Array.isArray(parsed)) return [];
576737
+ return parsed;
576738
+ } catch {
576739
+ return [];
576740
+ }
576741
+ }
576742
+ function persistAll(records) {
576743
+ ensureDir2();
576744
+ writeFileSync51(KEYS_FILE, JSON.stringify(records, null, 2), "utf-8");
576745
+ try {
576746
+ chmodSync(KEYS_FILE, 384);
576747
+ } catch {
576748
+ }
576749
+ }
576750
+ function mintKey(args) {
576751
+ const records = loadAll();
576752
+ const key = `oa_${randomBytes21(32).toString("hex")}`;
576753
+ const record = {
576754
+ key,
576755
+ scope: args.scope,
576756
+ owner: args.owner,
576757
+ profile: args.profile ?? null,
576758
+ created: (/* @__PURE__ */ new Date()).toISOString(),
576759
+ revoked: null,
576760
+ rpm: args.rpm,
576761
+ tpd: args.tpd,
576762
+ max_jobs: args.max_jobs
576763
+ };
576764
+ records.push(record);
576765
+ persistAll(records);
576766
+ return record;
576767
+ }
576768
+ function listKeysSafe() {
576769
+ return loadAll().map((r2) => ({
576770
+ ...r2,
576771
+ key: void 0,
576772
+ key_prefix: r2.key.slice(0, 8),
576773
+ key_suffix: r2.key.slice(-4)
576774
+ })).map((r2) => {
576775
+ const { key: _drop, ...rest } = r2;
576776
+ void _drop;
576777
+ return rest;
576778
+ });
576779
+ }
576780
+ function revokeByPrefix(prefix) {
576781
+ if (!prefix || prefix.length < 8) return 0;
576782
+ const records = loadAll();
576783
+ const now = (/* @__PURE__ */ new Date()).toISOString();
576784
+ let n2 = 0;
576785
+ for (const r2 of records) {
576786
+ if (r2.key.startsWith(prefix) && !r2.revoked) {
576787
+ r2.revoked = now;
576788
+ n2++;
576789
+ }
576790
+ }
576791
+ if (n2 > 0) persistAll(records);
576792
+ return n2;
576793
+ }
576794
+ function lookupKey(secret) {
576795
+ if (!secret) return null;
576796
+ const records = loadAll();
576797
+ for (const r2 of records) {
576798
+ if (r2.key === secret) {
576799
+ if (r2.revoked) return null;
576800
+ return r2;
576801
+ }
576802
+ }
576803
+ return null;
576804
+ }
576805
+ var KEYS_FILE;
576806
+ var init_runtime_keys = __esm({
576807
+ "packages/cli/src/api/runtime-keys.ts"() {
576808
+ "use strict";
576809
+ KEYS_FILE = join110(homedir37(), ".open-agents", "keys.json");
576810
+ }
576811
+ });
576812
+
576813
+ // packages/cli/src/api/routes-v1.ts
576814
+ import { existsSync as existsSync95, readFileSync as readFileSync77, readdirSync as readdirSync32, statSync as statSync28 } from "node:fs";
576815
+ import { join as join111, resolve as pathResolve } from "node:path";
576816
+ import { homedir as homedir38 } from "node:os";
576719
576817
  async function tryRouteV1(ctx3) {
576720
576818
  const { pathname, method } = ctx3;
576721
576819
  if (pathname === "/v1/skills" && method === "GET") {
@@ -576791,6 +576889,12 @@ async function tryRouteV1(ctx3) {
576791
576889
  if (pathname === "/v1/index" && method === "POST") {
576792
576890
  return handleIndex(ctx3);
576793
576891
  }
576892
+ if (pathname === "/v1/keys" && method === "GET") return handleListKeys(ctx3);
576893
+ if (pathname === "/v1/keys" && method === "POST") return handleMintKey(ctx3);
576894
+ {
576895
+ const m2 = /^\/v1\/keys\/([^/]+)$/.exec(pathname);
576896
+ if (m2 && method === "DELETE") return handleRevokeKey(ctx3, decodeURIComponent(m2[1]));
576897
+ }
576794
576898
  if (pathname === "/v1/tools" && method === "GET") {
576795
576899
  return handleListTools(ctx3);
576796
576900
  }
@@ -576937,11 +577041,11 @@ async function handleGetSkill(ctx3, name10) {
576937
577041
  async function fallbackDiscoverSkills() {
576938
577042
  return (_root) => {
576939
577043
  const roots = [
576940
- join110(homedir37(), ".local", "share", "ai-writing-guide")
577044
+ join111(homedir38(), ".local", "share", "ai-writing-guide")
576941
577045
  ];
576942
577046
  const out = [];
576943
577047
  for (const root of roots) {
576944
- if (!existsSync94(root)) continue;
577048
+ if (!existsSync95(root)) continue;
576945
577049
  walkForSkills(root, out, 0);
576946
577050
  }
576947
577051
  return out;
@@ -576952,12 +577056,12 @@ function walkForSkills(dir, out, depth) {
576952
577056
  try {
576953
577057
  for (const e2 of readdirSync32(dir, { withFileTypes: true })) {
576954
577058
  if (e2.name.startsWith(".") || e2.name === "node_modules") continue;
576955
- const p2 = join110(dir, e2.name);
577059
+ const p2 = join111(dir, e2.name);
576956
577060
  if (e2.isDirectory()) {
576957
577061
  walkForSkills(p2, out, depth + 1);
576958
577062
  } else if (e2.isFile() && e2.name === "SKILL.md") {
576959
577063
  try {
576960
- const content = readFileSync76(p2, "utf-8").slice(0, 2e3);
577064
+ const content = readFileSync77(p2, "utf-8").slice(0, 2e3);
576961
577065
  const nameMatch = content.match(/^name:\s*(.+)$/m);
576962
577066
  const descMatch = content.match(/^description:\s*(.+)$/m);
576963
577067
  out.push({
@@ -577141,7 +577245,7 @@ async function getMemoryStores() {
577141
577245
  if (memoryInitTried) return null;
577142
577246
  memoryInitTried = true;
577143
577247
  try {
577144
- const dbPath = join110(homedir37(), ".open-agents", "memory.db");
577248
+ const dbPath = join111(homedir38(), ".open-agents", "memory.db");
577145
577249
  const sharedDb = initDb(dbPath);
577146
577250
  memoryStoresCache = {
577147
577251
  episode: new EpisodeStore(dbPath),
@@ -577399,7 +577503,7 @@ async function handleFilesRead(ctx3) {
577399
577503
  }));
577400
577504
  return true;
577401
577505
  }
577402
- if (!existsSync94(resolved)) {
577506
+ if (!existsSync95(resolved)) {
577403
577507
  sendProblem(res, problemDetails({
577404
577508
  type: P.notFound,
577405
577509
  status: 404,
@@ -577431,7 +577535,7 @@ async function handleFilesRead(ctx3) {
577431
577535
  }));
577432
577536
  return true;
577433
577537
  }
577434
- const content = readFileSync76(resolved, "utf-8");
577538
+ const content = readFileSync77(resolved, "utf-8");
577435
577539
  const offset = typeof body.offset === "number" && body.offset >= 0 ? body.offset : 0;
577436
577540
  const limit = typeof body.limit === "number" && body.limit > 0 ? body.limit : content.length;
577437
577541
  const slice2 = content.slice(offset, offset + limit);
@@ -577664,14 +577768,14 @@ async function handleNexusStatus(ctx3) {
577664
577768
  const { res, requestId } = ctx3;
577665
577769
  try {
577666
577770
  const statePaths = [
577667
- join110(process.cwd(), ".oa", "nexus-peer-state.json"),
577668
- join110(homedir37(), ".open-agents", "nexus-peer-cache.json")
577771
+ join111(process.cwd(), ".oa", "nexus-peer-state.json"),
577772
+ join111(homedir38(), ".open-agents", "nexus-peer-cache.json")
577669
577773
  ];
577670
577774
  const states = [];
577671
577775
  for (const p2 of statePaths) {
577672
- if (!existsSync94(p2)) continue;
577776
+ if (!existsSync95(p2)) continue;
577673
577777
  try {
577674
- const raw = readFileSync76(p2, "utf-8");
577778
+ const raw = readFileSync77(p2, "utf-8");
577675
577779
  states.push({ source: p2, data: JSON.parse(raw) });
577676
577780
  } catch (e2) {
577677
577781
  states.push({ source: p2, error: String(e2) });
@@ -577698,8 +577802,8 @@ async function handleNexusStatus(ctx3) {
577698
577802
  }
577699
577803
  function loadAgentName() {
577700
577804
  try {
577701
- const p2 = join110(homedir37(), ".open-agents", "agent-name");
577702
- if (existsSync94(p2)) return readFileSync76(p2, "utf-8").trim();
577805
+ const p2 = join111(homedir38(), ".open-agents", "agent-name");
577806
+ if (existsSync95(p2)) return readFileSync77(p2, "utf-8").trim();
577703
577807
  } catch {
577704
577808
  }
577705
577809
  return null;
@@ -577708,14 +577812,14 @@ async function handleSponsors(ctx3) {
577708
577812
  const { req: req2, res, url, requestId } = ctx3;
577709
577813
  try {
577710
577814
  const candidates = [
577711
- join110(homedir37(), ".open-agents", "sponsor-cache.json"),
577712
- join110(homedir37(), ".open-agents", "sponsors.json")
577815
+ join111(homedir38(), ".open-agents", "sponsor-cache.json"),
577816
+ join111(homedir38(), ".open-agents", "sponsors.json")
577713
577817
  ];
577714
577818
  let sponsors = [];
577715
577819
  for (const p2 of candidates) {
577716
- if (!existsSync94(p2)) continue;
577820
+ if (!existsSync95(p2)) continue;
577717
577821
  try {
577718
- const raw = JSON.parse(readFileSync76(p2, "utf-8"));
577822
+ const raw = JSON.parse(readFileSync77(p2, "utf-8"));
577719
577823
  if (Array.isArray(raw)) {
577720
577824
  sponsors = raw;
577721
577825
  break;
@@ -577784,8 +577888,8 @@ async function handleEvaluate(ctx3) {
577784
577888
  }));
577785
577889
  return true;
577786
577890
  }
577787
- const jobPath = join110(process.cwd(), ".oa", "jobs", `${runId}.json`);
577788
- if (!existsSync94(jobPath)) {
577891
+ const jobPath = join111(process.cwd(), ".oa", "jobs", `${runId}.json`);
577892
+ if (!existsSync95(jobPath)) {
577789
577893
  sendProblem(res, problemDetails({
577790
577894
  type: P.notFound,
577791
577895
  status: 404,
@@ -577795,7 +577899,7 @@ async function handleEvaluate(ctx3) {
577795
577899
  }));
577796
577900
  return true;
577797
577901
  }
577798
- const job = JSON.parse(readFileSync76(jobPath, "utf-8"));
577902
+ const job = JSON.parse(readFileSync77(jobPath, "utf-8"));
577799
577903
  sendJson(res, 200, {
577800
577904
  run_id: runId,
577801
577905
  task: job.task,
@@ -577846,6 +577950,130 @@ async function handleIndex(ctx3) {
577846
577950
  return true;
577847
577951
  }
577848
577952
  }
577953
+ async function handleListKeys(ctx3) {
577954
+ const { req: req2, res, requestId } = ctx3;
577955
+ const reqAuth = req2;
577956
+ if (reqAuth._authScope !== "admin") {
577957
+ sendProblem(res, problemDetails({
577958
+ type: P.forbidden,
577959
+ status: 403,
577960
+ title: "Admin scope required",
577961
+ detail: "Listing keys requires 'admin' scope.",
577962
+ instance: requestId
577963
+ }));
577964
+ return true;
577965
+ }
577966
+ try {
577967
+ const { listKeysSafe: listKeysSafe2 } = await Promise.resolve().then(() => (init_runtime_keys(), runtime_keys_exports));
577968
+ sendJson(res, 200, { keys: listKeysSafe2() });
577969
+ } catch (err) {
577970
+ sendProblem(res, problemDetails({
577971
+ type: P.internalError,
577972
+ status: 500,
577973
+ title: "List keys failed",
577974
+ detail: err instanceof Error ? err.message : String(err),
577975
+ instance: requestId
577976
+ }));
577977
+ }
577978
+ return true;
577979
+ }
577980
+ async function handleMintKey(ctx3) {
577981
+ const { req: req2, res, requestId } = ctx3;
577982
+ const reqAuth = req2;
577983
+ if (reqAuth._authScope !== "admin") {
577984
+ sendProblem(res, problemDetails({
577985
+ type: P.forbidden,
577986
+ status: 403,
577987
+ title: "Admin scope required",
577988
+ detail: "Minting keys requires 'admin' scope.",
577989
+ instance: requestId
577990
+ }));
577991
+ return true;
577992
+ }
577993
+ try {
577994
+ const body = await parseJsonBodyStrict(req2).catch(() => null);
577995
+ if (!body || typeof body !== "object") {
577996
+ sendProblem(res, problemDetails({
577997
+ type: P.invalidRequest,
577998
+ status: 400,
577999
+ title: "Body must be JSON object",
578000
+ detail: "POST {scope: 'read'|'run'|'admin', owner: string, profile?: string}",
578001
+ instance: requestId
578002
+ }));
578003
+ return true;
578004
+ }
578005
+ const scope = body.scope;
578006
+ if (scope !== "read" && scope !== "run" && scope !== "admin") {
578007
+ sendProblem(res, problemDetails({
578008
+ type: P.invalidRequest,
578009
+ status: 400,
578010
+ title: "Invalid scope",
578011
+ detail: "scope must be one of 'read', 'run', 'admin'.",
578012
+ instance: requestId
578013
+ }));
578014
+ return true;
578015
+ }
578016
+ const owner = typeof body.owner === "string" ? body.owner.slice(0, 200) : "anonymous";
578017
+ const profile = typeof body.profile === "string" ? body.profile.slice(0, 100) : null;
578018
+ const { mintKey: mintKey2 } = await Promise.resolve().then(() => (init_runtime_keys(), runtime_keys_exports));
578019
+ const rec = mintKey2({
578020
+ scope,
578021
+ owner,
578022
+ profile,
578023
+ rpm: typeof body.rpm === "number" ? body.rpm : void 0,
578024
+ tpd: typeof body.tpd === "number" ? body.tpd : void 0,
578025
+ max_jobs: typeof body.max_jobs === "number" ? body.max_jobs : void 0
578026
+ });
578027
+ sendJson(res, 201, { ...rec, _note: "This is the ONLY response that contains the full key. Persist it now." });
578028
+ } catch (err) {
578029
+ sendProblem(res, problemDetails({
578030
+ type: P.internalError,
578031
+ status: 500,
578032
+ title: "Key mint failed",
578033
+ detail: err instanceof Error ? err.message : String(err),
578034
+ instance: requestId
578035
+ }));
578036
+ }
578037
+ return true;
578038
+ }
578039
+ async function handleRevokeKey(ctx3, prefix) {
578040
+ const { req: req2, res, requestId } = ctx3;
578041
+ const reqAuth = req2;
578042
+ if (reqAuth._authScope !== "admin") {
578043
+ sendProblem(res, problemDetails({
578044
+ type: P.forbidden,
578045
+ status: 403,
578046
+ title: "Admin scope required",
578047
+ detail: "Revoking keys requires 'admin' scope.",
578048
+ instance: requestId
578049
+ }));
578050
+ return true;
578051
+ }
578052
+ if (!prefix || prefix.length < 8) {
578053
+ sendProblem(res, problemDetails({
578054
+ type: P.invalidRequest,
578055
+ status: 400,
578056
+ title: "Prefix too short",
578057
+ detail: "Provide at least the first 8 characters of the key to revoke.",
578058
+ instance: requestId
578059
+ }));
578060
+ return true;
578061
+ }
578062
+ try {
578063
+ const { revokeByPrefix: revokeByPrefix2 } = await Promise.resolve().then(() => (init_runtime_keys(), runtime_keys_exports));
578064
+ const n2 = revokeByPrefix2(prefix);
578065
+ sendJson(res, 200, { revoked: n2 });
578066
+ } catch (err) {
578067
+ sendProblem(res, problemDetails({
578068
+ type: P.internalError,
578069
+ status: 500,
578070
+ title: "Key revoke failed",
578071
+ detail: err instanceof Error ? err.message : String(err),
578072
+ instance: requestId
578073
+ }));
578074
+ }
578075
+ return true;
578076
+ }
577849
578077
  async function handleListTools(ctx3) {
577850
578078
  const { req: req2, res, url, requestId } = ctx3;
577851
578079
  try {
@@ -577889,10 +578117,15 @@ async function handleListTools(ctx3) {
577889
578117
  const filterCat = url.searchParams.get("category");
577890
578118
  const filterScope = url.searchParams.get("scope");
577891
578119
  const filterRisk = url.searchParams.get("risk");
578120
+ const filterOffDeviceRaw = url.searchParams.get("off_device");
577892
578121
  let filtered = tools;
577893
578122
  if (filterCat) filtered = filtered.filter((t2) => t2.security?.categories?.includes(filterCat));
577894
578123
  if (filterScope) filtered = filtered.filter((t2) => t2.security?.requires_scope === filterScope);
577895
578124
  if (filterRisk) filtered = filtered.filter((t2) => t2.security?.risk === filterRisk);
578125
+ if (filterOffDeviceRaw !== null) {
578126
+ const want = filterOffDeviceRaw === "true";
578127
+ filtered = filtered.filter((t2) => t2.security?.off_device_allowed === want);
578128
+ }
577896
578129
  const page2 = parsePagination(url.searchParams);
577897
578130
  const envelope = paginated(filtered, page2);
577898
578131
  const etag = computeEtag(envelope);
@@ -577965,6 +578198,13 @@ async function handleGetTool(ctx3, name10) {
577965
578198
  endpoints: {
577966
578199
  call: `/v1/tools/${encodeURIComponent(name10)}/call`,
577967
578200
  schema: `/v1/tools/${encodeURIComponent(name10)}`
578201
+ },
578202
+ // Q4: document the canonical body shape for /v1/tools/{name}/call.
578203
+ // The `parameters` field IS the JSON-schema describing what goes
578204
+ // INSIDE the args wrapper — it's not the wrapper itself.
578205
+ call_body_shape: {
578206
+ args: "<object matching the `parameters` JSON schema>",
578207
+ working_dir: "<optional absolute path; defaults to daemon cwd>"
577968
578208
  }
577969
578209
  };
577970
578210
  const etag = computeEtag(body);
@@ -578147,17 +578387,17 @@ async function handleListAgentTypes(ctx3) {
578147
578387
  }
578148
578388
  async function handleListEngines(ctx3) {
578149
578389
  const { res } = ctx3;
578150
- const home = homedir37();
578390
+ const home = homedir38();
578151
578391
  sendJson(res, 200, {
578152
578392
  engines: [
578153
- { name: "dream", state_file: join110(process.cwd(), ".oa", "dreams"), controllable_via: "SSE + slash commands" },
578154
- { name: "bless", state_file: join110(process.cwd(), ".oa", "bless-state.json"), controllable_via: "slash commands" },
578155
- { name: "call", state_file: join110(process.cwd(), ".oa", "call-state.json"), controllable_via: "slash commands" },
578156
- { name: "listen", state_file: join110(process.cwd(), ".oa", "listen-state.json"), controllable_via: "slash commands" },
578157
- { name: "telegram", state_file: join110(home, ".open-agents", "telegram-state.json"), controllable_via: "slash commands" },
578158
- { name: "expose", state_file: join110(process.cwd(), ".oa", "expose-state.json"), controllable_via: "/expose commands" },
578159
- { name: "nexus", state_file: join110(home, ".open-agents", "nexus-peer-cache.json"), controllable_via: "/nexus commands" },
578160
- { name: "ipfs", state_file: join110(process.cwd(), ".oa", "ipfs"), controllable_via: "slash commands" }
578393
+ { name: "dream", state_file: join111(process.cwd(), ".oa", "dreams"), controllable_via: "SSE + slash commands" },
578394
+ { name: "bless", state_file: join111(process.cwd(), ".oa", "bless-state.json"), controllable_via: "slash commands" },
578395
+ { name: "call", state_file: join111(process.cwd(), ".oa", "call-state.json"), controllable_via: "slash commands" },
578396
+ { name: "listen", state_file: join111(process.cwd(), ".oa", "listen-state.json"), controllable_via: "slash commands" },
578397
+ { name: "telegram", state_file: join111(home, ".open-agents", "telegram-state.json"), controllable_via: "slash commands" },
578398
+ { name: "expose", state_file: join111(process.cwd(), ".oa", "expose-state.json"), controllable_via: "/expose commands" },
578399
+ { name: "nexus", state_file: join111(home, ".open-agents", "nexus-peer-cache.json"), controllable_via: "/nexus commands" },
578400
+ { name: "ipfs", state_file: join111(process.cwd(), ".oa", "ipfs"), controllable_via: "slash commands" }
578161
578401
  ],
578162
578402
  note: "Engine instrumentation lives in the running TUI process. Full status + control requires the daemon↔TUI bridge (PT-07). See parity audit WO-PARITY-04."
578163
578403
  });
@@ -578240,21 +578480,21 @@ async function tryAimsRoute(ctx3) {
578240
578480
  return false;
578241
578481
  }
578242
578482
  function aimsDir() {
578243
- return join110(homedir37(), ".open-agents", "aims");
578483
+ return join111(homedir38(), ".open-agents", "aims");
578244
578484
  }
578245
578485
  function readAimsFile(name10, fallback) {
578246
578486
  try {
578247
- const p2 = join110(aimsDir(), name10);
578248
- if (existsSync94(p2)) return JSON.parse(readFileSync76(p2, "utf-8"));
578487
+ const p2 = join111(aimsDir(), name10);
578488
+ if (existsSync95(p2)) return JSON.parse(readFileSync77(p2, "utf-8"));
578249
578489
  } catch {
578250
578490
  }
578251
578491
  return fallback;
578252
578492
  }
578253
578493
  function writeAimsFile(name10, data) {
578254
578494
  const dir = aimsDir();
578255
- const { mkdirSync: mkdirSync66, writeFileSync: wf, renameSync: rn } = __require("node:fs");
578256
- mkdirSync66(dir, { recursive: true });
578257
- const finalPath = join110(dir, name10);
578495
+ const { mkdirSync: mkdirSync67, writeFileSync: wf, renameSync: rn } = __require("node:fs");
578496
+ mkdirSync67(dir, { recursive: true });
578497
+ const finalPath = join111(dir, name10);
578258
578498
  const tmpPath = `${finalPath}.tmp.${process.pid}.${Date.now()}`;
578259
578499
  try {
578260
578500
  wf(tmpPath, JSON.stringify(data, null, 2) + "\n", { encoding: "utf-8", mode: 384 });
@@ -578584,12 +578824,12 @@ async function handleAimsSuppliers(ctx3) {
578584
578824
  }
578585
578825
  ];
578586
578826
  const sponsorPaths = [
578587
- join110(homedir37(), ".open-agents", "sponsor-cache.json")
578827
+ join111(homedir38(), ".open-agents", "sponsor-cache.json")
578588
578828
  ];
578589
578829
  for (const p2 of sponsorPaths) {
578590
- if (!existsSync94(p2)) continue;
578830
+ if (!existsSync95(p2)) continue;
578591
578831
  try {
578592
- const raw = JSON.parse(readFileSync76(p2, "utf-8"));
578832
+ const raw = JSON.parse(readFileSync77(p2, "utf-8"));
578593
578833
  const list = Array.isArray(raw) ? raw : raw?.sponsors ?? [];
578594
578834
  for (const s2 of list) {
578595
578835
  suppliers.push({
@@ -578651,11 +578891,11 @@ async function handleAimsIncidentPost(ctx3) {
578651
578891
  }));
578652
578892
  return true;
578653
578893
  }
578654
- const { randomBytes: randomBytes24 } = await import("node:crypto");
578894
+ const { randomBytes: randomBytes25 } = await import("node:crypto");
578655
578895
  const record = await withAimsLock("incidents.json", () => {
578656
578896
  const existing = readAimsFile("incidents.json", []);
578657
578897
  const rec = {
578658
- id: `INC-${Date.now()}-${randomBytes24(4).toString("hex")}`,
578898
+ id: `INC-${Date.now()}-${randomBytes25(4).toString("hex")}`,
578659
578899
  ts: (/* @__PURE__ */ new Date()).toISOString(),
578660
578900
  raised_by: user ?? "anonymous",
578661
578901
  status: "open",
@@ -585783,10 +586023,24 @@ function getOpenApiSpec() {
585783
586023
  "/v1/models": { get: { summary: "List aggregated models", tags: ["Inference"], responses: { 200: { description: "OpenAI-format model list" }, ...ErrorResponses } } },
585784
586024
  "/v1/chat/completions": {
585785
586025
  post: {
585786
- summary: "OpenAI-compatible chat completion",
586026
+ summary: "OpenAI-compatible chat completion (with optional server-side agent loop)",
585787
586027
  tags: ["Inference"],
585788
- requestBody: { required: true, content: { "application/json": { schema: { type: "object", required: ["model", "messages"], properties: { model: { type: "string" }, messages: { type: "array" }, stream: { type: "boolean" }, temperature: { type: "number" } } } } } },
585789
- responses: { 200: { description: "Chat response (SSE if stream=true)" }, ...ErrorResponses }
586028
+ description: "Standard OpenAI chat-completion proxy by default. Set agent_loop=true to enter a synchronous server-side loop that auto-executes daemon tools (web_search, web_fetch, file_read, ocr, etc.) and re-prompts the model collapsing N round-trips into 1. include_daemon_tools=['read','run'] auto-merges daemon tools matching those scopes into the offered tools[]. timeout_s overrides OA_BACKEND_TIMEOUT_S (default 120s, max 1h). prompt_template='factual-first' prepends a web_search-first policy. SSE streaming honors tool_calls in delta as standard OpenAI shape: delta.tool_calls=[{id,type:'function',index,function:{name,arguments:<JSON-string>}}]. parallel_tool_calls is forwarded to the backend.",
586029
+ requestBody: { required: true, content: { "application/json": { schema: { type: "object", required: ["model", "messages"], properties: {
586030
+ model: { type: "string" },
586031
+ messages: { type: "array" },
586032
+ stream: { type: "boolean" },
586033
+ temperature: { type: "number" },
586034
+ tools: { type: "array", description: "OpenAI-shape tools array. Forwarded to the backend." },
586035
+ tool_choice: { description: "OpenAI-shape tool_choice. Forwarded to the backend." },
586036
+ parallel_tool_calls: { type: "boolean", description: "Q7 — forwarded to backend; some models (qwen3, llama3) emit multiple tool_calls in one response." },
586037
+ timeout_s: { type: "number", description: "Q1 — per-request timeout in seconds (1-3600). Overrides OA_BACKEND_TIMEOUT_S." },
586038
+ agent_loop: { type: "boolean", description: "Q2 — when true, daemon runs an N-turn agent loop server-side. Daemon tools execute inline; client tool_calls cause the loop to yield." },
586039
+ include_daemon_tools: { type: "array", items: { type: "string", enum: ["read", "run", "admin"] }, description: "Q3 — scopes to merge from daemon tool catalog into tools[]." },
586040
+ max_turns: { type: "integer", description: "Q2 — agent_loop max iterations (default 8, max 64)." },
586041
+ prompt_template: { type: "string", enum: ["factual-first"], description: "Q8 — prepended system policy template. 'factual-first' instructs model to call web_search FIRST for any factual question." }
586042
+ } } } } },
586043
+ responses: { 200: { description: "OpenAI chat.completion shape, SSE if stream=true. agent_loop responses include _agent_loop:{turns,log,done,reason}." }, ...ErrorResponses }
585790
586044
  }
585791
586045
  },
585792
586046
  "/v1/embeddings": { post: { summary: "Generate embeddings", tags: ["Inference"], responses: { 200: { description: "Embedding vectors" }, ...ErrorResponses } } },
@@ -585998,7 +586252,7 @@ function getOpenApiSpec() {
585998
586252
  post: {
585999
586253
  summary: "Execute a single tool directly (MCP-style, no agent loop)",
586000
586254
  tags: ["Tools"],
586001
- description: "Body: {args: object, working_dir?: string}. Returns the tool's ToolResult {success, output, llmContent?, error?, durationMs} plus security metadata. Auth gating: request scope (read/run/admin) must satisfy tool.requires_scope; non-loopback callers need admin scope unless tool.off_device_allowed=true. Anonymous loopback callers default to admin scope; anonymous remote callers default to read scope.",
586255
+ description: "Body: {args: object, working_dir?: string}. The `args` key is canonical (Q4) — the JSON-schema in /v1/tools/{name} `parameters` describes what goes INSIDE the args wrapper. Returns ToolResult {success, output, llmContent?, error?, durationMs} plus security metadata. Auth gating: request scope (read/run/admin) must satisfy tool.requires_scope; non-loopback callers need admin scope unless tool.off_device_allowed=true. Anonymous loopback callers default to admin scope; anonymous remote callers default to read scope.",
586002
586256
  parameters: [{ name: "name", in: "path", required: true, schema: { type: "string" } }],
586003
586257
  responses: {
586004
586258
  200: { description: "Tool result envelope" },
@@ -586008,6 +586262,28 @@ function getOpenApiSpec() {
586008
586262
  }
586009
586263
  }
586010
586264
  },
586265
+ "/v1/keys": {
586266
+ get: {
586267
+ summary: "List runtime API keys (admin scope)",
586268
+ tags: ["Tools"],
586269
+ description: "Returns runtime keys with secrets masked (only first 8 + last 4 chars). Q6 — runtime-mintable alternative to OA_API_KEY/OA_API_KEYS env vars.",
586270
+ responses: { 200: { description: "Masked key list" }, 403: { description: "Admin scope required" } }
586271
+ },
586272
+ post: {
586273
+ summary: "Mint a new runtime API key (admin scope)",
586274
+ tags: ["Tools"],
586275
+ description: "Body: {scope: 'read'|'run'|'admin', owner: string, profile?: string, rpm?, tpd?, max_jobs?}. Returns the FULL key — only response that contains the secret. Persisted at ~/.open-agents/keys.json mode 0600.",
586276
+ responses: { 201: { description: "Key minted" }, 400: { description: "Invalid body" }, 403: { description: "Admin scope required" } }
586277
+ }
586278
+ },
586279
+ "/v1/keys/{prefix}": {
586280
+ delete: {
586281
+ summary: "Revoke a runtime key by prefix (admin scope)",
586282
+ tags: ["Tools"],
586283
+ parameters: [{ name: "prefix", in: "path", required: true, schema: { type: "string", minLength: 8 }, description: "First 8+ characters of the key" }],
586284
+ responses: { 200: { description: "Number of keys revoked" }, 400: { description: "Prefix too short" }, 403: { description: "Admin scope required" } }
586285
+ }
586286
+ },
586011
586287
  "/v1/hooks": { get: { summary: "List hook types and counts", tags: ["Tools"], responses: { 200: { description: "Hook registry" } } } },
586012
586288
  "/v1/agents": { get: { summary: "List agent types", tags: ["Tools"], responses: { 200: { description: "Agent type registry" } } } },
586013
586289
  "/v1/engines": { get: { summary: "List long-running engines", tags: ["Engines"], responses: { 200: { description: "Engine status + state files" } } } },
@@ -586440,15 +586716,15 @@ var init_auth_oidc = __esm({
586440
586716
  });
586441
586717
 
586442
586718
  // packages/cli/src/api/usage-tracker.ts
586443
- import { mkdirSync as mkdirSync59, readFileSync as readFileSync77, writeFileSync as writeFileSync51, existsSync as existsSync95 } from "node:fs";
586444
- import { join as join111 } from "node:path";
586719
+ import { mkdirSync as mkdirSync60, readFileSync as readFileSync78, writeFileSync as writeFileSync52, existsSync as existsSync96 } from "node:fs";
586720
+ import { join as join112 } from "node:path";
586445
586721
  function initUsageTracker(oaDir) {
586446
- const dir = join111(oaDir, "usage");
586447
- mkdirSync59(dir, { recursive: true });
586448
- usageFile = join111(dir, "token-usage.json");
586722
+ const dir = join112(oaDir, "usage");
586723
+ mkdirSync60(dir, { recursive: true });
586724
+ usageFile = join112(dir, "token-usage.json");
586449
586725
  try {
586450
- if (existsSync95(usageFile)) {
586451
- store = JSON.parse(readFileSync77(usageFile, "utf-8"));
586726
+ if (existsSync96(usageFile)) {
586727
+ store = JSON.parse(readFileSync78(usageFile, "utf-8"));
586452
586728
  }
586453
586729
  } catch {
586454
586730
  store = { providers: {}, lastSaved: "" };
@@ -586484,7 +586760,7 @@ function flush2() {
586484
586760
  if (!initialized2 || !dirty) return;
586485
586761
  try {
586486
586762
  store.lastSaved = (/* @__PURE__ */ new Date()).toISOString();
586487
- writeFileSync51(usageFile, JSON.stringify(store, null, 2), "utf-8");
586763
+ writeFileSync52(usageFile, JSON.stringify(store, null, 2), "utf-8");
586488
586764
  dirty = false;
586489
586765
  } catch {
586490
586766
  }
@@ -586512,24 +586788,24 @@ var init_usage_tracker = __esm({
586512
586788
  });
586513
586789
 
586514
586790
  // packages/cli/src/api/profiles.ts
586515
- import { existsSync as existsSync96, readFileSync as readFileSync78, writeFileSync as writeFileSync52, mkdirSync as mkdirSync60, readdirSync as readdirSync33, unlinkSync as unlinkSync23 } from "node:fs";
586516
- import { join as join112 } from "node:path";
586517
- import { homedir as homedir38 } from "node:os";
586518
- import { createCipheriv as createCipheriv4, createDecipheriv as createDecipheriv4, randomBytes as randomBytes21, scryptSync as scryptSync3 } from "node:crypto";
586791
+ import { existsSync as existsSync97, readFileSync as readFileSync79, writeFileSync as writeFileSync53, mkdirSync as mkdirSync61, readdirSync as readdirSync33, unlinkSync as unlinkSync23 } from "node:fs";
586792
+ import { join as join113 } from "node:path";
586793
+ import { homedir as homedir39 } from "node:os";
586794
+ import { createCipheriv as createCipheriv4, createDecipheriv as createDecipheriv4, randomBytes as randomBytes22, scryptSync as scryptSync3 } from "node:crypto";
586519
586795
  function globalProfileDir() {
586520
- return join112(homedir38(), ".open-agents", "profiles");
586796
+ return join113(homedir39(), ".open-agents", "profiles");
586521
586797
  }
586522
586798
  function projectProfileDir(projectDir2) {
586523
- return join112(projectDir2 || process.cwd(), ".oa", "profiles");
586799
+ return join113(projectDir2 || process.cwd(), ".oa", "profiles");
586524
586800
  }
586525
586801
  function listProfiles(projectDir2) {
586526
586802
  const result = [];
586527
586803
  const seen = /* @__PURE__ */ new Set();
586528
586804
  const projDir = projectProfileDir(projectDir2);
586529
- if (existsSync96(projDir)) {
586805
+ if (existsSync97(projDir)) {
586530
586806
  for (const f2 of readdirSync33(projDir).filter((f3) => f3.endsWith(".json"))) {
586531
586807
  try {
586532
- const raw = JSON.parse(readFileSync78(join112(projDir, f2), "utf8"));
586808
+ const raw = JSON.parse(readFileSync79(join113(projDir, f2), "utf8"));
586533
586809
  const name10 = f2.replace(".json", "");
586534
586810
  seen.add(name10);
586535
586811
  result.push({
@@ -586543,12 +586819,12 @@ function listProfiles(projectDir2) {
586543
586819
  }
586544
586820
  }
586545
586821
  const globDir = globalProfileDir();
586546
- if (existsSync96(globDir)) {
586822
+ if (existsSync97(globDir)) {
586547
586823
  for (const f2 of readdirSync33(globDir).filter((f3) => f3.endsWith(".json"))) {
586548
586824
  const name10 = f2.replace(".json", "");
586549
586825
  if (seen.has(name10)) continue;
586550
586826
  try {
586551
- const raw = JSON.parse(readFileSync78(join112(globDir, f2), "utf8"));
586827
+ const raw = JSON.parse(readFileSync79(join113(globDir, f2), "utf8"));
586552
586828
  result.push({
586553
586829
  name: name10,
586554
586830
  description: raw.description || "",
@@ -586563,11 +586839,11 @@ function listProfiles(projectDir2) {
586563
586839
  }
586564
586840
  function loadProfile(name10, password, projectDir2) {
586565
586841
  const sanitized = name10.replace(/[^a-zA-Z0-9_-]/g, "");
586566
- const projPath = join112(projectProfileDir(projectDir2), `${sanitized}.json`);
586567
- const globPath = join112(globalProfileDir(), `${sanitized}.json`);
586568
- const filePath = existsSync96(projPath) ? projPath : existsSync96(globPath) ? globPath : null;
586842
+ const projPath = join113(projectProfileDir(projectDir2), `${sanitized}.json`);
586843
+ const globPath = join113(globalProfileDir(), `${sanitized}.json`);
586844
+ const filePath = existsSync97(projPath) ? projPath : existsSync97(globPath) ? globPath : null;
586569
586845
  if (!filePath) return null;
586570
- const raw = JSON.parse(readFileSync78(filePath, "utf8"));
586846
+ const raw = JSON.parse(readFileSync79(filePath, "utf8"));
586571
586847
  if (raw.encrypted === true) {
586572
586848
  if (!password) return null;
586573
586849
  return decryptProfile(raw, password);
@@ -586576,32 +586852,32 @@ function loadProfile(name10, password, projectDir2) {
586576
586852
  }
586577
586853
  function saveProfile(profile, password, scope = "global", projectDir2) {
586578
586854
  const dir = scope === "project" ? projectProfileDir(projectDir2) : globalProfileDir();
586579
- mkdirSync60(dir, { recursive: true });
586855
+ mkdirSync61(dir, { recursive: true });
586580
586856
  const sanitized = profile.name.replace(/[^a-zA-Z0-9_-]/g, "");
586581
- const filePath = join112(dir, `${sanitized}.json`);
586857
+ const filePath = join113(dir, `${sanitized}.json`);
586582
586858
  profile.modified = (/* @__PURE__ */ new Date()).toISOString();
586583
586859
  if (password) {
586584
586860
  const encrypted = encryptProfile(profile, password);
586585
- writeFileSync52(filePath, JSON.stringify(encrypted, null, 2), { mode: 384 });
586861
+ writeFileSync53(filePath, JSON.stringify(encrypted, null, 2), { mode: 384 });
586586
586862
  } else {
586587
586863
  profile.encrypted = false;
586588
- writeFileSync52(filePath, JSON.stringify(profile, null, 2), { mode: 420 });
586864
+ writeFileSync53(filePath, JSON.stringify(profile, null, 2), { mode: 420 });
586589
586865
  }
586590
586866
  }
586591
586867
  function deleteProfile(name10, scope = "global", projectDir2) {
586592
586868
  const sanitized = name10.replace(/[^a-zA-Z0-9_-]/g, "");
586593
586869
  const dir = scope === "project" ? projectProfileDir(projectDir2) : globalProfileDir();
586594
- const filePath = join112(dir, `${sanitized}.json`);
586595
- if (existsSync96(filePath)) {
586870
+ const filePath = join113(dir, `${sanitized}.json`);
586871
+ if (existsSync97(filePath)) {
586596
586872
  unlinkSync23(filePath);
586597
586873
  return true;
586598
586874
  }
586599
586875
  return false;
586600
586876
  }
586601
586877
  function encryptProfile(profile, password) {
586602
- const salt = randomBytes21(32);
586878
+ const salt = randomBytes22(32);
586603
586879
  const key = scryptSync3(password, salt, 32);
586604
- const iv = randomBytes21(16);
586880
+ const iv = randomBytes22(16);
586605
586881
  const cipher = createCipheriv4("aes-256-gcm", key, iv);
586606
586882
  const plaintext = JSON.stringify(profile);
586607
586883
  const encrypted = Buffer.concat([cipher.update(plaintext, "utf8"), cipher.final()]);
@@ -586707,23 +586983,23 @@ var init_profiles = __esm({
586707
586983
 
586708
586984
  // packages/cli/src/docker.ts
586709
586985
  import { execSync as execSync54, spawn as spawn24 } from "node:child_process";
586710
- import { existsSync as existsSync97, mkdirSync as mkdirSync61, writeFileSync as writeFileSync53 } from "node:fs";
586711
- import { join as join113, resolve as resolve37, dirname as dirname34 } from "node:path";
586712
- import { homedir as homedir39 } from "node:os";
586986
+ import { existsSync as existsSync98, mkdirSync as mkdirSync62, writeFileSync as writeFileSync54 } from "node:fs";
586987
+ import { join as join114, resolve as resolve37, dirname as dirname34 } from "node:path";
586988
+ import { homedir as homedir40 } from "node:os";
586713
586989
  import { fileURLToPath as fileURLToPath16 } from "node:url";
586714
586990
  function getDockerDir() {
586715
586991
  try {
586716
586992
  if (typeof __dirname !== "undefined") {
586717
- return join113(__dirname, "..", "..", "..", "docker");
586993
+ return join114(__dirname, "..", "..", "..", "docker");
586718
586994
  }
586719
586995
  } catch {
586720
586996
  }
586721
586997
  try {
586722
586998
  const thisDir = dirname34(fileURLToPath16(import.meta.url));
586723
- return join113(thisDir, "..", "..", "..", "docker");
586999
+ return join114(thisDir, "..", "..", "..", "docker");
586724
587000
  } catch {
586725
587001
  }
586726
- return join113(process.cwd(), "docker");
587002
+ return join114(process.cwd(), "docker");
586727
587003
  }
586728
587004
  function isDockerAvailable() {
586729
587005
  try {
@@ -586854,11 +587130,11 @@ async function ensureOaImage(force = false) {
586854
587130
  }
586855
587131
  let buildContext;
586856
587132
  const dockerDir = getDockerDir();
586857
- if (existsSync97(join113(dockerDir, "Dockerfile"))) {
587133
+ if (existsSync98(join114(dockerDir, "Dockerfile"))) {
586858
587134
  buildContext = dockerDir;
586859
587135
  } else {
586860
- buildContext = join113(homedir39(), ".oa", "docker-build");
586861
- mkdirSync61(buildContext, { recursive: true });
587136
+ buildContext = join114(homedir40(), ".oa", "docker-build");
587137
+ mkdirSync62(buildContext, { recursive: true });
586862
587138
  writeDockerfiles(buildContext);
586863
587139
  }
586864
587140
  try {
@@ -586932,8 +587208,8 @@ chown -R node:node /workspace /home/node/.oa /home/node/.open-agents 2>/dev/null
586932
587208
  if [ "$1" = "oa" ]; then shift; exec su - node -c "cd /workspace && oa $*"; fi
586933
587209
  exec "$@"
586934
587210
  `;
586935
- writeFileSync53(join113(dir, "Dockerfile"), dockerfile);
586936
- writeFileSync53(join113(dir, "docker-entrypoint.sh"), entrypoint, { mode: 493 });
587211
+ writeFileSync54(join114(dir, "Dockerfile"), dockerfile);
587212
+ writeFileSync54(join114(dir, "docker-entrypoint.sh"), entrypoint, { mode: 493 });
586937
587213
  }
586938
587214
  function hasNvidiaGpu() {
586939
587215
  try {
@@ -587186,23 +587462,23 @@ import * as http5 from "node:http";
587186
587462
  import * as https3 from "node:https";
587187
587463
  import { createRequire as createRequire4 } from "node:module";
587188
587464
  import { fileURLToPath as fileURLToPath17 } from "node:url";
587189
- import { dirname as dirname35, join as join114, resolve as resolve38 } from "node:path";
587190
- import { homedir as homedir40 } from "node:os";
587465
+ import { dirname as dirname35, join as join115, resolve as resolve38 } from "node:path";
587466
+ import { homedir as homedir41 } from "node:os";
587191
587467
  import { spawn as spawn25, execSync as execSync55 } from "node:child_process";
587192
- import { mkdirSync as mkdirSync62, writeFileSync as writeFileSync54, readFileSync as readFileSync79, readdirSync as readdirSync34, existsSync as existsSync98, watch as fsWatch3, renameSync as renameSync8, unlinkSync as unlinkSync24 } from "node:fs";
587193
- import { randomBytes as randomBytes22, randomUUID as randomUUID14 } from "node:crypto";
587468
+ import { mkdirSync as mkdirSync63, writeFileSync as writeFileSync55, readFileSync as readFileSync80, readdirSync as readdirSync34, existsSync as existsSync99, watch as fsWatch3, renameSync as renameSync8, unlinkSync as unlinkSync24 } from "node:fs";
587469
+ import { randomBytes as randomBytes23, randomUUID as randomUUID14 } from "node:crypto";
587194
587470
  import { createHash as createHash15 } from "node:crypto";
587195
587471
  function getVersion3() {
587196
587472
  try {
587197
587473
  const thisDir = dirname35(fileURLToPath17(import.meta.url));
587198
587474
  const candidates = [
587199
- join114(thisDir, "..", "package.json"),
587200
- join114(thisDir, "..", "..", "package.json"),
587201
- join114(thisDir, "..", "..", "..", "package.json")
587475
+ join115(thisDir, "..", "package.json"),
587476
+ join115(thisDir, "..", "..", "package.json"),
587477
+ join115(thisDir, "..", "..", "..", "package.json")
587202
587478
  ];
587203
587479
  for (const pkgPath of candidates) {
587204
587480
  try {
587205
- if (!existsSync98(pkgPath)) continue;
587481
+ if (!existsSync99(pkgPath)) continue;
587206
587482
  const pkg = require3(pkgPath);
587207
587483
  if (pkg.name === "open-agents-ai" || pkg.name === "@open-agents/cli" || pkg.name === "@open-agents/monorepo") {
587208
587484
  return pkg.version ?? "0.0.0";
@@ -587349,6 +587625,19 @@ function resolveModelEndpoint(modelId) {
587349
587625
  }
587350
587626
  return null;
587351
587627
  }
587628
+ function getBackendTimeoutMs(perRequestSeconds) {
587629
+ if (typeof perRequestSeconds === "number" && perRequestSeconds > 0) {
587630
+ return Math.min(Math.floor(perRequestSeconds * 1e3), BACKEND_TIMEOUT_MAX_MS);
587631
+ }
587632
+ const envS = process.env["OA_BACKEND_TIMEOUT_S"];
587633
+ if (envS) {
587634
+ const n2 = parseFloat(envS);
587635
+ if (Number.isFinite(n2) && n2 > 0) {
587636
+ return Math.min(Math.floor(n2 * 1e3), BACKEND_TIMEOUT_MAX_MS);
587637
+ }
587638
+ }
587639
+ return BACKEND_TIMEOUT_DEFAULT_MS;
587640
+ }
587352
587641
  function recordMetric(method, path8, status) {
587353
587642
  const key = `${method}|${path8}|${status}`;
587354
587643
  const existing = metrics.requests.get(key);
@@ -587391,9 +587680,9 @@ function isOriginAllowed(origin) {
587391
587680
  if (!origin) return true;
587392
587681
  let accessMode = (process.env["OA_ACCESS"] || "").toLowerCase().trim();
587393
587682
  try {
587394
- const accessFile = join114(homedir40(), ".open-agents", "access");
587395
- if (existsSync98(accessFile)) {
587396
- const persisted = readFileSync79(accessFile, "utf8").trim().toLowerCase();
587683
+ const accessFile = join115(homedir41(), ".open-agents", "access");
587684
+ if (existsSync99(accessFile)) {
587685
+ const persisted = readFileSync80(accessFile, "utf8").trim().toLowerCase();
587397
587686
  if (persisted === "any" || persisted === "lan" || persisted === "loopback") {
587398
587687
  accessMode = persisted;
587399
587688
  }
@@ -587453,7 +587742,7 @@ async function retrieveMemoryContext(userMessage, sessionId, maxEpisodes = 5) {
587453
587742
  if (!memMod || !memMod.EpisodeStore) {
587454
587743
  return { contextBlock: "", retrieved: [] };
587455
587744
  }
587456
- const dbPath = join114(homedir40(), ".open-agents", "memory.db");
587745
+ const dbPath = join115(homedir41(), ".open-agents", "memory.db");
587457
587746
  const store2 = new memMod.EpisodeStore(dbPath);
587458
587747
  const recent = store2.search({ limit: 30, sessionId: void 0 }) ?? [];
587459
587748
  const qLower = userMessage.toLowerCase();
@@ -587496,7 +587785,7 @@ async function writeMemoryEpisodes(sessionId, userMessage, assistantContent, too
587496
587785
  try {
587497
587786
  const memMod = await Promise.resolve().then(() => (init_dist7(), dist_exports2)).catch(() => null);
587498
587787
  if (!memMod || !memMod.EpisodeStore) return 0;
587499
- const dbPath = join114(homedir40(), ".open-agents", "memory.db");
587788
+ const dbPath = join115(homedir41(), ".open-agents", "memory.db");
587500
587789
  const store2 = new memMod.EpisodeStore(dbPath);
587501
587790
  let written = 0;
587502
587791
  try {
@@ -587598,7 +587887,8 @@ async function directChatBackend(opts) {
587598
587887
  proxyRes.on("error", reject);
587599
587888
  });
587600
587889
  proxyReq.on("error", reject);
587601
- proxyReq.setTimeout(12e4, () => proxyReq.destroy(new Error("Backend timeout (120s)")));
587890
+ const _dctTo = getBackendTimeoutMs(extraFields?.["timeout_s"]);
587891
+ proxyReq.setTimeout(_dctTo, () => proxyReq.destroy(new Error(`Backend timeout (${Math.round(_dctTo / 1e3)}s)`)));
587602
587892
  proxyReq.write(reqBody);
587603
587893
  proxyReq.end();
587604
587894
  });
@@ -587751,7 +588041,7 @@ function backendAuthHeaders(endpoint) {
587751
588041
  if (key) return { Authorization: `Bearer ${key}` };
587752
588042
  return {};
587753
588043
  }
587754
- function ollamaRequest(ollamaUrl, path8, method, body) {
588044
+ function ollamaRequest(ollamaUrl, path8, method, body, timeoutMs) {
587755
588045
  return new Promise((resolve43, reject) => {
587756
588046
  const url = new URL(path8, ollamaUrl);
587757
588047
  const isHttps = url.protocol === "https:";
@@ -587778,15 +588068,16 @@ function ollamaRequest(ollamaUrl, path8, method, body) {
587778
588068
  });
587779
588069
  });
587780
588070
  });
587781
- proxyReq.setTimeout(12e4, () => {
587782
- proxyReq.destroy(new Error("Backend request timeout (120s)"));
588071
+ const _to = getBackendTimeoutMs(timeoutMs ? timeoutMs / 1e3 : void 0);
588072
+ proxyReq.setTimeout(_to, () => {
588073
+ proxyReq.destroy(new Error(`Backend request timeout (${Math.round(_to / 1e3)}s)`));
587783
588074
  });
587784
588075
  proxyReq.on("error", reject);
587785
588076
  if (body) proxyReq.write(body);
587786
588077
  proxyReq.end();
587787
588078
  });
587788
588079
  }
587789
- function ollamaStream(ollamaUrl, path8, method, body, onData, onEnd, onError) {
588080
+ function ollamaStream(ollamaUrl, path8, method, body, onData, onEnd, onError, timeoutMs) {
587790
588081
  const url = new URL(path8, ollamaUrl);
587791
588082
  const isHttps = url.protocol === "https:";
587792
588083
  const options2 = {
@@ -587806,8 +588097,9 @@ function ollamaStream(ollamaUrl, path8, method, body, onData, onEnd, onError) {
587806
588097
  proxyRes.on("data", onData);
587807
588098
  proxyRes.on("end", onEnd);
587808
588099
  });
587809
- proxyReq.setTimeout(12e4, () => {
587810
- proxyReq.destroy(new Error("Backend stream timeout (120s)"));
588100
+ const _streamTo = getBackendTimeoutMs(timeoutMs ? timeoutMs / 1e3 : void 0);
588101
+ proxyReq.setTimeout(_streamTo, () => {
588102
+ proxyReq.destroy(new Error(`Backend stream timeout (${Math.round(_streamTo / 1e3)}s)`));
587811
588103
  });
587812
588104
  proxyReq.on("error", onError);
587813
588105
  if (body) proxyReq.write(body);
@@ -587815,32 +588107,76 @@ function ollamaStream(ollamaUrl, path8, method, body, onData, onEnd, onError) {
587815
588107
  }
587816
588108
  function jobsDir() {
587817
588109
  const root = resolve38(process.cwd());
587818
- const dir = join114(root, ".oa", "jobs");
587819
- mkdirSync62(dir, { recursive: true });
588110
+ const dir = join115(root, ".oa", "jobs");
588111
+ mkdirSync63(dir, { recursive: true });
587820
588112
  return dir;
587821
588113
  }
587822
588114
  function loadJob(id) {
587823
- const file = join114(jobsDir(), `${id}.json`);
587824
- if (!existsSync98(file)) return null;
588115
+ const file = join115(jobsDir(), `${id}.json`);
588116
+ if (!existsSync99(file)) return null;
587825
588117
  try {
587826
- return JSON.parse(readFileSync79(file, "utf-8"));
588118
+ return JSON.parse(readFileSync80(file, "utf-8"));
587827
588119
  } catch {
587828
588120
  return null;
587829
588121
  }
587830
588122
  }
587831
588123
  function listJobs() {
587832
588124
  const dir = jobsDir();
587833
- if (!existsSync98(dir)) return [];
588125
+ if (!existsSync99(dir)) return [];
587834
588126
  const files = readdirSync34(dir).filter((f2) => f2.endsWith(".json")).sort();
587835
588127
  const jobs = [];
587836
588128
  for (const file of files) {
587837
588129
  try {
587838
- jobs.push(JSON.parse(readFileSync79(join114(dir, file), "utf-8")));
588130
+ jobs.push(JSON.parse(readFileSync80(join115(dir, file), "utf-8")));
587839
588131
  } catch {
587840
588132
  }
587841
588133
  }
587842
588134
  return jobs;
587843
588135
  }
588136
+ function pruneOldJobs() {
588137
+ const retentionH = parseFloat(process.env["OA_RUN_RETENTION_H"] || "24");
588138
+ const cutoffMs = Date.now() - (Number.isFinite(retentionH) && retentionH > 0 ? retentionH : 24) * 36e5;
588139
+ const dir = jobsDir();
588140
+ if (!existsSync99(dir)) return { pruned: 0, kept: 0 };
588141
+ let pruned = 0;
588142
+ let kept = 0;
588143
+ for (const file of readdirSync34(dir)) {
588144
+ if (!file.endsWith(".json")) continue;
588145
+ const path8 = join115(dir, file);
588146
+ try {
588147
+ const job = JSON.parse(readFileSync80(path8, "utf-8"));
588148
+ if (job.status === "running") {
588149
+ kept++;
588150
+ continue;
588151
+ }
588152
+ const ageRef = job.completedAt || job.startedAt;
588153
+ const ts = ageRef ? Date.parse(ageRef) : NaN;
588154
+ if (Number.isFinite(ts) && ts < cutoffMs) {
588155
+ try {
588156
+ unlinkSync24(path8);
588157
+ } catch {
588158
+ }
588159
+ const outFile = path8.replace(/\.json$/, ".output");
588160
+ if (existsSync99(outFile)) {
588161
+ try {
588162
+ unlinkSync24(outFile);
588163
+ } catch {
588164
+ }
588165
+ }
588166
+ pruned++;
588167
+ } else {
588168
+ kept++;
588169
+ }
588170
+ } catch {
588171
+ try {
588172
+ unlinkSync24(path8);
588173
+ pruned++;
588174
+ } catch {
588175
+ }
588176
+ }
588177
+ }
588178
+ return { pruned, kept };
588179
+ }
587844
588180
  function getKeyUsage(user) {
587845
588181
  if (!perKeyUsage.has(user)) {
587846
588182
  perKeyUsage.set(user, { requestTimestamps: [], tokensToday: 0, tokenDate: (/* @__PURE__ */ new Date()).toISOString().slice(0, 10), activeJobs: 0 });
@@ -587909,14 +588245,14 @@ function autoSeedTodosFromPrompt(prompt) {
587909
588245
  return [];
587910
588246
  }
587911
588247
  function atomicJobWrite(dir, id, job) {
587912
- const finalPath = join114(dir, `${id}.json`);
588248
+ const finalPath = join115(dir, `${id}.json`);
587913
588249
  const tmpPath = `${finalPath}.tmp.${process.pid}.${Date.now()}`;
587914
588250
  try {
587915
- writeFileSync54(tmpPath, JSON.stringify(job, null, 2), "utf-8");
588251
+ writeFileSync55(tmpPath, JSON.stringify(job, null, 2), "utf-8");
587916
588252
  renameSync8(tmpPath, finalPath);
587917
588253
  } catch {
587918
588254
  try {
587919
- writeFileSync54(finalPath, JSON.stringify(job, null, 2), "utf-8");
588255
+ writeFileSync55(finalPath, JSON.stringify(job, null, 2), "utf-8");
587920
588256
  } catch {
587921
588257
  }
587922
588258
  try {
@@ -587948,8 +588284,27 @@ function resolveAuth(req2) {
587948
588284
  return { authenticated: false, scope: "read" };
587949
588285
  }
587950
588286
  const singleKey = process.env["OA_API_KEY"];
588287
+ if (singleKey) {
588288
+ if (token === singleKey) return { authenticated: true, scope: "admin" };
588289
+ }
588290
+ if (token) {
588291
+ try {
588292
+ const { lookupKey: _lk } = require3("./runtime-keys.js");
588293
+ const rec = _lk(token);
588294
+ if (rec) {
588295
+ return {
588296
+ authenticated: true,
588297
+ scope: rec.scope,
588298
+ user: rec.owner,
588299
+ rpm: rec.rpm,
588300
+ tpd: rec.tpd,
588301
+ maxJobs: rec.max_jobs
588302
+ };
588303
+ }
588304
+ } catch {
588305
+ }
588306
+ }
587951
588307
  if (!singleKey) return { authenticated: true, scope: "admin" };
587952
- if (token === singleKey) return { authenticated: true, scope: "admin" };
587953
588308
  return { authenticated: false, scope: "read" };
587954
588309
  }
587955
588310
  function checkAuth(req2, res, requiredScope = "read") {
@@ -588070,7 +588425,41 @@ function handleHelp(req2, res) {
588070
588425
  },
588071
588426
  off_device_policy: "Tools with off_device_allowed=false are loopback-only; remote callers need admin scope to bypass.",
588072
588427
  anonymous_default: "Loopback callers default to admin scope. Remote callers default to read scope until authenticated.",
588073
- override: "Operators can override per-tool classification via OA_TOOL_OVERRIDES env var (JSON map of name → partial security info)."
588428
+ override: "Operators can override per-tool classification via OA_TOOL_OVERRIDES env var (JSON map of name → partial security info).",
588429
+ filters: "GET /v1/tools supports ?category=, ?scope=, ?risk=, ?off_device=true|false (Q9). E.g. /v1/tools?scope=read&off_device=true returns the safe-to-expose set."
588430
+ },
588431
+ runtime_keys: {
588432
+ "GET /v1/keys": "List runtime keys (admin scope). Secrets masked.",
588433
+ "POST /v1/keys": "Mint a runtime key (admin scope). Body: {scope, owner, profile?, rpm?, tpd?, max_jobs?}. Returns full secret ONCE.",
588434
+ "DELETE /v1/keys/{prefix}": "Revoke a key by its first 8+ chars (admin scope). Soft-delete; record retained for audit.",
588435
+ env_alternative: 'OA_API_KEY (single, admin) or OA_API_KEYS="key:scope:owner:rpm:tpd:max_jobs,...". Runtime keys are checked AFTER env keys.',
588436
+ per_key_profile: "Bind a key to a tool profile (allow/deny lists from /v1/profiles) for capability scoping beyond coarse read/run/admin.",
588437
+ storage: "~/.open-agents/keys.json (mode 600). Soft-deleted keys are kept for audit."
588438
+ },
588439
+ agent_loop_inference: {
588440
+ enable: "POST /v1/chat/completions with body {agent_loop: true} runs an N-turn server-side loop. Daemon executes any tool_calls matching daemon-resident tools and re-prompts; client-side tool_calls cause the loop to yield back so caller can execute them.",
588441
+ merge_daemon_tools: "Body field include_daemon_tools: ['read'] (or ['read','run']) merges the daemon's matching tools into the offered tools[] array automatically — no need to hand-list web_search/web_fetch/file_read/etc.",
588442
+ max_turns: "Body field max_turns (default 8, max 64).",
588443
+ timeout: "Body field timeout_s caps the whole loop (default 30 min, max 1 hour). Per-backend-call timeout is separate (see OA_BACKEND_TIMEOUT_S).",
588444
+ prompt_template: "Body field prompt_template: 'factual-first' prepends a system message instructing the model to call web_search FIRST for any factual question.",
588445
+ response_envelope: "Standard OpenAI chat.completion shape PLUS a _agent_loop: {turns, log:[], done, reason} field. Reason is one of: model_returned_content, client_tools_pending, max_turns_exhausted."
588446
+ },
588447
+ backend_timeout: {
588448
+ env: "OA_BACKEND_TIMEOUT_S — default 120 seconds, max 3600 (1 hour).",
588449
+ per_request: "Body field timeout_s on /v1/chat/completions, /v1/run, agent_loop. Caps to 1h.",
588450
+ rationale: "Q1 (Cody/SA) — was hardcoded 120s, broke cold-start tool-calling thinks on large models."
588451
+ },
588452
+ run_lifecycle: {
588453
+ gc: "Q10 — completed/failed runs older than OA_RUN_RETENTION_H hours (default 24) are pruned at startup and on a 1h interval. Set OA_RUN_RETENTION_H=0 to disable. Running runs are never pruned.",
588454
+ manual_delete: "DELETE /v1/runs/{id} — explicit cleanup."
588455
+ },
588456
+ sse_chunk_shape: {
588457
+ chat_completions: "Standard OpenAI delta shape. tool_calls in delta: [{id:'call_<hex>', type:'function', index:N, function:{name:'...', arguments:'<JSON-string>'}}]. Final chunk: {choices:[{finish_reason:'stop', delta:{}}]} then 'data: [DONE]'.",
588458
+ events_bus: "GET /v1/events streams the daemon-event bus (different shape): event:'tool.called', data:{run_id, tool, args, ts}. NOT the same as chat completions SSE."
588459
+ },
588460
+ parallel_tool_calls: {
588461
+ passthrough: "Request body field parallel_tool_calls: true is forwarded to the backend (Ollama/vLLM). Models supporting it (qwen3, llama3) emit multiple tool_calls in one response — daemon forwards them in delta.tool_calls[].",
588462
+ agent_loop: "When agent_loop=true with multiple daemon-tool tool_calls, all execute sequentially (still server-side; saves N round-trips vs the client doing each one)."
588074
588463
  },
588075
588464
  mcp: {
588076
588465
  "GET /v1/mcps": "List connected MCP (Model Context Protocol) servers",
@@ -588309,6 +588698,277 @@ async function handleApiTags(res) {
588309
588698
  });
588310
588699
  }
588311
588700
  }
588701
+ async function runAgentLoopChatCompletions(opts) {
588702
+ const { req: req2, res, ollamaUrl, requestBody, perReqTimeoutS } = opts;
588703
+ const startMs = Date.now();
588704
+ const reqTimeoutMs = getBackendTimeoutMs(perReqTimeoutS);
588705
+ const totalDeadline = startMs + (perReqTimeoutS && perReqTimeoutS > 0 ? Math.min(perReqTimeoutS * 1e3, BACKEND_TIMEOUT_MAX_MS) : 30 * 60 * 1e3);
588706
+ const model = requestBody["model"] || "unknown";
588707
+ const route = resolveModelEndpoint(model);
588708
+ const targetUrl = route?.endpoint.url ?? ollamaUrl;
588709
+ const targetType = route?.endpoint.type ?? loadConfig().backendType ?? "ollama";
588710
+ const originalModel = route?.originalId ?? model;
588711
+ const maxTurns = typeof requestBody["max_turns"] === "number" ? Math.max(1, Math.min(64, requestBody["max_turns"])) : 8;
588712
+ const remoteIp = (req2.socket?.remoteAddress || "").replace(/^::ffff:/, "");
588713
+ const origin = /^(127\.\d+\.\d+\.\d+|::1|localhost)$/.test(remoteIp) ? "loopback" : "remote";
588714
+ const reqAuth = req2;
588715
+ const scope = reqAuth._authScope ?? (origin === "loopback" ? "admin" : "read");
588716
+ const execMod = await Promise.resolve().then(() => (init_dist5(), dist_exports)).catch(() => null);
588717
+ if (!execMod) {
588718
+ jsonResponse(res, 500, { error: "Execution module unavailable" });
588719
+ return;
588720
+ }
588721
+ const classify = execMod.classifyTool;
588722
+ const canInvoke = execMod.canInvokeTool;
588723
+ const daemonTools = /* @__PURE__ */ new Map();
588724
+ for (const [classKey, value2] of Object.entries(execMod)) {
588725
+ if (typeof value2 !== "function") continue;
588726
+ const proto = value2.prototype;
588727
+ if (!proto || typeof proto.execute !== "function") continue;
588728
+ let probe = null;
588729
+ try {
588730
+ probe = new value2();
588731
+ } catch {
588732
+ try {
588733
+ probe = new value2(process.cwd());
588734
+ } catch {
588735
+ probe = null;
588736
+ }
588737
+ }
588738
+ if (probe?.name && typeof probe.name === "string") {
588739
+ daemonTools.set(probe.name, { ToolClass: value2, classKey });
588740
+ }
588741
+ }
588742
+ const includeDaemonTools = Array.isArray(requestBody["include_daemon_tools"]) ? requestBody["include_daemon_tools"].filter((s2) => typeof s2 === "string") : [];
588743
+ const advertisedDaemonNames = /* @__PURE__ */ new Set();
588744
+ if (includeDaemonTools.length > 0) {
588745
+ for (const [toolName, { ToolClass: _T }] of daemonTools.entries()) {
588746
+ void _T;
588747
+ const sec = classify(toolName);
588748
+ if (!sec) continue;
588749
+ if (!includeDaemonTools.includes(sec.requires_scope)) continue;
588750
+ if (canInvoke({ toolName, origin, scope }) !== null) continue;
588751
+ advertisedDaemonNames.add(toolName);
588752
+ }
588753
+ }
588754
+ const callerTools = Array.isArray(requestBody["tools"]) ? requestBody["tools"].filter((t2) => t2?.type === "function" && typeof t2?.function?.name === "string") : [];
588755
+ const callerToolNames = new Set(callerTools.map((t2) => t2.function.name));
588756
+ const daemonToolEntries = [];
588757
+ for (const name10 of advertisedDaemonNames) {
588758
+ if (callerToolNames.has(name10)) continue;
588759
+ const meta = daemonTools.get(name10);
588760
+ if (!meta) continue;
588761
+ let inst = null;
588762
+ try {
588763
+ inst = new meta.ToolClass();
588764
+ } catch {
588765
+ try {
588766
+ inst = new meta.ToolClass(process.cwd());
588767
+ } catch {
588768
+ inst = null;
588769
+ }
588770
+ }
588771
+ if (!inst) continue;
588772
+ daemonToolEntries.push({
588773
+ type: "function",
588774
+ function: {
588775
+ name: name10,
588776
+ description: typeof inst.description === "string" ? inst.description : "",
588777
+ parameters: inst.parameters ?? { type: "object", properties: {} }
588778
+ }
588779
+ });
588780
+ }
588781
+ const mergedTools = [...callerTools, ...daemonToolEntries];
588782
+ const messages2 = Array.isArray(requestBody["messages"]) ? requestBody["messages"].slice() : [];
588783
+ const promptTemplate = typeof requestBody["prompt_template"] === "string" ? requestBody["prompt_template"] : null;
588784
+ if (promptTemplate === "factual-first" && advertisedDaemonNames.has("web_search")) {
588785
+ const SYSTEM_FACTUAL_FIRST = [
588786
+ "[POLICY: factual-first]",
588787
+ "For any user question that asks for a fact, statistic, current event,",
588788
+ "person/place/product detail, or anything you might be tempted to answer",
588789
+ "from training data alone — your FIRST tool_call MUST be `web_search`",
588790
+ "with a query derived from the user's question. Do NOT answer from",
588791
+ "training data. After web_search returns, optionally call `web_fetch`",
588792
+ "on the most relevant URL, then synthesize a grounded answer citing",
588793
+ "the source. Decline to answer factual questions if web_search is",
588794
+ "unavailable."
588795
+ ].join(" ");
588796
+ const firstSystemIdx = messages2.findIndex((m2) => m2.role === "system");
588797
+ if (firstSystemIdx >= 0 && typeof messages2[firstSystemIdx].content === "string") {
588798
+ messages2[firstSystemIdx] = {
588799
+ ...messages2[firstSystemIdx],
588800
+ content: `${SYSTEM_FACTUAL_FIRST}
588801
+
588802
+ ${messages2[firstSystemIdx].content}`
588803
+ };
588804
+ } else {
588805
+ messages2.unshift({ role: "system", content: SYSTEM_FACTUAL_FIRST });
588806
+ }
588807
+ }
588808
+ const chatId = `chatcmpl-${randomBytes23(12).toString("hex")}`;
588809
+ const turnsLog = [];
588810
+ for (let turn = 1; turn <= maxTurns; turn++) {
588811
+ if (Date.now() > totalDeadline) {
588812
+ jsonResponse(res, 504, {
588813
+ error: "agent_loop timed out",
588814
+ turn,
588815
+ elapsed_ms: Date.now() - startMs,
588816
+ timeout_s: perReqTimeoutS ?? null
588817
+ });
588818
+ return;
588819
+ }
588820
+ const turnBody = {
588821
+ ...requestBody,
588822
+ model: originalModel,
588823
+ messages: messages2,
588824
+ tools: mergedTools.length > 0 ? mergedTools : void 0,
588825
+ stream: false,
588826
+ // Strip our own loop-control fields so they don't confuse the backend
588827
+ agent_loop: void 0,
588828
+ include_daemon_tools: void 0,
588829
+ max_turns: void 0,
588830
+ timeout_s: void 0
588831
+ };
588832
+ for (const k of Object.keys(turnBody)) if (turnBody[k] === void 0) delete turnBody[k];
588833
+ let parsed;
588834
+ try {
588835
+ const path8 = targetType === "vllm" || targetType === "openai" ? "/v1/chat/completions" : "/v1/chat/completions";
588836
+ const result = await ollamaRequest(targetUrl, path8, "POST", JSON.stringify(turnBody), reqTimeoutMs);
588837
+ if (result.status !== 200) {
588838
+ jsonResponse(res, result.status, {
588839
+ error: "Backend request failed during agent_loop",
588840
+ turn,
588841
+ details: result.body.slice(0, 500)
588842
+ });
588843
+ return;
588844
+ }
588845
+ parsed = JSON.parse(result.body);
588846
+ } catch (err) {
588847
+ jsonResponse(res, 502, {
588848
+ error: "Backend proxy error during agent_loop",
588849
+ message: err instanceof Error ? err.message : String(err),
588850
+ turn
588851
+ });
588852
+ return;
588853
+ }
588854
+ const choice = parsed?.choices?.[0];
588855
+ const message2 = choice?.message || { role: "assistant", content: "" };
588856
+ const toolCalls = Array.isArray(message2.tool_calls) ? message2.tool_calls : [];
588857
+ if (toolCalls.length === 0) {
588858
+ jsonResponse(res, 200, {
588859
+ ...parsed,
588860
+ id: chatId,
588861
+ _agent_loop: { turns: turn, log: turnsLog, done: true, reason: "model_returned_content" }
588862
+ });
588863
+ return;
588864
+ }
588865
+ const daemonCalls = [];
588866
+ const clientCalls = [];
588867
+ for (const tc of toolCalls) {
588868
+ const name10 = tc?.function?.name ?? tc?.name ?? "";
588869
+ if (advertisedDaemonNames.has(name10)) daemonCalls.push(tc);
588870
+ else clientCalls.push(tc);
588871
+ }
588872
+ if (clientCalls.length > 0) {
588873
+ turnsLog.push({ turn, tool_calls: toolCalls.length, daemon_executed: 0, client_yielded: clientCalls.length });
588874
+ jsonResponse(res, 200, {
588875
+ ...parsed,
588876
+ id: chatId,
588877
+ _agent_loop: {
588878
+ turns: turn,
588879
+ log: turnsLog,
588880
+ done: false,
588881
+ reason: "client_tools_pending",
588882
+ pending_tool_calls: clientCalls.length
588883
+ }
588884
+ });
588885
+ return;
588886
+ }
588887
+ messages2.push(message2);
588888
+ let executed = 0;
588889
+ for (const tc of daemonCalls) {
588890
+ const name10 = tc?.function?.name ?? tc?.name ?? "";
588891
+ const argsRaw = tc?.function?.arguments ?? tc?.arguments ?? "{}";
588892
+ let args = {};
588893
+ try {
588894
+ args = typeof argsRaw === "string" ? JSON.parse(argsRaw) : argsRaw || {};
588895
+ } catch {
588896
+ args = {};
588897
+ }
588898
+ const denial = canInvoke({ toolName: name10, origin, scope });
588899
+ if (denial) {
588900
+ messages2.push({
588901
+ role: "tool",
588902
+ tool_call_id: tc.id,
588903
+ name: name10,
588904
+ content: JSON.stringify({ error: denial.reason, status: denial.status })
588905
+ });
588906
+ continue;
588907
+ }
588908
+ const meta = daemonTools.get(name10);
588909
+ if (!meta) {
588910
+ messages2.push({
588911
+ role: "tool",
588912
+ tool_call_id: tc.id,
588913
+ name: name10,
588914
+ content: JSON.stringify({ error: `Daemon tool '${name10}' not found` })
588915
+ });
588916
+ continue;
588917
+ }
588918
+ let tool = null;
588919
+ try {
588920
+ tool = new meta.ToolClass(process.cwd());
588921
+ } catch {
588922
+ try {
588923
+ tool = new meta.ToolClass();
588924
+ } catch {
588925
+ tool = null;
588926
+ }
588927
+ }
588928
+ if (!tool) {
588929
+ messages2.push({
588930
+ role: "tool",
588931
+ tool_call_id: tc.id,
588932
+ name: name10,
588933
+ content: JSON.stringify({ error: `Could not instantiate ${meta.classKey}` })
588934
+ });
588935
+ continue;
588936
+ }
588937
+ let toolResult;
588938
+ try {
588939
+ toolResult = await tool.execute(args);
588940
+ } catch (e2) {
588941
+ toolResult = { success: false, output: "", error: e2 instanceof Error ? e2.message : String(e2), durationMs: 0 };
588942
+ }
588943
+ messages2.push({
588944
+ role: "tool",
588945
+ tool_call_id: tc.id,
588946
+ name: name10,
588947
+ content: JSON.stringify(toolResult)
588948
+ });
588949
+ executed++;
588950
+ }
588951
+ turnsLog.push({ turn, tool_calls: toolCalls.length, daemon_executed: executed, client_yielded: 0 });
588952
+ }
588953
+ jsonResponse(res, 200, {
588954
+ id: chatId,
588955
+ object: "chat.completion",
588956
+ created: Math.floor(Date.now() / 1e3),
588957
+ model,
588958
+ choices: [{
588959
+ index: 0,
588960
+ message: { role: "assistant", content: "[agent_loop reached max_turns without producing a final answer]" },
588961
+ finish_reason: "length"
588962
+ }],
588963
+ _agent_loop: {
588964
+ turns: maxTurns,
588965
+ log: turnsLog,
588966
+ done: false,
588967
+ reason: "max_turns_exhausted",
588968
+ max_turns: maxTurns
588969
+ }
588970
+ });
588971
+ }
588312
588972
  async function handleV1ChatCompletions(req2, res, ollamaUrl) {
588313
588973
  const body = await parseJsonBody(req2);
588314
588974
  if (!body || typeof body !== "object") {
@@ -588318,6 +588978,18 @@ async function handleV1ChatCompletions(req2, res, ollamaUrl) {
588318
588978
  const requestBody = body;
588319
588979
  const stream = requestBody["stream"] === true;
588320
588980
  const model = requestBody["model"] || "unknown";
588981
+ const perReqTimeoutS = typeof requestBody["timeout_s"] === "number" ? requestBody["timeout_s"] : void 0;
588982
+ if (requestBody["agent_loop"] === true) {
588983
+ await runAgentLoopChatCompletions({
588984
+ req: req2,
588985
+ res,
588986
+ ollamaUrl,
588987
+ requestBody,
588988
+ perReqTimeoutS
588989
+ });
588990
+ return;
588991
+ }
588992
+ const reqTimeoutMs = getBackendTimeoutMs(perReqTimeoutS);
588321
588993
  const route = resolveModelEndpoint(model);
588322
588994
  const targetUrl = route?.endpoint.url ?? ollamaUrl;
588323
588995
  const targetType = route?.endpoint.type ?? loadConfig().backendType ?? "ollama";
@@ -588357,11 +589029,12 @@ async function handleV1ChatCompletions(req2, res, ollamaUrl) {
588357
589029
  } catch {
588358
589030
  }
588359
589031
  res.end();
588360
- }
589032
+ },
589033
+ reqTimeoutMs
588361
589034
  );
588362
589035
  } else {
588363
589036
  try {
588364
- const result = await ollamaRequest(targetUrl, "/v1/chat/completions", "POST", payload);
589037
+ const result = await ollamaRequest(targetUrl, "/v1/chat/completions", "POST", payload, reqTimeoutMs);
588365
589038
  if (result.status !== 200) {
588366
589039
  jsonResponse(res, result.status, { error: "Backend request failed", details: result.body });
588367
589040
  return;
@@ -588397,7 +589070,7 @@ async function handleV1ChatCompletions(req2, res, ollamaUrl) {
588397
589070
  "Cache-Control": "no-cache",
588398
589071
  "Connection": "keep-alive"
588399
589072
  });
588400
- const chatId = `chatcmpl-${randomBytes22(12).toString("hex")}`;
589073
+ const chatId = `chatcmpl-${randomBytes23(12).toString("hex")}`;
588401
589074
  let buffer2 = "";
588402
589075
  ollamaStream(
588403
589076
  targetUrl,
@@ -588439,7 +589112,7 @@ async function handleV1ChatCompletions(req2, res, ollamaUrl) {
588439
589112
  if (ollamaChunk.message.content) delta.content = ollamaChunk.message.content;
588440
589113
  if (ollamaChunk.message.tool_calls) {
588441
589114
  delta.tool_calls = ollamaChunk.message.tool_calls.map((tc, idx) => ({
588442
- id: tc.id || `call_${randomBytes22(8).toString("hex")}`,
589115
+ id: tc.id || `call_${randomBytes23(8).toString("hex")}`,
588443
589116
  type: "function",
588444
589117
  function: {
588445
589118
  name: tc?.function?.name ?? tc?.name ?? "",
@@ -588501,11 +589174,12 @@ async function handleV1ChatCompletions(req2, res, ollamaUrl) {
588501
589174
  } catch {
588502
589175
  }
588503
589176
  res.end();
588504
- }
589177
+ },
589178
+ reqTimeoutMs
588505
589179
  );
588506
589180
  } else {
588507
589181
  try {
588508
- const result = await ollamaRequest(targetUrl, "/api/chat", "POST", ollamaPayload);
589182
+ const result = await ollamaRequest(targetUrl, "/api/chat", "POST", ollamaPayload, reqTimeoutMs);
588509
589183
  if (result.status !== 200) {
588510
589184
  jsonResponse(res, result.status, {
588511
589185
  error: "Ollama request failed",
@@ -588517,14 +589191,14 @@ async function handleV1ChatCompletions(req2, res, ollamaUrl) {
588517
589191
  if (ollamaResp.eval_count) metrics.totalTokensOut += ollamaResp.eval_count;
588518
589192
  if (ollamaResp.prompt_eval_count) metrics.totalTokensIn += ollamaResp.prompt_eval_count;
588519
589193
  trackTokens("local", ollamaResp.prompt_eval_count ?? 0, ollamaResp.eval_count ?? 0);
588520
- const chatId = `chatcmpl-${randomBytes22(12).toString("hex")}`;
589194
+ const chatId = `chatcmpl-${randomBytes23(12).toString("hex")}`;
588521
589195
  const responseMessage = {
588522
589196
  role: ollamaResp.message?.role ?? "assistant",
588523
589197
  content: ollamaResp.message?.content ?? ""
588524
589198
  };
588525
589199
  if (ollamaResp.message?.tool_calls && ollamaResp.message.tool_calls.length > 0) {
588526
589200
  responseMessage.tool_calls = ollamaResp.message.tool_calls.map((tc, idx) => ({
588527
- id: tc.id || `call_${randomBytes22(8).toString("hex")}`,
589201
+ id: tc.id || `call_${randomBytes23(8).toString("hex")}`,
588528
589202
  type: "function",
588529
589203
  function: {
588530
589204
  name: tc?.function?.name ?? tc?.name ?? "",
@@ -588997,27 +589671,27 @@ ${task}` : task;
588997
589671
  });
588998
589672
  }
588999
589673
  function updateStateFile() {
589000
- return join114(homedir40(), ".open-agents", "update-state.json");
589674
+ return join115(homedir41(), ".open-agents", "update-state.json");
589001
589675
  }
589002
589676
  function updateLogPath() {
589003
- return join114(homedir40(), ".open-agents", "update.log");
589677
+ return join115(homedir41(), ".open-agents", "update.log");
589004
589678
  }
589005
589679
  function readUpdateState() {
589006
589680
  try {
589007
589681
  const p2 = updateStateFile();
589008
- if (!existsSync98(p2)) return null;
589009
- return JSON.parse(readFileSync79(p2, "utf-8"));
589682
+ if (!existsSync99(p2)) return null;
589683
+ return JSON.parse(readFileSync80(p2, "utf-8"));
589010
589684
  } catch {
589011
589685
  return null;
589012
589686
  }
589013
589687
  }
589014
589688
  function writeUpdateState(state) {
589015
589689
  try {
589016
- const dir = join114(homedir40(), ".open-agents");
589017
- mkdirSync62(dir, { recursive: true });
589690
+ const dir = join115(homedir41(), ".open-agents");
589691
+ mkdirSync63(dir, { recursive: true });
589018
589692
  const finalPath = updateStateFile();
589019
589693
  const tmpPath = `${finalPath}.tmp.${process.pid}`;
589020
- writeFileSync54(tmpPath, JSON.stringify(state, null, 2), "utf-8");
589694
+ writeFileSync55(tmpPath, JSON.stringify(state, null, 2), "utf-8");
589021
589695
  renameSync8(tmpPath, finalPath);
589022
589696
  } catch {
589023
589697
  }
@@ -589061,15 +589735,15 @@ async function handleV1Update(req2, res, requestId) {
589061
589735
  const { execSync: es } = require3("node:child_process");
589062
589736
  const isWin2 = process.platform === "win32";
589063
589737
  let npmBin = "";
589064
- for (const candidate of isWin2 ? [join114(nodeDir, "npm.cmd"), join114(nodeDir, "npm")] : [join114(nodeDir, "npm"), "/usr/local/bin/npm", "/usr/bin/npm"]) {
589065
- if (existsSync98(candidate)) {
589738
+ for (const candidate of isWin2 ? [join115(nodeDir, "npm.cmd"), join115(nodeDir, "npm")] : [join115(nodeDir, "npm"), "/usr/local/bin/npm", "/usr/bin/npm"]) {
589739
+ if (existsSync99(candidate)) {
589066
589740
  npmBin = candidate;
589067
589741
  break;
589068
589742
  }
589069
589743
  }
589070
589744
  if (!npmBin) npmBin = isWin2 ? "npm.cmd" : "npm";
589071
589745
  const pkgSpec = `open-agents-ai@${targetVersion}`;
589072
- const dir = join114(homedir40(), ".open-agents");
589746
+ const dir = join115(homedir41(), ".open-agents");
589073
589747
  fs7.mkdirSync(dir, { recursive: true });
589074
589748
  const logFd = fs7.openSync(logPath3, "w");
589075
589749
  const npmPrefix = dirname35(nodeDir);
@@ -589079,13 +589753,13 @@ async function handleV1Update(req2, res, requestId) {
589079
589753
  globalBinDir = es(`${npmBin} bin -g`, { encoding: "utf8", timeout: 5e3, stdio: "pipe" }).trim();
589080
589754
  } else {
589081
589755
  const npmCliCandidates = [
589082
- join114(nodeDir, "..", "lib", "node_modules", "npm", "bin", "npm-cli.js"),
589083
- join114(npmBin, "..", "..", "lib", "node_modules", "npm", "bin", "npm-cli.js")
589756
+ join115(nodeDir, "..", "lib", "node_modules", "npm", "bin", "npm-cli.js"),
589757
+ join115(npmBin, "..", "..", "lib", "node_modules", "npm", "bin", "npm-cli.js")
589084
589758
  ];
589085
589759
  let npmCli = "";
589086
589760
  for (const c9 of npmCliCandidates) {
589087
589761
  try {
589088
- if (existsSync98(c9)) {
589762
+ if (existsSync99(c9)) {
589089
589763
  npmCli = c9;
589090
589764
  break;
589091
589765
  }
@@ -589117,13 +589791,13 @@ async function handleV1Update(req2, res, requestId) {
589117
589791
  });
589118
589792
  } else {
589119
589793
  const npmCliCandidates = [
589120
- join114(nodeDir, "..", "lib", "node_modules", "npm", "bin", "npm-cli.js"),
589121
- join114(npmBin, "..", "..", "lib", "node_modules", "npm", "bin", "npm-cli.js")
589794
+ join115(nodeDir, "..", "lib", "node_modules", "npm", "bin", "npm-cli.js"),
589795
+ join115(npmBin, "..", "..", "lib", "node_modules", "npm", "bin", "npm-cli.js")
589122
589796
  ];
589123
589797
  let npmCli = "";
589124
589798
  for (const c9 of npmCliCandidates) {
589125
589799
  try {
589126
- if (existsSync98(c9)) {
589800
+ if (existsSync99(c9)) {
589127
589801
  npmCli = c9;
589128
589802
  break;
589129
589803
  }
@@ -589220,8 +589894,8 @@ function handleV1UpdateStatus(res) {
589220
589894
  let logTail = "";
589221
589895
  let exitCode = null;
589222
589896
  try {
589223
- if (existsSync98(logPath3)) {
589224
- const raw = readFileSync79(logPath3, "utf-8");
589897
+ if (existsSync99(logPath3)) {
589898
+ const raw = readFileSync80(logPath3, "utf-8");
589225
589899
  const m2 = raw.match(/__EXIT_CODE=(\d+)/);
589226
589900
  if (m2) exitCode = parseInt(m2[1], 10);
589227
589901
  logTail = raw.slice(-2e3);
@@ -589309,7 +589983,7 @@ async function handleV1Run(req2, res) {
589309
589983
  return;
589310
589984
  }
589311
589985
  }
589312
- const id = `job-${randomBytes22(8).toString("hex")}`;
589986
+ const id = `job-${randomBytes23(8).toString("hex")}`;
589313
589987
  const dir = jobsDir();
589314
589988
  const workingDir = requestBody["working_directory"] || req2.headers["x-working-directory"];
589315
589989
  const isolate = requestBody["isolate"] === true;
@@ -589317,8 +589991,8 @@ async function handleV1Run(req2, res) {
589317
589991
  if (workingDir) {
589318
589992
  cwd4 = resolve38(workingDir);
589319
589993
  } else if (isolate) {
589320
- const wsDir = join114(dir, "..", "workspaces", id);
589321
- mkdirSync62(wsDir, { recursive: true });
589994
+ const wsDir = join115(dir, "..", "workspaces", id);
589995
+ mkdirSync63(wsDir, { recursive: true });
589322
589996
  cwd4 = wsDir;
589323
589997
  } else {
589324
589998
  cwd4 = resolve38(process.cwd());
@@ -589460,7 +590134,7 @@ async function handleV1Run(req2, res) {
589460
590134
  let output = "";
589461
590135
  let tailBytes = 0;
589462
590136
  const TAIL_BUDGET = 1048576;
589463
- const outputWriter = new DiskTaskOutput(join114(dir, `${id}.output`));
590137
+ const outputWriter = new DiskTaskOutput(join115(dir, `${id}.output`));
589464
590138
  job.outputFile = outputWriter.path;
589465
590139
  atomicJobWrite(dir, id, job);
589466
590140
  child.stdout?.on("data", (chunk) => {
@@ -590353,10 +591027,10 @@ async function handleRequest(req2, res, ollamaUrl, verbose) {
590353
591027
  return;
590354
591028
  }
590355
591029
  const { tmpdir: tmpdir22 } = await import("node:os");
590356
- const { writeFileSync: writeFileSync58, unlinkSync: unlinkSync25 } = await import("node:fs");
591030
+ const { writeFileSync: writeFileSync59, unlinkSync: unlinkSync25 } = await import("node:fs");
590357
591031
  const { join: pjoin } = await import("node:path");
590358
591032
  const tmpPath = pjoin(tmpdir22(), `oa-clone-upload-${Date.now()}-${safeName}`);
590359
- writeFileSync58(tmpPath, buf);
591033
+ writeFileSync59(tmpPath, buf);
590360
591034
  try {
590361
591035
  const ve = getVoiceEngine();
590362
591036
  const msg = await ve.setCloneVoice(tmpPath);
@@ -590906,7 +591580,7 @@ data: ${JSON.stringify(data)}
590906
591580
  }
590907
591581
  for (const f2 of seenFiles) {
590908
591582
  try {
590909
- writeFileSync54(f2, JSON.stringify({ tasks: [] }, null, 2));
591583
+ writeFileSync55(f2, JSON.stringify({ tasks: [] }, null, 2));
590910
591584
  deleted++;
590911
591585
  } catch {
590912
591586
  }
@@ -591968,7 +592642,7 @@ ${steering}`;
591968
592642
  function getScheduleRoots() {
591969
592643
  const rootsEnv = process.env["OA_SCHEDULE_ROOTS"] || "";
591970
592644
  const roots = rootsEnv.split(rootsEnv.includes(";") ? ";" : ":").filter(Boolean);
591971
- const defaults3 = [process.cwd(), join114(homedir40(), "Documents")];
592645
+ const defaults3 = [process.cwd(), join115(homedir41(), "Documents")];
591972
592646
  const set = /* @__PURE__ */ new Set([...defaults3, ...roots]);
591973
592647
  return [...set];
591974
592648
  }
@@ -591980,10 +592654,10 @@ function listScheduledTasks() {
591980
592654
  for (const root of roots) {
591981
592655
  try {
591982
592656
  walk(root, 0, (dir) => {
591983
- if (dir.endsWith(`${join114(".oa", "scheduled")}`) || dir.includes(`${join114(".oa", "scheduled")}`)) {
591984
- const file = join114(dir, "tasks.json");
592657
+ if (dir.endsWith(`${join115(".oa", "scheduled")}`) || dir.includes(`${join115(".oa", "scheduled")}`)) {
592658
+ const file = join115(dir, "tasks.json");
591985
592659
  try {
591986
- const raw = readFileSync79(file, "utf-8");
592660
+ const raw = readFileSync80(file, "utf-8");
591987
592661
  const json = JSON.parse(raw);
591988
592662
  const tasks = Array.isArray(json?.tasks) ? json.tasks : Array.isArray(json) ? json : [];
591989
592663
  tasks.forEach((t2, i2) => {
@@ -592048,7 +592722,7 @@ function walk(dir, depth, onDir, maxDepth) {
592048
592722
  if (e2.name === "node_modules" || e2.name.startsWith(".")) {
592049
592723
  if (e2.name !== ".oa") continue;
592050
592724
  }
592051
- const child = join114(dir, e2.name);
592725
+ const child = join115(dir, e2.name);
592052
592726
  walk(child, depth + 1, onDir, maxDepth);
592053
592727
  }
592054
592728
  }
@@ -592057,18 +592731,18 @@ function setScheduledEnabled(id, enabled2) {
592057
592731
  const target = tasks.find((t2) => t2.id === id);
592058
592732
  if (!target) return false;
592059
592733
  try {
592060
- const raw = readFileSync79(target.file, "utf-8");
592734
+ const raw = readFileSync80(target.file, "utf-8");
592061
592735
  const json = JSON.parse(raw);
592062
592736
  const arr = Array.isArray(json?.tasks) ? json.tasks : Array.isArray(json) ? json : [];
592063
592737
  if (!arr[target.index]) return false;
592064
592738
  arr[target.index].enabled = enabled2;
592065
592739
  if (Array.isArray(json?.tasks)) {
592066
592740
  json.tasks = arr;
592067
- writeFileSync54(target.file, JSON.stringify(json, null, 2));
592741
+ writeFileSync55(target.file, JSON.stringify(json, null, 2));
592068
592742
  } else if (Array.isArray(json)) {
592069
- writeFileSync54(target.file, JSON.stringify(arr, null, 2));
592743
+ writeFileSync55(target.file, JSON.stringify(arr, null, 2));
592070
592744
  } else {
592071
- writeFileSync54(target.file, JSON.stringify({ tasks: arr }, null, 2));
592745
+ writeFileSync55(target.file, JSON.stringify({ tasks: arr }, null, 2));
592072
592746
  }
592073
592747
  if (!enabled2) {
592074
592748
  try {
@@ -592090,7 +592764,7 @@ function deleteScheduledById(id) {
592090
592764
  const target = tasks.find((t2) => t2.id === id);
592091
592765
  if (!target) return false;
592092
592766
  try {
592093
- const raw = readFileSync79(target.file, "utf-8");
592767
+ const raw = readFileSync80(target.file, "utf-8");
592094
592768
  const json = JSON.parse(raw);
592095
592769
  const arr = Array.isArray(json?.tasks) ? json.tasks : Array.isArray(json) ? json : [];
592096
592770
  if (!arr[target.index]) return false;
@@ -592098,11 +592772,11 @@ function deleteScheduledById(id) {
592098
592772
  arr.splice(target.index, 1);
592099
592773
  if (Array.isArray(json?.tasks)) {
592100
592774
  json.tasks = arr;
592101
- writeFileSync54(target.file, JSON.stringify(json, null, 2));
592775
+ writeFileSync55(target.file, JSON.stringify(json, null, 2));
592102
592776
  } else if (Array.isArray(json)) {
592103
- writeFileSync54(target.file, JSON.stringify(arr, null, 2));
592777
+ writeFileSync55(target.file, JSON.stringify(arr, null, 2));
592104
592778
  } else {
592105
- writeFileSync54(target.file, JSON.stringify({ tasks: arr }, null, 2));
592779
+ writeFileSync55(target.file, JSON.stringify({ tasks: arr }, null, 2));
592106
592780
  }
592107
592781
  const candidates = [];
592108
592782
  if (id) candidates.push(id);
@@ -592355,11 +593029,11 @@ function reconcileScheduledTasks(apply) {
592355
593029
  const errors = [];
592356
593030
  for (const f2 of found) {
592357
593031
  const wdir = f2.workingDir || process.cwd();
592358
- const file = join114(wdir, ".oa", "scheduled", "tasks.json");
593032
+ const file = join115(wdir, ".oa", "scheduled", "tasks.json");
592359
593033
  try {
592360
593034
  let json = { tasks: [] };
592361
593035
  try {
592362
- const raw = readFileSync79(file, "utf-8");
593036
+ const raw = readFileSync80(file, "utf-8");
592363
593037
  json = JSON.parse(raw);
592364
593038
  } catch {
592365
593039
  }
@@ -592370,9 +593044,9 @@ function reconcileScheduledTasks(apply) {
592370
593044
  const entry = { task: f2.task || `legacy ${f2.id}`, schedule: f2.cron, enabled: true };
592371
593045
  arr.push(entry);
592372
593046
  const toWrite = Array.isArray(json?.tasks) ? { ...json, tasks: arr } : Array.isArray(json) ? arr : { tasks: arr };
592373
- mkdirSync62(join114(wdir, ".oa", "scheduled"), { recursive: true });
592374
- mkdirSync62(join114(wdir, ".oa", "scheduled", "logs"), { recursive: true });
592375
- writeFileSync54(file, JSON.stringify(toWrite, null, 2));
593047
+ mkdirSync63(join115(wdir, ".oa", "scheduled"), { recursive: true });
593048
+ mkdirSync63(join115(wdir, ".oa", "scheduled", "logs"), { recursive: true });
593049
+ writeFileSync55(file, JSON.stringify(toWrite, null, 2));
592376
593050
  adopted.push({ file, index: arr.length - 1 });
592377
593051
  }
592378
593052
  } else {
@@ -592416,32 +593090,32 @@ function writeCrontabLines(lines) {
592416
593090
  }
592417
593091
  function canonicalCronLine(rec) {
592418
593092
  const oaBin = findOaBinary4();
592419
- const logDir = join114(rec.workingDir, ".oa", "scheduled", "logs");
592420
- const logFile = join114(logDir, `${rec.id}.log`);
592421
- const storeFile = join114(rec.workingDir, ".oa", "scheduled", "tasks.json");
593093
+ const logDir = join115(rec.workingDir, ".oa", "scheduled", "logs");
593094
+ const logFile = join115(logDir, `${rec.id}.log`);
593095
+ const storeFile = join115(rec.workingDir, ".oa", "scheduled", "tasks.json");
592422
593096
  const taskEsc = rec.task.replace(/'/g, "'\\''");
592423
- const lockDir = join114(rec.workingDir, ".oa", "run");
592424
- const lockPath = join114(lockDir, `${rec.id}.lock`);
593097
+ const lockDir = join115(rec.workingDir, ".oa", "run");
593098
+ const lockPath = join115(lockDir, `${rec.id}.lock`);
592425
593099
  const wrapper = [
592426
593100
  `cd ${JSON.stringify(rec.workingDir)}`,
592427
593101
  `mkdir -p ${JSON.stringify(logDir)}`,
592428
593102
  `mkdir -p ${JSON.stringify(lockDir)}`,
592429
593103
  `if mkdir ${JSON.stringify(lockPath)} 2>/dev/null; then`,
592430
- ` echo $$ > ${JSON.stringify(join114(lockPath, "pid"))}`,
593104
+ ` echo $$ > ${JSON.stringify(join115(lockPath, "pid"))}`,
592431
593105
  ` trap 'rm -rf ${lockPath}' EXIT`,
592432
593106
  `else`,
592433
- ` if [ -f ${JSON.stringify(join114(lockPath, "pid"))} ]; then`,
592434
- ` oldpid=$(cat ${JSON.stringify(join114(lockPath, "pid"))} 2>/dev/null || echo)`,
593107
+ ` if [ -f ${JSON.stringify(join115(lockPath, "pid"))} ]; then`,
593108
+ ` oldpid=$(cat ${JSON.stringify(join115(lockPath, "pid"))} 2>/dev/null || echo)`,
592435
593109
  ` if [ -n "$oldpid" ] && kill -0 "$oldpid" 2>/dev/null; then`,
592436
593110
  ` echo "[oa-scheduler] ${rec.id} already running as PID $oldpid; skipping" >> ${JSON.stringify(logFile)}`,
592437
593111
  ` exit 0`,
592438
593112
  ` else`,
592439
593113
  ` rm -rf ${JSON.stringify(lockPath)} 2>/dev/null || true`,
592440
- ` mkdir -p ${JSON.stringify(lockPath)} && echo $$ > ${JSON.stringify(join114(lockPath, "pid"))} && trap 'rm -rf ${lockPath}' EXIT`,
593114
+ ` mkdir -p ${JSON.stringify(lockPath)} && echo $$ > ${JSON.stringify(join115(lockPath, "pid"))} && trap 'rm -rf ${lockPath}' EXIT`,
592441
593115
  ` fi`,
592442
593116
  ` else`,
592443
593117
  ` rm -rf ${JSON.stringify(lockPath)} 2>/dev/null || true`,
592444
- ` mkdir -p ${JSON.stringify(lockPath)} && echo $$ > ${JSON.stringify(join114(lockPath, "pid"))} && trap 'rm -rf ${lockPath}' EXIT`,
593118
+ ` mkdir -p ${JSON.stringify(lockPath)} && echo $$ > ${JSON.stringify(join115(lockPath, "pid"))} && trap 'rm -rf ${lockPath}' EXIT`,
592445
593119
  ` fi`,
592446
593120
  `fi`,
592447
593121
  `${oaBin} '${taskEsc}' >> ${JSON.stringify(logFile)} 2>&1; _oa_exit=$?`,
@@ -592473,9 +593147,9 @@ function fixupOrMigrateScheduled(mode, dryRun) {
592473
593147
  try {
592474
593148
  if (!f2.workingDir || !f2.task) continue;
592475
593149
  const unitBase = `oa-${f2.id}`;
592476
- const unitDir = join114(homedir40(), ".config", "systemd", "user");
592477
- const svc = join114(unitDir, `${unitBase}.service`);
592478
- const tim = join114(unitDir, `${unitBase}.timer`);
593150
+ const unitDir = join115(homedir41(), ".config", "systemd", "user");
593151
+ const svc = join115(unitDir, `${unitBase}.service`);
593152
+ const tim = join115(unitDir, `${unitBase}.timer`);
592479
593153
  const oaBin = findOaBinary4();
592480
593154
  const rec = { id: f2.id, cron: f2.cron, workingDir: f2.workingDir, task: f2.task };
592481
593155
  const cmd = canonicalCronLine(rec).split(" ").slice(5).join(" ");
@@ -592505,9 +593179,9 @@ Persistent=true
592505
593179
  WantedBy=timers.target
592506
593180
  `;
592507
593181
  if (!dryRun) {
592508
- mkdirSync62(unitDir, { recursive: true });
592509
- writeFileSync54(svc, svcText);
592510
- writeFileSync54(tim, timText);
593182
+ mkdirSync63(unitDir, { recursive: true });
593183
+ writeFileSync55(svc, svcText);
593184
+ writeFileSync55(tim, timText);
592511
593185
  try {
592512
593186
  const { execSync: es } = require3("node:child_process");
592513
593187
  es("systemctl --user daemon-reload", { stdio: "pipe" });
@@ -592601,8 +593275,8 @@ function startApiServer(options2 = {}) {
592601
593275
  const config = loadConfig();
592602
593276
  const ollamaUrl = options2.ollamaUrl ?? config.backendUrl;
592603
593277
  const cwd4 = process.cwd();
592604
- initAuditLog(join114(cwd4, ".oa"));
592605
- initUsageTracker(join114(cwd4, ".oa"));
593278
+ initAuditLog(join115(cwd4, ".oa"));
593279
+ initUsageTracker(join115(cwd4, ".oa"));
592606
593280
  try {
592607
593281
  const taskMgr = getSharedTaskManager();
592608
593282
  taskMgr.setEventPublisher((type, data, opts) => {
@@ -592644,7 +593318,7 @@ function startApiServer(options2 = {}) {
592644
593318
  try {
592645
593319
  const dir = todoDir();
592646
593320
  try {
592647
- mkdirSync62(dir, { recursive: true });
593321
+ mkdirSync63(dir, { recursive: true });
592648
593322
  } catch {
592649
593323
  }
592650
593324
  const cache8 = /* @__PURE__ */ new Map();
@@ -592653,7 +593327,7 @@ function startApiServer(options2 = {}) {
592653
593327
  if (!f2.endsWith(".json") || f2.includes(".tmp.")) continue;
592654
593328
  const sid = f2.replace(/\.json$/, "");
592655
593329
  try {
592656
- const items = JSON.parse(readFileSync79(join114(dir, f2), "utf-8"));
593330
+ const items = JSON.parse(readFileSync80(join115(dir, f2), "utf-8"));
592657
593331
  if (Array.isArray(items)) {
592658
593332
  cache8.set(sid, new Map(items.map((t2) => [t2.id, t2])));
592659
593333
  }
@@ -592665,10 +593339,10 @@ function startApiServer(options2 = {}) {
592665
593339
  const watcher = fsWatch3(dir, (_evt, fname) => {
592666
593340
  if (!fname || !fname.endsWith(".json") || fname.includes(".tmp.")) return;
592667
593341
  const sid = fname.replace(/\.json$/, "");
592668
- const fp = join114(dir, fname);
593342
+ const fp = join115(dir, fname);
592669
593343
  let next = [];
592670
593344
  try {
592671
- if (!existsSync98(fp)) {
593345
+ if (!existsSync99(fp)) {
592672
593346
  const old = cache8.get(sid);
592673
593347
  if (old) {
592674
593348
  for (const t2 of old.values()) {
@@ -592681,7 +593355,7 @@ function startApiServer(options2 = {}) {
592681
593355
  }
592682
593356
  return;
592683
593357
  }
592684
- next = JSON.parse(readFileSync79(fp, "utf-8"));
593358
+ next = JSON.parse(readFileSync80(fp, "utf-8"));
592685
593359
  if (!Array.isArray(next)) return;
592686
593360
  } catch {
592687
593361
  return;
@@ -592720,14 +593394,14 @@ function startApiServer(options2 = {}) {
592720
593394
  const retentionDays = parseInt(process.env["OA_JOB_RETENTION_DAYS"] ?? "30", 10);
592721
593395
  if (retentionDays > 0) {
592722
593396
  try {
592723
- const jobsDir3 = join114(cwd4, ".oa", "jobs");
592724
- if (existsSync98(jobsDir3)) {
593397
+ const jobsDir3 = join115(cwd4, ".oa", "jobs");
593398
+ if (existsSync99(jobsDir3)) {
592725
593399
  const cutoff = Date.now() - retentionDays * 864e5;
592726
593400
  for (const f2 of readdirSync34(jobsDir3)) {
592727
593401
  if (!f2.endsWith(".json")) continue;
592728
593402
  try {
592729
- const jobPath = join114(jobsDir3, f2);
592730
- const job = JSON.parse(readFileSync79(jobPath, "utf-8"));
593403
+ const jobPath = join115(jobsDir3, f2);
593404
+ const job = JSON.parse(readFileSync80(jobPath, "utf-8"));
592731
593405
  const jobTime = new Date(job.startedAt ?? job.completedAt ?? 0).getTime();
592732
593406
  if (jobTime > 0 && jobTime < cutoff && job.status !== "running") {
592733
593407
  const { unlinkSync: unlinkSync25 } = require3("node:fs");
@@ -592747,8 +593421,8 @@ function startApiServer(options2 = {}) {
592747
593421
  if (useTls) {
592748
593422
  try {
592749
593423
  tlsOpts = {
592750
- cert: readFileSync79(resolve38(tlsCert)),
592751
- key: readFileSync79(resolve38(tlsKey))
593424
+ cert: readFileSync80(resolve38(tlsCert)),
593425
+ key: readFileSync80(resolve38(tlsKey))
592752
593426
  };
592753
593427
  } catch (e2) {
592754
593428
  log22(`
@@ -592759,9 +593433,9 @@ function startApiServer(options2 = {}) {
592759
593433
  }
592760
593434
  let runtimeAccessMode = resolveAccessMode(process.env["OA_ACCESS"], host);
592761
593435
  try {
592762
- const accessFile = join114(homedir40(), ".open-agents", "access");
592763
- if (existsSync98(accessFile)) {
592764
- const persisted = readFileSync79(accessFile, "utf8").trim();
593436
+ const accessFile = join115(homedir41(), ".open-agents", "access");
593437
+ if (existsSync99(accessFile)) {
593438
+ const persisted = readFileSync80(accessFile, "utf8").trim();
592765
593439
  const resolved = resolveAccessMode(persisted, host);
592766
593440
  if (resolved) runtimeAccessMode = resolved;
592767
593441
  }
@@ -592814,9 +593488,9 @@ function startApiServer(options2 = {}) {
592814
593488
  const previous = runtimeAccessMode;
592815
593489
  runtimeAccessMode = requested;
592816
593490
  try {
592817
- const dir = join114(homedir40(), ".open-agents");
592818
- mkdirSync62(dir, { recursive: true });
592819
- writeFileSync54(join114(dir, "access"), `${runtimeAccessMode}
593491
+ const dir = join115(homedir41(), ".open-agents");
593492
+ mkdirSync63(dir, { recursive: true });
593493
+ writeFileSync55(join115(dir, "access"), `${runtimeAccessMode}
592820
593494
  `, "utf8");
592821
593495
  } catch {
592822
593496
  }
@@ -593029,9 +593703,9 @@ function startApiServer(options2 = {}) {
593029
593703
  try {
593030
593704
  const { startEmbeddingWorkers: startEmbeddingWorkers2 } = await Promise.resolve().then(() => (init_embedding_workers(), embedding_workers_exports));
593031
593705
  const { ensureEmbedDeps: ensureEmbedDeps2, runEmbedImage: runEmbedImage2, runEmbedAudio: runEmbedAudio2 } = await Promise.resolve().then(() => (init_py_embed(), py_embed_exports));
593032
- const dbBase = join114(cwd4, ".oa");
593033
- const epStore = new mem.EpisodeStore(join114(dbBase, "memory.db"));
593034
- const kg = new mem.TemporalGraph(join114(dbBase, "kg.db"));
593706
+ const dbBase = join115(cwd4, ".oa");
593707
+ const epStore = new mem.EpisodeStore(join115(dbBase, "memory.db"));
593708
+ const kg = new mem.TemporalGraph(join115(dbBase, "kg.db"));
593035
593709
  try {
593036
593710
  ensureEmbedDeps2();
593037
593711
  } catch {
@@ -593108,6 +593782,24 @@ function startApiServer(options2 = {}) {
593108
593782
  `);
593109
593783
  log22(` Primary: ${config.backendUrl} (${config.backendType || "ollama"})
593110
593784
  `);
593785
+ const _retCheck = process.env["OA_RUN_RETENTION_H"];
593786
+ const _retOff = _retCheck === "0";
593787
+ if (!_retOff) {
593788
+ try {
593789
+ const initial = pruneOldJobs();
593790
+ if (initial.pruned > 0) {
593791
+ log22(` Run GC: pruned ${initial.pruned} old job records (kept ${initial.kept}).
593792
+ `);
593793
+ }
593794
+ } catch {
593795
+ }
593796
+ setInterval(() => {
593797
+ try {
593798
+ pruneOldJobs();
593799
+ } catch {
593800
+ }
593801
+ }, 36e5).unref();
593802
+ }
593111
593803
  if (process.env["OA_API_KEYS"]) {
593112
593804
  const keyCount = process.env["OA_API_KEYS"].split(",").length;
593113
593805
  log22(` Auth: ${keyCount} scoped key(s) (read/run/admin)
@@ -593228,10 +593920,10 @@ async function handleMemoryIngest(req2, res, ollamaUrl) {
593228
593920
  const labels = Array.isArray(b.labels) ? b.labels : [];
593229
593921
  const mediaPath = typeof b.media_path === "string" ? b.media_path : void 0;
593230
593922
  const cwd4 = process.cwd();
593231
- const dbBase = join114(cwd4, ".oa");
593923
+ const dbBase = join115(cwd4, ".oa");
593232
593924
  const { EpisodeStore: EpisodeStore3, TemporalGraph: TemporalGraph3 } = await Promise.resolve().then(() => (init_dist7(), dist_exports2));
593233
- const epStore = new EpisodeStore3(join114(dbBase, "memory.db"));
593234
- const kg = new TemporalGraph3(join114(dbBase, "kg.db"));
593925
+ const epStore = new EpisodeStore3(join115(dbBase, "memory.db"));
593926
+ const kg = new TemporalGraph3(join115(dbBase, "kg.db"));
593235
593927
  const meta = {};
593236
593928
  if (mediaPath) meta.media_path = mediaPath;
593237
593929
  const epId = epStore.insert({ modality, content: content || (mediaPath || ""), metadata: meta, toolName: "memory_ingest" });
@@ -593298,7 +593990,7 @@ async function handleEntitiesList(req2, res) {
593298
593990
  const type = url.searchParams.get("type") || "person";
593299
593991
  const limit = Math.max(1, Math.min(1e3, parseInt(url.searchParams.get("limit") || "100", 10)));
593300
593992
  const { TemporalGraph: TemporalGraph3 } = await Promise.resolve().then(() => (init_dist7(), dist_exports2));
593301
- const kg = new TemporalGraph3(join114(process.cwd(), ".oa", "kg.db"));
593993
+ const kg = new TemporalGraph3(join115(process.cwd(), ".oa", "kg.db"));
593302
593994
  const nodes = kg.nodesByType(type, limit).map((n2) => ({ id: n2.id, text: n2.text, mentionCount: n2.mentionCount, firstSeen: n2.firstSeen, lastSeen: n2.lastSeen }));
593303
593995
  jsonResponse(res, 200, { object: "list", data: nodes });
593304
593996
  } catch (err) {
@@ -593320,7 +594012,7 @@ async function handleMemorySearch2(req2, res) {
593320
594012
  const wLex = typeof b.lexical_weight === "number" ? b.lexical_weight : 1;
593321
594013
  const wEmb = typeof b.embedding_weight === "number" ? b.embedding_weight : 1;
593322
594014
  const { EpisodeStore: EpisodeStore3 } = await Promise.resolve().then(() => (init_dist7(), dist_exports2));
593323
- const epStore = new EpisodeStore3(join114(process.cwd(), ".oa", "memory.db"));
594015
+ const epStore = new EpisodeStore3(join115(process.cwd(), ".oa", "memory.db"));
593324
594016
  const results = epStore.search({ query, modality, limit }, { queryEmbedding: qEmb, lexicalWeight: wLex, embeddingWeight: wEmb });
593325
594017
  jsonResponse(res, 200, { object: "list", data: results.map((e2) => ({ id: e2.id, modality: e2.modality, content: e2.content, timestamp: e2.timestamp })) });
593326
594018
  } catch (err) {
@@ -593387,7 +594079,7 @@ function setTimerEnabled(name10, enabled2) {
593387
594079
  return false;
593388
594080
  }
593389
594081
  }
593390
- var require3, endpointRegistry, modelRouteMap, endpointUsage, metrics, startedAt, runningProcesses, perKeyUsage, CRON_MARKER2;
594082
+ var require3, endpointRegistry, modelRouteMap, endpointUsage, BACKEND_TIMEOUT_DEFAULT_MS, BACKEND_TIMEOUT_MAX_MS, metrics, startedAt, runningProcesses, perKeyUsage, CRON_MARKER2;
593391
594083
  var init_serve = __esm({
593392
594084
  "packages/cli/src/api/serve.ts"() {
593393
594085
  "use strict";
@@ -593419,6 +594111,8 @@ var init_serve = __esm({
593419
594111
  endpointRegistry = [];
593420
594112
  modelRouteMap = /* @__PURE__ */ new Map();
593421
594113
  endpointUsage = /* @__PURE__ */ new Map();
594114
+ BACKEND_TIMEOUT_DEFAULT_MS = 12e4;
594115
+ BACKEND_TIMEOUT_MAX_MS = 36e5;
593422
594116
  metrics = {
593423
594117
  requests: /* @__PURE__ */ new Map(),
593424
594118
  totalTokensIn: 0,
@@ -593434,13 +594128,13 @@ var init_serve = __esm({
593434
594128
 
593435
594129
  // packages/cli/src/tui/interactive.ts
593436
594130
  import { cwd } from "node:process";
593437
- import { resolve as resolve39, join as join115, dirname as dirname36, extname as extname12 } from "node:path";
594131
+ import { resolve as resolve39, join as join116, dirname as dirname36, extname as extname12 } from "node:path";
593438
594132
  import { createRequire as createRequire5 } from "node:module";
593439
594133
  import { fileURLToPath as fileURLToPath18 } from "node:url";
593440
- import { readFileSync as readFileSync80, writeFileSync as writeFileSync55, appendFileSync as appendFileSync8, rmSync as rmSync5, readdirSync as readdirSync35, mkdirSync as mkdirSync63 } from "node:fs";
593441
- import { existsSync as existsSync99 } from "node:fs";
594134
+ import { readFileSync as readFileSync81, writeFileSync as writeFileSync56, appendFileSync as appendFileSync8, rmSync as rmSync5, readdirSync as readdirSync35, mkdirSync as mkdirSync64 } from "node:fs";
594135
+ import { existsSync as existsSync100 } from "node:fs";
593442
594136
  import { execSync as execSync56 } from "node:child_process";
593443
- import { homedir as homedir41 } from "node:os";
594137
+ import { homedir as homedir42 } from "node:os";
593444
594138
  function formatTimeAgo2(date) {
593445
594139
  const seconds = Math.floor((Date.now() - date.getTime()) / 1e3);
593446
594140
  if (seconds < 60) return "just now";
@@ -593456,12 +594150,12 @@ function getVersion4() {
593456
594150
  const require4 = createRequire5(import.meta.url);
593457
594151
  const thisDir = dirname36(fileURLToPath18(import.meta.url));
593458
594152
  const candidates = [
593459
- join115(thisDir, "..", "package.json"),
593460
- join115(thisDir, "..", "..", "package.json"),
593461
- join115(thisDir, "..", "..", "..", "package.json")
594153
+ join116(thisDir, "..", "package.json"),
594154
+ join116(thisDir, "..", "..", "package.json"),
594155
+ join116(thisDir, "..", "..", "..", "package.json")
593462
594156
  ];
593463
594157
  for (const pkgPath of candidates) {
593464
- if (existsSync99(pkgPath)) {
594158
+ if (existsSync100(pkgPath)) {
593465
594159
  const pkg = require4(pkgPath);
593466
594160
  if (pkg.name === "open-agents-ai" || pkg.name === "@open-agents/cli" || pkg.name === "@open-agents/monorepo") {
593467
594161
  return pkg.version ?? "0.0.0";
@@ -594224,14 +594918,14 @@ Meta-critique: quality ${meta.quality}/5, thorough: ${meta.thorough}`;
594224
594918
  function gatherMemorySnippets(root) {
594225
594919
  const snippets = [];
594226
594920
  const dirs = [
594227
- join115(root, ".oa", "memory"),
594228
- join115(root, ".open-agents", "memory")
594921
+ join116(root, ".oa", "memory"),
594922
+ join116(root, ".open-agents", "memory")
594229
594923
  ];
594230
594924
  for (const dir of dirs) {
594231
- if (!existsSync99(dir)) continue;
594925
+ if (!existsSync100(dir)) continue;
594232
594926
  try {
594233
594927
  for (const f2 of readdirSync35(dir).filter((f3) => f3.endsWith(".json"))) {
594234
- const data = JSON.parse(readFileSync80(join115(dir, f2), "utf-8"));
594928
+ const data = JSON.parse(readFileSync81(join116(dir, f2), "utf-8"));
594235
594929
  for (const val of Object.values(data)) {
594236
594930
  const v = typeof val === "object" && val !== null && "value" in val ? String(val.value) : String(val);
594237
594931
  if (v.length > 10) snippets.push(v);
@@ -594385,9 +595079,9 @@ ${metabolismMemories}
594385
595079
  } catch {
594386
595080
  }
594387
595081
  try {
594388
- const archeFile = join115(repoRoot, ".oa", "arche", "variants.json");
594389
- if (existsSync99(archeFile)) {
594390
- const variants = JSON.parse(readFileSync80(archeFile, "utf8"));
595082
+ const archeFile = join116(repoRoot, ".oa", "arche", "variants.json");
595083
+ if (existsSync100(archeFile)) {
595084
+ const variants = JSON.parse(readFileSync81(archeFile, "utf8"));
594391
595085
  if (variants.length > 0) {
594392
595086
  let filtered = variants;
594393
595087
  if (taskType) {
@@ -594559,9 +595253,9 @@ RULES:
594559
595253
  const compactionThreshold = Number.isFinite(envOverride) && envOverride > 0 ? envOverride : modelTier === "small" ? 12e3 : modelTier === "medium" ? 24e3 : 4e4;
594560
595254
  let identityInjection = "";
594561
595255
  try {
594562
- const ikStateFile = join115(repoRoot, ".oa", "identity", "self-state.json");
594563
- if (existsSync99(ikStateFile)) {
594564
- const selfState = JSON.parse(readFileSync80(ikStateFile, "utf8"));
595256
+ const ikStateFile = join116(repoRoot, ".oa", "identity", "self-state.json");
595257
+ if (existsSync100(ikStateFile)) {
595258
+ const selfState = JSON.parse(readFileSync81(ikStateFile, "utf8"));
594565
595259
  const lines = [
594566
595260
  `[Identity State v${selfState.version}]`,
594567
595261
  `Self: ${selfState.narrative_summary}`,
@@ -595422,13 +596116,13 @@ When done, either call task_complete with your answer, or use FINAL_VAR(variable
595422
596116
  });
595423
596117
  }
595424
596118
  try {
595425
- const ikDir = join115(repoRoot, ".oa", "identity");
595426
- const ikFile = join115(ikDir, "self-state.json");
596119
+ const ikDir = join116(repoRoot, ".oa", "identity");
596120
+ const ikFile = join116(ikDir, "self-state.json");
595427
596121
  let ikState;
595428
- if (existsSync99(ikFile)) {
595429
- ikState = JSON.parse(readFileSync80(ikFile, "utf8"));
596122
+ if (existsSync100(ikFile)) {
596123
+ ikState = JSON.parse(readFileSync81(ikFile, "utf8"));
595430
596124
  } else {
595431
- mkdirSync63(ikDir, { recursive: true });
596125
+ mkdirSync64(ikDir, { recursive: true });
595432
596126
  const machineId = Date.now().toString(36) + Math.random().toString(36).slice(2, 8);
595433
596127
  ikState = {
595434
596128
  self_id: `oa-${machineId}`,
@@ -595484,7 +596178,7 @@ When done, either call task_complete with your answer, or use FINAL_VAR(variable
595484
596178
  }
595485
596179
  ikState.session_count = (ikState.session_count || 0) + 1;
595486
596180
  ikState.updated_at = (/* @__PURE__ */ new Date()).toISOString();
595487
- writeFileSync55(ikFile, JSON.stringify(ikState, null, 2));
596181
+ writeFileSync56(ikFile, JSON.stringify(ikState, null, 2));
595488
596182
  } catch (ikErr) {
595489
596183
  try {
595490
596184
  console.error("[IK-OBSERVE]", ikErr);
@@ -595503,9 +596197,9 @@ When done, either call task_complete with your answer, or use FINAL_VAR(variable
595503
596197
  } else {
595504
596198
  renderTaskIncomplete(result.turns, result.toolCalls, result.durationMs, tokens);
595505
596199
  try {
595506
- const ikFile = join115(repoRoot, ".oa", "identity", "self-state.json");
595507
- if (existsSync99(ikFile)) {
595508
- const ikState = JSON.parse(readFileSync80(ikFile, "utf8"));
596200
+ const ikFile = join116(repoRoot, ".oa", "identity", "self-state.json");
596201
+ if (existsSync100(ikFile)) {
596202
+ const ikState = JSON.parse(readFileSync81(ikFile, "utf8"));
595509
596203
  if (!ikState.stats) ikState.stats = { queries_served: 0 };
595510
596204
  ikState.stats.queries_served = (ikState.stats.queries_served || 0) + 1;
595511
596205
  ikState.homeostasis.uncertainty = Math.min(1, ikState.homeostasis.uncertainty + 0.03);
@@ -595516,7 +596210,7 @@ When done, either call task_complete with your answer, or use FINAL_VAR(variable
595516
596210
  if (ikState.version_history.length > 200) ikState.version_history = ikState.version_history.slice(-200);
595517
596211
  ikState.session_count = (ikState.session_count || 0) + 1;
595518
596212
  ikState.updated_at = (/* @__PURE__ */ new Date()).toISOString();
595519
- writeFileSync55(ikFile, JSON.stringify(ikState, null, 2));
596213
+ writeFileSync56(ikFile, JSON.stringify(ikState, null, 2));
595520
596214
  }
595521
596215
  } catch {
595522
596216
  }
@@ -595761,10 +596455,10 @@ async function startInteractive(config, repoPath) {
595761
596455
  process.stdin.pause();
595762
596456
  }
595763
596457
  try {
595764
- const oaDir = join115(repoRoot, ".oa");
595765
- const nexusPidFile = join115(oaDir, "nexus", "daemon.pid");
595766
- if (existsSync99(nexusPidFile)) {
595767
- const pid = parseInt(readFileSync80(nexusPidFile, "utf8").trim(), 10);
596458
+ const oaDir = join116(repoRoot, ".oa");
596459
+ const nexusPidFile = join116(oaDir, "nexus", "daemon.pid");
596460
+ if (existsSync100(nexusPidFile)) {
596461
+ const pid = parseInt(readFileSync81(nexusPidFile, "utf8").trim(), 10);
595768
596462
  if (pid > 0) {
595769
596463
  try {
595770
596464
  process.kill(pid, 0);
@@ -596424,7 +597118,7 @@ ${result.summary}`
596424
597118
  let p2pGateway = null;
596425
597119
  let peerMesh = null;
596426
597120
  let inferenceRouter = null;
596427
- const secretVault = new SecretVault(join115(repoRoot, ".oa", "vault.enc"));
597121
+ const secretVault = new SecretVault(join116(repoRoot, ".oa", "vault.enc"));
596428
597122
  let adminSessionKey = null;
596429
597123
  const callSubAgents = /* @__PURE__ */ new Map();
596430
597124
  const streamRenderer = new StreamRenderer();
@@ -596655,13 +597349,13 @@ Rationale: ${proposal.rationale}${provenanceNote}`;
596655
597349
  const hits = allCompletions.filter((c9) => c9.toLowerCase().startsWith(lower));
596656
597350
  return [hits, line];
596657
597351
  }
596658
- const HISTORY_DIR = join115(homedir41(), ".open-agents");
596659
- const HISTORY_FILE = join115(HISTORY_DIR, "repl-history");
597352
+ const HISTORY_DIR = join116(homedir42(), ".open-agents");
597353
+ const HISTORY_FILE = join116(HISTORY_DIR, "repl-history");
596660
597354
  const MAX_HISTORY_LINES = 500;
596661
597355
  let savedHistory = [];
596662
597356
  try {
596663
- if (existsSync99(HISTORY_FILE)) {
596664
- const raw = readFileSync80(HISTORY_FILE, "utf8").trim();
597357
+ if (existsSync100(HISTORY_FILE)) {
597358
+ const raw = readFileSync81(HISTORY_FILE, "utf8").trim();
596665
597359
  if (raw) savedHistory = raw.split("\n").reverse();
596666
597360
  }
596667
597361
  } catch {
@@ -596808,12 +597502,12 @@ Rationale: ${proposal.rationale}${provenanceNote}`;
596808
597502
  function persistHistoryLine(line) {
596809
597503
  if (!line.trim()) return;
596810
597504
  try {
596811
- mkdirSync63(HISTORY_DIR, { recursive: true });
597505
+ mkdirSync64(HISTORY_DIR, { recursive: true });
596812
597506
  appendFileSync8(HISTORY_FILE, line + "\n", "utf8");
596813
597507
  if (Math.random() < 0.02) {
596814
- const all2 = readFileSync80(HISTORY_FILE, "utf8").trim().split("\n");
597508
+ const all2 = readFileSync81(HISTORY_FILE, "utf8").trim().split("\n");
596815
597509
  if (all2.length > MAX_HISTORY_LINES) {
596816
- writeFileSync55(HISTORY_FILE, all2.slice(-MAX_HISTORY_LINES).join("\n") + "\n", "utf8");
597510
+ writeFileSync56(HISTORY_FILE, all2.slice(-MAX_HISTORY_LINES).join("\n") + "\n", "utf8");
596817
597511
  }
596818
597512
  }
596819
597513
  } catch {
@@ -596998,10 +597692,10 @@ Rationale: ${proposal.rationale}${provenanceNote}`;
596998
597692
  const { unlinkSync: _rmStale } = await import("node:fs");
596999
597693
  const { homedir: _hdir } = await import("node:os");
597000
597694
  for (const dp of [
597001
- join115(repoRoot, ".oa", "nexus", "nexus-daemon.mjs"),
597002
- join115(_hdir(), ".open-agents", ".oa", "nexus", "nexus-daemon.mjs")
597695
+ join116(repoRoot, ".oa", "nexus", "nexus-daemon.mjs"),
597696
+ join116(_hdir(), ".open-agents", ".oa", "nexus", "nexus-daemon.mjs")
597003
597697
  ]) {
597004
- if (existsSync99(dp)) try {
597698
+ if (existsSync100(dp)) try {
597005
597699
  _rmStale(dp);
597006
597700
  } catch {
597007
597701
  }
@@ -597012,9 +597706,9 @@ Rationale: ${proposal.rationale}${provenanceNote}`;
597012
597706
  const autoNexus = new NexusTool(repoRoot);
597013
597707
  const _registerNexusDaemon = () => {
597014
597708
  try {
597015
- const nexusPidFile = join115(repoRoot, ".oa", "nexus", "daemon.pid");
597016
- if (existsSync99(nexusPidFile)) {
597017
- const nPid = parseInt(readFileSync80(nexusPidFile, "utf8").trim(), 10);
597709
+ const nexusPidFile = join116(repoRoot, ".oa", "nexus", "daemon.pid");
597710
+ if (existsSync100(nexusPidFile)) {
597711
+ const nPid = parseInt(readFileSync81(nexusPidFile, "utf8").trim(), 10);
597018
597712
  if (nPid > 0 && !registry2.daemons.has("Nexus")) {
597019
597713
  registry2.register({ name: "Nexus", pid: nPid, startedAt: Date.now(), status: "running" });
597020
597714
  statusBar.ensureMonitorTimer();
@@ -597071,7 +597765,7 @@ Rationale: ${proposal.rationale}${provenanceNote}`;
597071
597765
  } catch {
597072
597766
  }
597073
597767
  try {
597074
- const oaDir = join115(repoRoot, ".oa");
597768
+ const oaDir = join116(repoRoot, ".oa");
597075
597769
  const reconnected = await ExposeGateway.checkAndReconnect(oaDir, {
597076
597770
  onInfo: (msg) => writeContent(() => renderInfo2(msg)),
597077
597771
  onError: (msg) => writeContent(() => renderWarning2(msg))
@@ -597103,7 +597797,7 @@ Rationale: ${proposal.rationale}${provenanceNote}`;
597103
597797
  } catch {
597104
597798
  }
597105
597799
  try {
597106
- const oaDir = join115(repoRoot, ".oa");
597800
+ const oaDir = join116(repoRoot, ".oa");
597107
597801
  const reconnectedP2P = await ExposeP2PGateway.checkAndReconnect(oaDir, new NexusTool(repoRoot), {
597108
597802
  onInfo: (msg) => writeContent(() => renderInfo2(msg)),
597109
597803
  onError: (msg) => writeContent(() => renderWarning2(msg))
@@ -597144,10 +597838,10 @@ Rationale: ${proposal.rationale}${provenanceNote}`;
597144
597838
  }
597145
597839
  try {
597146
597840
  const { homedir: _hd, hostname: _hn, userInfo: _ui } = await import("node:os");
597147
- const globalNamePath = join115(_hd(), ".open-agents", "agent-name");
597841
+ const globalNamePath = join116(_hd(), ".open-agents", "agent-name");
597148
597842
  let agName = "";
597149
597843
  try {
597150
- if (existsSync99(globalNamePath)) agName = readFileSync80(globalNamePath, "utf8").trim();
597844
+ if (existsSync100(globalNamePath)) agName = readFileSync81(globalNamePath, "utf8").trim();
597151
597845
  } catch {
597152
597846
  }
597153
597847
  if (!agName) {
@@ -597176,11 +597870,11 @@ Rationale: ${proposal.rationale}${provenanceNote}`;
597176
597870
  }
597177
597871
  if (!ollamaAlive) {
597178
597872
  try {
597179
- const savedSponsorsPath = join115(repoRoot, ".oa", "sponsor", "known-sponsors.json");
597873
+ const savedSponsorsPath = join116(repoRoot, ".oa", "sponsor", "known-sponsors.json");
597180
597874
  let savedSponsors = [];
597181
597875
  try {
597182
- if (existsSync99(savedSponsorsPath)) {
597183
- savedSponsors = JSON.parse(readFileSync80(savedSponsorsPath, "utf8"));
597876
+ if (existsSync100(savedSponsorsPath)) {
597877
+ savedSponsors = JSON.parse(readFileSync81(savedSponsorsPath, "utf8"));
597184
597878
  const oneHourAgo = Date.now() - 36e5;
597185
597879
  savedSponsors = savedSponsors.filter((s2) => (s2.lastVerified || 0) > oneHourAgo);
597186
597880
  }
@@ -598272,10 +598966,10 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
598272
598966
  if (name10 === "voice_list_files") {
598273
598967
  const baseDir = String(args?.dir ?? ".");
598274
598968
  const { readdirSync: readdirSync37, statSync: statSync30 } = __require("node:fs");
598275
- const { join: join120, resolve: resolve43 } = __require("node:path");
598276
- const base3 = baseDir.startsWith("/") ? baseDir : resolve43(join120(repoRoot, baseDir));
598969
+ const { join: join121, resolve: resolve43 } = __require("node:path");
598970
+ const base3 = baseDir.startsWith("/") ? baseDir : resolve43(join121(repoRoot, baseDir));
598277
598971
  const items = readdirSync37(base3).slice(0, 200).map((f2) => {
598278
- const s2 = statSync30(join120(base3, f2));
598972
+ const s2 = statSync30(join121(base3, f2));
598279
598973
  return { name: f2, dir: s2.isDirectory(), size: s2.size };
598280
598974
  });
598281
598975
  return JSON.stringify({ dir: base3, items }, null, 2);
@@ -598363,7 +599057,7 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
598363
599057
  kind,
598364
599058
  targetUrl,
598365
599059
  authKey,
598366
- stateDir: join115(repoRoot, ".oa"),
599060
+ stateDir: join116(repoRoot, ".oa"),
598367
599061
  passthrough: passthrough ?? false,
598368
599062
  loadbalance: loadbalance ?? false,
598369
599063
  endpointAuth: passthrough ? currentConfig.apiKey : void 0,
@@ -598409,7 +599103,7 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
598409
599103
  await tunnelGateway.stop();
598410
599104
  tunnelGateway = null;
598411
599105
  }
598412
- const newTunnel = new ExposeGateway({ kind, targetUrl, authKey, fullAccess, stateDir: join115(repoRoot, ".oa") });
599106
+ const newTunnel = new ExposeGateway({ kind, targetUrl, authKey, fullAccess, stateDir: join116(repoRoot, ".oa") });
598413
599107
  newTunnel.on("stats", (stats) => {
598414
599108
  statusBar.setExposeStatus({
598415
599109
  status: stats.status,
@@ -598496,9 +599190,9 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
598496
599190
  });
598497
599191
  if (!result.success) throw new Error(result.error || "Connect failed");
598498
599192
  try {
598499
- const nexusPidFile = join115(repoRoot, ".oa", "nexus", "daemon.pid");
598500
- if (existsSync99(nexusPidFile)) {
598501
- const pid = parseInt(readFileSync80(nexusPidFile, "utf8").trim(), 10);
599193
+ const nexusPidFile = join116(repoRoot, ".oa", "nexus", "daemon.pid");
599194
+ if (existsSync100(nexusPidFile)) {
599195
+ const pid = parseInt(readFileSync81(nexusPidFile, "utf8").trim(), 10);
598502
599196
  if (pid > 0) {
598503
599197
  registry2.register({
598504
599198
  name: "Nexus",
@@ -598686,10 +599380,10 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
598686
599380
  writeContent(() => renderInfo2(`Killed ${bgKilled} background task(s).`));
598687
599381
  }
598688
599382
  try {
598689
- const nexusDir = join115(repoRoot, OA_DIR, "nexus");
598690
- const pidFile = join115(nexusDir, "daemon.pid");
598691
- if (existsSync99(pidFile)) {
598692
- const pid = parseInt(readFileSync80(pidFile, "utf8").trim(), 10);
599383
+ const nexusDir = join116(repoRoot, OA_DIR, "nexus");
599384
+ const pidFile = join116(nexusDir, "daemon.pid");
599385
+ if (existsSync100(pidFile)) {
599386
+ const pid = parseInt(readFileSync81(pidFile, "utf8").trim(), 10);
598693
599387
  if (pid > 0) {
598694
599388
  try {
598695
599389
  if (process.platform === "win32") {
@@ -598711,13 +599405,13 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
598711
599405
  } catch {
598712
599406
  }
598713
599407
  try {
598714
- const voiceDir2 = join115(homedir41(), ".open-agents", "voice");
599408
+ const voiceDir2 = join116(homedir42(), ".open-agents", "voice");
598715
599409
  const voicePidFiles = ["luxtts-daemon.pid", "piper-daemon.pid"];
598716
599410
  for (const pf of voicePidFiles) {
598717
- const pidPath = join115(voiceDir2, pf);
598718
- if (existsSync99(pidPath)) {
599411
+ const pidPath = join116(voiceDir2, pf);
599412
+ if (existsSync100(pidPath)) {
598719
599413
  try {
598720
- const pid = parseInt(readFileSync80(pidPath, "utf8").trim(), 10);
599414
+ const pid = parseInt(readFileSync81(pidPath, "utf8").trim(), 10);
598721
599415
  if (pid > 0) {
598722
599416
  if (process.platform === "win32") {
598723
599417
  try {
@@ -598741,8 +599435,8 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
598741
599435
  execSync56(process.platform === "win32" ? "timeout /t 1 /nobreak >nul" : "sleep 0.5", { timeout: 3e3, stdio: "ignore" });
598742
599436
  } catch {
598743
599437
  }
598744
- const oaPath = join115(repoRoot, OA_DIR);
598745
- if (existsSync99(oaPath)) {
599438
+ const oaPath = join116(repoRoot, OA_DIR);
599439
+ if (existsSync100(oaPath)) {
598746
599440
  let deleted = false;
598747
599441
  for (let attempt = 0; attempt < 3; attempt++) {
598748
599442
  try {
@@ -598826,19 +599520,19 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
598826
599520
  try {
598827
599521
  const { isPersonaPlexRunning: isPersonaPlexRunning2 } = await Promise.resolve().then(() => (init_personaplex(), personaplex_exports));
598828
599522
  if (isPersonaPlexRunning2()) {
598829
- const ppPidFile = join115(homedir41(), ".open-agents", "voice", "personaplex", "daemon.pid");
598830
- const ppPortFile = join115(homedir41(), ".open-agents", "voice", "personaplex", "daemon.port");
598831
- if (existsSync99(ppPidFile)) {
598832
- const ppPid = parseInt(readFileSync80(ppPidFile, "utf8").trim(), 10);
598833
- const ppPort = existsSync99(ppPortFile) ? parseInt(readFileSync80(ppPortFile, "utf8").trim(), 10) : void 0;
599523
+ const ppPidFile = join116(homedir42(), ".open-agents", "voice", "personaplex", "daemon.pid");
599524
+ const ppPortFile = join116(homedir42(), ".open-agents", "voice", "personaplex", "daemon.port");
599525
+ if (existsSync100(ppPidFile)) {
599526
+ const ppPid = parseInt(readFileSync81(ppPidFile, "utf8").trim(), 10);
599527
+ const ppPort = existsSync100(ppPortFile) ? parseInt(readFileSync81(ppPortFile, "utf8").trim(), 10) : void 0;
598834
599528
  if (ppPid > 0 && !registry2.daemons.has("PersonaPlex")) {
598835
599529
  registry2.register({ name: "PersonaPlex", pid: ppPid, port: ppPort, startedAt: Date.now(), status: "running" });
598836
599530
  }
598837
599531
  }
598838
599532
  }
598839
- const nexusPidFile = join115(repoRoot, ".oa", "nexus", "daemon.pid");
598840
- if (existsSync99(nexusPidFile)) {
598841
- const nPid = parseInt(readFileSync80(nexusPidFile, "utf8").trim(), 10);
599533
+ const nexusPidFile = join116(repoRoot, ".oa", "nexus", "daemon.pid");
599534
+ if (existsSync100(nexusPidFile)) {
599535
+ const nPid = parseInt(readFileSync81(nexusPidFile, "utf8").trim(), 10);
598842
599536
  if (nPid > 0 && !registry2.daemons.has("Nexus")) {
598843
599537
  try {
598844
599538
  process.kill(nPid, 0);
@@ -599205,9 +599899,9 @@ Execute this skill now. Follow the behavioral guidance above.`;
599205
599899
  }
599206
599900
  }
599207
599901
  const cleanPath = input.replace(/^['"]|['"]$/g, "").trim();
599208
- const isImage = isImagePath(cleanPath) && existsSync99(resolve39(repoRoot, cleanPath));
599209
- const isMedia = !isImage && isTranscribablePath(cleanPath) && existsSync99(resolve39(repoRoot, cleanPath));
599210
- const isMarkdown = !isImage && !isMedia && /\.(md|markdown)$/i.test(cleanPath) && existsSync99(resolve39(repoRoot, cleanPath));
599902
+ const isImage = isImagePath(cleanPath) && existsSync100(resolve39(repoRoot, cleanPath));
599903
+ const isMedia = !isImage && isTranscribablePath(cleanPath) && existsSync100(resolve39(repoRoot, cleanPath));
599904
+ const isMarkdown = !isImage && !isMedia && /\.(md|markdown)$/i.test(cleanPath) && existsSync100(resolve39(repoRoot, cleanPath));
599211
599905
  if (activeTask) {
599212
599906
  if (activeTask.runner.isPaused) {
599213
599907
  activeTask.runner.resume();
@@ -599216,7 +599910,7 @@ Execute this skill now. Follow the behavioral guidance above.`;
599216
599910
  if (isImage) {
599217
599911
  try {
599218
599912
  const imgPath = resolve39(repoRoot, cleanPath);
599219
- const imgBuffer = readFileSync80(imgPath);
599913
+ const imgBuffer = readFileSync81(imgPath);
599220
599914
  const base642 = imgBuffer.toString("base64");
599221
599915
  const ext = extname12(cleanPath).toLowerCase();
599222
599916
  const mime = ext === ".png" ? "image/png" : ext === ".gif" ? "image/gif" : ext === ".webp" ? "image/webp" : "image/jpeg";
@@ -599372,7 +600066,7 @@ ${result.text}`;
599372
600066
  if (isMarkdown && fullInput === input) {
599373
600067
  try {
599374
600068
  const mdPath = resolve39(repoRoot, cleanPath);
599375
- const mdContent = readFileSync80(mdPath, "utf8");
600069
+ const mdContent = readFileSync81(mdPath, "utf8");
599376
600070
  const { parseMcpMarkdown: parseMcpMarkdown2 } = await Promise.resolve().then(() => (init_dist5(), dist_exports));
599377
600071
  const result = parseMcpMarkdown2(mdContent);
599378
600072
  if (result.servers.length > 0) {
@@ -599794,13 +600488,13 @@ async function runWithTUI(task, config, repoPath, callbacks) {
599794
600488
  const handle2 = startTask(task, config, repoRoot);
599795
600489
  await handle2.promise;
599796
600490
  try {
599797
- const ikDir = join115(repoRoot, ".oa", "identity");
599798
- const ikFile = join115(ikDir, "self-state.json");
600491
+ const ikDir = join116(repoRoot, ".oa", "identity");
600492
+ const ikFile = join116(ikDir, "self-state.json");
599799
600493
  let ikState;
599800
- if (existsSync99(ikFile)) {
599801
- ikState = JSON.parse(readFileSync80(ikFile, "utf8"));
600494
+ if (existsSync100(ikFile)) {
600495
+ ikState = JSON.parse(readFileSync81(ikFile, "utf8"));
599802
600496
  } else {
599803
- mkdirSync63(ikDir, { recursive: true });
600497
+ mkdirSync64(ikDir, { recursive: true });
599804
600498
  ikState = {
599805
600499
  self_id: `oa-${Date.now().toString(36)}`,
599806
600500
  version: 1,
@@ -599822,7 +600516,7 @@ async function runWithTUI(task, config, repoPath, callbacks) {
599822
600516
  ikState.homeostasis.coherence = Math.min(1, ikState.homeostasis.coherence + 0.05);
599823
600517
  ikState.session_count = (ikState.session_count || 0) + 1;
599824
600518
  ikState.updated_at = (/* @__PURE__ */ new Date()).toISOString();
599825
- writeFileSync55(ikFile, JSON.stringify(ikState, null, 2));
600519
+ writeFileSync56(ikFile, JSON.stringify(ikState, null, 2));
599826
600520
  } catch (ikErr) {
599827
600521
  }
599828
600522
  try {
@@ -599835,11 +600529,11 @@ async function runWithTUI(task, config, repoPath, callbacks) {
599835
600529
  );
599836
600530
  } catch {
599837
600531
  try {
599838
- const archeDir = join115(repoRoot, ".oa", "arche");
599839
- const archeFile = join115(archeDir, "variants.json");
600532
+ const archeDir = join116(repoRoot, ".oa", "arche");
600533
+ const archeFile = join116(archeDir, "variants.json");
599840
600534
  let variants = [];
599841
600535
  try {
599842
- if (existsSync99(archeFile)) variants = JSON.parse(readFileSync80(archeFile, "utf8"));
600536
+ if (existsSync100(archeFile)) variants = JSON.parse(readFileSync81(archeFile, "utf8"));
599843
600537
  } catch {
599844
600538
  }
599845
600539
  variants.push({
@@ -599853,15 +600547,15 @@ async function runWithTUI(task, config, repoPath, callbacks) {
599853
600547
  tags: ["general"]
599854
600548
  });
599855
600549
  if (variants.length > 50) variants = variants.slice(-50);
599856
- mkdirSync63(archeDir, { recursive: true });
599857
- writeFileSync55(archeFile, JSON.stringify(variants, null, 2));
600550
+ mkdirSync64(archeDir, { recursive: true });
600551
+ writeFileSync56(archeFile, JSON.stringify(variants, null, 2));
599858
600552
  } catch {
599859
600553
  }
599860
600554
  }
599861
600555
  try {
599862
- const metaFile = join115(repoRoot, ".oa", "memory", "metabolism", "store.json");
599863
- if (existsSync99(metaFile)) {
599864
- const store2 = JSON.parse(readFileSync80(metaFile, "utf8"));
600556
+ const metaFile = join116(repoRoot, ".oa", "memory", "metabolism", "store.json");
600557
+ if (existsSync100(metaFile)) {
600558
+ const store2 = JSON.parse(readFileSync81(metaFile, "utf8"));
599865
600559
  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);
599866
600560
  let updated = false;
599867
600561
  for (const item of surfaced) {
@@ -599872,7 +600566,7 @@ async function runWithTUI(task, config, repoPath, callbacks) {
599872
600566
  updated = true;
599873
600567
  }
599874
600568
  if (updated) {
599875
- writeFileSync55(metaFile, JSON.stringify(store2, null, 2));
600569
+ writeFileSync56(metaFile, JSON.stringify(store2, null, 2));
599876
600570
  }
599877
600571
  }
599878
600572
  } catch {
@@ -599925,9 +600619,9 @@ Rules:
599925
600619
  try {
599926
600620
  const { initDb: initDb2 } = __require("@open-agents/memory");
599927
600621
  const { ProceduralMemoryStore: ProceduralMemoryStore2 } = __require("@open-agents/memory");
599928
- const dbDir = join115(repoRoot, ".oa", "memory");
599929
- mkdirSync63(dbDir, { recursive: true });
599930
- const db = initDb2(join115(dbDir, "structured.db"));
600622
+ const dbDir = join116(repoRoot, ".oa", "memory");
600623
+ mkdirSync64(dbDir, { recursive: true });
600624
+ const db = initDb2(join116(dbDir, "structured.db"));
599931
600625
  const memStore = new ProceduralMemoryStore2(db);
599932
600626
  memStore.createWithEmbedding({
599933
600627
  content: content.slice(0, 600),
@@ -599942,11 +600636,11 @@ Rules:
599942
600636
  db.close();
599943
600637
  } catch {
599944
600638
  }
599945
- const metaDir = join115(repoRoot, ".oa", "memory", "metabolism");
599946
- const storeFile = join115(metaDir, "store.json");
600639
+ const metaDir = join116(repoRoot, ".oa", "memory", "metabolism");
600640
+ const storeFile = join116(metaDir, "store.json");
599947
600641
  let store2 = [];
599948
600642
  try {
599949
- if (existsSync99(storeFile)) store2 = JSON.parse(readFileSync80(storeFile, "utf8"));
600643
+ if (existsSync100(storeFile)) store2 = JSON.parse(readFileSync81(storeFile, "utf8"));
599950
600644
  } catch {
599951
600645
  }
599952
600646
  store2.push({
@@ -599961,26 +600655,26 @@ Rules:
599961
600655
  accessCount: 0
599962
600656
  });
599963
600657
  if (store2.length > 100) store2 = store2.slice(-100);
599964
- mkdirSync63(metaDir, { recursive: true });
599965
- writeFileSync55(storeFile, JSON.stringify(store2, null, 2));
600658
+ mkdirSync64(metaDir, { recursive: true });
600659
+ writeFileSync56(storeFile, JSON.stringify(store2, null, 2));
599966
600660
  }
599967
600661
  }
599968
600662
  } catch {
599969
600663
  }
599970
600664
  try {
599971
- const cohereSettingsFile = join115(repoRoot, ".oa", "settings.json");
600665
+ const cohereSettingsFile = join116(repoRoot, ".oa", "settings.json");
599972
600666
  let cohereActive = false;
599973
600667
  try {
599974
- if (existsSync99(cohereSettingsFile)) {
599975
- const settings = JSON.parse(readFileSync80(cohereSettingsFile, "utf8"));
600668
+ if (existsSync100(cohereSettingsFile)) {
600669
+ const settings = JSON.parse(readFileSync81(cohereSettingsFile, "utf8"));
599976
600670
  cohereActive = settings.cohere === true;
599977
600671
  }
599978
600672
  } catch {
599979
600673
  }
599980
600674
  if (cohereActive) {
599981
- const metaFile = join115(repoRoot, ".oa", "memory", "metabolism", "store.json");
599982
- if (existsSync99(metaFile)) {
599983
- const store2 = JSON.parse(readFileSync80(metaFile, "utf8"));
600675
+ const metaFile = join116(repoRoot, ".oa", "memory", "metabolism", "store.json");
600676
+ if (existsSync100(metaFile)) {
600677
+ const store2 = JSON.parse(readFileSync81(metaFile, "utf8"));
599984
600678
  const latest = store2.filter((m2) => m2.sourceTrace === "trajectory-extraction" || m2.sourceTrace === "llm-trajectory-extraction").slice(-1)[0];
599985
600679
  if (latest && latest.scores?.confidence >= 0.6) {
599986
600680
  try {
@@ -600005,18 +600699,18 @@ Rules:
600005
600699
  }
600006
600700
  } catch (err) {
600007
600701
  try {
600008
- const ikFile = join115(repoRoot, ".oa", "identity", "self-state.json");
600009
- if (existsSync99(ikFile)) {
600010
- const ikState = JSON.parse(readFileSync80(ikFile, "utf8"));
600702
+ const ikFile = join116(repoRoot, ".oa", "identity", "self-state.json");
600703
+ if (existsSync100(ikFile)) {
600704
+ const ikState = JSON.parse(readFileSync81(ikFile, "utf8"));
600011
600705
  ikState.homeostasis.uncertainty = Math.min(1, ikState.homeostasis.uncertainty + 0.1);
600012
600706
  ikState.homeostasis.coherence = Math.max(0, ikState.homeostasis.coherence - 0.05);
600013
600707
  ikState.session_count = (ikState.session_count || 0) + 1;
600014
600708
  ikState.updated_at = (/* @__PURE__ */ new Date()).toISOString();
600015
- writeFileSync55(ikFile, JSON.stringify(ikState, null, 2));
600709
+ writeFileSync56(ikFile, JSON.stringify(ikState, null, 2));
600016
600710
  }
600017
- const metaFile = join115(repoRoot, ".oa", "memory", "metabolism", "store.json");
600018
- if (existsSync99(metaFile)) {
600019
- const store2 = JSON.parse(readFileSync80(metaFile, "utf8"));
600711
+ const metaFile = join116(repoRoot, ".oa", "memory", "metabolism", "store.json");
600712
+ if (existsSync100(metaFile)) {
600713
+ const store2 = JSON.parse(readFileSync81(metaFile, "utf8"));
600020
600714
  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);
600021
600715
  for (const item of surfaced) {
600022
600716
  item.accessCount = (item.accessCount || 0) + 1;
@@ -600024,14 +600718,14 @@ Rules:
600024
600718
  item.scores.utility = Math.max(0, (item.scores.utility || 0.5) - 0.05);
600025
600719
  item.scores.confidence = Math.max(0, (item.scores.confidence || 0.5) - 0.02);
600026
600720
  }
600027
- writeFileSync55(metaFile, JSON.stringify(store2, null, 2));
600721
+ writeFileSync56(metaFile, JSON.stringify(store2, null, 2));
600028
600722
  }
600029
600723
  try {
600030
- const archeDir = join115(repoRoot, ".oa", "arche");
600031
- const archeFile = join115(archeDir, "variants.json");
600724
+ const archeDir = join116(repoRoot, ".oa", "arche");
600725
+ const archeFile = join116(archeDir, "variants.json");
600032
600726
  let variants = [];
600033
600727
  try {
600034
- if (existsSync99(archeFile)) variants = JSON.parse(readFileSync80(archeFile, "utf8"));
600728
+ if (existsSync100(archeFile)) variants = JSON.parse(readFileSync81(archeFile, "utf8"));
600035
600729
  } catch {
600036
600730
  }
600037
600731
  variants.push({
@@ -600045,8 +600739,8 @@ Rules:
600045
600739
  tags: ["general"]
600046
600740
  });
600047
600741
  if (variants.length > 50) variants = variants.slice(-50);
600048
- mkdirSync63(archeDir, { recursive: true });
600049
- writeFileSync55(archeFile, JSON.stringify(variants, null, 2));
600742
+ mkdirSync64(archeDir, { recursive: true });
600743
+ writeFileSync56(archeFile, JSON.stringify(variants, null, 2));
600050
600744
  } catch {
600051
600745
  }
600052
600746
  } catch {
@@ -600135,13 +600829,13 @@ __export(run_exports, {
600135
600829
  });
600136
600830
  import { resolve as resolve40 } from "node:path";
600137
600831
  import { spawn as spawn26 } from "node:child_process";
600138
- import { mkdirSync as mkdirSync64, writeFileSync as writeFileSync56, readFileSync as readFileSync81, readdirSync as readdirSync36, existsSync as existsSync100 } from "node:fs";
600139
- import { randomBytes as randomBytes23 } from "node:crypto";
600140
- import { join as join116 } from "node:path";
600832
+ import { mkdirSync as mkdirSync65, writeFileSync as writeFileSync57, readFileSync as readFileSync82, readdirSync as readdirSync36, existsSync as existsSync101 } from "node:fs";
600833
+ import { randomBytes as randomBytes24 } from "node:crypto";
600834
+ import { join as join117 } from "node:path";
600141
600835
  function jobsDir2(repoPath) {
600142
600836
  const root = resolve40(repoPath ?? process.cwd());
600143
- const dir = join116(root, ".oa", "jobs");
600144
- mkdirSync64(dir, { recursive: true });
600837
+ const dir = join117(root, ".oa", "jobs");
600838
+ mkdirSync65(dir, { recursive: true });
600145
600839
  return dir;
600146
600840
  }
600147
600841
  async function runCommand(opts, config) {
@@ -600231,7 +600925,7 @@ function extractSummary(captured) {
600231
600925
  return lines.slice(-3).join(" ").slice(0, 200);
600232
600926
  }
600233
600927
  async function runBackground(task, config, opts) {
600234
- const id = `job-${randomBytes23(3).toString("hex")}`;
600928
+ const id = `job-${randomBytes24(3).toString("hex")}`;
600235
600929
  const dir = jobsDir2(opts.repoPath);
600236
600930
  const repoRoot = resolve40(opts.repoPath ?? process.cwd());
600237
600931
  const job = {
@@ -600260,7 +600954,7 @@ async function runBackground(task, config, opts) {
600260
600954
  }
600261
600955
  });
600262
600956
  job.pid = child.pid ?? 0;
600263
- writeFileSync56(join116(dir, `${id}.json`), JSON.stringify(job, null, 2));
600957
+ writeFileSync57(join117(dir, `${id}.json`), JSON.stringify(job, null, 2));
600264
600958
  let output = "";
600265
600959
  child.stdout?.on("data", (chunk) => {
600266
600960
  output += chunk.toString();
@@ -600276,7 +600970,7 @@ async function runBackground(task, config, opts) {
600276
600970
  job.summary = result.summary;
600277
600971
  job.durationMs = result.durationMs;
600278
600972
  job.error = result.error;
600279
- writeFileSync56(join116(dir, `${id}.json`), JSON.stringify(job, null, 2));
600973
+ writeFileSync57(join117(dir, `${id}.json`), JSON.stringify(job, null, 2));
600280
600974
  } catch {
600281
600975
  }
600282
600976
  });
@@ -600292,13 +600986,13 @@ async function runBackground(task, config, opts) {
600292
600986
  }
600293
600987
  function statusCommand(jobId, repoPath) {
600294
600988
  const dir = jobsDir2(repoPath);
600295
- const file = join116(dir, `${jobId}.json`);
600296
- if (!existsSync100(file)) {
600989
+ const file = join117(dir, `${jobId}.json`);
600990
+ if (!existsSync101(file)) {
600297
600991
  console.error(`Job not found: ${jobId}`);
600298
600992
  console.log(`Available jobs: oa jobs`);
600299
600993
  process.exit(1);
600300
600994
  }
600301
- const job = JSON.parse(readFileSync81(file, "utf-8"));
600995
+ const job = JSON.parse(readFileSync82(file, "utf-8"));
600302
600996
  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`;
600303
600997
  const icon = job.status === "completed" ? "✓" : job.status === "failed" ? "✗" : "●";
600304
600998
  console.log(`${icon} ${job.id} [${job.status}] ${runtime}`);
@@ -600319,7 +601013,7 @@ function jobsCommand(repoPath) {
600319
601013
  console.log("Jobs:");
600320
601014
  for (const file of files) {
600321
601015
  try {
600322
- const job = JSON.parse(readFileSync81(join116(dir, file), "utf-8"));
601016
+ const job = JSON.parse(readFileSync82(join117(dir, file), "utf-8"));
600323
601017
  const icon = job.status === "completed" ? "✓" : job.status === "failed" ? "✗" : "●";
600324
601018
  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`;
600325
601019
  const cleanListTask = cleanForStorage(job.task) || job.task;
@@ -600343,13 +601037,13 @@ __export(index_repo_exports, {
600343
601037
  indexRepoCommand: () => indexRepoCommand
600344
601038
  });
600345
601039
  import { resolve as resolve41 } from "node:path";
600346
- import { existsSync as existsSync101, statSync as statSync29 } from "node:fs";
601040
+ import { existsSync as existsSync102, statSync as statSync29 } from "node:fs";
600347
601041
  import { cwd as cwd2 } from "node:process";
600348
601042
  async function indexRepoCommand(opts, _config3) {
600349
601043
  const repoRoot = resolve41(opts.repoPath ?? cwd2());
600350
601044
  printHeader("Index Repository");
600351
601045
  printInfo(`Indexing: ${repoRoot}`);
600352
- if (!existsSync101(repoRoot)) {
601046
+ if (!existsSync102(repoRoot)) {
600353
601047
  printError(`Path does not exist: ${repoRoot}`);
600354
601048
  process.exit(1);
600355
601049
  }
@@ -600601,8 +601295,8 @@ var config_exports2 = {};
600601
601295
  __export(config_exports2, {
600602
601296
  configCommand: () => configCommand
600603
601297
  });
600604
- import { join as join117, resolve as resolve42 } from "node:path";
600605
- import { homedir as homedir42 } from "node:os";
601298
+ import { join as join118, resolve as resolve42 } from "node:path";
601299
+ import { homedir as homedir43 } from "node:os";
600606
601300
  import { cwd as cwd3 } from "node:process";
600607
601301
  function redactIfSensitive(key, value2) {
600608
601302
  if (SENSITIVE_KEYS.has(key) && typeof value2 === "string" && value2.length > 0) {
@@ -600683,7 +601377,7 @@ function handleShow(opts, config) {
600683
601377
  }
600684
601378
  }
600685
601379
  printSection("Config File");
600686
- printInfo(`~/.open-agents/config.json (${join117(homedir42(), ".open-agents", "config.json")})`);
601380
+ printInfo(`~/.open-agents/config.json (${join118(homedir43(), ".open-agents", "config.json")})`);
600687
601381
  printSection("Priority Chain");
600688
601382
  printInfo(" 1. CLI flags (--model, --backend-url, etc.)");
600689
601383
  printInfo(" 2. Project .oa/settings.json (--local)");
@@ -600722,7 +601416,7 @@ function handleSet(opts, _config3) {
600722
601416
  const coerced = coerceForSettings(key, value2);
600723
601417
  saveProjectSettings(repoRoot, { [key]: coerced });
600724
601418
  printSuccess(`Project override set: ${key} = ${redactIfSensitive(key, value2)}`);
600725
- printInfo(`Saved to ${join117(repoRoot, ".oa", "settings.json")}`);
601419
+ printInfo(`Saved to ${join118(repoRoot, ".oa", "settings.json")}`);
600726
601420
  printInfo("This override applies only when running in this workspace.");
600727
601421
  } catch (err) {
600728
601422
  printError(`Failed to save: ${err instanceof Error ? err.message : String(err)}`);
@@ -600906,8 +601600,8 @@ __export(eval_exports, {
600906
601600
  evalCommand: () => evalCommand
600907
601601
  });
600908
601602
  import { tmpdir as tmpdir21 } from "node:os";
600909
- import { mkdirSync as mkdirSync65, writeFileSync as writeFileSync57 } from "node:fs";
600910
- import { join as join118 } from "node:path";
601603
+ import { mkdirSync as mkdirSync66, writeFileSync as writeFileSync58 } from "node:fs";
601604
+ import { join as join119 } from "node:path";
600911
601605
  async function evalCommand(opts, config) {
600912
601606
  const suiteName = opts.suite ?? "basic";
600913
601607
  const suite = SUITES[suiteName];
@@ -601036,10 +601730,10 @@ async function evalCommand(opts, config) {
601036
601730
  process.exit(failed > 0 ? 1 : 0);
601037
601731
  }
601038
601732
  function createTempEvalRepo() {
601039
- const dir = join118(tmpdir21(), `open-agents-eval-${Date.now()}`);
601040
- mkdirSync65(dir, { recursive: true });
601041
- writeFileSync57(
601042
- join118(dir, "package.json"),
601733
+ const dir = join119(tmpdir21(), `open-agents-eval-${Date.now()}`);
601734
+ mkdirSync66(dir, { recursive: true });
601735
+ writeFileSync58(
601736
+ join119(dir, "package.json"),
601043
601737
  JSON.stringify({ name: "eval-repo", version: "0.0.0" }, null, 2) + "\n",
601044
601738
  "utf8"
601045
601739
  );
@@ -601103,7 +601797,7 @@ init_typed_node_events();
601103
601797
  import { parseArgs as nodeParseArgs2 } from "node:util";
601104
601798
  import { createRequire as createRequire6 } from "node:module";
601105
601799
  import { fileURLToPath as fileURLToPath19 } from "node:url";
601106
- import { dirname as dirname37, join as join119 } from "node:path";
601800
+ import { dirname as dirname37, join as join120 } from "node:path";
601107
601801
 
601108
601802
  // packages/cli/src/cli.ts
601109
601803
  init_typed_node_events();
@@ -601243,7 +601937,7 @@ init_output();
601243
601937
  function getVersion5() {
601244
601938
  try {
601245
601939
  const require4 = createRequire6(import.meta.url);
601246
- const pkgPath = join119(dirname37(fileURLToPath19(import.meta.url)), "..", "package.json");
601940
+ const pkgPath = join120(dirname37(fileURLToPath19(import.meta.url)), "..", "package.json");
601247
601941
  const pkg = require4(pkgPath);
601248
601942
  return pkg.version;
601249
601943
  } catch {
@@ -601557,12 +602251,12 @@ function crashLog(label, err) {
601557
602251
  const logLine = `[${timestamp}] ${label}: ${msg}
601558
602252
  `;
601559
602253
  try {
601560
- const { appendFileSync: appendFileSync9, mkdirSync: mkdirSync66 } = __require("node:fs");
601561
- const { join: join120 } = __require("node:path");
601562
- const { homedir: homedir43 } = __require("node:os");
601563
- const logDir = join120(homedir43(), ".open-agents");
601564
- mkdirSync66(logDir, { recursive: true });
601565
- appendFileSync9(join120(logDir, "crash.log"), logLine);
602254
+ const { appendFileSync: appendFileSync9, mkdirSync: mkdirSync67 } = __require("node:fs");
602255
+ const { join: join121 } = __require("node:path");
602256
+ const { homedir: homedir44 } = __require("node:os");
602257
+ const logDir = join121(homedir44(), ".open-agents");
602258
+ mkdirSync67(logDir, { recursive: true });
602259
+ appendFileSync9(join121(logDir, "crash.log"), logLine);
601566
602260
  } catch {
601567
602261
  }
601568
602262
  try {