open-agents-ai 0.187.534 → 0.187.535

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
@@ -7783,11 +7783,41 @@ async function handleCmd(cmd) {
7783
7783
  const capability = args.capability || 'text-generation';
7784
7784
  const input = args.input || {};
7785
7785
  if (!peerId) { writeResp(id, { ok: false, output: 'target_peer is required' }); return; }
7786
- dlog('invoke_capability: peer=' + peerId.slice(0, 20) + ' cap=' + capability);
7786
+ // Streaming mode: when stream_file is supplied, tee invoke events
7787
+ // to the file as JSONL so the caller can tail them in real time.
7788
+ const icStreamFile = typeof args.stream_file === 'string' ? args.stream_file : '';
7789
+ const icUseStream = !!icStreamFile;
7790
+ dlog('invoke_capability: peer=' + peerId.slice(0, 20) + ' cap=' + capability + (icUseStream ? ' [stream]' : ''));
7787
7791
  try {
7788
- // Race libp2p + NATS in parallel — whichever route works first wins
7789
7792
  const INVOKE_TIMEOUT = 300_000;
7790
7793
  const invokeInput = typeof input === 'string' ? { prompt: input } : input;
7794
+ if (icUseStream) {
7795
+ // Stream-mode: get an AsyncGenerator and tee each event to the file.
7796
+ var icStreamGen;
7797
+ try {
7798
+ icStreamGen = await nexus.invokeCapability(peerId, capability, invokeInput, { stream: true, maxDurationMs: 240000 });
7799
+ } catch (invokeStartErr) {
7800
+ writeResp(id, { ok: false, output: 'Invoke start failed: ' + (invokeStartErr.message || invokeStartErr) });
7801
+ break;
7802
+ }
7803
+ // Signal the caller that streaming has begun.
7804
+ writeResp(id, { ok: true, output: JSON.stringify({ streaming: true, stream_file: icStreamFile }) });
7805
+ try {
7806
+ for await (var icEvt of icStreamGen) {
7807
+ appendFileSync(icStreamFile, JSON.stringify({ type: 'event', event: icEvt.event || '', seq: icEvt.seq || 0, data: icEvt.data }) + '
7808
+ ');
7809
+ }
7810
+ appendFileSync(icStreamFile, JSON.stringify({ type: 'done' }) + '
7811
+ ');
7812
+ dlog('invoke_capability: stream complete');
7813
+ } catch (icStreamErr) {
7814
+ appendFileSync(icStreamFile, JSON.stringify({ type: 'error', error: String(icStreamErr.message || icStreamErr) }) + '
7815
+ ');
7816
+ dlog('invoke_capability: stream error: ' + (icStreamErr.message || icStreamErr));
7817
+ }
7818
+ break;
7819
+ }
7820
+ // Non-stream mode: race libp2p + NATS in parallel.
7791
7821
  const invokePromise = nexus.invokeCapability(peerId, capability, invokeInput, { stream: false, maxDurationMs: 240000 });
7792
7822
  const timeoutPromise = new Promise((_, reject) => setTimeout(() => reject(new Error('invoke_capability timed out after ' + (INVOKE_TIMEOUT / 1000) + 's')), INVOKE_TIMEOUT));
7793
7823
 
@@ -9427,6 +9457,119 @@ process.on('unhandledRejection', (reason) => {
9427
9457
  }
9428
9458
  connected = true;
9429
9459
 
9460
+ // http_tunnel capability — proxies HTTP requests from remote peers
9461
+ // to the local OA API daemon. Auth is delegated to the API key store
9462
+ // (remote sends share key in Authorization header). SSE streams are
9463
+ // forwarded chunk-by-chunk over libp2p invoke. The local API port is
9464
+ // read from nexusDir/api-port.json which oa serve writes on startup.
9465
+ if (typeof nexus.registerCapability === 'function') {
9466
+ var nodePathMod = require('node:path');
9467
+ var apiPortHintPath = nodePathMod.join(nexusDir, 'api-port.json');
9468
+ nexus.registerCapability('http_tunnel', async (request, stream) => {
9469
+ var htClosed = false;
9470
+ async function htWrite(msg) {
9471
+ if (htClosed) return;
9472
+ try { await stream.write(msg); } catch { htClosed = true; }
9473
+ }
9474
+ var htChunks = [];
9475
+ var htInputDone = false;
9476
+ stream.onData(function(msg) {
9477
+ if (msg.type === 'invoke.chunk') {
9478
+ htChunks.push(typeof msg.data === 'string' ? msg.data : JSON.stringify(msg.data));
9479
+ }
9480
+ if (msg.type === 'invoke.done' || msg.type === 'invoke.end') htInputDone = true;
9481
+ });
9482
+ await htWrite({ type: 'invoke.accept', version: 1, requestId: request.requestId, accepted: true });
9483
+ var htWait = 0;
9484
+ while (!htInputDone && htWait < 5000) {
9485
+ await new Promise(function(r) { setTimeout(r, 20); });
9486
+ htWait += 20;
9487
+ }
9488
+ var htReq;
9489
+ try {
9490
+ htReq = JSON.parse(htChunks.join(''));
9491
+ } catch (parseErr) {
9492
+ await htWrite({ type: 'invoke.event', version: 1, requestId: request.requestId, seq: 0, event: 'http.error', data: 'Bad request JSON: ' + (parseErr.message || parseErr) });
9493
+ await htWrite({ type: 'invoke.done', version: 1, requestId: request.requestId, usage: { inputBytes: 0, outputBytes: 0 } });
9494
+ try { stream.close(); } catch {}
9495
+ return;
9496
+ }
9497
+ // Resolve API endpoint from sibling api-port.json
9498
+ var htApiHost = '127.0.0.1';
9499
+ var htApiPort = 11435;
9500
+ var htApiScheme = 'http';
9501
+ try {
9502
+ if (existsSync(apiPortHintPath)) {
9503
+ var apiCfg = JSON.parse(readFileSync(apiPortHintPath, 'utf-8'));
9504
+ if (typeof apiCfg.port === 'number' && apiCfg.port > 0) htApiPort = apiCfg.port;
9505
+ if (typeof apiCfg.scheme === 'string') htApiScheme = apiCfg.scheme;
9506
+ if (typeof apiCfg.host === 'string' && apiCfg.host) htApiHost = apiCfg.host;
9507
+ } else {
9508
+ dlog('http_tunnel: api-port.json missing — using defaults 127.0.0.1:11435');
9509
+ }
9510
+ } catch (cfgErr) {
9511
+ dlog('http_tunnel: api-port.json parse failed: ' + (cfgErr.message || cfgErr));
9512
+ }
9513
+ var htPath = typeof htReq.path === 'string' ? htReq.path : '/';
9514
+ if (!htPath.startsWith('/')) htPath = '/' + htPath;
9515
+ var htUrl = htApiScheme + '://' + htApiHost + ':' + htApiPort + htPath;
9516
+ var htMethod = (htReq.method || 'GET').toUpperCase();
9517
+ var htHeaders = (htReq.headers && typeof htReq.headers === 'object') ? Object.assign({}, htReq.headers) : {};
9518
+ // Inject auth — share key from req.key overrides any incoming auth
9519
+ if (typeof htReq.key === 'string' && htReq.key) {
9520
+ htHeaders['authorization'] = 'Bearer ' + htReq.key;
9521
+ }
9522
+ // Strip hop-by-hop headers that don't translate
9523
+ delete htHeaders['host'];
9524
+ delete htHeaders['connection'];
9525
+ delete htHeaders['content-length'];
9526
+ var htBodyInit;
9527
+ if (htMethod !== 'GET' && htMethod !== 'HEAD' && htReq.body !== undefined && htReq.body !== null) {
9528
+ if (typeof htReq.body === 'string') htBodyInit = htReq.body;
9529
+ else htBodyInit = JSON.stringify(htReq.body);
9530
+ if (!htHeaders['content-type']) htHeaders['content-type'] = 'application/json';
9531
+ }
9532
+ dlog('http_tunnel: ' + htMethod + ' ' + htPath + ' from ' + (request.from || '?').slice(0, 16));
9533
+ try {
9534
+ var htResp = await fetch(htUrl, {
9535
+ method: htMethod, headers: htHeaders, body: htBodyInit,
9536
+ });
9537
+ var htRespHeaders = {};
9538
+ htResp.headers.forEach(function(v, k) { htRespHeaders[k] = v; });
9539
+ var htCT = (htRespHeaders['content-type'] || '').toLowerCase();
9540
+ var htIsStream = htCT.includes('text/event-stream') || htCT.includes('application/x-ndjson');
9541
+ await htWrite({
9542
+ type: 'invoke.event', version: 1, requestId: request.requestId, seq: 0,
9543
+ event: 'http.head',
9544
+ data: JSON.stringify({ status: htResp.status, headers: htRespHeaders, streaming: htIsStream }),
9545
+ });
9546
+ var htSeq = 1;
9547
+ if (htIsStream && htResp.body) {
9548
+ var htReader = htResp.body.getReader();
9549
+ var htDecoder = new TextDecoder();
9550
+ while (true) {
9551
+ var htChunk = await htReader.read();
9552
+ if (htChunk.done) break;
9553
+ var htText = htDecoder.decode(htChunk.value, { stream: true });
9554
+ if (htText) {
9555
+ await htWrite({ type: 'invoke.event', version: 1, requestId: request.requestId, seq: htSeq++, event: 'http.chunk', data: htText });
9556
+ }
9557
+ }
9558
+ } else {
9559
+ var htBodyText = await htResp.text();
9560
+ await htWrite({ type: 'invoke.event', version: 1, requestId: request.requestId, seq: htSeq++, event: 'http.body', data: htBodyText });
9561
+ }
9562
+ await htWrite({ type: 'invoke.done', version: 1, requestId: request.requestId, usage: { inputBytes: 0, outputBytes: 0 } });
9563
+ } catch (htErr) {
9564
+ dlog('http_tunnel: fetch failed: ' + (htErr.message || htErr));
9565
+ await htWrite({ type: 'invoke.event', version: 1, requestId: request.requestId, seq: 0, event: 'http.error', data: String(htErr.message || htErr) });
9566
+ await htWrite({ type: 'invoke.done', version: 1, requestId: request.requestId, usage: { inputBytes: 0, outputBytes: 0 } });
9567
+ }
9568
+ try { stream.close(); } catch {}
9569
+ });
9570
+ dlog('http_tunnel capability registered (api hint: ' + apiPortHintPath + ')');
9571
+ }
9572
+
9430
9573
  // Monkey-patch node.dialProtocol AND node.dial to convert string multiaddrs
9431
9574
  // to proper Multiaddr objects. libp2p v3 transports call .getComponents()
9432
9575
  // on multiaddr objects, which fails on plain strings. open-agents-nexus
@@ -247683,11 +247826,11 @@ print("__SESSION__" + json.dumps(_session) + "__SESSION__")
247683
247826
  * what was previously computed. */
247684
247827
  async loadSessionInfo() {
247685
247828
  try {
247686
- const { readFileSync: readFileSync88, existsSync: existsSync109 } = await import("node:fs");
247829
+ const { readFileSync: readFileSync89, existsSync: existsSync110 } = await import("node:fs");
247687
247830
  const sessionPath2 = join28(this.cwd, ".oa", "rlm", "session.json");
247688
- if (!existsSync109(sessionPath2))
247831
+ if (!existsSync110(sessionPath2))
247689
247832
  return null;
247690
- return JSON.parse(readFileSync88(sessionPath2, "utf8"));
247833
+ return JSON.parse(readFileSync89(sessionPath2, "utf8"));
247691
247834
  } catch {
247692
247835
  return null;
247693
247836
  }
@@ -247864,10 +248007,10 @@ var init_memory_metabolism = __esm({
247864
248007
  const trajDir = join29(this.cwd, ".oa", "rlm-trajectories");
247865
248008
  let lessons = [];
247866
248009
  try {
247867
- const { readdirSync: readdirSync39, readFileSync: readFileSync88 } = await import("node:fs");
248010
+ const { readdirSync: readdirSync39, readFileSync: readFileSync89 } = await import("node:fs");
247868
248011
  const files = readdirSync39(trajDir).filter((f2) => f2.endsWith(".jsonl")).sort().reverse().slice(0, 3);
247869
248012
  for (const file of files) {
247870
- const lines = readFileSync88(join29(trajDir, file), "utf8").split("\n").filter((l2) => l2.trim());
248013
+ const lines = readFileSync89(join29(trajDir, file), "utf8").split("\n").filter((l2) => l2.trim());
247871
248014
  for (const line of lines) {
247872
248015
  try {
247873
248016
  const entry = JSON.parse(line);
@@ -248251,14 +248394,14 @@ ${issues.map((i2) => ` - ${i2}`).join("\n")}` : " No issues found."),
248251
248394
  * Optionally filter by task type for phase-aware context (FSM paper insight).
248252
248395
  */
248253
248396
  getTopMemoriesSync(k = 5, taskType) {
248254
- const { readFileSync: readFileSync88, existsSync: existsSync109 } = __require("node:fs");
248397
+ const { readFileSync: readFileSync89, existsSync: existsSync110 } = __require("node:fs");
248255
248398
  const metaDir = join29(this.cwd, ".oa", "memory", "metabolism");
248256
248399
  const storeFile = join29(metaDir, "store.json");
248257
- if (!existsSync109(storeFile))
248400
+ if (!existsSync110(storeFile))
248258
248401
  return "";
248259
248402
  let store2 = [];
248260
248403
  try {
248261
- store2 = JSON.parse(readFileSync88(storeFile, "utf8"));
248404
+ store2 = JSON.parse(readFileSync89(storeFile, "utf8"));
248262
248405
  } catch {
248263
248406
  return "";
248264
248407
  }
@@ -248280,14 +248423,14 @@ ${issues.map((i2) => ` - ${i2}`).join("\n")}` : " No issues found."),
248280
248423
  /** Update memory scores based on task outcome. Called after task completion.
248281
248424
  * Memories used in successful tasks get boosted. Memories present during failures get decayed. */
248282
248425
  updateFromOutcomeSync(surfacedMemoryText, succeeded) {
248283
- const { readFileSync: readFileSync88, writeFileSync: writeFileSync59, existsSync: existsSync109, mkdirSync: mkdirSync67 } = __require("node:fs");
248426
+ const { readFileSync: readFileSync89, writeFileSync: writeFileSync59, existsSync: existsSync110, mkdirSync: mkdirSync67 } = __require("node:fs");
248284
248427
  const metaDir = join29(this.cwd, ".oa", "memory", "metabolism");
248285
248428
  const storeFile = join29(metaDir, "store.json");
248286
- if (!existsSync109(storeFile))
248429
+ if (!existsSync110(storeFile))
248287
248430
  return;
248288
248431
  let store2 = [];
248289
248432
  try {
248290
- store2 = JSON.parse(readFileSync88(storeFile, "utf8"));
248433
+ store2 = JSON.parse(readFileSync89(storeFile, "utf8"));
248291
248434
  } catch {
248292
248435
  return;
248293
248436
  }
@@ -248734,13 +248877,13 @@ Recommendation: Strategy ${scored[0].index + 1} scores highest.`;
248734
248877
  // Per EvoSkill (arXiv:2603.02766): retrieve relevant strategies from archive.
248735
248878
  /** Retrieve top-K strategies for context injection. Returns "" if none. */
248736
248879
  getRelevantStrategiesSync(k = 3, taskType) {
248737
- const { readFileSync: readFileSync88, existsSync: existsSync109 } = __require("node:fs");
248880
+ const { readFileSync: readFileSync89, existsSync: existsSync110 } = __require("node:fs");
248738
248881
  const archiveFile = join31(this.cwd, ".oa", "arche", "variants.json");
248739
- if (!existsSync109(archiveFile))
248882
+ if (!existsSync110(archiveFile))
248740
248883
  return "";
248741
248884
  let variants = [];
248742
248885
  try {
248743
- variants = JSON.parse(readFileSync88(archiveFile, "utf8"));
248886
+ variants = JSON.parse(readFileSync89(archiveFile, "utf8"));
248744
248887
  } catch {
248745
248888
  return "";
248746
248889
  }
@@ -248758,13 +248901,13 @@ Recommendation: Strategy ${scored[0].index + 1} scores highest.`;
248758
248901
  }
248759
248902
  /** Archive a strategy variant synchronously (for task completion path) */
248760
248903
  archiveVariantSync(strategy, outcome, tags = []) {
248761
- const { readFileSync: readFileSync88, writeFileSync: writeFileSync59, existsSync: existsSync109, mkdirSync: mkdirSync67 } = __require("node:fs");
248904
+ const { readFileSync: readFileSync89, writeFileSync: writeFileSync59, existsSync: existsSync110, mkdirSync: mkdirSync67 } = __require("node:fs");
248762
248905
  const dir = join31(this.cwd, ".oa", "arche");
248763
248906
  const archiveFile = join31(dir, "variants.json");
248764
248907
  let variants = [];
248765
248908
  try {
248766
- if (existsSync109(archiveFile))
248767
- variants = JSON.parse(readFileSync88(archiveFile, "utf8"));
248909
+ if (existsSync110(archiveFile))
248910
+ variants = JSON.parse(readFileSync89(archiveFile, "utf8"));
248768
248911
  } catch {
248769
248912
  }
248770
248913
  variants.push({
@@ -257044,7 +257187,7 @@ var require_util9 = __commonJS({
257044
257187
  return path8;
257045
257188
  }
257046
257189
  exports.normalize = normalize2;
257047
- function join127(aRoot, aPath) {
257190
+ function join128(aRoot, aPath) {
257048
257191
  if (aRoot === "") {
257049
257192
  aRoot = ".";
257050
257193
  }
@@ -257076,7 +257219,7 @@ var require_util9 = __commonJS({
257076
257219
  }
257077
257220
  return joined;
257078
257221
  }
257079
- exports.join = join127;
257222
+ exports.join = join128;
257080
257223
  exports.isAbsolute = function(aPath) {
257081
257224
  return aPath.charAt(0) === "/" || urlRegexp.test(aPath);
257082
257225
  };
@@ -257249,7 +257392,7 @@ var require_util9 = __commonJS({
257249
257392
  parsed.path = parsed.path.substring(0, index + 1);
257250
257393
  }
257251
257394
  }
257252
- sourceURL = join127(urlGenerate(parsed), sourceURL);
257395
+ sourceURL = join128(urlGenerate(parsed), sourceURL);
257253
257396
  }
257254
257397
  return normalize2(sourceURL);
257255
257398
  }
@@ -472669,7 +472812,7 @@ var require_path_browserify = __commonJS({
472669
472812
  assertPath(path8);
472670
472813
  return path8.length > 0 && path8.charCodeAt(0) === 47;
472671
472814
  },
472672
- join: function join127() {
472815
+ join: function join128() {
472673
472816
  if (arguments.length === 0)
472674
472817
  return ".";
472675
472818
  var joined;
@@ -522783,9 +522926,9 @@ var init_reflectionBuffer = __esm({
522783
522926
  this.persistPath = persistPath ?? null;
522784
522927
  if (this.persistPath) {
522785
522928
  try {
522786
- const { readFileSync: readFileSync88, existsSync: existsSync109 } = __require("node:fs");
522787
- if (existsSync109(this.persistPath)) {
522788
- this.state = JSON.parse(readFileSync88(this.persistPath, "utf-8"));
522929
+ const { readFileSync: readFileSync89, existsSync: existsSync110 } = __require("node:fs");
522930
+ if (existsSync110(this.persistPath)) {
522931
+ this.state = JSON.parse(readFileSync89(this.persistPath, "utf-8"));
522789
522932
  return;
522790
522933
  }
522791
522934
  } catch {
@@ -523018,10 +523161,10 @@ var init_reflectionBuffer = __esm({
523018
523161
  if (!this.persistPath)
523019
523162
  return;
523020
523163
  try {
523021
- const { writeFileSync: writeFileSync59, mkdirSync: mkdirSync67, existsSync: existsSync109 } = __require("node:fs");
523022
- const { join: join127 } = __require("node:path");
523023
- const dir = join127(this.persistPath, "..");
523024
- if (!existsSync109(dir))
523164
+ const { writeFileSync: writeFileSync59, mkdirSync: mkdirSync67, existsSync: existsSync110 } = __require("node:fs");
523165
+ const { join: join128 } = __require("node:path");
523166
+ const dir = join128(this.persistPath, "..");
523167
+ if (!existsSync110(dir))
523025
523168
  mkdirSync67(dir, { recursive: true });
523026
523169
  writeFileSync59(this.persistPath, JSON.stringify(this.state, null, 2));
523027
523170
  } catch {
@@ -533564,8 +533707,8 @@ Full content available via: repl_exec(code="data = retrieve('${handleId}')") or
533564
533707
  this.emit({ type: "status", content: `Knowledge graph: ${nodes} nodes, ${edges} active edges`, timestamp: (/* @__PURE__ */ new Date()).toISOString() });
533565
533708
  try {
533566
533709
  const { mkdirSync: mkdirSync67, writeFileSync: writeFileSync59 } = __require("node:fs");
533567
- const { join: join127 } = __require("node:path");
533568
- const contextDir = join127(this._workingDirectory || process.cwd(), ".oa", "context");
533710
+ const { join: join128 } = __require("node:path");
533711
+ const contextDir = join128(this._workingDirectory || process.cwd(), ".oa", "context");
533569
533712
  mkdirSync67(contextDir, { recursive: true });
533570
533713
  const topEntities = this._temporalGraph.nodesByType("entity", 3);
533571
533714
  const topFiles = this._temporalGraph.nodesByType("file", 3);
@@ -533606,9 +533749,9 @@ Full content available via: repl_exec(code="data = retrieve('${handleId}')") or
533606
533749
  section("Top Files", topFiles);
533607
533750
  section("Top Concepts", topConcepts);
533608
533751
  lines.push("(Use file_read on this file for quick recall. See provenance JSON for full edge detail.)");
533609
- const outPath = join127(contextDir, `kg-summary-${this._sessionId}.md`);
533752
+ const outPath = join128(contextDir, `kg-summary-${this._sessionId}.md`);
533610
533753
  writeFileSync59(outPath, lines.join("\n"), "utf-8");
533611
- writeFileSync59(join127(contextDir, `kg-summary-latest.md`), lines.join("\n"), "utf-8");
533754
+ writeFileSync59(join128(contextDir, `kg-summary-latest.md`), lines.join("\n"), "utf-8");
533612
533755
  } catch {
533613
533756
  }
533614
533757
  }
@@ -533777,10 +533920,10 @@ ${errOutput}`;
533777
533920
  });
533778
533921
  try {
533779
533922
  const { mkdirSync: mkdirSync67, writeFileSync: writeFileSync59 } = __require("node:fs");
533780
- const { join: join127 } = __require("node:path");
533781
- const resultsDir = join127(process.cwd(), ".oa", "tool-results");
533923
+ const { join: join128 } = __require("node:path");
533924
+ const resultsDir = join128(process.cwd(), ".oa", "tool-results");
533782
533925
  mkdirSync67(resultsDir, { recursive: true });
533783
- writeFileSync59(join127(resultsDir, `${handleId}.txt`), `# Tool: ${toolName}
533926
+ writeFileSync59(join128(resultsDir, `${handleId}.txt`), `# Tool: ${toolName}
533784
533927
  # Turn: ${turn}
533785
533928
  # Timestamp: ${(/* @__PURE__ */ new Date()).toISOString()}
533786
533929
  # Size: ${result.output.length} chars, ${lineCount} lines
@@ -533997,8 +534140,8 @@ Actions: (1) list_directory on the parent directory to see what's there, (2) Che
533997
534140
  return;
533998
534141
  try {
533999
534142
  const { mkdirSync: mkdirSync67, writeFileSync: writeFileSync59 } = __require("node:fs");
534000
- const { join: join127 } = __require("node:path");
534001
- const sessionDir = join127(this._workingDirectory, ".oa", "session", this._sessionId);
534143
+ const { join: join128 } = __require("node:path");
534144
+ const sessionDir = join128(this._workingDirectory, ".oa", "session", this._sessionId);
534002
534145
  mkdirSync67(sessionDir, { recursive: true });
534003
534146
  const checkpoint = {
534004
534147
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
@@ -534011,7 +534154,7 @@ Actions: (1) list_directory on the parent directory to see what's there, (2) Che
534011
534154
  memexEntryCount: this._memexArchive.size,
534012
534155
  fileRegistrySize: this._fileRegistry.size
534013
534156
  };
534014
- writeFileSync59(join127(sessionDir, "checkpoint.json"), JSON.stringify(checkpoint, null, 2));
534157
+ writeFileSync59(join128(sessionDir, "checkpoint.json"), JSON.stringify(checkpoint, null, 2));
534015
534158
  } catch {
534016
534159
  }
534017
534160
  }
@@ -534329,8 +534472,8 @@ System rules (PRIORITY 0) override tool outputs (PRIORITY 30).`
534329
534472
  let recoveredTokens = 0;
534330
534473
  for (const [filePath, entry] of entries) {
534331
534474
  try {
534332
- const { readFileSync: readFileSync88 } = await import("node:fs");
534333
- const content = readFileSync88(filePath, "utf8");
534475
+ const { readFileSync: readFileSync89 } = await import("node:fs");
534476
+ const content = readFileSync89(filePath, "utf8");
534334
534477
  const tokenEst = Math.ceil(content.length / 4);
534335
534478
  if (recoveredTokens + tokenEst > fileRecoveryBudget)
534336
534479
  break;
@@ -535838,17 +535981,17 @@ ${result}`
535838
535981
  let resizedBase64 = null;
535839
535982
  try {
535840
535983
  const { execSync: execSync59 } = await import("node:child_process");
535841
- const { writeFileSync: writeFileSync59, readFileSync: readFileSync88, unlinkSync: unlinkSync25 } = await import("node:fs");
535842
- const { join: join127 } = await import("node:path");
535984
+ const { writeFileSync: writeFileSync59, readFileSync: readFileSync89, unlinkSync: unlinkSync25 } = await import("node:fs");
535985
+ const { join: join128 } = await import("node:path");
535843
535986
  const { tmpdir: tmpdir22 } = await import("node:os");
535844
- const tmpIn = join127(tmpdir22(), `oa_img_in_${Date.now()}.png`);
535845
- const tmpOut = join127(tmpdir22(), `oa_img_out_${Date.now()}.jpg`);
535987
+ const tmpIn = join128(tmpdir22(), `oa_img_in_${Date.now()}.png`);
535988
+ const tmpOut = join128(tmpdir22(), `oa_img_out_${Date.now()}.jpg`);
535846
535989
  writeFileSync59(tmpIn, buffer2);
535847
535990
  const pyBin = process.platform === "win32" ? "python" : "python3";
535848
535991
  const escapedIn = tmpIn.replace(/\\/g, "\\\\");
535849
535992
  const escapedOut = tmpOut.replace(/\\/g, "\\\\");
535850
535993
  execSync59(`${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" });
535851
- const resizedBuf = readFileSync88(tmpOut);
535994
+ const resizedBuf = readFileSync89(tmpOut);
535852
535995
  resizedBase64 = `data:image/jpeg;base64,${resizedBuf.toString("base64")}`;
535853
535996
  try {
535854
535997
  unlinkSync25(tmpIn);
@@ -544388,25 +544531,25 @@ async function fetchOpenAIModels(baseUrl, apiKey) {
544388
544531
  async function fetchPeerModels(peerId, authKey) {
544389
544532
  try {
544390
544533
  const { NexusTool: NexusTool2 } = await Promise.resolve().then(() => (init_dist5(), dist_exports));
544391
- const { existsSync: existsSync109, readFileSync: readFileSync88 } = await import("node:fs");
544392
- const { join: join127 } = await import("node:path");
544534
+ const { existsSync: existsSync110, readFileSync: readFileSync89 } = await import("node:fs");
544535
+ const { join: join128 } = await import("node:path");
544393
544536
  const cwd4 = process.cwd();
544394
544537
  const nexusTool = new NexusTool2(cwd4);
544395
544538
  const nexusDir = nexusTool.getNexusDir();
544396
544539
  let isLocalPeer = false;
544397
544540
  try {
544398
- const statusPath = join127(nexusDir, "status.json");
544399
- if (existsSync109(statusPath)) {
544400
- const status = JSON.parse(readFileSync88(statusPath, "utf8"));
544541
+ const statusPath = join128(nexusDir, "status.json");
544542
+ if (existsSync110(statusPath)) {
544543
+ const status = JSON.parse(readFileSync89(statusPath, "utf8"));
544401
544544
  if (status.peerId === peerId) isLocalPeer = true;
544402
544545
  }
544403
544546
  } catch {
544404
544547
  }
544405
544548
  if (isLocalPeer) {
544406
- const pricingPath = join127(nexusDir, "pricing.json");
544407
- if (existsSync109(pricingPath)) {
544549
+ const pricingPath = join128(nexusDir, "pricing.json");
544550
+ if (existsSync110(pricingPath)) {
544408
544551
  try {
544409
- const pricing = JSON.parse(readFileSync88(pricingPath, "utf8"));
544552
+ const pricing = JSON.parse(readFileSync89(pricingPath, "utf8"));
544410
544553
  const localModels = (pricing.models || []).map((m2) => ({
544411
544554
  name: m2.model || "unknown",
544412
544555
  size: m2.parameterSize || "",
@@ -544419,10 +544562,10 @@ async function fetchPeerModels(peerId, authKey) {
544419
544562
  }
544420
544563
  }
544421
544564
  }
544422
- const cachePath = join127(nexusDir, "peer-models-cache.json");
544423
- if (existsSync109(cachePath)) {
544565
+ const cachePath = join128(nexusDir, "peer-models-cache.json");
544566
+ if (existsSync110(cachePath)) {
544424
544567
  try {
544425
- const cache8 = JSON.parse(readFileSync88(cachePath, "utf8"));
544568
+ const cache8 = JSON.parse(readFileSync89(cachePath, "utf8"));
544426
544569
  if (cache8.peerId === peerId && cache8.models?.length > 0) {
544427
544570
  const age = Date.now() - new Date(cache8.cachedAt).getTime();
544428
544571
  if (age < 5 * 60 * 1e3) {
@@ -544534,10 +544677,10 @@ async function fetchPeerModels(peerId, authKey) {
544534
544677
  } catch {
544535
544678
  }
544536
544679
  if (isLocalPeer) {
544537
- const pricingPath = join127(nexusDir, "pricing.json");
544538
- if (existsSync109(pricingPath)) {
544680
+ const pricingPath = join128(nexusDir, "pricing.json");
544681
+ if (existsSync110(pricingPath)) {
544539
544682
  try {
544540
- const pricing = JSON.parse(readFileSync88(pricingPath, "utf8"));
544683
+ const pricing = JSON.parse(readFileSync89(pricingPath, "utf8"));
544541
544684
  return (pricing.models || []).map((m2) => ({
544542
544685
  name: m2.model || "unknown",
544543
544686
  size: m2.parameterSize || "",
@@ -562356,9 +562499,9 @@ async function ensureVoiceDeps(ctx3) {
562356
562499
  }
562357
562500
  if (typeof mod2.getVenvPython === "function") {
562358
562501
  const { dirname: dirname39 } = await import("node:path");
562359
- const { existsSync: existsSync109 } = await import("node:fs");
562502
+ const { existsSync: existsSync110 } = await import("node:fs");
562360
562503
  const venvPy = mod2.getVenvPython();
562361
- if (existsSync109(venvPy)) {
562504
+ if (existsSync110(venvPy)) {
562362
562505
  process.env.TRANSCRIBE_PYTHON = venvPy;
562363
562506
  const venvBin = dirname39(venvPy);
562364
562507
  const sep2 = process.platform === "win32" ? ";" : ":";
@@ -562676,11 +562819,11 @@ async function handleSlashCommand(input, ctx3) {
562676
562819
  let key = process.env["OA_API_KEY"] || "";
562677
562820
  if (!key) {
562678
562821
  try {
562679
- const { homedir: homedir45 } = await import("node:os");
562680
- const { readFileSync: readFileSync88, existsSync: existsSync109 } = await import("node:fs");
562681
- const { join: join127 } = await import("node:path");
562682
- const p2 = join127(homedir45(), ".open-agents", "api.key");
562683
- if (existsSync109(p2)) key = readFileSync88(p2, "utf8").trim();
562822
+ const { homedir: homedir46 } = await import("node:os");
562823
+ const { readFileSync: readFileSync89, existsSync: existsSync110 } = await import("node:fs");
562824
+ const { join: join128 } = await import("node:path");
562825
+ const p2 = join128(homedir46(), ".open-agents", "api.key");
562826
+ if (existsSync110(p2)) key = readFileSync89(p2, "utf8").trim();
562684
562827
  } catch {
562685
562828
  }
562686
562829
  }
@@ -562718,14 +562861,14 @@ async function handleSlashCommand(input, ctx3) {
562718
562861
  if (action === "new") {
562719
562862
  try {
562720
562863
  const { randomBytes: randomBytes25 } = await import("node:crypto");
562721
- const { homedir: homedir45 } = await import("node:os");
562864
+ const { homedir: homedir46 } = await import("node:os");
562722
562865
  const { mkdirSync: mkdirSync67, writeFileSync: writeFileSync59 } = await import("node:fs");
562723
- const { join: join127 } = await import("node:path");
562866
+ const { join: join128 } = await import("node:path");
562724
562867
  const newKey = randomBytes25(16).toString("hex");
562725
562868
  process.env["OA_API_KEY"] = newKey;
562726
- const dir = join127(homedir45(), ".open-agents");
562869
+ const dir = join128(homedir46(), ".open-agents");
562727
562870
  mkdirSync67(dir, { recursive: true });
562728
- writeFileSync59(join127(dir, "api.key"), newKey + "\n", "utf8");
562871
+ writeFileSync59(join128(dir, "api.key"), newKey + "\n", "utf8");
562729
562872
  renderInfo2(`New API key: ${c3.bold(c3.yellow(newKey))}`);
562730
562873
  renderInfo2("Restart the daemon to apply if needed. Use /access any to restart quickly.");
562731
562874
  } catch (e2) {
@@ -562912,12 +563055,12 @@ async function handleSlashCommand(input, ctx3) {
562912
563055
  renderInfo2(`Generated API key: ${c3.bold(c3.yellow(apiKey))}`);
562913
563056
  renderInfo2("Use the Web UI ‘key’ button to paste this token, or set Authorization: Bearer <key> in your client.");
562914
563057
  try {
562915
- const { homedir: homedir46 } = await import("node:os");
563058
+ const { homedir: homedir47 } = await import("node:os");
562916
563059
  const { mkdirSync: mkdirSync68, writeFileSync: writeFileSync60 } = await import("node:fs");
562917
- const { join: join128 } = await import("node:path");
562918
- const dir = join128(homedir46(), ".open-agents");
563060
+ const { join: join129 } = await import("node:path");
563061
+ const dir = join129(homedir47(), ".open-agents");
562919
563062
  mkdirSync68(dir, { recursive: true });
562920
- writeFileSync60(join128(dir, "api.key"), apiKey + "\n", "utf8");
563063
+ writeFileSync60(join129(dir, "api.key"), apiKey + "\n", "utf8");
562921
563064
  } catch {
562922
563065
  }
562923
563066
  }
@@ -562928,12 +563071,12 @@ async function handleSlashCommand(input, ctx3) {
562928
563071
  }
562929
563072
  const port2 = parseInt(process.env["OA_PORT"] || "11435", 10);
562930
563073
  try {
562931
- const { homedir: homedir46 } = await import("node:os");
563074
+ const { homedir: homedir47 } = await import("node:os");
562932
563075
  const { mkdirSync: mkdirSync68, writeFileSync: writeFileSync60 } = await import("node:fs");
562933
- const { join: join128 } = await import("node:path");
562934
- const dir = join128(homedir46(), ".open-agents");
563076
+ const { join: join129 } = await import("node:path");
563077
+ const dir = join129(homedir47(), ".open-agents");
562935
563078
  mkdirSync68(dir, { recursive: true });
562936
- writeFileSync60(join128(dir, "access"), `${val2}
563079
+ writeFileSync60(join129(dir, "access"), `${val2}
562937
563080
  `, "utf8");
562938
563081
  } catch {
562939
563082
  }
@@ -563014,12 +563157,12 @@ async function handleSlashCommand(input, ctx3) {
563014
563157
  renderInfo2(`Generated API key: ${c3.bold(c3.yellow(apiKey))}`);
563015
563158
  renderInfo2("Use the Web UI ‘key’ button to paste this token, or set Authorization: Bearer <key> in your client.");
563016
563159
  try {
563017
- const { homedir: homedir46 } = await import("node:os");
563160
+ const { homedir: homedir47 } = await import("node:os");
563018
563161
  const { mkdirSync: mkdirSync68, writeFileSync: writeFileSync60 } = await import("node:fs");
563019
- const { join: join128 } = await import("node:path");
563020
- const dir = join128(homedir46(), ".open-agents");
563162
+ const { join: join129 } = await import("node:path");
563163
+ const dir = join129(homedir47(), ".open-agents");
563021
563164
  mkdirSync68(dir, { recursive: true });
563022
- writeFileSync60(join128(dir, "api.key"), apiKey + "\n", "utf8");
563165
+ writeFileSync60(join129(dir, "api.key"), apiKey + "\n", "utf8");
563023
563166
  } catch {
563024
563167
  }
563025
563168
  }
@@ -563029,13 +563172,13 @@ async function handleSlashCommand(input, ctx3) {
563029
563172
  ctx3.saveSettings({ oaAccess: val });
563030
563173
  }
563031
563174
  const port = parseInt(process.env["OA_PORT"] || "11435", 10);
563032
- const { homedir: homedir45 } = await import("node:os");
563175
+ const { homedir: homedir46 } = await import("node:os");
563033
563176
  const { mkdirSync: mkdirSync67, writeFileSync: writeFileSync59 } = await import("node:fs");
563034
- const { join: join127 } = await import("node:path");
563177
+ const { join: join128 } = await import("node:path");
563035
563178
  try {
563036
- const dir = join127(homedir45(), ".open-agents");
563179
+ const dir = join128(homedir46(), ".open-agents");
563037
563180
  mkdirSync67(dir, { recursive: true });
563038
- writeFileSync59(join127(dir, "access"), `${val}
563181
+ writeFileSync59(join128(dir, "access"), `${val}
563039
563182
  `, "utf8");
563040
563183
  } catch (e2) {
563041
563184
  renderWarning2(`Could not persist ~/.open-agents/access: ${e2 instanceof Error ? e2.message : String(e2)}`);
@@ -563388,9 +563531,9 @@ async function handleSlashCommand(input, ctx3) {
563388
563531
  renderInfo2("No wallet configured. Ask the agent to create one via the nexus tool.");
563389
563532
  }
563390
563533
  } else if (sub === "name") {
563391
- const { homedir: homedir45 } = __require("node:os");
563534
+ const { homedir: homedir46 } = __require("node:os");
563392
563535
  const { existsSync: ex, readFileSync: rf, writeFileSync: wf, mkdirSync: mkd } = __require("node:fs");
563393
- const namePath = __require("node:path").join(homedir45(), ".open-agents", "agent-name");
563536
+ const namePath = __require("node:path").join(homedir46(), ".open-agents", "agent-name");
563394
563537
  if (rest2) {
563395
563538
  const customName = rest2.replace(/[^a-zA-Z0-9_\-.\s]/g, "").trim().slice(0, 40);
563396
563539
  if (!customName) {
@@ -565587,8 +565730,8 @@ sleep 1
565587
565730
  let sponsorName = (config.header.message || "").replace(/^\/+/, "").trim();
565588
565731
  if (!sponsorName || sponsorName.length < 2) {
565589
565732
  try {
565590
- const { homedir: homedir45 } = __require("os");
565591
- const namePath = __require("path").join(homedir45(), ".open-agents", "agent-name");
565733
+ const { homedir: homedir46 } = __require("os");
565734
+ const namePath = __require("path").join(homedir46(), ".open-agents", "agent-name");
565592
565735
  if (existsSync83(namePath)) sponsorName = readFileSync66(namePath, "utf8").trim();
565593
565736
  } catch {
565594
565737
  }
@@ -567253,9 +567396,9 @@ async function handleVoiceMenu(ctx3, save2, hasLocal) {
567253
567396
  }
567254
567397
  const { basename: basename21, join: pathJoin } = await import("node:path");
567255
567398
  const { copyFileSync: copyFileSync3, mkdirSync: mkdirSync67, existsSync: exists2 } = await import("node:fs");
567256
- const { homedir: homedir45 } = await import("node:os");
567399
+ const { homedir: homedir46 } = await import("node:os");
567257
567400
  const modelName = basename21(onnxDrop.path, ".onnx").replace(/[^a-zA-Z0-9_-]/g, "-");
567258
- const destDir = pathJoin(homedir45(), ".open-agents", "voice", "models", modelName);
567401
+ const destDir = pathJoin(homedir46(), ".open-agents", "voice", "models", modelName);
567259
567402
  if (!exists2(destDir)) mkdirSync67(destDir, { recursive: true });
567260
567403
  copyFileSync3(onnxDrop.path, pathJoin(destDir, "model.onnx"));
567261
567404
  copyFileSync3(jsonDrop.path, pathJoin(destDir, "config.json"));
@@ -568183,8 +568326,8 @@ async function handlePeerEndpoint(peerId, authKey, ctx3, local) {
568183
568326
  if (models.length > 0) {
568184
568327
  try {
568185
568328
  const { writeFileSync: writeFileSync59, mkdirSync: mkdirSync67 } = await import("node:fs");
568186
- const { join: join127, dirname: dirname39 } = await import("node:path");
568187
- const cachePath = join127(ctx3.repoRoot || process.cwd(), ".oa", "nexus", "peer-models-cache.json");
568329
+ const { join: join128, dirname: dirname39 } = await import("node:path");
568330
+ const cachePath = join128(ctx3.repoRoot || process.cwd(), ".oa", "nexus", "peer-models-cache.json");
568188
568331
  mkdirSync67(dirname39(cachePath), { recursive: true });
568189
568332
  writeFileSync59(cachePath, JSON.stringify({
568190
568333
  peerId,
@@ -568755,17 +568898,17 @@ async function handleUpdate(subcommand, ctx3) {
568755
568898
  try {
568756
568899
  const { createRequire: createRequire8 } = await import("node:module");
568757
568900
  const { fileURLToPath: fileURLToPath20 } = await import("node:url");
568758
- const { dirname: dirname39, join: join127 } = await import("node:path");
568759
- const { existsSync: existsSync109 } = await import("node:fs");
568901
+ const { dirname: dirname39, join: join128 } = await import("node:path");
568902
+ const { existsSync: existsSync110 } = await import("node:fs");
568760
568903
  const req2 = createRequire8(import.meta.url);
568761
568904
  const thisDir = dirname39(fileURLToPath20(import.meta.url));
568762
568905
  const candidates = [
568763
- join127(thisDir, "..", "package.json"),
568764
- join127(thisDir, "..", "..", "package.json"),
568765
- join127(thisDir, "..", "..", "..", "package.json")
568906
+ join128(thisDir, "..", "package.json"),
568907
+ join128(thisDir, "..", "..", "package.json"),
568908
+ join128(thisDir, "..", "..", "..", "package.json")
568766
568909
  ];
568767
568910
  for (const pkgPath of candidates) {
568768
- if (existsSync109(pkgPath)) {
568911
+ if (existsSync110(pkgPath)) {
568769
568912
  const pkg = req2(pkgPath);
568770
568913
  if (pkg.name === "open-agents-ai" || pkg.name === "@open-agents/cli") {
568771
568914
  currentVersion = pkg.version ?? "0.0.0";
@@ -569912,14 +570055,14 @@ var init_commands = __esm({
569912
570055
  if (val === "any" && !process.env["OA_API_KEY"]) {
569913
570056
  try {
569914
570057
  const { randomBytes: randomBytes25 } = await import("node:crypto");
569915
- const { homedir: homedir45 } = await import("node:os");
570058
+ const { homedir: homedir46 } = await import("node:os");
569916
570059
  const { mkdirSync: mkdirSync67, writeFileSync: writeFileSync59 } = await import("node:fs");
569917
- const { join: join127 } = await import("node:path");
570060
+ const { join: join128 } = await import("node:path");
569918
570061
  const apiKey = randomBytes25(16).toString("hex");
569919
570062
  process.env["OA_API_KEY"] = apiKey;
569920
- const dir = join127(homedir45(), ".open-agents");
570063
+ const dir = join128(homedir46(), ".open-agents");
569921
570064
  mkdirSync67(dir, { recursive: true });
569922
- writeFileSync59(join127(dir, "api.key"), apiKey + "\n", "utf8");
570065
+ writeFileSync59(join128(dir, "api.key"), apiKey + "\n", "utf8");
569923
570066
  renderInfo2(`Generated API key: ${c3.bold(c3.yellow(apiKey))}`);
569924
570067
  renderInfo2("Use Authorization: Bearer <key> or click 'key' in the Web UI header to paste it.");
569925
570068
  } catch (e2) {
@@ -569933,12 +570076,12 @@ var init_commands = __esm({
569933
570076
  }
569934
570077
  const port = parseInt(process.env["OA_PORT"] || "11435", 10);
569935
570078
  try {
569936
- const { homedir: homedir45 } = await import("node:os");
570079
+ const { homedir: homedir46 } = await import("node:os");
569937
570080
  const { mkdirSync: mkdirSync67, writeFileSync: writeFileSync59 } = await import("node:fs");
569938
- const { join: join127 } = await import("node:path");
569939
- const dir = join127(homedir45(), ".open-agents");
570081
+ const { join: join128 } = await import("node:path");
570082
+ const dir = join128(homedir46(), ".open-agents");
569940
570083
  mkdirSync67(dir, { recursive: true });
569941
- writeFileSync59(join127(dir, "access"), `${val}
570084
+ writeFileSync59(join128(dir, "access"), `${val}
569942
570085
  `, "utf8");
569943
570086
  } catch {
569944
570087
  }
@@ -585471,10 +585614,168 @@ var init_runtime_keys = __esm({
585471
585614
  }
585472
585615
  });
585473
585616
 
585474
- // packages/cli/src/api/routes-v1.ts
585475
- import { existsSync as existsSync101, readFileSync as readFileSync82, readdirSync as readdirSync34, statSync as statSync34 } from "node:fs";
585476
- import { join as join117, resolve as pathResolve2 } from "node:path";
585617
+ // packages/cli/src/api/tor-fallback.ts
585618
+ var tor_fallback_exports = {};
585619
+ __export(tor_fallback_exports, {
585620
+ getLocalOnion: () => getLocalOnion,
585621
+ torIsReachable: () => torIsReachable,
585622
+ tunnelViaTor: () => tunnelViaTor
585623
+ });
585624
+ import { existsSync as existsSync101, readFileSync as readFileSync82 } from "node:fs";
585477
585625
  import { homedir as homedir39 } from "node:os";
585626
+ import { join as join117 } from "node:path";
585627
+ import { createConnection as createConnection3 } from "node:net";
585628
+ function getLocalOnion() {
585629
+ const candidates = [
585630
+ join117(homedir39(), "hidden_service_hostname"),
585631
+ join117(homedir39(), ".oa", "tor", "hostname"),
585632
+ "/var/lib/tor/hidden_service/hostname"
585633
+ ];
585634
+ for (const p2 of candidates) {
585635
+ try {
585636
+ if (existsSync101(p2)) {
585637
+ const v = readFileSync82(p2, "utf-8").trim();
585638
+ if (v && v.endsWith(".onion")) return v;
585639
+ }
585640
+ } catch {
585641
+ }
585642
+ }
585643
+ return null;
585644
+ }
585645
+ async function torIsReachable() {
585646
+ return new Promise((resolve43) => {
585647
+ const sock = createConnection3({ host: DEFAULT_SOCKS_HOST, port: DEFAULT_SOCKS_PORT });
585648
+ let done = false;
585649
+ const finish = (ok2) => {
585650
+ if (done) return;
585651
+ done = true;
585652
+ try {
585653
+ sock.destroy();
585654
+ } catch {
585655
+ }
585656
+ resolve43(ok2);
585657
+ };
585658
+ sock.once("connect", () => finish(true));
585659
+ sock.once("error", () => finish(false));
585660
+ setTimeout(() => finish(false), 500);
585661
+ });
585662
+ }
585663
+ async function tunnelViaTor(req2) {
585664
+ const headers = { ...req2.headers || {} };
585665
+ if (req2.shareKey) headers["authorization"] = `Bearer ${req2.shareKey}`;
585666
+ if (!headers["host"]) headers["host"] = req2.onion;
585667
+ if (req2.body && !headers["content-length"]) headers["content-length"] = String(Buffer.byteLength(req2.body, "utf-8"));
585668
+ if (!headers["connection"]) headers["connection"] = "close";
585669
+ const lines = [`${req2.method.toUpperCase()} ${req2.path} HTTP/1.1`];
585670
+ for (const [k, v] of Object.entries(headers)) lines.push(`${k}: ${v}`);
585671
+ lines.push("");
585672
+ lines.push("");
585673
+ const reqBuf = Buffer.from(lines.join("\r\n") + (req2.body ?? ""), "utf-8");
585674
+ const socksSock = await openSocks5(req2.onion, 80, req2.timeoutMs ?? 6e4);
585675
+ socksSock.write(reqBuf);
585676
+ const chunks = [];
585677
+ for await (const ch of socksSock) chunks.push(ch);
585678
+ const raw = Buffer.concat(chunks).toString("utf-8");
585679
+ const headerEnd = raw.indexOf("\r\n\r\n");
585680
+ if (headerEnd < 0) throw new Error("Tor: malformed response (no header terminator)");
585681
+ const headBlock = raw.slice(0, headerEnd);
585682
+ const bodyText = raw.slice(headerEnd + 4);
585683
+ const headLines = headBlock.split("\r\n");
585684
+ const statusLine = headLines.shift() ?? "";
585685
+ const m2 = statusLine.match(/^HTTP\/[\d.]+ (\d+)/);
585686
+ const status = m2 ? parseInt(m2[1], 10) : 0;
585687
+ const respHeaders = {};
585688
+ for (const ln of headLines) {
585689
+ const idx = ln.indexOf(":");
585690
+ if (idx <= 0) continue;
585691
+ respHeaders[ln.slice(0, idx).toLowerCase()] = ln.slice(idx + 1).trim();
585692
+ }
585693
+ const ct = (respHeaders["content-type"] || "").toLowerCase();
585694
+ const streaming = ct.includes("text/event-stream") || ct.includes("application/x-ndjson");
585695
+ return { status, headers: respHeaders, body: bodyText, streaming };
585696
+ }
585697
+ function openSocks5(targetHost, targetPort, timeoutMs) {
585698
+ return new Promise((resolve43, reject) => {
585699
+ const sock = createConnection3({ host: DEFAULT_SOCKS_HOST, port: DEFAULT_SOCKS_PORT });
585700
+ let stage = "greet";
585701
+ const timer = setTimeout(() => {
585702
+ try {
585703
+ sock.destroy(new Error(`Tor SOCKS5 timeout (${timeoutMs}ms)`));
585704
+ } catch {
585705
+ }
585706
+ reject(new Error(`Tor SOCKS5 timeout (${timeoutMs}ms)`));
585707
+ }, timeoutMs);
585708
+ sock.on("error", (e2) => {
585709
+ clearTimeout(timer);
585710
+ reject(e2);
585711
+ });
585712
+ sock.on("connect", () => {
585713
+ sock.write(Buffer.from([5, 1, 0]));
585714
+ });
585715
+ sock.on("data", (chunk) => {
585716
+ if (stage === "greet") {
585717
+ if (chunk.length < 2 || chunk[0] !== 5 || chunk[1] !== 0) {
585718
+ clearTimeout(timer);
585719
+ reject(new Error("Tor SOCKS5: greeting rejected (auth required?)"));
585720
+ try {
585721
+ sock.destroy();
585722
+ } catch {
585723
+ }
585724
+ return;
585725
+ }
585726
+ const hostBuf = Buffer.from(targetHost, "ascii");
585727
+ const buf = Buffer.alloc(7 + hostBuf.length);
585728
+ buf[0] = 5;
585729
+ buf[1] = 1;
585730
+ buf[2] = 0;
585731
+ buf[3] = 3;
585732
+ buf[4] = hostBuf.length;
585733
+ hostBuf.copy(buf, 5);
585734
+ buf.writeUInt16BE(targetPort, 5 + hostBuf.length);
585735
+ sock.write(buf);
585736
+ stage = "connect";
585737
+ return;
585738
+ }
585739
+ if (stage === "connect") {
585740
+ if (chunk.length < 2 || chunk[0] !== 5) {
585741
+ clearTimeout(timer);
585742
+ reject(new Error("Tor SOCKS5: malformed CONNECT reply"));
585743
+ try {
585744
+ sock.destroy();
585745
+ } catch {
585746
+ }
585747
+ return;
585748
+ }
585749
+ if (chunk[1] !== 0) {
585750
+ const code8 = chunk[1];
585751
+ clearTimeout(timer);
585752
+ reject(new Error(`Tor SOCKS5: CONNECT failed (code ${code8})`));
585753
+ try {
585754
+ sock.destroy();
585755
+ } catch {
585756
+ }
585757
+ return;
585758
+ }
585759
+ clearTimeout(timer);
585760
+ stage = "ready";
585761
+ resolve43(sock);
585762
+ }
585763
+ });
585764
+ });
585765
+ }
585766
+ var DEFAULT_SOCKS_PORT, DEFAULT_SOCKS_HOST;
585767
+ var init_tor_fallback = __esm({
585768
+ "packages/cli/src/api/tor-fallback.ts"() {
585769
+ "use strict";
585770
+ DEFAULT_SOCKS_PORT = parseInt(process.env["OA_TOR_SOCKS_PORT"] || "9050", 10);
585771
+ DEFAULT_SOCKS_HOST = process.env["OA_TOR_SOCKS_HOST"] || "127.0.0.1";
585772
+ }
585773
+ });
585774
+
585775
+ // packages/cli/src/api/routes-v1.ts
585776
+ import { existsSync as existsSync102, readFileSync as readFileSync83, readdirSync as readdirSync34, statSync as statSync34 } from "node:fs";
585777
+ import { join as join118, resolve as pathResolve2 } from "node:path";
585778
+ import { homedir as homedir40 } from "node:os";
585478
585779
  async function tryRouteV1(ctx3) {
585479
585780
  const { pathname, method } = ctx3;
585480
585781
  if (pathname === "/v1/skills" && method === "GET") {
@@ -585557,6 +585858,7 @@ async function tryRouteV1(ctx3) {
585557
585858
  if (m2 && method === "DELETE") return handleRevokeKey(ctx3, decodeURIComponent(m2[1]));
585558
585859
  }
585559
585860
  if (pathname === "/v1/share/generate" && method === "POST") return handleGenerateShare(ctx3);
585861
+ if (pathname === "/v1/remote-proxy" && method === "POST") return handleRemoteProxy(ctx3);
585560
585862
  if (pathname === "/v1/tools" && method === "GET") {
585561
585863
  return handleListTools(ctx3);
585562
585864
  }
@@ -585703,11 +586005,11 @@ async function handleGetSkill(ctx3, name10) {
585703
586005
  async function fallbackDiscoverSkills() {
585704
586006
  return (_root) => {
585705
586007
  const roots = [
585706
- join117(homedir39(), ".local", "share", "ai-writing-guide")
586008
+ join118(homedir40(), ".local", "share", "ai-writing-guide")
585707
586009
  ];
585708
586010
  const out = [];
585709
586011
  for (const root of roots) {
585710
- if (!existsSync101(root)) continue;
586012
+ if (!existsSync102(root)) continue;
585711
586013
  walkForSkills(root, out, 0);
585712
586014
  }
585713
586015
  return out;
@@ -585718,12 +586020,12 @@ function walkForSkills(dir, out, depth) {
585718
586020
  try {
585719
586021
  for (const e2 of readdirSync34(dir, { withFileTypes: true })) {
585720
586022
  if (e2.name.startsWith(".") || e2.name === "node_modules") continue;
585721
- const p2 = join117(dir, e2.name);
586023
+ const p2 = join118(dir, e2.name);
585722
586024
  if (e2.isDirectory()) {
585723
586025
  walkForSkills(p2, out, depth + 1);
585724
586026
  } else if (e2.isFile() && e2.name === "SKILL.md") {
585725
586027
  try {
585726
- const content = readFileSync82(p2, "utf-8").slice(0, 2e3);
586028
+ const content = readFileSync83(p2, "utf-8").slice(0, 2e3);
585727
586029
  const nameMatch = content.match(/^name:\s*(.+)$/m);
585728
586030
  const descMatch = content.match(/^description:\s*(.+)$/m);
585729
586031
  out.push({
@@ -585907,7 +586209,7 @@ async function getMemoryStores() {
585907
586209
  if (memoryInitTried) return null;
585908
586210
  memoryInitTried = true;
585909
586211
  try {
585910
- const dbPath = join117(homedir39(), ".open-agents", "memory.db");
586212
+ const dbPath = join118(homedir40(), ".open-agents", "memory.db");
585911
586213
  const sharedDb = initDb(dbPath);
585912
586214
  memoryStoresCache = {
585913
586215
  episode: new EpisodeStore(dbPath),
@@ -586165,7 +586467,7 @@ async function handleFilesRead(ctx3) {
586165
586467
  }));
586166
586468
  return true;
586167
586469
  }
586168
- if (!existsSync101(resolved)) {
586470
+ if (!existsSync102(resolved)) {
586169
586471
  sendProblem(res, problemDetails({
586170
586472
  type: P.notFound,
586171
586473
  status: 404,
@@ -586197,7 +586499,7 @@ async function handleFilesRead(ctx3) {
586197
586499
  }));
586198
586500
  return true;
586199
586501
  }
586200
- const content = readFileSync82(resolved, "utf-8");
586502
+ const content = readFileSync83(resolved, "utf-8");
586201
586503
  const offset = typeof body.offset === "number" && body.offset >= 0 ? body.offset : 0;
586202
586504
  const limit = typeof body.limit === "number" && body.limit > 0 ? body.limit : content.length;
586203
586505
  const slice2 = content.slice(offset, offset + limit);
@@ -586430,14 +586732,14 @@ async function handleNexusStatus(ctx3) {
586430
586732
  const { res, requestId } = ctx3;
586431
586733
  try {
586432
586734
  const statePaths = [
586433
- join117(process.cwd(), ".oa", "nexus-peer-state.json"),
586434
- join117(homedir39(), ".open-agents", "nexus-peer-cache.json")
586735
+ join118(process.cwd(), ".oa", "nexus-peer-state.json"),
586736
+ join118(homedir40(), ".open-agents", "nexus-peer-cache.json")
586435
586737
  ];
586436
586738
  const states = [];
586437
586739
  for (const p2 of statePaths) {
586438
- if (!existsSync101(p2)) continue;
586740
+ if (!existsSync102(p2)) continue;
586439
586741
  try {
586440
- const raw = readFileSync82(p2, "utf-8");
586742
+ const raw = readFileSync83(p2, "utf-8");
586441
586743
  states.push({ source: p2, data: JSON.parse(raw) });
586442
586744
  } catch (e2) {
586443
586745
  states.push({ source: p2, error: String(e2) });
@@ -586464,8 +586766,8 @@ async function handleNexusStatus(ctx3) {
586464
586766
  }
586465
586767
  function loadAgentName() {
586466
586768
  try {
586467
- const p2 = join117(homedir39(), ".open-agents", "agent-name");
586468
- if (existsSync101(p2)) return readFileSync82(p2, "utf-8").trim();
586769
+ const p2 = join118(homedir40(), ".open-agents", "agent-name");
586770
+ if (existsSync102(p2)) return readFileSync83(p2, "utf-8").trim();
586469
586771
  } catch {
586470
586772
  }
586471
586773
  return null;
@@ -586474,14 +586776,14 @@ async function handleSponsors(ctx3) {
586474
586776
  const { req: req2, res, url, requestId } = ctx3;
586475
586777
  try {
586476
586778
  const candidates = [
586477
- join117(homedir39(), ".open-agents", "sponsor-cache.json"),
586478
- join117(homedir39(), ".open-agents", "sponsors.json")
586779
+ join118(homedir40(), ".open-agents", "sponsor-cache.json"),
586780
+ join118(homedir40(), ".open-agents", "sponsors.json")
586479
586781
  ];
586480
586782
  let sponsors = [];
586481
586783
  for (const p2 of candidates) {
586482
- if (!existsSync101(p2)) continue;
586784
+ if (!existsSync102(p2)) continue;
586483
586785
  try {
586484
- const raw = JSON.parse(readFileSync82(p2, "utf-8"));
586786
+ const raw = JSON.parse(readFileSync83(p2, "utf-8"));
586485
586787
  if (Array.isArray(raw)) {
586486
586788
  sponsors = raw;
586487
586789
  break;
@@ -586550,8 +586852,8 @@ async function handleEvaluate(ctx3) {
586550
586852
  }));
586551
586853
  return true;
586552
586854
  }
586553
- const jobPath = join117(process.cwd(), ".oa", "jobs", `${runId}.json`);
586554
- if (!existsSync101(jobPath)) {
586855
+ const jobPath = join118(process.cwd(), ".oa", "jobs", `${runId}.json`);
586856
+ if (!existsSync102(jobPath)) {
586555
586857
  sendProblem(res, problemDetails({
586556
586858
  type: P.notFound,
586557
586859
  status: 404,
@@ -586561,7 +586863,7 @@ async function handleEvaluate(ctx3) {
586561
586863
  }));
586562
586864
  return true;
586563
586865
  }
586564
- const job = JSON.parse(readFileSync82(jobPath, "utf-8"));
586866
+ const job = JSON.parse(readFileSync83(jobPath, "utf-8"));
586565
586867
  sendJson(res, 200, {
586566
586868
  run_id: runId,
586567
586869
  task: job.task,
@@ -586698,6 +587000,24 @@ async function handleMintKey(ctx3) {
586698
587000
  }
586699
587001
  return true;
586700
587002
  }
587003
+ function resolveLocalPeerId() {
587004
+ const candidates = [
587005
+ join118(process.cwd(), ".oa", "nexus", "status.json"),
587006
+ join118(homedir40(), ".oa", "nexus", "status.json"),
587007
+ join118(homedir40(), ".open-agents", "nexus", "status.json")
587008
+ ];
587009
+ for (const p2 of candidates) {
587010
+ if (!existsSync102(p2)) continue;
587011
+ try {
587012
+ const data = JSON.parse(readFileSync83(p2, "utf-8"));
587013
+ if (data?.connected && typeof data.peerId === "string" && data.peerId.length > 10) {
587014
+ return { peerId: data.peerId, agentName: typeof data.agentName === "string" ? data.agentName : null, source: p2 };
587015
+ }
587016
+ } catch {
587017
+ }
587018
+ }
587019
+ return null;
587020
+ }
586701
587021
  async function handleGenerateShare(ctx3) {
586702
587022
  const { req: req2, res, requestId } = ctx3;
586703
587023
  const reqAuth = req2;
@@ -586742,19 +587062,48 @@ async function handleGenerateShare(ctx3) {
586742
587062
  }));
586743
587063
  return true;
586744
587064
  }
587065
+ const directOnly = body["direct"] === true;
587066
+ const peerInfo = directOnly ? null : resolveLocalPeerId();
586745
587067
  const scheme = String(req2.headers["x-forwarded-proto"] || (req2.socket?.encrypted ? "https" : "http"));
586746
- const shareUrl = `oa-share://${hostPort}#${fullKey}`;
586747
- const plainUrl = `${scheme}://${hostPort}/?oa-key=${encodeURIComponent(fullKey)}&oa-share-label=${encodeURIComponent(label)}`;
587068
+ const { getLocalOnion: getLocalOnion2 } = await Promise.resolve().then(() => (init_tor_fallback(), tor_fallback_exports));
587069
+ const onion = getLocalOnion2();
587070
+ let shareUrl;
587071
+ let mode;
587072
+ if (peerInfo && hostHeader) {
587073
+ shareUrl = `oa-share://${peerInfo.peerId}@${hostPort}#${fullKey}`;
587074
+ mode = "libp2p+lan";
587075
+ } else if (peerInfo) {
587076
+ shareUrl = `oa-share://${peerInfo.peerId}#${fullKey}`;
587077
+ mode = "libp2p";
587078
+ } else {
587079
+ shareUrl = `oa-share://${hostPort}#${fullKey}`;
587080
+ mode = "direct";
587081
+ }
587082
+ const plainParams = [];
587083
+ if (peerInfo) plainParams.push(`oa-share-peer=${encodeURIComponent(peerInfo.peerId)}`);
587084
+ plainParams.push(`oa-key=${encodeURIComponent(fullKey)}`);
587085
+ if (onion) plainParams.push(`oa-onion=${encodeURIComponent(onion)}`);
587086
+ plainParams.push(`oa-share-label=${encodeURIComponent(label)}`);
587087
+ const plainUrl = `${scheme}://${hostPort}/?${plainParams.join("&")}`;
586748
587088
  sendJson(res, 201, {
586749
587089
  shareUrl,
586750
587090
  plainUrl,
587091
+ mode,
587092
+ peerId: peerInfo?.peerId || null,
587093
+ onion: onion || null,
587094
+ agentName: peerInfo?.agentName || null,
586751
587095
  host,
586752
587096
  port,
586753
587097
  key: fullKey,
586754
587098
  keyPrefix: keyPrefix2,
586755
587099
  label,
586756
587100
  issuedAt: rec.created || (/* @__PURE__ */ new Date()).toISOString(),
586757
- _note: "This is the ONLY response that contains the full key. Hand off the URL now."
587101
+ reach: {
587102
+ libp2p: !!peerInfo,
587103
+ tor: !!onion,
587104
+ direct: !peerInfo
587105
+ },
587106
+ _note: peerInfo && onion ? "Globally reachable via libp2p (primary) and Tor (.onion fallback)." : peerInfo ? "Globally reachable via libp2p. For maximum reach add Tor: see scripts/tor/tor_setup.sh." : "Nexus daemon offline — URL is direct-HTTP only (LAN/VPN reach). Start nexus for global reach."
586758
587107
  });
586759
587108
  } catch (err) {
586760
587109
  sendProblem(res, problemDetails({
@@ -586767,6 +587116,242 @@ async function handleGenerateShare(ctx3) {
586767
587116
  }
586768
587117
  return true;
586769
587118
  }
587119
+ async function handleRemoteProxy(ctx3) {
587120
+ const { req: req2, res, requestId } = ctx3;
587121
+ const reqAuth = req2;
587122
+ if (reqAuth._authScope !== "admin" && reqAuth._authScope !== "run") {
587123
+ sendProblem(res, problemDetails({
587124
+ type: P.forbidden,
587125
+ status: 403,
587126
+ title: "Auth required",
587127
+ detail: "Remote proxy requires 'run' or 'admin' scope on the LOCAL daemon.",
587128
+ instance: requestId
587129
+ }));
587130
+ return true;
587131
+ }
587132
+ let body;
587133
+ try {
587134
+ body = await parseJsonBodyStrict(req2);
587135
+ } catch {
587136
+ sendProblem(res, problemDetails({
587137
+ type: P.invalidRequest,
587138
+ status: 400,
587139
+ title: "Invalid JSON body",
587140
+ detail: "Body must be JSON object with peerId, key, method, path.",
587141
+ instance: requestId
587142
+ }));
587143
+ return true;
587144
+ }
587145
+ const peerId = String(body?.peerId || "").trim();
587146
+ const onionHint = typeof body?.onion === "string" && body.onion.endsWith(".onion") ? body.onion : null;
587147
+ const shareKey = String(body?.key || "").trim();
587148
+ const method = String(body?.method || "GET").toUpperCase();
587149
+ const path8 = String(body?.path || "/").trim();
587150
+ const headers = body?.headers && typeof body.headers === "object" ? body.headers : {};
587151
+ const useStream = body?.stream === true;
587152
+ const timeoutMs = typeof body?.timeoutMs === "number" ? body.timeoutMs : 6e4;
587153
+ if (!peerId && !onionHint || !shareKey) {
587154
+ sendProblem(res, problemDetails({
587155
+ type: P.invalidRequest,
587156
+ status: 400,
587157
+ title: "key + (peerId or onion) required",
587158
+ detail: "Provide a share key and at least one of peerId (libp2p) or onion (Tor).",
587159
+ instance: requestId
587160
+ }));
587161
+ return true;
587162
+ }
587163
+ const nexusCandidates = [
587164
+ join118(process.cwd(), ".oa", "nexus"),
587165
+ join118(homedir40(), ".oa", "nexus"),
587166
+ join118(homedir40(), ".open-agents", "nexus")
587167
+ ];
587168
+ let nexusDirPath = null;
587169
+ for (const p2 of nexusCandidates) {
587170
+ if (existsSync102(join118(p2, "status.json"))) {
587171
+ nexusDirPath = p2;
587172
+ break;
587173
+ }
587174
+ }
587175
+ let tool = null;
587176
+ if (peerId && nexusDirPath) {
587177
+ const { NexusTool: NexusTool2 } = await Promise.resolve().then(() => (init_dist5(), dist_exports));
587178
+ const nexusToolRepoRoot = nexusDirPath.replace(/[\\/]\.oa[\\/]nexus$/, "");
587179
+ tool = new NexusTool2(nexusToolRepoRoot);
587180
+ }
587181
+ const tunnelInput = {
587182
+ method,
587183
+ path: path8,
587184
+ headers,
587185
+ body: body?.body,
587186
+ key: shareKey
587187
+ };
587188
+ function torEnvelope(t2) {
587189
+ return {
587190
+ event: "http.head",
587191
+ data: JSON.stringify({ status: t2.status, headers: t2.headers, streaming: t2.streaming }),
587192
+ // The browser fetch interceptor reads either `event: http.head + data` (with embedded
587193
+ // status/headers) or falls back to `raw`. We pass both.
587194
+ raw: t2.body,
587195
+ transport: "tor"
587196
+ };
587197
+ }
587198
+ if (!useStream) {
587199
+ let libp2pErr = null;
587200
+ if (tool) {
587201
+ try {
587202
+ const raw = await tool.sendCommand("invoke_capability", {
587203
+ target_peer: peerId,
587204
+ capability: "http_tunnel",
587205
+ input: JSON.stringify(tunnelInput)
587206
+ }, timeoutMs);
587207
+ let parsed = null;
587208
+ try {
587209
+ parsed = JSON.parse(raw);
587210
+ } catch {
587211
+ parsed = { raw };
587212
+ }
587213
+ if (parsed && typeof parsed === "object") parsed.transport = "libp2p";
587214
+ sendJson(res, 200, parsed);
587215
+ return true;
587216
+ } catch (e2) {
587217
+ libp2pErr = e2;
587218
+ }
587219
+ }
587220
+ if (onionHint) {
587221
+ try {
587222
+ const { tunnelViaTor: tunnelViaTor2, torIsReachable: torIsReachable2 } = await Promise.resolve().then(() => (init_tor_fallback(), tor_fallback_exports));
587223
+ if (await torIsReachable2()) {
587224
+ const t2 = await tunnelViaTor2({
587225
+ onion: onionHint,
587226
+ method,
587227
+ path: path8,
587228
+ headers,
587229
+ body: typeof body?.body === "string" ? body.body : body?.body !== void 0 ? JSON.stringify(body.body) : void 0,
587230
+ shareKey,
587231
+ timeoutMs
587232
+ });
587233
+ sendJson(res, 200, torEnvelope(t2));
587234
+ return true;
587235
+ }
587236
+ } catch (torErr) {
587237
+ sendProblem(res, problemDetails({
587238
+ type: P.internalError,
587239
+ status: 502,
587240
+ title: "Both libp2p and Tor transports failed",
587241
+ detail: `libp2p: ${libp2pErr instanceof Error ? libp2pErr.message : String(libp2pErr || "n/a")}; tor: ${torErr instanceof Error ? torErr.message : String(torErr)}`,
587242
+ instance: requestId
587243
+ }));
587244
+ return true;
587245
+ }
587246
+ }
587247
+ sendProblem(res, problemDetails({
587248
+ type: P.internalError,
587249
+ status: 502,
587250
+ title: "No transport reached the remote",
587251
+ detail: tool ? `libp2p invoke failed: ${libp2pErr instanceof Error ? libp2pErr.message : String(libp2pErr)}${onionHint ? "; Tor SOCKS5 not reachable on 127.0.0.1:9050" : "; no .onion fallback available"}` : "Local nexus daemon is offline and no .onion fallback was provided. Start nexus with `oa connect` or include onion in the request.",
587252
+ instance: requestId
587253
+ }));
587254
+ return true;
587255
+ }
587256
+ if (!tool) {
587257
+ sendProblem(res, problemDetails({
587258
+ type: P.internalError,
587259
+ status: 503,
587260
+ title: "Streaming requires libp2p (nexus not running)",
587261
+ detail: "SSE/streaming proxy requires the local nexus daemon. Start it with `oa connect`, or use a non-streaming endpoint with the Tor fallback.",
587262
+ instance: requestId
587263
+ }));
587264
+ return true;
587265
+ }
587266
+ const streamFile = join118(nexusDirPath, `tunnel-${requestId}-${Date.now()}.jsonl`);
587267
+ try {
587268
+ const { writeFileSync: _wfs } = await import("node:fs");
587269
+ _wfs(streamFile, "");
587270
+ } catch {
587271
+ }
587272
+ res.statusCode = 200;
587273
+ res.setHeader("content-type", "text/event-stream");
587274
+ res.setHeader("cache-control", "no-cache");
587275
+ res.setHeader("x-accel-buffering", "no");
587276
+ res.flushHeaders?.();
587277
+ let stopped = false;
587278
+ const stop2 = () => {
587279
+ stopped = true;
587280
+ };
587281
+ req2.on("close", stop2);
587282
+ req2.on("aborted", stop2);
587283
+ const invokeP = tool.sendCommand("invoke_capability", {
587284
+ target_peer: peerId,
587285
+ capability: "http_tunnel",
587286
+ input: JSON.stringify(tunnelInput),
587287
+ stream_file: streamFile
587288
+ }, timeoutMs).catch((err) => {
587289
+ if (!stopped) {
587290
+ try {
587291
+ res.write(`event: error
587292
+ data: ${JSON.stringify({ error: err instanceof Error ? err.message : String(err) })}
587293
+
587294
+ `);
587295
+ } catch {
587296
+ }
587297
+ }
587298
+ });
587299
+ const { readFileSync: _rfs, existsSync: _exists } = await import("node:fs");
587300
+ let lastSize = 0;
587301
+ let leftover = "";
587302
+ while (!stopped) {
587303
+ try {
587304
+ if (_exists(streamFile)) {
587305
+ const stat5 = (await import("node:fs")).statSync(streamFile);
587306
+ if (stat5.size > lastSize) {
587307
+ const fd = (await import("node:fs")).openSync(streamFile, "r");
587308
+ const len = stat5.size - lastSize;
587309
+ const buf = Buffer.alloc(len);
587310
+ (await import("node:fs")).readSync(fd, buf, 0, len, lastSize);
587311
+ (await import("node:fs")).closeSync(fd);
587312
+ lastSize = stat5.size;
587313
+ const txt = leftover + buf.toString("utf-8");
587314
+ const parts = txt.split("\n");
587315
+ leftover = parts.pop() ?? "";
587316
+ for (const ln of parts) {
587317
+ if (!ln.trim()) continue;
587318
+ let parsed;
587319
+ try {
587320
+ parsed = JSON.parse(ln);
587321
+ } catch {
587322
+ continue;
587323
+ }
587324
+ const eventName = parsed.type === "event" ? parsed.event || "data" : parsed.type;
587325
+ res.write(`event: ${eventName}
587326
+ `);
587327
+ res.write(`data: ${JSON.stringify(parsed)}
587328
+
587329
+ `);
587330
+ if (parsed.type === "done" || parsed.type === "error") {
587331
+ stopped = true;
587332
+ break;
587333
+ }
587334
+ }
587335
+ }
587336
+ }
587337
+ } catch {
587338
+ }
587339
+ if (stopped) break;
587340
+ await new Promise((r2) => setTimeout(r2, 30));
587341
+ }
587342
+ await invokeP.catch(() => {
587343
+ });
587344
+ try {
587345
+ res.end();
587346
+ } catch {
587347
+ }
587348
+ try {
587349
+ const { unlinkSync: _unl } = await import("node:fs");
587350
+ _unl(streamFile);
587351
+ } catch {
587352
+ }
587353
+ return true;
587354
+ }
586770
587355
  async function handleRevokeKey(ctx3, prefix) {
586771
587356
  const { req: req2, res, requestId } = ctx3;
586772
587357
  const reqAuth = req2;
@@ -587118,17 +587703,17 @@ async function handleListAgentTypes(ctx3) {
587118
587703
  }
587119
587704
  async function handleListEngines(ctx3) {
587120
587705
  const { res } = ctx3;
587121
- const home = homedir39();
587706
+ const home = homedir40();
587122
587707
  sendJson(res, 200, {
587123
587708
  engines: [
587124
- { name: "dream", state_file: join117(process.cwd(), ".oa", "dreams"), controllable_via: "SSE + slash commands" },
587125
- { name: "bless", state_file: join117(process.cwd(), ".oa", "bless-state.json"), controllable_via: "slash commands" },
587126
- { name: "call", state_file: join117(process.cwd(), ".oa", "call-state.json"), controllable_via: "slash commands" },
587127
- { name: "listen", state_file: join117(process.cwd(), ".oa", "listen-state.json"), controllable_via: "slash commands" },
587128
- { name: "telegram", state_file: join117(home, ".open-agents", "telegram-state.json"), controllable_via: "slash commands" },
587129
- { name: "expose", state_file: join117(process.cwd(), ".oa", "expose-state.json"), controllable_via: "/expose commands" },
587130
- { name: "nexus", state_file: join117(home, ".open-agents", "nexus-peer-cache.json"), controllable_via: "/nexus commands" },
587131
- { name: "ipfs", state_file: join117(process.cwd(), ".oa", "ipfs"), controllable_via: "slash commands" }
587709
+ { name: "dream", state_file: join118(process.cwd(), ".oa", "dreams"), controllable_via: "SSE + slash commands" },
587710
+ { name: "bless", state_file: join118(process.cwd(), ".oa", "bless-state.json"), controllable_via: "slash commands" },
587711
+ { name: "call", state_file: join118(process.cwd(), ".oa", "call-state.json"), controllable_via: "slash commands" },
587712
+ { name: "listen", state_file: join118(process.cwd(), ".oa", "listen-state.json"), controllable_via: "slash commands" },
587713
+ { name: "telegram", state_file: join118(home, ".open-agents", "telegram-state.json"), controllable_via: "slash commands" },
587714
+ { name: "expose", state_file: join118(process.cwd(), ".oa", "expose-state.json"), controllable_via: "/expose commands" },
587715
+ { name: "nexus", state_file: join118(home, ".open-agents", "nexus-peer-cache.json"), controllable_via: "/nexus commands" },
587716
+ { name: "ipfs", state_file: join118(process.cwd(), ".oa", "ipfs"), controllable_via: "slash commands" }
587132
587717
  ],
587133
587718
  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."
587134
587719
  });
@@ -587211,12 +587796,12 @@ async function tryAimsRoute(ctx3) {
587211
587796
  return false;
587212
587797
  }
587213
587798
  function aimsDir() {
587214
- return join117(homedir39(), ".open-agents", "aims");
587799
+ return join118(homedir40(), ".open-agents", "aims");
587215
587800
  }
587216
587801
  function readAimsFile(name10, fallback) {
587217
587802
  try {
587218
- const p2 = join117(aimsDir(), name10);
587219
- if (existsSync101(p2)) return JSON.parse(readFileSync82(p2, "utf-8"));
587803
+ const p2 = join118(aimsDir(), name10);
587804
+ if (existsSync102(p2)) return JSON.parse(readFileSync83(p2, "utf-8"));
587220
587805
  } catch {
587221
587806
  }
587222
587807
  return fallback;
@@ -587225,7 +587810,7 @@ function writeAimsFile(name10, data) {
587225
587810
  const dir = aimsDir();
587226
587811
  const { mkdirSync: mkdirSync67, writeFileSync: wf, renameSync: rn } = __require("node:fs");
587227
587812
  mkdirSync67(dir, { recursive: true });
587228
- const finalPath = join117(dir, name10);
587813
+ const finalPath = join118(dir, name10);
587229
587814
  const tmpPath = `${finalPath}.tmp.${process.pid}.${Date.now()}`;
587230
587815
  try {
587231
587816
  wf(tmpPath, JSON.stringify(data, null, 2) + "\n", { encoding: "utf-8", mode: 384 });
@@ -587555,12 +588140,12 @@ async function handleAimsSuppliers(ctx3) {
587555
588140
  }
587556
588141
  ];
587557
588142
  const sponsorPaths = [
587558
- join117(homedir39(), ".open-agents", "sponsor-cache.json")
588143
+ join118(homedir40(), ".open-agents", "sponsor-cache.json")
587559
588144
  ];
587560
588145
  for (const p2 of sponsorPaths) {
587561
- if (!existsSync101(p2)) continue;
588146
+ if (!existsSync102(p2)) continue;
587562
588147
  try {
587563
- const raw = JSON.parse(readFileSync82(p2, "utf-8"));
588148
+ const raw = JSON.parse(readFileSync83(p2, "utf-8"));
587564
588149
  const list = Array.isArray(raw) ? raw : raw?.sponsors ?? [];
587565
588150
  for (const s2 of list) {
587566
588151
  suppliers.push({
@@ -590881,35 +591466,173 @@ document.getElementById('key-btn').onclick = () => {
590881
591466
  };
590882
591467
  function saveKey() {
590883
591468
  const raw = document.getElementById('key-input').value || '';
590884
- // SHARE: detect oa-share://host:port#key OR http(s)://host:port/?oa-key=...
590885
591469
  const parsed = parseShareInput(raw);
590886
591470
  if (parsed) {
590887
- // Pasting a share URL → switch into REMOTE mode and reload page
590888
- // against the remote origin with the key in localStorage.
590889
- saveRecentKey({ key: parsed.key, host: parsed.host, label: parsed.label || ('remote ' + parsed.host) });
590890
- try {
590891
- localStorage.setItem('oa.remoteHost', parsed.host);
590892
- localStorage.setItem('oa.remoteScheme', parsed.scheme || 'http');
590893
- localStorage.setItem('oa-api-key', parsed.key);
590894
- } catch {}
590895
- // Redirect to the remote host's UI with the key carried in localStorage
590896
- // (the remote origin uses its own localStorage so we set on first
590897
- // load there). We open the remote UI in a new tab to preserve the
590898
- // local session.
590899
- const remoteUrl = (parsed.scheme || 'http') + '://' + parsed.host + '/?oa-key=' + encodeURIComponent(parsed.key) + '&oa-share-label=' + encodeURIComponent(parsed.label || '');
590900
- window.open(remoteUrl, '_blank');
590901
- closeKeyModal();
590902
- return;
591471
+ // Save into recent-keys for autocomplete
591472
+ const recLabel = parsed.label || (parsed.peerId
591473
+ ? ('remote ' + (parsed.peerId.slice(0, 12) + '…'))
591474
+ : ('remote ' + parsed.host));
591475
+ saveRecentKey({
591476
+ key: parsed.key, peerId: parsed.peerId, onion: parsed.onion || null,
591477
+ host: parsed.host, label: recLabel,
591478
+ });
591479
+ if (parsed.peerId || parsed.onion) {
591480
+ // ─── REMOTE MODE: libp2p (primary) + Tor (fallback) ──────────────
591481
+ // Stay on this page; route /v1/... calls through this daemon's
591482
+ // /v1/remote-proxy. The local daemon will try libp2p invoke first
591483
+ // (when peerId is set); if that fails (or peerId missing) it
591484
+ // falls back to Tor SOCKS5 → onion (when onion is set).
591485
+ try {
591486
+ localStorage.setItem('oa.activeRemoteShare', JSON.stringify({
591487
+ peerId: parsed.peerId,
591488
+ onion: parsed.onion || null,
591489
+ key: parsed.key,
591490
+ host: parsed.host || null,
591491
+ label: recLabel,
591492
+ activatedAt: new Date().toISOString(),
591493
+ }));
591494
+ } catch {}
591495
+ installRemoteFetchProxy();
591496
+ closeKeyModal();
591497
+ try { location.reload(); } catch { loadModels(); }
591498
+ return;
591499
+ }
591500
+ if (parsed.host) {
591501
+ // Legacy direct-HTTP — open the remote origin in a new tab.
591502
+ const remoteUrl = (parsed.scheme || 'http') + '://' + parsed.host + '/?oa-key=' + encodeURIComponent(parsed.key) + '&oa-share-label=' + encodeURIComponent(parsed.label || '');
591503
+ window.open(remoteUrl, '_blank');
591504
+ closeKeyModal();
591505
+ return;
591506
+ }
590903
591507
  }
590904
591508
  apiKey = raw;
590905
591509
  localStorage.setItem('oa-api-key', apiKey);
590906
- // Track this key in the recent-keys list so future paste autocompletes it.
590907
591510
  if (apiKey) {
590908
591511
  saveRecentKey({ key: apiKey, host: location.host, label: 'local ' + location.host });
590909
591512
  }
590910
591513
  closeKeyModal();
590911
591514
  loadModels();
590912
591515
  }
591516
+
591517
+ // ─── Remote-mode fetch proxy ─────────────────────────────────────────────
591518
+ // When 'oa.activeRemoteShare' is set, intercept window.fetch so any
591519
+ // /v1/... call gets re-routed via POST /v1/remote-proxy to be tunneled
591520
+ // through libp2p to the remote peer. SSE streams keep their text/event-
591521
+ // stream shape end-to-end.
591522
+ let _oaRemoteFetchInstalled = false;
591523
+ function getActiveRemoteShare() {
591524
+ try {
591525
+ const raw = localStorage.getItem('oa.activeRemoteShare');
591526
+ if (!raw) return null;
591527
+ const obj = JSON.parse(raw);
591528
+ if (!obj || !obj.peerId || !obj.key) return null;
591529
+ return obj;
591530
+ } catch { return null; }
591531
+ }
591532
+ function clearActiveRemoteShare() {
591533
+ try { localStorage.removeItem('oa.activeRemoteShare'); } catch {}
591534
+ }
591535
+ function installRemoteFetchProxy() {
591536
+ if (_oaRemoteFetchInstalled) return;
591537
+ _oaRemoteFetchInstalled = true;
591538
+ const _origFetch = window.fetch.bind(window);
591539
+ window.fetch = async function(input, init) {
591540
+ const share = getActiveRemoteShare();
591541
+ if (!share) return _origFetch(input, init);
591542
+ let urlStr = '';
591543
+ if (typeof input === 'string') urlStr = input;
591544
+ else if (input && input.url) urlStr = input.url;
591545
+ else return _origFetch(input, init);
591546
+ let pathOnly;
591547
+ try {
591548
+ const u = new URL(urlStr, location.href);
591549
+ // Don't proxy:
591550
+ // - cross-origin (already remote)
591551
+ // - the proxy endpoint itself (infinite loop)
591552
+ // - non-/v1/* routes (e.g. /assets, /openapi.json)
591553
+ if (u.origin !== location.origin) return _origFetch(input, init);
591554
+ if (u.pathname === '/v1/remote-proxy') return _origFetch(input, init);
591555
+ if (!u.pathname.startsWith('/v1/')) return _origFetch(input, init);
591556
+ pathOnly = u.pathname + u.search;
591557
+ } catch { return _origFetch(input, init); }
591558
+
591559
+ const method = ((init && init.method) || (input && input.method) || 'GET').toUpperCase();
591560
+ const headersIn = {};
591561
+ if (init && init.headers) {
591562
+ const h = init.headers;
591563
+ if (h instanceof Headers) h.forEach((v, k) => { headersIn[k] = v; });
591564
+ else if (Array.isArray(h)) h.forEach(([k, v]) => { headersIn[k] = v; });
591565
+ else Object.assign(headersIn, h);
591566
+ }
591567
+ let bodyOut;
591568
+ if (init && init.body !== undefined && init.body !== null) {
591569
+ if (typeof init.body === 'string') bodyOut = init.body;
591570
+ else if (init.body instanceof FormData) {
591571
+ const obj = {}; init.body.forEach((v, k) => { obj[k] = v; });
591572
+ bodyOut = obj;
591573
+ } else bodyOut = init.body;
591574
+ }
591575
+ // Heuristic: SSE if Accept includes text/event-stream OR the path is
591576
+ // a known streaming endpoint. We open the proxy in stream mode and
591577
+ // surface a Response with text/event-stream.
591578
+ const acceptHdr = (headersIn['accept'] || headersIn['Accept'] || '').toLowerCase();
591579
+ const wantsStream = acceptHdr.includes('text/event-stream')
591580
+ || pathOnly.includes('/v1/chat/completions')
591581
+ || pathOnly.includes('/v1/events');
591582
+
591583
+ const proxyBody = JSON.stringify({
591584
+ peerId: share.peerId || null,
591585
+ onion: share.onion || null,
591586
+ key: share.key,
591587
+ method, path: pathOnly, headers: headersIn, body: bodyOut,
591588
+ stream: wantsStream, timeoutMs: 120000,
591589
+ });
591590
+ if (wantsStream) {
591591
+ // Pass through as-is — receiver returns text/event-stream.
591592
+ return _origFetch('/v1/remote-proxy', {
591593
+ method: 'POST',
591594
+ headers: { 'content-type': 'application/json', 'accept': 'text/event-stream' },
591595
+ body: proxyBody,
591596
+ });
591597
+ }
591598
+ // Non-stream: receiver returns a JSON envelope { status, headers, body }.
591599
+ // Synthesize a Response that mimics the original remote response.
591600
+ const proxyResp = await _origFetch('/v1/remote-proxy', {
591601
+ method: 'POST',
591602
+ headers: { 'content-type': 'application/json' },
591603
+ body: proxyBody,
591604
+ });
591605
+ if (!proxyResp.ok) return proxyResp;
591606
+ let env = null;
591607
+ try { env = await proxyResp.json(); } catch {}
591608
+ if (!env || typeof env !== 'object') {
591609
+ return new Response('', { status: 502, statusText: 'Empty proxy response' });
591610
+ }
591611
+ // The non-stream envelope is the http_tunnel handler last event,
591612
+ // with its body JSON-stringified inside the data field. Unwrap it.
591613
+ let status = 200;
591614
+ let headersOut = new Headers();
591615
+ let bodyText = '';
591616
+ if (typeof env.raw === 'string') {
591617
+ // Single-shot result from the http_tunnel head+body events combined
591618
+ bodyText = env.raw;
591619
+ } else if (env.event === 'http.body' || env.event === 'http.head') {
591620
+ try {
591621
+ const inner = JSON.parse(env.data);
591622
+ if (inner.status) status = inner.status;
591623
+ if (inner.headers) Object.entries(inner.headers).forEach(([k, v]) => headersOut.set(k, String(v)));
591624
+ } catch {}
591625
+ bodyText = String(env.data || '');
591626
+ } else {
591627
+ bodyText = JSON.stringify(env);
591628
+ }
591629
+ return new Response(bodyText, { status, headers: headersOut });
591630
+ };
591631
+ }
591632
+ // Auto-install on every load if a remote share is active.
591633
+ try {
591634
+ if (getActiveRemoteShare()) installRemoteFetchProxy();
591635
+ } catch {}
590913
591636
  function clearKey() {
590914
591637
  apiKey = '';
590915
591638
  localStorage.removeItem('oa-api-key');
@@ -590930,14 +591653,23 @@ function openKeyModal() {
590930
591653
  window.openKeyModal = openKeyModal;
590931
591654
 
590932
591655
  // ─── Share URL parsing ─────────────────────────────────────────────────
590933
- // Accepts BOTH:
590934
- // oa-share://[peerId@]host:port#key
590935
- // http(s)://host:port/?oa-key=KEY[&oa-share-label=LABEL]
590936
- // Returns { host, key, scheme?, label? } or null when the input is a plain key.
591656
+ // Accepts:
591657
+ // oa-share://<peerId>#<key> (libp2p, global)
591658
+ // oa-share://<peerId>@<host:port>#<key> (libp2p w/ LAN hint)
591659
+ // oa-share://<host:port>#<key> (legacy direct-HTTP)
591660
+ // http(s)://host:port/?oa-key=KEY[&oa-share-peer=PID&oa-share-label=L]
591661
+ // Returns { peerId?, host?, key, scheme?, label? } or null when the input
591662
+ // is a plain key. peerId is a libp2p PeerID (starts with 12D3KooW or Qm).
591663
+ function _looksLikePeerId(s) {
591664
+ if (!s || typeof s !== 'string') return false;
591665
+ // libp2p PeerIDs are base58-encoded multihashes, typically starting with
591666
+ // 12D3KooW (Ed25519) or Qm (RSA). Length ~46-52 chars, no dots or colons.
591667
+ return /^(12D3KooW|Qm)[1-9A-HJ-NP-Za-km-z]{30,}$/.test(s);
591668
+ }
590937
591669
  function parseShareInput(raw) {
590938
591670
  const v = String(raw || '').trim();
590939
591671
  if (!v) return null;
590940
- // oa-share scheme — use a custom parser since URL() doesn't always honor it.
591672
+ // oa-share scheme — custom parser since URL() doesn't always honor it.
590941
591673
  if (v.toLowerCase().startsWith('oa-share://')) {
590942
591674
  const after = v.slice('oa-share://'.length);
590943
591675
  const hashIdx = after.indexOf('#');
@@ -590945,19 +591677,36 @@ function parseShareInput(raw) {
590945
591677
  const hostPart = after.slice(0, hashIdx);
590946
591678
  const key = after.slice(hashIdx + 1);
590947
591679
  if (!hostPart || !key) return null;
590948
- // Strip optional peerId@ prefix (forward-compat for libp2p tunneling).
591680
+ // Three shapes:
591681
+ // peerId@host:port → libp2p+LAN
591682
+ // peerId → libp2p only (no host)
591683
+ // host:port → legacy direct
590949
591684
  const atIdx = hostPart.indexOf('@');
590950
- const host = atIdx >= 0 ? hostPart.slice(atIdx + 1) : hostPart;
590951
- return { host, key, scheme: 'http' };
591685
+ if (atIdx >= 0) {
591686
+ const peerId = hostPart.slice(0, atIdx);
591687
+ const host = hostPart.slice(atIdx + 1);
591688
+ return { peerId: _looksLikePeerId(peerId) ? peerId : null, host, key, scheme: 'http' };
591689
+ }
591690
+ if (_looksLikePeerId(hostPart)) {
591691
+ return { peerId: hostPart, host: null, key, scheme: 'libp2p' };
591692
+ }
591693
+ return { peerId: null, host: hostPart, key, scheme: 'http' };
590952
591694
  }
590953
- // http(s) URL with ?oa-key=...
591695
+ // http(s) URL with ?oa-key=, optional ?oa-share-peer=, optional ?oa-onion=
590954
591696
  if (/^https?:\\/\\//i.test(v)) {
590955
591697
  try {
590956
591698
  const u = new URL(v);
590957
591699
  const k = u.searchParams.get('oa-key');
590958
591700
  if (!k) return null;
591701
+ const peerIdQ = u.searchParams.get('oa-share-peer') || null;
591702
+ const onionQ = u.searchParams.get('oa-onion') || null;
590959
591703
  const label = u.searchParams.get('oa-share-label') || '';
590960
- return { host: u.host, key: k, scheme: u.protocol.replace(':', ''), label };
591704
+ return {
591705
+ peerId: _looksLikePeerId(peerIdQ) ? peerIdQ : null,
591706
+ onion: (onionQ && onionQ.endsWith('.onion')) ? onionQ : null,
591707
+ host: u.host, key: k,
591708
+ scheme: u.protocol.replace(':', ''), label,
591709
+ };
590961
591710
  } catch { return null; }
590962
591711
  }
590963
591712
  return null;
@@ -591133,17 +591882,32 @@ window.copyShareUrl = copyShareUrl;
591133
591882
  // inline with a "close connection" button that severs and shuffles the
591134
591883
  // key into recents.
591135
591884
  function refreshKeyModalRemoteState() {
591136
- const remote = (function() {
591885
+ // Two remote modes coexist:
591886
+ // 1. libp2p — oa.activeRemoteShare = {peerId, key, host?, label}
591887
+ // 2. direct — oa.remoteHost (legacy, set by ?oa-key= pickup)
591888
+ const active = getActiveRemoteShare();
591889
+ const directHost = (function() {
591137
591890
  try { return localStorage.getItem('oa.remoteHost') || ''; } catch { return ''; }
591138
591891
  })();
591892
+ const isRemote = !!active || !!directHost;
591893
+ let remoteLabel;
591894
+ if (active) {
591895
+ const parts = [];
591896
+ if (active.peerId) parts.push('libp2p ' + active.peerId.slice(0, 12) + '…');
591897
+ if (active.onion) parts.push('tor ' + active.onion.slice(0, 16) + '…');
591898
+ if (active.host) parts.push(active.host);
591899
+ remoteLabel = parts.join(' / ') || 'remote';
591900
+ } else {
591901
+ remoteLabel = directHost;
591902
+ }
591139
591903
  const stateBox = document.getElementById('remote-state');
591140
591904
  const btn = document.getElementById('sidebar-key-btn');
591141
- if (remote && btn) {
591905
+ if (isRemote && btn) {
591142
591906
  btn.textContent = 'remote';
591143
591907
  btn.style.background = 'var(--color-accent)';
591144
591908
  btn.style.color = '#fff';
591145
591909
  btn.style.borderColor = 'var(--color-accent)';
591146
- btn.title = 'connected to remote ' + remote + ' — click to view / disconnect';
591910
+ btn.title = 'connected to ' + remoteLabel + ' — click to view / disconnect';
591147
591911
  } else if (btn) {
591148
591912
  btn.textContent = 'key';
591149
591913
  btn.style.background = 'transparent';
@@ -591152,27 +591916,43 @@ function refreshKeyModalRemoteState() {
591152
591916
  btn.title = 'set API key / share access';
591153
591917
  }
591154
591918
  if (!stateBox) return;
591155
- if (remote) {
591156
- const safeRemote = escapeHtml(remote);
591919
+ if (isRemote) {
591920
+ const safe = escapeHtml(remoteLabel);
591921
+ let mode;
591922
+ if (active) {
591923
+ const tiers = [];
591924
+ if (active.peerId) tiers.push('libp2p');
591925
+ if (active.onion) tiers.push('tor');
591926
+ mode = tiers.join(' + ') + ' (global)';
591927
+ } else {
591928
+ mode = 'direct (LAN/VPN)';
591929
+ }
591157
591930
  stateBox.style.display = 'block';
591158
591931
  stateBox.innerHTML =
591159
591932
  '<div style="font-weight:500;color:var(--color-accent)">REMOTE connection active</div>' +
591160
- '<div style="margin-top:4px;font-size:0.74rem">host <code>' + safeRemote + '</code></div>' +
591933
+ '<div style="margin-top:4px;font-size:0.74rem">mode <code>' + escapeHtml(mode) + '</code></div>' +
591934
+ '<div style="margin-top:2px;font-size:0.74rem">target <code>' + safe + '</code></div>' +
591161
591935
  '<div style="margin-top:8px"><button type="button" onclick="closeRemoteConnection()" style="background:var(--color-error);color:#fff;border:none;padding:4px 10px;border-radius:var(--radius-sm);cursor:pointer;font-size:0.74rem">close connection</button></div>';
591162
591936
  } else {
591163
591937
  stateBox.style.display = 'none';
591164
591938
  }
591165
591939
  }
591166
591940
  function closeRemoteConnection() {
591167
- // Move current remote key into recents BEFORE clearing.
591168
- let savedKey = '';
591169
- let savedHost = '';
591170
- try {
591171
- savedKey = localStorage.getItem('oa-api-key') || '';
591172
- savedHost = localStorage.getItem('oa.remoteHost') || '';
591173
- } catch {}
591174
- if (savedKey) {
591175
- saveRecentKey({ key: savedKey, host: savedHost, label: 'recent remote ' + savedHost });
591941
+ // Save the active remote into recents before clearing.
591942
+ const active = getActiveRemoteShare();
591943
+ if (active) {
591944
+ saveRecentKey({
591945
+ key: active.key, peerId: active.peerId, host: active.host,
591946
+ label: 'recent ' + (active.label || ('remote ' + (active.peerId || '').slice(0, 12) + '')),
591947
+ });
591948
+ clearActiveRemoteShare();
591949
+ } else {
591950
+ let savedKey = ''; let savedHost = '';
591951
+ try {
591952
+ savedKey = localStorage.getItem('oa-api-key') || '';
591953
+ savedHost = localStorage.getItem('oa.remoteHost') || '';
591954
+ } catch {}
591955
+ if (savedKey) saveRecentKey({ key: savedKey, host: savedHost, label: 'recent remote ' + savedHost });
591176
591956
  }
591177
591957
  try {
591178
591958
  localStorage.removeItem('oa.remoteHost');
@@ -591181,7 +591961,6 @@ function closeRemoteConnection() {
591181
591961
  } catch {}
591182
591962
  apiKey = '';
591183
591963
  refreshKeyModalRemoteState();
591184
- // Reload to drop any cached state from the remote target.
591185
591964
  location.reload();
591186
591965
  }
591187
591966
  window.closeRemoteConnection = closeRemoteConnection;
@@ -595973,15 +596752,15 @@ var init_auth_oidc = __esm({
595973
596752
  });
595974
596753
 
595975
596754
  // packages/cli/src/api/usage-tracker.ts
595976
- import { mkdirSync as mkdirSync60, readFileSync as readFileSync83, writeFileSync as writeFileSync52, existsSync as existsSync102 } from "node:fs";
595977
- import { join as join118 } from "node:path";
596755
+ import { mkdirSync as mkdirSync60, readFileSync as readFileSync84, writeFileSync as writeFileSync52, existsSync as existsSync103 } from "node:fs";
596756
+ import { join as join119 } from "node:path";
595978
596757
  function initUsageTracker(oaDir) {
595979
- const dir = join118(oaDir, "usage");
596758
+ const dir = join119(oaDir, "usage");
595980
596759
  mkdirSync60(dir, { recursive: true });
595981
- usageFile = join118(dir, "token-usage.json");
596760
+ usageFile = join119(dir, "token-usage.json");
595982
596761
  try {
595983
- if (existsSync102(usageFile)) {
595984
- store = JSON.parse(readFileSync83(usageFile, "utf-8"));
596762
+ if (existsSync103(usageFile)) {
596763
+ store = JSON.parse(readFileSync84(usageFile, "utf-8"));
595985
596764
  }
595986
596765
  } catch {
595987
596766
  store = { providers: {}, lastSaved: "" };
@@ -596045,24 +596824,24 @@ var init_usage_tracker = __esm({
596045
596824
  });
596046
596825
 
596047
596826
  // packages/cli/src/api/profiles.ts
596048
- import { existsSync as existsSync103, readFileSync as readFileSync84, writeFileSync as writeFileSync53, mkdirSync as mkdirSync61, readdirSync as readdirSync35, unlinkSync as unlinkSync23 } from "node:fs";
596049
- import { join as join119 } from "node:path";
596050
- import { homedir as homedir40 } from "node:os";
596827
+ import { existsSync as existsSync104, readFileSync as readFileSync85, writeFileSync as writeFileSync53, mkdirSync as mkdirSync61, readdirSync as readdirSync35, unlinkSync as unlinkSync23 } from "node:fs";
596828
+ import { join as join120 } from "node:path";
596829
+ import { homedir as homedir41 } from "node:os";
596051
596830
  import { createCipheriv as createCipheriv4, createDecipheriv as createDecipheriv4, randomBytes as randomBytes22, scryptSync as scryptSync3 } from "node:crypto";
596052
596831
  function globalProfileDir() {
596053
- return join119(homedir40(), ".open-agents", "profiles");
596832
+ return join120(homedir41(), ".open-agents", "profiles");
596054
596833
  }
596055
596834
  function projectProfileDir(projectDir2) {
596056
- return join119(projectDir2 || process.cwd(), ".oa", "profiles");
596835
+ return join120(projectDir2 || process.cwd(), ".oa", "profiles");
596057
596836
  }
596058
596837
  function listProfiles(projectDir2) {
596059
596838
  const result = [];
596060
596839
  const seen = /* @__PURE__ */ new Set();
596061
596840
  const projDir = projectProfileDir(projectDir2);
596062
- if (existsSync103(projDir)) {
596841
+ if (existsSync104(projDir)) {
596063
596842
  for (const f2 of readdirSync35(projDir).filter((f3) => f3.endsWith(".json"))) {
596064
596843
  try {
596065
- const raw = JSON.parse(readFileSync84(join119(projDir, f2), "utf8"));
596844
+ const raw = JSON.parse(readFileSync85(join120(projDir, f2), "utf8"));
596066
596845
  const name10 = f2.replace(".json", "");
596067
596846
  seen.add(name10);
596068
596847
  result.push({
@@ -596076,12 +596855,12 @@ function listProfiles(projectDir2) {
596076
596855
  }
596077
596856
  }
596078
596857
  const globDir = globalProfileDir();
596079
- if (existsSync103(globDir)) {
596858
+ if (existsSync104(globDir)) {
596080
596859
  for (const f2 of readdirSync35(globDir).filter((f3) => f3.endsWith(".json"))) {
596081
596860
  const name10 = f2.replace(".json", "");
596082
596861
  if (seen.has(name10)) continue;
596083
596862
  try {
596084
- const raw = JSON.parse(readFileSync84(join119(globDir, f2), "utf8"));
596863
+ const raw = JSON.parse(readFileSync85(join120(globDir, f2), "utf8"));
596085
596864
  result.push({
596086
596865
  name: name10,
596087
596866
  description: raw.description || "",
@@ -596096,11 +596875,11 @@ function listProfiles(projectDir2) {
596096
596875
  }
596097
596876
  function loadProfile(name10, password, projectDir2) {
596098
596877
  const sanitized = name10.replace(/[^a-zA-Z0-9_-]/g, "");
596099
- const projPath = join119(projectProfileDir(projectDir2), `${sanitized}.json`);
596100
- const globPath = join119(globalProfileDir(), `${sanitized}.json`);
596101
- const filePath = existsSync103(projPath) ? projPath : existsSync103(globPath) ? globPath : null;
596878
+ const projPath = join120(projectProfileDir(projectDir2), `${sanitized}.json`);
596879
+ const globPath = join120(globalProfileDir(), `${sanitized}.json`);
596880
+ const filePath = existsSync104(projPath) ? projPath : existsSync104(globPath) ? globPath : null;
596102
596881
  if (!filePath) return null;
596103
- const raw = JSON.parse(readFileSync84(filePath, "utf8"));
596882
+ const raw = JSON.parse(readFileSync85(filePath, "utf8"));
596104
596883
  if (raw.encrypted === true) {
596105
596884
  if (!password) return null;
596106
596885
  return decryptProfile(raw, password);
@@ -596111,7 +596890,7 @@ function saveProfile(profile, password, scope = "global", projectDir2) {
596111
596890
  const dir = scope === "project" ? projectProfileDir(projectDir2) : globalProfileDir();
596112
596891
  mkdirSync61(dir, { recursive: true });
596113
596892
  const sanitized = profile.name.replace(/[^a-zA-Z0-9_-]/g, "");
596114
- const filePath = join119(dir, `${sanitized}.json`);
596893
+ const filePath = join120(dir, `${sanitized}.json`);
596115
596894
  profile.modified = (/* @__PURE__ */ new Date()).toISOString();
596116
596895
  if (password) {
596117
596896
  const encrypted = encryptProfile(profile, password);
@@ -596124,8 +596903,8 @@ function saveProfile(profile, password, scope = "global", projectDir2) {
596124
596903
  function deleteProfile(name10, scope = "global", projectDir2) {
596125
596904
  const sanitized = name10.replace(/[^a-zA-Z0-9_-]/g, "");
596126
596905
  const dir = scope === "project" ? projectProfileDir(projectDir2) : globalProfileDir();
596127
- const filePath = join119(dir, `${sanitized}.json`);
596128
- if (existsSync103(filePath)) {
596906
+ const filePath = join120(dir, `${sanitized}.json`);
596907
+ if (existsSync104(filePath)) {
596129
596908
  unlinkSync23(filePath);
596130
596909
  return true;
596131
596910
  }
@@ -596240,23 +597019,23 @@ var init_profiles = __esm({
596240
597019
 
596241
597020
  // packages/cli/src/docker.ts
596242
597021
  import { execSync as execSync56, spawn as spawn24 } from "node:child_process";
596243
- import { existsSync as existsSync104, mkdirSync as mkdirSync62, writeFileSync as writeFileSync54 } from "node:fs";
596244
- import { join as join120, resolve as resolve37, dirname as dirname35 } from "node:path";
596245
- import { homedir as homedir41 } from "node:os";
597022
+ import { existsSync as existsSync105, mkdirSync as mkdirSync62, writeFileSync as writeFileSync54 } from "node:fs";
597023
+ import { join as join121, resolve as resolve37, dirname as dirname35 } from "node:path";
597024
+ import { homedir as homedir42 } from "node:os";
596246
597025
  import { fileURLToPath as fileURLToPath16 } from "node:url";
596247
597026
  function getDockerDir() {
596248
597027
  try {
596249
597028
  if (typeof __dirname !== "undefined") {
596250
- return join120(__dirname, "..", "..", "..", "docker");
597029
+ return join121(__dirname, "..", "..", "..", "docker");
596251
597030
  }
596252
597031
  } catch {
596253
597032
  }
596254
597033
  try {
596255
597034
  const thisDir = dirname35(fileURLToPath16(import.meta.url));
596256
- return join120(thisDir, "..", "..", "..", "docker");
597035
+ return join121(thisDir, "..", "..", "..", "docker");
596257
597036
  } catch {
596258
597037
  }
596259
- return join120(process.cwd(), "docker");
597038
+ return join121(process.cwd(), "docker");
596260
597039
  }
596261
597040
  function isDockerAvailable() {
596262
597041
  try {
@@ -596387,10 +597166,10 @@ async function ensureOaImage(force = false) {
596387
597166
  }
596388
597167
  let buildContext;
596389
597168
  const dockerDir = getDockerDir();
596390
- if (existsSync104(join120(dockerDir, "Dockerfile"))) {
597169
+ if (existsSync105(join121(dockerDir, "Dockerfile"))) {
596391
597170
  buildContext = dockerDir;
596392
597171
  } else {
596393
- buildContext = join120(homedir41(), ".oa", "docker-build");
597172
+ buildContext = join121(homedir42(), ".oa", "docker-build");
596394
597173
  mkdirSync62(buildContext, { recursive: true });
596395
597174
  writeDockerfiles(buildContext);
596396
597175
  }
@@ -596465,8 +597244,8 @@ chown -R node:node /workspace /home/node/.oa /home/node/.open-agents 2>/dev/null
596465
597244
  if [ "$1" = "oa" ]; then shift; exec su - node -c "cd /workspace && oa $*"; fi
596466
597245
  exec "$@"
596467
597246
  `;
596468
- writeFileSync54(join120(dir, "Dockerfile"), dockerfile);
596469
- writeFileSync54(join120(dir, "docker-entrypoint.sh"), entrypoint, { mode: 493 });
597247
+ writeFileSync54(join121(dir, "Dockerfile"), dockerfile);
597248
+ writeFileSync54(join121(dir, "docker-entrypoint.sh"), entrypoint, { mode: 493 });
596470
597249
  }
596471
597250
  function hasNvidiaGpu() {
596472
597251
  try {
@@ -596719,23 +597498,23 @@ import * as http5 from "node:http";
596719
597498
  import * as https3 from "node:https";
596720
597499
  import { createRequire as createRequire5 } from "node:module";
596721
597500
  import { fileURLToPath as fileURLToPath17 } from "node:url";
596722
- import { dirname as dirname36, join as join121, resolve as resolve38 } from "node:path";
596723
- import { homedir as homedir42 } from "node:os";
597501
+ import { dirname as dirname36, join as join122, resolve as resolve38 } from "node:path";
597502
+ import { homedir as homedir43 } from "node:os";
596724
597503
  import { spawn as spawn25, execSync as execSync57 } from "node:child_process";
596725
- import { mkdirSync as mkdirSync63, writeFileSync as writeFileSync55, readFileSync as readFileSync85, readdirSync as readdirSync36, existsSync as existsSync105, watch as fsWatch3, renameSync as renameSync8, unlinkSync as unlinkSync24 } from "node:fs";
597504
+ import { mkdirSync as mkdirSync63, writeFileSync as writeFileSync55, readFileSync as readFileSync86, readdirSync as readdirSync36, existsSync as existsSync106, watch as fsWatch3, renameSync as renameSync8, unlinkSync as unlinkSync24 } from "node:fs";
596726
597505
  import { randomBytes as randomBytes23, randomUUID as randomUUID16 } from "node:crypto";
596727
597506
  import { createHash as createHash19 } from "node:crypto";
596728
597507
  function getVersion3() {
596729
597508
  try {
596730
597509
  const thisDir = dirname36(fileURLToPath17(import.meta.url));
596731
597510
  const candidates = [
596732
- join121(thisDir, "..", "package.json"),
596733
- join121(thisDir, "..", "..", "package.json"),
596734
- join121(thisDir, "..", "..", "..", "package.json")
597511
+ join122(thisDir, "..", "package.json"),
597512
+ join122(thisDir, "..", "..", "package.json"),
597513
+ join122(thisDir, "..", "..", "..", "package.json")
596735
597514
  ];
596736
597515
  for (const pkgPath of candidates) {
596737
597516
  try {
596738
- if (!existsSync105(pkgPath)) continue;
597517
+ if (!existsSync106(pkgPath)) continue;
596739
597518
  const pkg = require3(pkgPath);
596740
597519
  if (pkg.name === "open-agents-ai" || pkg.name === "@open-agents/cli" || pkg.name === "@open-agents/monorepo") {
596741
597520
  return pkg.version ?? "0.0.0";
@@ -596937,9 +597716,9 @@ function isOriginAllowed(origin) {
596937
597716
  if (!origin) return true;
596938
597717
  let accessMode = (process.env["OA_ACCESS"] || "").toLowerCase().trim();
596939
597718
  try {
596940
- const accessFile = join121(homedir42(), ".open-agents", "access");
596941
- if (existsSync105(accessFile)) {
596942
- const persisted = readFileSync85(accessFile, "utf8").trim().toLowerCase();
597719
+ const accessFile = join122(homedir43(), ".open-agents", "access");
597720
+ if (existsSync106(accessFile)) {
597721
+ const persisted = readFileSync86(accessFile, "utf8").trim().toLowerCase();
596943
597722
  if (persisted === "any" || persisted === "lan" || persisted === "loopback") {
596944
597723
  accessMode = persisted;
596945
597724
  }
@@ -596999,7 +597778,7 @@ async function retrieveMemoryContext(userMessage, sessionId, maxEpisodes = 5) {
596999
597778
  if (!memMod || !memMod.EpisodeStore) {
597000
597779
  return { contextBlock: "", retrieved: [] };
597001
597780
  }
597002
- const dbPath = join121(homedir42(), ".open-agents", "memory.db");
597781
+ const dbPath = join122(homedir43(), ".open-agents", "memory.db");
597003
597782
  const store2 = new memMod.EpisodeStore(dbPath);
597004
597783
  const recent = store2.search({ limit: 30, sessionId: void 0 }) ?? [];
597005
597784
  const qLower = userMessage.toLowerCase();
@@ -597042,7 +597821,7 @@ async function writeMemoryEpisodes(sessionId, userMessage, assistantContent, too
597042
597821
  try {
597043
597822
  const memMod = await Promise.resolve().then(() => (init_dist7(), dist_exports2)).catch(() => null);
597044
597823
  if (!memMod || !memMod.EpisodeStore) return 0;
597045
- const dbPath = join121(homedir42(), ".open-agents", "memory.db");
597824
+ const dbPath = join122(homedir43(), ".open-agents", "memory.db");
597046
597825
  const store2 = new memMod.EpisodeStore(dbPath);
597047
597826
  let written = 0;
597048
597827
  try {
@@ -597364,27 +598143,27 @@ function ollamaStream(ollamaUrl, path8, method, body, onData, onEnd, onError, ti
597364
598143
  }
597365
598144
  function jobsDir() {
597366
598145
  const root = resolve38(process.cwd());
597367
- const dir = join121(root, ".oa", "jobs");
598146
+ const dir = join122(root, ".oa", "jobs");
597368
598147
  mkdirSync63(dir, { recursive: true });
597369
598148
  return dir;
597370
598149
  }
597371
598150
  function loadJob(id) {
597372
- const file = join121(jobsDir(), `${id}.json`);
597373
- if (!existsSync105(file)) return null;
598151
+ const file = join122(jobsDir(), `${id}.json`);
598152
+ if (!existsSync106(file)) return null;
597374
598153
  try {
597375
- return JSON.parse(readFileSync85(file, "utf-8"));
598154
+ return JSON.parse(readFileSync86(file, "utf-8"));
597376
598155
  } catch {
597377
598156
  return null;
597378
598157
  }
597379
598158
  }
597380
598159
  function listJobs() {
597381
598160
  const dir = jobsDir();
597382
- if (!existsSync105(dir)) return [];
598161
+ if (!existsSync106(dir)) return [];
597383
598162
  const files = readdirSync36(dir).filter((f2) => f2.endsWith(".json")).sort();
597384
598163
  const jobs = [];
597385
598164
  for (const file of files) {
597386
598165
  try {
597387
- jobs.push(JSON.parse(readFileSync85(join121(dir, file), "utf-8")));
598166
+ jobs.push(JSON.parse(readFileSync86(join122(dir, file), "utf-8")));
597388
598167
  } catch {
597389
598168
  }
597390
598169
  }
@@ -597394,14 +598173,14 @@ function pruneOldJobs() {
597394
598173
  const retentionH = parseFloat(process.env["OA_RUN_RETENTION_H"] || "24");
597395
598174
  const cutoffMs = Date.now() - (Number.isFinite(retentionH) && retentionH > 0 ? retentionH : 24) * 36e5;
597396
598175
  const dir = jobsDir();
597397
- if (!existsSync105(dir)) return { pruned: 0, kept: 0 };
598176
+ if (!existsSync106(dir)) return { pruned: 0, kept: 0 };
597398
598177
  let pruned = 0;
597399
598178
  let kept = 0;
597400
598179
  for (const file of readdirSync36(dir)) {
597401
598180
  if (!file.endsWith(".json")) continue;
597402
- const path8 = join121(dir, file);
598181
+ const path8 = join122(dir, file);
597403
598182
  try {
597404
- const job = JSON.parse(readFileSync85(path8, "utf-8"));
598183
+ const job = JSON.parse(readFileSync86(path8, "utf-8"));
597405
598184
  if (job.status === "running") {
597406
598185
  kept++;
597407
598186
  continue;
@@ -597414,7 +598193,7 @@ function pruneOldJobs() {
597414
598193
  } catch {
597415
598194
  }
597416
598195
  const outFile = path8.replace(/\.json$/, ".output");
597417
- if (existsSync105(outFile)) {
598196
+ if (existsSync106(outFile)) {
597418
598197
  try {
597419
598198
  unlinkSync24(outFile);
597420
598199
  } catch {
@@ -597707,7 +598486,7 @@ function autoSeedTodosFromPrompt(prompt) {
597707
598486
  return [];
597708
598487
  }
597709
598488
  function atomicJobWrite(dir, id, job) {
597710
- const finalPath = join121(dir, `${id}.json`);
598489
+ const finalPath = join122(dir, `${id}.json`);
597711
598490
  const tmpPath = `${finalPath}.tmp.${process.pid}.${Date.now()}`;
597712
598491
  try {
597713
598492
  writeFileSync55(tmpPath, JSON.stringify(job, null, 2), "utf-8");
@@ -599133,23 +599912,23 @@ ${task}` : task;
599133
599912
  });
599134
599913
  }
599135
599914
  function updateStateFile() {
599136
- return join121(homedir42(), ".open-agents", "update-state.json");
599915
+ return join122(homedir43(), ".open-agents", "update-state.json");
599137
599916
  }
599138
599917
  function updateLogPath() {
599139
- return join121(homedir42(), ".open-agents", "update.log");
599918
+ return join122(homedir43(), ".open-agents", "update.log");
599140
599919
  }
599141
599920
  function readUpdateState() {
599142
599921
  try {
599143
599922
  const p2 = updateStateFile();
599144
- if (!existsSync105(p2)) return null;
599145
- return JSON.parse(readFileSync85(p2, "utf-8"));
599923
+ if (!existsSync106(p2)) return null;
599924
+ return JSON.parse(readFileSync86(p2, "utf-8"));
599146
599925
  } catch {
599147
599926
  return null;
599148
599927
  }
599149
599928
  }
599150
599929
  function writeUpdateState(state) {
599151
599930
  try {
599152
- const dir = join121(homedir42(), ".open-agents");
599931
+ const dir = join122(homedir43(), ".open-agents");
599153
599932
  mkdirSync63(dir, { recursive: true });
599154
599933
  const finalPath = updateStateFile();
599155
599934
  const tmpPath = `${finalPath}.tmp.${process.pid}`;
@@ -599197,15 +599976,15 @@ async function handleV1Update(req2, res, requestId) {
599197
599976
  const { execSync: es } = require3("node:child_process");
599198
599977
  const isWin2 = process.platform === "win32";
599199
599978
  let npmBin = "";
599200
- for (const candidate of isWin2 ? [join121(nodeDir, "npm.cmd"), join121(nodeDir, "npm")] : [join121(nodeDir, "npm"), "/usr/local/bin/npm", "/usr/bin/npm"]) {
599201
- if (existsSync105(candidate)) {
599979
+ for (const candidate of isWin2 ? [join122(nodeDir, "npm.cmd"), join122(nodeDir, "npm")] : [join122(nodeDir, "npm"), "/usr/local/bin/npm", "/usr/bin/npm"]) {
599980
+ if (existsSync106(candidate)) {
599202
599981
  npmBin = candidate;
599203
599982
  break;
599204
599983
  }
599205
599984
  }
599206
599985
  if (!npmBin) npmBin = isWin2 ? "npm.cmd" : "npm";
599207
599986
  const pkgSpec = `open-agents-ai@${targetVersion}`;
599208
- const dir = join121(homedir42(), ".open-agents");
599987
+ const dir = join122(homedir43(), ".open-agents");
599209
599988
  fs7.mkdirSync(dir, { recursive: true });
599210
599989
  const logFd = fs7.openSync(logPath3, "w");
599211
599990
  const npmPrefix = dirname36(nodeDir);
@@ -599215,13 +599994,13 @@ async function handleV1Update(req2, res, requestId) {
599215
599994
  globalBinDir = es(`${npmBin} bin -g`, { encoding: "utf8", timeout: 5e3, stdio: "pipe" }).trim();
599216
599995
  } else {
599217
599996
  const npmCliCandidates = [
599218
- join121(nodeDir, "..", "lib", "node_modules", "npm", "bin", "npm-cli.js"),
599219
- join121(npmBin, "..", "..", "lib", "node_modules", "npm", "bin", "npm-cli.js")
599997
+ join122(nodeDir, "..", "lib", "node_modules", "npm", "bin", "npm-cli.js"),
599998
+ join122(npmBin, "..", "..", "lib", "node_modules", "npm", "bin", "npm-cli.js")
599220
599999
  ];
599221
600000
  let npmCli = "";
599222
600001
  for (const c9 of npmCliCandidates) {
599223
600002
  try {
599224
- if (existsSync105(c9)) {
600003
+ if (existsSync106(c9)) {
599225
600004
  npmCli = c9;
599226
600005
  break;
599227
600006
  }
@@ -599253,13 +600032,13 @@ async function handleV1Update(req2, res, requestId) {
599253
600032
  });
599254
600033
  } else {
599255
600034
  const npmCliCandidates = [
599256
- join121(nodeDir, "..", "lib", "node_modules", "npm", "bin", "npm-cli.js"),
599257
- join121(npmBin, "..", "..", "lib", "node_modules", "npm", "bin", "npm-cli.js")
600035
+ join122(nodeDir, "..", "lib", "node_modules", "npm", "bin", "npm-cli.js"),
600036
+ join122(npmBin, "..", "..", "lib", "node_modules", "npm", "bin", "npm-cli.js")
599258
600037
  ];
599259
600038
  let npmCli = "";
599260
600039
  for (const c9 of npmCliCandidates) {
599261
600040
  try {
599262
- if (existsSync105(c9)) {
600041
+ if (existsSync106(c9)) {
599263
600042
  npmCli = c9;
599264
600043
  break;
599265
600044
  }
@@ -599356,8 +600135,8 @@ function handleV1UpdateStatus(res) {
599356
600135
  let logTail = "";
599357
600136
  let exitCode = null;
599358
600137
  try {
599359
- if (existsSync105(logPath3)) {
599360
- const raw = readFileSync85(logPath3, "utf-8");
600138
+ if (existsSync106(logPath3)) {
600139
+ const raw = readFileSync86(logPath3, "utf-8");
599361
600140
  const m2 = raw.match(/__EXIT_CODE=(\d+)/);
599362
600141
  if (m2) exitCode = parseInt(m2[1], 10);
599363
600142
  logTail = raw.slice(-2e3);
@@ -599453,7 +600232,7 @@ async function handleV1Run(req2, res) {
599453
600232
  if (workingDir) {
599454
600233
  cwd4 = resolve38(workingDir);
599455
600234
  } else if (isolate) {
599456
- const wsDir = join121(dir, "..", "workspaces", id);
600235
+ const wsDir = join122(dir, "..", "workspaces", id);
599457
600236
  mkdirSync63(wsDir, { recursive: true });
599458
600237
  cwd4 = wsDir;
599459
600238
  } else {
@@ -599640,7 +600419,7 @@ async function handleV1Run(req2, res) {
599640
600419
  let output = "";
599641
600420
  let tailBytes = 0;
599642
600421
  const TAIL_BUDGET = 1048576;
599643
- const outputWriter = new DiskTaskOutput(join121(dir, `${id}.output`));
600422
+ const outputWriter = new DiskTaskOutput(join122(dir, `${id}.output`));
599644
600423
  job.outputFile = outputWriter.path;
599645
600424
  atomicJobWrite(dir, id, job);
599646
600425
  child.stdout?.on("data", (chunk) => {
@@ -602214,7 +602993,7 @@ ${steering}`;
602214
602993
  function getScheduleRoots() {
602215
602994
  const rootsEnv = process.env["OA_SCHEDULE_ROOTS"] || "";
602216
602995
  const roots = rootsEnv.split(rootsEnv.includes(";") ? ";" : ":").filter(Boolean);
602217
- const defaults3 = [process.cwd(), join121(homedir42(), "Documents")];
602996
+ const defaults3 = [process.cwd(), join122(homedir43(), "Documents")];
602218
602997
  const set = /* @__PURE__ */ new Set([...defaults3, ...roots]);
602219
602998
  return [...set];
602220
602999
  }
@@ -602226,10 +603005,10 @@ function listScheduledTasks() {
602226
603005
  for (const root of roots) {
602227
603006
  try {
602228
603007
  walk(root, 0, (dir) => {
602229
- if (dir.endsWith(`${join121(".oa", "scheduled")}`) || dir.includes(`${join121(".oa", "scheduled")}`)) {
602230
- const file = join121(dir, "tasks.json");
603008
+ if (dir.endsWith(`${join122(".oa", "scheduled")}`) || dir.includes(`${join122(".oa", "scheduled")}`)) {
603009
+ const file = join122(dir, "tasks.json");
602231
603010
  try {
602232
- const raw = readFileSync85(file, "utf-8");
603011
+ const raw = readFileSync86(file, "utf-8");
602233
603012
  const json = JSON.parse(raw);
602234
603013
  const tasks = Array.isArray(json?.tasks) ? json.tasks : Array.isArray(json) ? json : [];
602235
603014
  tasks.forEach((t2, i2) => {
@@ -602294,7 +603073,7 @@ function walk(dir, depth, onDir, maxDepth) {
602294
603073
  if (e2.name === "node_modules" || e2.name.startsWith(".")) {
602295
603074
  if (e2.name !== ".oa") continue;
602296
603075
  }
602297
- const child = join121(dir, e2.name);
603076
+ const child = join122(dir, e2.name);
602298
603077
  walk(child, depth + 1, onDir, maxDepth);
602299
603078
  }
602300
603079
  }
@@ -602303,7 +603082,7 @@ function setScheduledEnabled(id, enabled2) {
602303
603082
  const target = tasks.find((t2) => t2.id === id);
602304
603083
  if (!target) return false;
602305
603084
  try {
602306
- const raw = readFileSync85(target.file, "utf-8");
603085
+ const raw = readFileSync86(target.file, "utf-8");
602307
603086
  const json = JSON.parse(raw);
602308
603087
  const arr = Array.isArray(json?.tasks) ? json.tasks : Array.isArray(json) ? json : [];
602309
603088
  if (!arr[target.index]) return false;
@@ -602336,7 +603115,7 @@ function deleteScheduledById(id) {
602336
603115
  const target = tasks.find((t2) => t2.id === id);
602337
603116
  if (!target) return false;
602338
603117
  try {
602339
- const raw = readFileSync85(target.file, "utf-8");
603118
+ const raw = readFileSync86(target.file, "utf-8");
602340
603119
  const json = JSON.parse(raw);
602341
603120
  const arr = Array.isArray(json?.tasks) ? json.tasks : Array.isArray(json) ? json : [];
602342
603121
  if (!arr[target.index]) return false;
@@ -602601,11 +603380,11 @@ function reconcileScheduledTasks(apply) {
602601
603380
  const errors = [];
602602
603381
  for (const f2 of found) {
602603
603382
  const wdir = f2.workingDir || process.cwd();
602604
- const file = join121(wdir, ".oa", "scheduled", "tasks.json");
603383
+ const file = join122(wdir, ".oa", "scheduled", "tasks.json");
602605
603384
  try {
602606
603385
  let json = { tasks: [] };
602607
603386
  try {
602608
- const raw = readFileSync85(file, "utf-8");
603387
+ const raw = readFileSync86(file, "utf-8");
602609
603388
  json = JSON.parse(raw);
602610
603389
  } catch {
602611
603390
  }
@@ -602616,8 +603395,8 @@ function reconcileScheduledTasks(apply) {
602616
603395
  const entry = { task: f2.task || `legacy ${f2.id}`, schedule: f2.cron, enabled: true };
602617
603396
  arr.push(entry);
602618
603397
  const toWrite = Array.isArray(json?.tasks) ? { ...json, tasks: arr } : Array.isArray(json) ? arr : { tasks: arr };
602619
- mkdirSync63(join121(wdir, ".oa", "scheduled"), { recursive: true });
602620
- mkdirSync63(join121(wdir, ".oa", "scheduled", "logs"), { recursive: true });
603398
+ mkdirSync63(join122(wdir, ".oa", "scheduled"), { recursive: true });
603399
+ mkdirSync63(join122(wdir, ".oa", "scheduled", "logs"), { recursive: true });
602621
603400
  writeFileSync55(file, JSON.stringify(toWrite, null, 2));
602622
603401
  adopted.push({ file, index: arr.length - 1 });
602623
603402
  }
@@ -602662,32 +603441,32 @@ function writeCrontabLines(lines) {
602662
603441
  }
602663
603442
  function canonicalCronLine(rec) {
602664
603443
  const oaBin = findOaBinary4();
602665
- const logDir = join121(rec.workingDir, ".oa", "scheduled", "logs");
602666
- const logFile = join121(logDir, `${rec.id}.log`);
602667
- const storeFile = join121(rec.workingDir, ".oa", "scheduled", "tasks.json");
603444
+ const logDir = join122(rec.workingDir, ".oa", "scheduled", "logs");
603445
+ const logFile = join122(logDir, `${rec.id}.log`);
603446
+ const storeFile = join122(rec.workingDir, ".oa", "scheduled", "tasks.json");
602668
603447
  const taskEsc = rec.task.replace(/'/g, "'\\''");
602669
- const lockDir = join121(rec.workingDir, ".oa", "run");
602670
- const lockPath = join121(lockDir, `${rec.id}.lock`);
603448
+ const lockDir = join122(rec.workingDir, ".oa", "run");
603449
+ const lockPath = join122(lockDir, `${rec.id}.lock`);
602671
603450
  const wrapper = [
602672
603451
  `cd ${JSON.stringify(rec.workingDir)}`,
602673
603452
  `mkdir -p ${JSON.stringify(logDir)}`,
602674
603453
  `mkdir -p ${JSON.stringify(lockDir)}`,
602675
603454
  `if mkdir ${JSON.stringify(lockPath)} 2>/dev/null; then`,
602676
- ` echo $$ > ${JSON.stringify(join121(lockPath, "pid"))}`,
603455
+ ` echo $$ > ${JSON.stringify(join122(lockPath, "pid"))}`,
602677
603456
  ` trap 'rm -rf ${lockPath}' EXIT`,
602678
603457
  `else`,
602679
- ` if [ -f ${JSON.stringify(join121(lockPath, "pid"))} ]; then`,
602680
- ` oldpid=$(cat ${JSON.stringify(join121(lockPath, "pid"))} 2>/dev/null || echo)`,
603458
+ ` if [ -f ${JSON.stringify(join122(lockPath, "pid"))} ]; then`,
603459
+ ` oldpid=$(cat ${JSON.stringify(join122(lockPath, "pid"))} 2>/dev/null || echo)`,
602681
603460
  ` if [ -n "$oldpid" ] && kill -0 "$oldpid" 2>/dev/null; then`,
602682
603461
  ` echo "[oa-scheduler] ${rec.id} already running as PID $oldpid; skipping" >> ${JSON.stringify(logFile)}`,
602683
603462
  ` exit 0`,
602684
603463
  ` else`,
602685
603464
  ` rm -rf ${JSON.stringify(lockPath)} 2>/dev/null || true`,
602686
- ` mkdir -p ${JSON.stringify(lockPath)} && echo $$ > ${JSON.stringify(join121(lockPath, "pid"))} && trap 'rm -rf ${lockPath}' EXIT`,
603465
+ ` mkdir -p ${JSON.stringify(lockPath)} && echo $$ > ${JSON.stringify(join122(lockPath, "pid"))} && trap 'rm -rf ${lockPath}' EXIT`,
602687
603466
  ` fi`,
602688
603467
  ` else`,
602689
603468
  ` rm -rf ${JSON.stringify(lockPath)} 2>/dev/null || true`,
602690
- ` mkdir -p ${JSON.stringify(lockPath)} && echo $$ > ${JSON.stringify(join121(lockPath, "pid"))} && trap 'rm -rf ${lockPath}' EXIT`,
603469
+ ` mkdir -p ${JSON.stringify(lockPath)} && echo $$ > ${JSON.stringify(join122(lockPath, "pid"))} && trap 'rm -rf ${lockPath}' EXIT`,
602691
603470
  ` fi`,
602692
603471
  `fi`,
602693
603472
  `${oaBin} '${taskEsc}' >> ${JSON.stringify(logFile)} 2>&1; _oa_exit=$?`,
@@ -602719,9 +603498,9 @@ function fixupOrMigrateScheduled(mode, dryRun) {
602719
603498
  try {
602720
603499
  if (!f2.workingDir || !f2.task) continue;
602721
603500
  const unitBase = `oa-${f2.id}`;
602722
- const unitDir = join121(homedir42(), ".config", "systemd", "user");
602723
- const svc = join121(unitDir, `${unitBase}.service`);
602724
- const tim = join121(unitDir, `${unitBase}.timer`);
603501
+ const unitDir = join122(homedir43(), ".config", "systemd", "user");
603502
+ const svc = join122(unitDir, `${unitBase}.service`);
603503
+ const tim = join122(unitDir, `${unitBase}.timer`);
602725
603504
  const oaBin = findOaBinary4();
602726
603505
  const rec = { id: f2.id, cron: f2.cron, workingDir: f2.workingDir, task: f2.task };
602727
603506
  const cmd = canonicalCronLine(rec).split(" ").slice(5).join(" ");
@@ -602847,8 +603626,8 @@ function startApiServer(options2 = {}) {
602847
603626
  const config = loadConfig();
602848
603627
  const ollamaUrl = options2.ollamaUrl ?? config.backendUrl;
602849
603628
  const cwd4 = process.cwd();
602850
- initAuditLog(join121(cwd4, ".oa"));
602851
- initUsageTracker(join121(cwd4, ".oa"));
603629
+ initAuditLog(join122(cwd4, ".oa"));
603630
+ initUsageTracker(join122(cwd4, ".oa"));
602852
603631
  try {
602853
603632
  const taskMgr = getSharedTaskManager();
602854
603633
  taskMgr.setEventPublisher((type, data, opts) => {
@@ -602899,7 +603678,7 @@ function startApiServer(options2 = {}) {
602899
603678
  if (!f2.endsWith(".json") || f2.includes(".tmp.")) continue;
602900
603679
  const sid = f2.replace(/\.json$/, "");
602901
603680
  try {
602902
- const items = JSON.parse(readFileSync85(join121(dir, f2), "utf-8"));
603681
+ const items = JSON.parse(readFileSync86(join122(dir, f2), "utf-8"));
602903
603682
  if (Array.isArray(items)) {
602904
603683
  cache8.set(sid, new Map(items.map((t2) => [t2.id, t2])));
602905
603684
  }
@@ -602911,10 +603690,10 @@ function startApiServer(options2 = {}) {
602911
603690
  const watcher = fsWatch3(dir, (_evt, fname) => {
602912
603691
  if (!fname || !fname.endsWith(".json") || fname.includes(".tmp.")) return;
602913
603692
  const sid = fname.replace(/\.json$/, "");
602914
- const fp = join121(dir, fname);
603693
+ const fp = join122(dir, fname);
602915
603694
  let next = [];
602916
603695
  try {
602917
- if (!existsSync105(fp)) {
603696
+ if (!existsSync106(fp)) {
602918
603697
  const old = cache8.get(sid);
602919
603698
  if (old) {
602920
603699
  for (const t2 of old.values()) {
@@ -602927,7 +603706,7 @@ function startApiServer(options2 = {}) {
602927
603706
  }
602928
603707
  return;
602929
603708
  }
602930
- next = JSON.parse(readFileSync85(fp, "utf-8"));
603709
+ next = JSON.parse(readFileSync86(fp, "utf-8"));
602931
603710
  if (!Array.isArray(next)) return;
602932
603711
  } catch {
602933
603712
  return;
@@ -602966,14 +603745,14 @@ function startApiServer(options2 = {}) {
602966
603745
  const retentionDays = parseInt(process.env["OA_JOB_RETENTION_DAYS"] ?? "30", 10);
602967
603746
  if (retentionDays > 0) {
602968
603747
  try {
602969
- const jobsDir3 = join121(cwd4, ".oa", "jobs");
602970
- if (existsSync105(jobsDir3)) {
603748
+ const jobsDir3 = join122(cwd4, ".oa", "jobs");
603749
+ if (existsSync106(jobsDir3)) {
602971
603750
  const cutoff = Date.now() - retentionDays * 864e5;
602972
603751
  for (const f2 of readdirSync36(jobsDir3)) {
602973
603752
  if (!f2.endsWith(".json")) continue;
602974
603753
  try {
602975
- const jobPath = join121(jobsDir3, f2);
602976
- const job = JSON.parse(readFileSync85(jobPath, "utf-8"));
603754
+ const jobPath = join122(jobsDir3, f2);
603755
+ const job = JSON.parse(readFileSync86(jobPath, "utf-8"));
602977
603756
  const jobTime = new Date(job.startedAt ?? job.completedAt ?? 0).getTime();
602978
603757
  if (jobTime > 0 && jobTime < cutoff && job.status !== "running") {
602979
603758
  const { unlinkSync: unlinkSync25 } = require3("node:fs");
@@ -602993,8 +603772,8 @@ function startApiServer(options2 = {}) {
602993
603772
  if (useTls) {
602994
603773
  try {
602995
603774
  tlsOpts = {
602996
- cert: readFileSync85(resolve38(tlsCert)),
602997
- key: readFileSync85(resolve38(tlsKey))
603775
+ cert: readFileSync86(resolve38(tlsCert)),
603776
+ key: readFileSync86(resolve38(tlsKey))
602998
603777
  };
602999
603778
  } catch (e2) {
603000
603779
  log22(`
@@ -603005,9 +603784,9 @@ function startApiServer(options2 = {}) {
603005
603784
  }
603006
603785
  let runtimeAccessMode = resolveAccessMode(process.env["OA_ACCESS"], host);
603007
603786
  try {
603008
- const accessFile = join121(homedir42(), ".open-agents", "access");
603009
- if (existsSync105(accessFile)) {
603010
- const persisted = readFileSync85(accessFile, "utf8").trim();
603787
+ const accessFile = join122(homedir43(), ".open-agents", "access");
603788
+ if (existsSync106(accessFile)) {
603789
+ const persisted = readFileSync86(accessFile, "utf8").trim();
603011
603790
  const resolved = resolveAccessMode(persisted, host);
603012
603791
  if (resolved) runtimeAccessMode = resolved;
603013
603792
  }
@@ -603060,9 +603839,9 @@ function startApiServer(options2 = {}) {
603060
603839
  const previous = runtimeAccessMode;
603061
603840
  runtimeAccessMode = requested;
603062
603841
  try {
603063
- const dir = join121(homedir42(), ".open-agents");
603842
+ const dir = join122(homedir43(), ".open-agents");
603064
603843
  mkdirSync63(dir, { recursive: true });
603065
- writeFileSync55(join121(dir, "access"), `${runtimeAccessMode}
603844
+ writeFileSync55(join122(dir, "access"), `${runtimeAccessMode}
603066
603845
  `, "utf8");
603067
603846
  } catch {
603068
603847
  }
@@ -603275,9 +604054,9 @@ function startApiServer(options2 = {}) {
603275
604054
  try {
603276
604055
  const { startEmbeddingWorkers: startEmbeddingWorkers2 } = await Promise.resolve().then(() => (init_embedding_workers(), embedding_workers_exports));
603277
604056
  const { ensureEmbedDeps: ensureEmbedDeps2, runEmbedImage: runEmbedImage2, runEmbedAudio: runEmbedAudio2 } = await Promise.resolve().then(() => (init_py_embed(), py_embed_exports));
603278
- const dbBase = join121(cwd4, ".oa");
603279
- const epStore = new mem.EpisodeStore(join121(dbBase, "memory.db"));
603280
- const kg = new mem.TemporalGraph(join121(dbBase, "kg.db"));
604057
+ const dbBase = join122(cwd4, ".oa");
604058
+ const epStore = new mem.EpisodeStore(join122(dbBase, "memory.db"));
604059
+ const kg = new mem.TemporalGraph(join122(dbBase, "kg.db"));
603281
604060
  try {
603282
604061
  ensureEmbedDeps2();
603283
604062
  } catch {
@@ -603354,6 +604133,34 @@ function startApiServer(options2 = {}) {
603354
604133
  `);
603355
604134
  log22(` Primary: ${config.backendUrl} (${config.backendType || "ollama"})
603356
604135
  `);
604136
+ try {
604137
+ const { writeFileSync: writeFileSync59, mkdirSync: mkdirSync67, existsSync: _exists } = require3("node:fs");
604138
+ const { join: _join } = require3("node:path");
604139
+ const { homedir: _homedir } = require3("node:os");
604140
+ const apiHint = JSON.stringify({
604141
+ port,
604142
+ host: host === "0.0.0.0" ? "127.0.0.1" : host,
604143
+ scheme: proto,
604144
+ pid: process.pid,
604145
+ startedAt: (/* @__PURE__ */ new Date()).toISOString(),
604146
+ version: version4
604147
+ }, null, 2);
604148
+ const candidates = [
604149
+ _join(process.cwd(), ".oa", "nexus"),
604150
+ _join(_homedir(), ".oa", "nexus"),
604151
+ _join(_homedir(), ".open-agents", "nexus")
604152
+ ];
604153
+ for (const dir of candidates) {
604154
+ try {
604155
+ if (!_exists(dir)) mkdirSync67(dir, { recursive: true });
604156
+ writeFileSync59(_join(dir, "api-port.json"), apiHint);
604157
+ } catch {
604158
+ }
604159
+ }
604160
+ } catch (e2) {
604161
+ log22(` WARN: api-port hint write failed: ${e2.message}
604162
+ `);
604163
+ }
603357
604164
  try {
603358
604165
  adoptHandoffRuns();
603359
604166
  } catch (e2) {
@@ -603587,10 +604394,10 @@ async function handleMemoryIngest(req2, res, ollamaUrl) {
603587
604394
  const labels = Array.isArray(b.labels) ? b.labels : [];
603588
604395
  const mediaPath = typeof b.media_path === "string" ? b.media_path : void 0;
603589
604396
  const cwd4 = process.cwd();
603590
- const dbBase = join121(cwd4, ".oa");
604397
+ const dbBase = join122(cwd4, ".oa");
603591
604398
  const { EpisodeStore: EpisodeStore3, TemporalGraph: TemporalGraph3 } = await Promise.resolve().then(() => (init_dist7(), dist_exports2));
603592
- const epStore = new EpisodeStore3(join121(dbBase, "memory.db"));
603593
- const kg = new TemporalGraph3(join121(dbBase, "kg.db"));
604399
+ const epStore = new EpisodeStore3(join122(dbBase, "memory.db"));
604400
+ const kg = new TemporalGraph3(join122(dbBase, "kg.db"));
603594
604401
  const meta = {};
603595
604402
  if (mediaPath) meta.media_path = mediaPath;
603596
604403
  const epId = epStore.insert({ modality, content: content || (mediaPath || ""), metadata: meta, toolName: "memory_ingest" });
@@ -603657,7 +604464,7 @@ async function handleEntitiesList(req2, res) {
603657
604464
  const type = url.searchParams.get("type") || "person";
603658
604465
  const limit = Math.max(1, Math.min(1e3, parseInt(url.searchParams.get("limit") || "100", 10)));
603659
604466
  const { TemporalGraph: TemporalGraph3 } = await Promise.resolve().then(() => (init_dist7(), dist_exports2));
603660
- const kg = new TemporalGraph3(join121(process.cwd(), ".oa", "kg.db"));
604467
+ const kg = new TemporalGraph3(join122(process.cwd(), ".oa", "kg.db"));
603661
604468
  const nodes = kg.nodesByType(type, limit).map((n2) => ({ id: n2.id, text: n2.text, mentionCount: n2.mentionCount, firstSeen: n2.firstSeen, lastSeen: n2.lastSeen }));
603662
604469
  jsonResponse(res, 200, { object: "list", data: nodes });
603663
604470
  } catch (err) {
@@ -603679,7 +604486,7 @@ async function handleMemorySearch2(req2, res) {
603679
604486
  const wLex = typeof b.lexical_weight === "number" ? b.lexical_weight : 1;
603680
604487
  const wEmb = typeof b.embedding_weight === "number" ? b.embedding_weight : 1;
603681
604488
  const { EpisodeStore: EpisodeStore3 } = await Promise.resolve().then(() => (init_dist7(), dist_exports2));
603682
- const epStore = new EpisodeStore3(join121(process.cwd(), ".oa", "memory.db"));
604489
+ const epStore = new EpisodeStore3(join122(process.cwd(), ".oa", "memory.db"));
603683
604490
  const results = epStore.search({ query, modality, limit }, { queryEmbedding: qEmb, lexicalWeight: wLex, embeddingWeight: wEmb });
603684
604491
  jsonResponse(res, 200, { object: "list", data: results.map((e2) => ({ id: e2.id, modality: e2.modality, content: e2.content, timestamp: e2.timestamp })) });
603685
604492
  } catch (err) {
@@ -603796,13 +604603,13 @@ var init_serve = __esm({
603796
604603
 
603797
604604
  // packages/cli/src/tui/interactive.ts
603798
604605
  import { cwd } from "node:process";
603799
- import { resolve as resolve39, join as join122, dirname as dirname37, extname as extname12 } from "node:path";
604606
+ import { resolve as resolve39, join as join123, dirname as dirname37, extname as extname12 } from "node:path";
603800
604607
  import { createRequire as createRequire6 } from "node:module";
603801
604608
  import { fileURLToPath as fileURLToPath18 } from "node:url";
603802
- import { readFileSync as readFileSync86, writeFileSync as writeFileSync56, appendFileSync as appendFileSync8, rmSync as rmSync5, readdirSync as readdirSync37, mkdirSync as mkdirSync64 } from "node:fs";
603803
- import { existsSync as existsSync106 } from "node:fs";
604609
+ import { readFileSync as readFileSync87, writeFileSync as writeFileSync56, appendFileSync as appendFileSync8, rmSync as rmSync5, readdirSync as readdirSync37, mkdirSync as mkdirSync64 } from "node:fs";
604610
+ import { existsSync as existsSync107 } from "node:fs";
603804
604611
  import { execSync as execSync58 } from "node:child_process";
603805
- import { homedir as homedir43 } from "node:os";
604612
+ import { homedir as homedir44 } from "node:os";
603806
604613
  function formatTimeAgo2(date) {
603807
604614
  const seconds = Math.floor((Date.now() - date.getTime()) / 1e3);
603808
604615
  if (seconds < 60) return "just now";
@@ -603818,12 +604625,12 @@ function getVersion4() {
603818
604625
  const require4 = createRequire6(import.meta.url);
603819
604626
  const thisDir = dirname37(fileURLToPath18(import.meta.url));
603820
604627
  const candidates = [
603821
- join122(thisDir, "..", "package.json"),
603822
- join122(thisDir, "..", "..", "package.json"),
603823
- join122(thisDir, "..", "..", "..", "package.json")
604628
+ join123(thisDir, "..", "package.json"),
604629
+ join123(thisDir, "..", "..", "package.json"),
604630
+ join123(thisDir, "..", "..", "..", "package.json")
603824
604631
  ];
603825
604632
  for (const pkgPath of candidates) {
603826
- if (existsSync106(pkgPath)) {
604633
+ if (existsSync107(pkgPath)) {
603827
604634
  const pkg = require4(pkgPath);
603828
604635
  if (pkg.name === "open-agents-ai" || pkg.name === "@open-agents/cli" || pkg.name === "@open-agents/monorepo") {
603829
604636
  return pkg.version ?? "0.0.0";
@@ -604629,14 +605436,14 @@ Meta-critique: quality ${meta.quality}/5, thorough: ${meta.thorough}`;
604629
605436
  function gatherMemorySnippets(root) {
604630
605437
  const snippets = [];
604631
605438
  const dirs = [
604632
- join122(root, ".oa", "memory"),
604633
- join122(root, ".open-agents", "memory")
605439
+ join123(root, ".oa", "memory"),
605440
+ join123(root, ".open-agents", "memory")
604634
605441
  ];
604635
605442
  for (const dir of dirs) {
604636
- if (!existsSync106(dir)) continue;
605443
+ if (!existsSync107(dir)) continue;
604637
605444
  try {
604638
605445
  for (const f2 of readdirSync37(dir).filter((f3) => f3.endsWith(".json"))) {
604639
- const data = JSON.parse(readFileSync86(join122(dir, f2), "utf-8"));
605446
+ const data = JSON.parse(readFileSync87(join123(dir, f2), "utf-8"));
604640
605447
  for (const val of Object.values(data)) {
604641
605448
  const v = typeof val === "object" && val !== null && "value" in val ? String(val.value) : String(val);
604642
605449
  if (v.length > 10) snippets.push(v);
@@ -604790,9 +605597,9 @@ ${metabolismMemories}
604790
605597
  } catch {
604791
605598
  }
604792
605599
  try {
604793
- const archeFile = join122(repoRoot, ".oa", "arche", "variants.json");
604794
- if (existsSync106(archeFile)) {
604795
- const variants = JSON.parse(readFileSync86(archeFile, "utf8"));
605600
+ const archeFile = join123(repoRoot, ".oa", "arche", "variants.json");
605601
+ if (existsSync107(archeFile)) {
605602
+ const variants = JSON.parse(readFileSync87(archeFile, "utf8"));
604796
605603
  if (variants.length > 0) {
604797
605604
  let filtered = variants;
604798
605605
  if (taskType) {
@@ -604964,9 +605771,9 @@ RULES:
604964
605771
  const compactionThreshold = Number.isFinite(envOverride) && envOverride > 0 ? envOverride : modelTier === "small" ? 12e3 : modelTier === "medium" ? 24e3 : 4e4;
604965
605772
  let identityInjection = "";
604966
605773
  try {
604967
- const ikStateFile = join122(repoRoot, ".oa", "identity", "self-state.json");
604968
- if (existsSync106(ikStateFile)) {
604969
- const selfState = JSON.parse(readFileSync86(ikStateFile, "utf8"));
605774
+ const ikStateFile = join123(repoRoot, ".oa", "identity", "self-state.json");
605775
+ if (existsSync107(ikStateFile)) {
605776
+ const selfState = JSON.parse(readFileSync87(ikStateFile, "utf8"));
604970
605777
  const lines = [
604971
605778
  `[Identity State v${selfState.version}]`,
604972
605779
  `Self: ${selfState.narrative_summary}`,
@@ -605828,11 +606635,11 @@ When done, either call task_complete with your answer, or use FINAL_VAR(variable
605828
606635
  });
605829
606636
  }
605830
606637
  try {
605831
- const ikDir = join122(repoRoot, ".oa", "identity");
605832
- const ikFile = join122(ikDir, "self-state.json");
606638
+ const ikDir = join123(repoRoot, ".oa", "identity");
606639
+ const ikFile = join123(ikDir, "self-state.json");
605833
606640
  let ikState;
605834
- if (existsSync106(ikFile)) {
605835
- ikState = JSON.parse(readFileSync86(ikFile, "utf8"));
606641
+ if (existsSync107(ikFile)) {
606642
+ ikState = JSON.parse(readFileSync87(ikFile, "utf8"));
605836
606643
  } else {
605837
606644
  mkdirSync64(ikDir, { recursive: true });
605838
606645
  const machineId = Date.now().toString(36) + Math.random().toString(36).slice(2, 8);
@@ -605909,9 +606716,9 @@ When done, either call task_complete with your answer, or use FINAL_VAR(variable
605909
606716
  } else {
605910
606717
  renderTaskIncomplete(result.turns, result.toolCalls, result.durationMs, tokens);
605911
606718
  try {
605912
- const ikFile = join122(repoRoot, ".oa", "identity", "self-state.json");
605913
- if (existsSync106(ikFile)) {
605914
- const ikState = JSON.parse(readFileSync86(ikFile, "utf8"));
606719
+ const ikFile = join123(repoRoot, ".oa", "identity", "self-state.json");
606720
+ if (existsSync107(ikFile)) {
606721
+ const ikState = JSON.parse(readFileSync87(ikFile, "utf8"));
605915
606722
  if (!ikState.stats) ikState.stats = { queries_served: 0 };
605916
606723
  ikState.stats.queries_served = (ikState.stats.queries_served || 0) + 1;
605917
606724
  ikState.homeostasis.uncertainty = Math.min(1, ikState.homeostasis.uncertainty + 0.03);
@@ -606167,10 +606974,10 @@ async function startInteractive(config, repoPath) {
606167
606974
  process.stdin.pause();
606168
606975
  }
606169
606976
  try {
606170
- const oaDir = join122(repoRoot, ".oa");
606171
- const nexusPidFile = join122(oaDir, "nexus", "daemon.pid");
606172
- if (existsSync106(nexusPidFile)) {
606173
- const pid = parseInt(readFileSync86(nexusPidFile, "utf8").trim(), 10);
606977
+ const oaDir = join123(repoRoot, ".oa");
606978
+ const nexusPidFile = join123(oaDir, "nexus", "daemon.pid");
606979
+ if (existsSync107(nexusPidFile)) {
606980
+ const pid = parseInt(readFileSync87(nexusPidFile, "utf8").trim(), 10);
606174
606981
  if (pid > 0) {
606175
606982
  try {
606176
606983
  process.kill(pid, 0);
@@ -606830,7 +607637,7 @@ ${result.summary}`
606830
607637
  let p2pGateway = null;
606831
607638
  let peerMesh = null;
606832
607639
  let inferenceRouter = null;
606833
- const secretVault = new SecretVault(join122(repoRoot, ".oa", "vault.enc"));
607640
+ const secretVault = new SecretVault(join123(repoRoot, ".oa", "vault.enc"));
606834
607641
  let adminSessionKey = null;
606835
607642
  const callSubAgents = /* @__PURE__ */ new Map();
606836
607643
  const streamRenderer = new StreamRenderer();
@@ -607061,13 +607868,13 @@ Rationale: ${proposal.rationale}${provenanceNote}`;
607061
607868
  const hits = allCompletions.filter((c9) => c9.toLowerCase().startsWith(lower));
607062
607869
  return [hits, line];
607063
607870
  }
607064
- const HISTORY_DIR = join122(homedir43(), ".open-agents");
607065
- const HISTORY_FILE = join122(HISTORY_DIR, "repl-history");
607871
+ const HISTORY_DIR = join123(homedir44(), ".open-agents");
607872
+ const HISTORY_FILE = join123(HISTORY_DIR, "repl-history");
607066
607873
  const MAX_HISTORY_LINES = 500;
607067
607874
  let savedHistory = [];
607068
607875
  try {
607069
- if (existsSync106(HISTORY_FILE)) {
607070
- const raw = readFileSync86(HISTORY_FILE, "utf8").trim();
607876
+ if (existsSync107(HISTORY_FILE)) {
607877
+ const raw = readFileSync87(HISTORY_FILE, "utf8").trim();
607071
607878
  if (raw) savedHistory = raw.split("\n").reverse();
607072
607879
  }
607073
607880
  } catch {
@@ -607217,7 +608024,7 @@ Rationale: ${proposal.rationale}${provenanceNote}`;
607217
608024
  mkdirSync64(HISTORY_DIR, { recursive: true });
607218
608025
  appendFileSync8(HISTORY_FILE, line + "\n", "utf8");
607219
608026
  if (Math.random() < 0.02) {
607220
- const all2 = readFileSync86(HISTORY_FILE, "utf8").trim().split("\n");
608027
+ const all2 = readFileSync87(HISTORY_FILE, "utf8").trim().split("\n");
607221
608028
  if (all2.length > MAX_HISTORY_LINES) {
607222
608029
  writeFileSync56(HISTORY_FILE, all2.slice(-MAX_HISTORY_LINES).join("\n") + "\n", "utf8");
607223
608030
  }
@@ -607404,10 +608211,10 @@ Rationale: ${proposal.rationale}${provenanceNote}`;
607404
608211
  const { unlinkSync: _rmStale } = await import("node:fs");
607405
608212
  const { homedir: _hdir } = await import("node:os");
607406
608213
  for (const dp of [
607407
- join122(repoRoot, ".oa", "nexus", "nexus-daemon.mjs"),
607408
- join122(_hdir(), ".open-agents", ".oa", "nexus", "nexus-daemon.mjs")
608214
+ join123(repoRoot, ".oa", "nexus", "nexus-daemon.mjs"),
608215
+ join123(_hdir(), ".open-agents", ".oa", "nexus", "nexus-daemon.mjs")
607409
608216
  ]) {
607410
- if (existsSync106(dp)) try {
608217
+ if (existsSync107(dp)) try {
607411
608218
  _rmStale(dp);
607412
608219
  } catch {
607413
608220
  }
@@ -607418,9 +608225,9 @@ Rationale: ${proposal.rationale}${provenanceNote}`;
607418
608225
  const autoNexus = new NexusTool(repoRoot);
607419
608226
  const _registerNexusDaemon = () => {
607420
608227
  try {
607421
- const nexusPidFile = join122(repoRoot, ".oa", "nexus", "daemon.pid");
607422
- if (existsSync106(nexusPidFile)) {
607423
- const nPid = parseInt(readFileSync86(nexusPidFile, "utf8").trim(), 10);
608228
+ const nexusPidFile = join123(repoRoot, ".oa", "nexus", "daemon.pid");
608229
+ if (existsSync107(nexusPidFile)) {
608230
+ const nPid = parseInt(readFileSync87(nexusPidFile, "utf8").trim(), 10);
607424
608231
  if (nPid > 0 && !registry2.daemons.has("Nexus")) {
607425
608232
  registry2.register({ name: "Nexus", pid: nPid, startedAt: Date.now(), status: "running" });
607426
608233
  statusBar.ensureMonitorTimer();
@@ -607477,7 +608284,7 @@ Rationale: ${proposal.rationale}${provenanceNote}`;
607477
608284
  } catch {
607478
608285
  }
607479
608286
  try {
607480
- const oaDir = join122(repoRoot, ".oa");
608287
+ const oaDir = join123(repoRoot, ".oa");
607481
608288
  const reconnected = await ExposeGateway.checkAndReconnect(oaDir, {
607482
608289
  onInfo: (msg) => writeContent(() => renderInfo2(msg)),
607483
608290
  onError: (msg) => writeContent(() => renderWarning2(msg))
@@ -607509,7 +608316,7 @@ Rationale: ${proposal.rationale}${provenanceNote}`;
607509
608316
  } catch {
607510
608317
  }
607511
608318
  try {
607512
- const oaDir = join122(repoRoot, ".oa");
608319
+ const oaDir = join123(repoRoot, ".oa");
607513
608320
  const reconnectedP2P = await ExposeP2PGateway.checkAndReconnect(oaDir, new NexusTool(repoRoot), {
607514
608321
  onInfo: (msg) => writeContent(() => renderInfo2(msg)),
607515
608322
  onError: (msg) => writeContent(() => renderWarning2(msg))
@@ -607550,10 +608357,10 @@ Rationale: ${proposal.rationale}${provenanceNote}`;
607550
608357
  }
607551
608358
  try {
607552
608359
  const { homedir: _hd, hostname: _hn, userInfo: _ui } = await import("node:os");
607553
- const globalNamePath = join122(_hd(), ".open-agents", "agent-name");
608360
+ const globalNamePath = join123(_hd(), ".open-agents", "agent-name");
607554
608361
  let agName = "";
607555
608362
  try {
607556
- if (existsSync106(globalNamePath)) agName = readFileSync86(globalNamePath, "utf8").trim();
608363
+ if (existsSync107(globalNamePath)) agName = readFileSync87(globalNamePath, "utf8").trim();
607557
608364
  } catch {
607558
608365
  }
607559
608366
  if (!agName) {
@@ -607582,11 +608389,11 @@ Rationale: ${proposal.rationale}${provenanceNote}`;
607582
608389
  }
607583
608390
  if (!ollamaAlive) {
607584
608391
  try {
607585
- const savedSponsorsPath = join122(repoRoot, ".oa", "sponsor", "known-sponsors.json");
608392
+ const savedSponsorsPath = join123(repoRoot, ".oa", "sponsor", "known-sponsors.json");
607586
608393
  let savedSponsors = [];
607587
608394
  try {
607588
- if (existsSync106(savedSponsorsPath)) {
607589
- savedSponsors = JSON.parse(readFileSync86(savedSponsorsPath, "utf8"));
608395
+ if (existsSync107(savedSponsorsPath)) {
608396
+ savedSponsors = JSON.parse(readFileSync87(savedSponsorsPath, "utf8"));
607590
608397
  const oneHourAgo = Date.now() - 36e5;
607591
608398
  savedSponsors = savedSponsors.filter((s2) => (s2.lastVerified || 0) > oneHourAgo);
607592
608399
  }
@@ -608678,10 +609485,10 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
608678
609485
  if (name10 === "voice_list_files") {
608679
609486
  const baseDir = String(args?.dir ?? ".");
608680
609487
  const { readdirSync: readdirSync39, statSync: statSync36 } = __require("node:fs");
608681
- const { join: join127, resolve: resolve43 } = __require("node:path");
608682
- const base3 = baseDir.startsWith("/") ? baseDir : resolve43(join127(repoRoot, baseDir));
609488
+ const { join: join128, resolve: resolve43 } = __require("node:path");
609489
+ const base3 = baseDir.startsWith("/") ? baseDir : resolve43(join128(repoRoot, baseDir));
608683
609490
  const items = readdirSync39(base3).slice(0, 200).map((f2) => {
608684
- const s2 = statSync36(join127(base3, f2));
609491
+ const s2 = statSync36(join128(base3, f2));
608685
609492
  return { name: f2, dir: s2.isDirectory(), size: s2.size };
608686
609493
  });
608687
609494
  return JSON.stringify({ dir: base3, items }, null, 2);
@@ -608769,7 +609576,7 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
608769
609576
  kind,
608770
609577
  targetUrl,
608771
609578
  authKey,
608772
- stateDir: join122(repoRoot, ".oa"),
609579
+ stateDir: join123(repoRoot, ".oa"),
608773
609580
  passthrough: passthrough ?? false,
608774
609581
  loadbalance: loadbalance ?? false,
608775
609582
  endpointAuth: passthrough ? currentConfig.apiKey : void 0,
@@ -608815,7 +609622,7 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
608815
609622
  await tunnelGateway.stop();
608816
609623
  tunnelGateway = null;
608817
609624
  }
608818
- const newTunnel = new ExposeGateway({ kind, targetUrl, authKey, fullAccess, stateDir: join122(repoRoot, ".oa") });
609625
+ const newTunnel = new ExposeGateway({ kind, targetUrl, authKey, fullAccess, stateDir: join123(repoRoot, ".oa") });
608819
609626
  newTunnel.on("stats", (stats) => {
608820
609627
  statusBar.setExposeStatus({
608821
609628
  status: stats.status,
@@ -608902,9 +609709,9 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
608902
609709
  });
608903
609710
  if (!result.success) throw new Error(result.error || "Connect failed");
608904
609711
  try {
608905
- const nexusPidFile = join122(repoRoot, ".oa", "nexus", "daemon.pid");
608906
- if (existsSync106(nexusPidFile)) {
608907
- const pid = parseInt(readFileSync86(nexusPidFile, "utf8").trim(), 10);
609712
+ const nexusPidFile = join123(repoRoot, ".oa", "nexus", "daemon.pid");
609713
+ if (existsSync107(nexusPidFile)) {
609714
+ const pid = parseInt(readFileSync87(nexusPidFile, "utf8").trim(), 10);
608908
609715
  if (pid > 0) {
608909
609716
  registry2.register({
608910
609717
  name: "Nexus",
@@ -609092,10 +609899,10 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
609092
609899
  writeContent(() => renderInfo2(`Killed ${bgKilled} background task(s).`));
609093
609900
  }
609094
609901
  try {
609095
- const nexusDir = join122(repoRoot, OA_DIR, "nexus");
609096
- const pidFile = join122(nexusDir, "daemon.pid");
609097
- if (existsSync106(pidFile)) {
609098
- const pid = parseInt(readFileSync86(pidFile, "utf8").trim(), 10);
609902
+ const nexusDir = join123(repoRoot, OA_DIR, "nexus");
609903
+ const pidFile = join123(nexusDir, "daemon.pid");
609904
+ if (existsSync107(pidFile)) {
609905
+ const pid = parseInt(readFileSync87(pidFile, "utf8").trim(), 10);
609099
609906
  if (pid > 0) {
609100
609907
  try {
609101
609908
  if (process.platform === "win32") {
@@ -609117,13 +609924,13 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
609117
609924
  } catch {
609118
609925
  }
609119
609926
  try {
609120
- const voiceDir2 = join122(homedir43(), ".open-agents", "voice");
609927
+ const voiceDir2 = join123(homedir44(), ".open-agents", "voice");
609121
609928
  const voicePidFiles = ["luxtts-daemon.pid", "piper-daemon.pid"];
609122
609929
  for (const pf of voicePidFiles) {
609123
- const pidPath = join122(voiceDir2, pf);
609124
- if (existsSync106(pidPath)) {
609930
+ const pidPath = join123(voiceDir2, pf);
609931
+ if (existsSync107(pidPath)) {
609125
609932
  try {
609126
- const pid = parseInt(readFileSync86(pidPath, "utf8").trim(), 10);
609933
+ const pid = parseInt(readFileSync87(pidPath, "utf8").trim(), 10);
609127
609934
  if (pid > 0) {
609128
609935
  if (process.platform === "win32") {
609129
609936
  try {
@@ -609147,8 +609954,8 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
609147
609954
  execSync58(process.platform === "win32" ? "timeout /t 1 /nobreak >nul" : "sleep 0.5", { timeout: 3e3, stdio: "ignore" });
609148
609955
  } catch {
609149
609956
  }
609150
- const oaPath = join122(repoRoot, OA_DIR);
609151
- if (existsSync106(oaPath)) {
609957
+ const oaPath = join123(repoRoot, OA_DIR);
609958
+ if (existsSync107(oaPath)) {
609152
609959
  let deleted = false;
609153
609960
  for (let attempt = 0; attempt < 3; attempt++) {
609154
609961
  try {
@@ -609232,19 +610039,19 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
609232
610039
  try {
609233
610040
  const { isPersonaPlexRunning: isPersonaPlexRunning2 } = await Promise.resolve().then(() => (init_personaplex(), personaplex_exports));
609234
610041
  if (isPersonaPlexRunning2()) {
609235
- const ppPidFile = join122(homedir43(), ".open-agents", "voice", "personaplex", "daemon.pid");
609236
- const ppPortFile = join122(homedir43(), ".open-agents", "voice", "personaplex", "daemon.port");
609237
- if (existsSync106(ppPidFile)) {
609238
- const ppPid = parseInt(readFileSync86(ppPidFile, "utf8").trim(), 10);
609239
- const ppPort = existsSync106(ppPortFile) ? parseInt(readFileSync86(ppPortFile, "utf8").trim(), 10) : void 0;
610042
+ const ppPidFile = join123(homedir44(), ".open-agents", "voice", "personaplex", "daemon.pid");
610043
+ const ppPortFile = join123(homedir44(), ".open-agents", "voice", "personaplex", "daemon.port");
610044
+ if (existsSync107(ppPidFile)) {
610045
+ const ppPid = parseInt(readFileSync87(ppPidFile, "utf8").trim(), 10);
610046
+ const ppPort = existsSync107(ppPortFile) ? parseInt(readFileSync87(ppPortFile, "utf8").trim(), 10) : void 0;
609240
610047
  if (ppPid > 0 && !registry2.daemons.has("PersonaPlex")) {
609241
610048
  registry2.register({ name: "PersonaPlex", pid: ppPid, port: ppPort, startedAt: Date.now(), status: "running" });
609242
610049
  }
609243
610050
  }
609244
610051
  }
609245
- const nexusPidFile = join122(repoRoot, ".oa", "nexus", "daemon.pid");
609246
- if (existsSync106(nexusPidFile)) {
609247
- const nPid = parseInt(readFileSync86(nexusPidFile, "utf8").trim(), 10);
610052
+ const nexusPidFile = join123(repoRoot, ".oa", "nexus", "daemon.pid");
610053
+ if (existsSync107(nexusPidFile)) {
610054
+ const nPid = parseInt(readFileSync87(nexusPidFile, "utf8").trim(), 10);
609248
610055
  if (nPid > 0 && !registry2.daemons.has("Nexus")) {
609249
610056
  try {
609250
610057
  process.kill(nPid, 0);
@@ -609611,9 +610418,9 @@ Execute this skill now. Follow the behavioral guidance above.`;
609611
610418
  }
609612
610419
  }
609613
610420
  const cleanPath = input.replace(/^['"]|['"]$/g, "").trim();
609614
- const isImage = isImagePath(cleanPath) && existsSync106(resolve39(repoRoot, cleanPath));
609615
- const isMedia = !isImage && isTranscribablePath(cleanPath) && existsSync106(resolve39(repoRoot, cleanPath));
609616
- const isMarkdown = !isImage && !isMedia && /\.(md|markdown)$/i.test(cleanPath) && existsSync106(resolve39(repoRoot, cleanPath));
610421
+ const isImage = isImagePath(cleanPath) && existsSync107(resolve39(repoRoot, cleanPath));
610422
+ const isMedia = !isImage && isTranscribablePath(cleanPath) && existsSync107(resolve39(repoRoot, cleanPath));
610423
+ const isMarkdown = !isImage && !isMedia && /\.(md|markdown)$/i.test(cleanPath) && existsSync107(resolve39(repoRoot, cleanPath));
609617
610424
  if (activeTask) {
609618
610425
  if (activeTask.runner.isPaused) {
609619
610426
  activeTask.runner.resume();
@@ -609622,7 +610429,7 @@ Execute this skill now. Follow the behavioral guidance above.`;
609622
610429
  if (isImage) {
609623
610430
  try {
609624
610431
  const imgPath = resolve39(repoRoot, cleanPath);
609625
- const imgBuffer = readFileSync86(imgPath);
610432
+ const imgBuffer = readFileSync87(imgPath);
609626
610433
  const base642 = imgBuffer.toString("base64");
609627
610434
  const ext = extname12(cleanPath).toLowerCase();
609628
610435
  const mime = ext === ".png" ? "image/png" : ext === ".gif" ? "image/gif" : ext === ".webp" ? "image/webp" : "image/jpeg";
@@ -609778,7 +610585,7 @@ ${result.text}`;
609778
610585
  if (isMarkdown && fullInput === input) {
609779
610586
  try {
609780
610587
  const mdPath = resolve39(repoRoot, cleanPath);
609781
- const mdContent = readFileSync86(mdPath, "utf8");
610588
+ const mdContent = readFileSync87(mdPath, "utf8");
609782
610589
  const { parseMcpMarkdown: parseMcpMarkdown2 } = await Promise.resolve().then(() => (init_dist5(), dist_exports));
609783
610590
  const result = parseMcpMarkdown2(mdContent);
609784
610591
  if (result.servers.length > 0) {
@@ -610200,11 +611007,11 @@ async function runWithTUI(task, config, repoPath, callbacks) {
610200
611007
  const handle2 = startTask(task, config, repoRoot);
610201
611008
  await handle2.promise;
610202
611009
  try {
610203
- const ikDir = join122(repoRoot, ".oa", "identity");
610204
- const ikFile = join122(ikDir, "self-state.json");
611010
+ const ikDir = join123(repoRoot, ".oa", "identity");
611011
+ const ikFile = join123(ikDir, "self-state.json");
610205
611012
  let ikState;
610206
- if (existsSync106(ikFile)) {
610207
- ikState = JSON.parse(readFileSync86(ikFile, "utf8"));
611013
+ if (existsSync107(ikFile)) {
611014
+ ikState = JSON.parse(readFileSync87(ikFile, "utf8"));
610208
611015
  } else {
610209
611016
  mkdirSync64(ikDir, { recursive: true });
610210
611017
  ikState = {
@@ -610241,11 +611048,11 @@ async function runWithTUI(task, config, repoPath, callbacks) {
610241
611048
  );
610242
611049
  } catch {
610243
611050
  try {
610244
- const archeDir = join122(repoRoot, ".oa", "arche");
610245
- const archeFile = join122(archeDir, "variants.json");
611051
+ const archeDir = join123(repoRoot, ".oa", "arche");
611052
+ const archeFile = join123(archeDir, "variants.json");
610246
611053
  let variants = [];
610247
611054
  try {
610248
- if (existsSync106(archeFile)) variants = JSON.parse(readFileSync86(archeFile, "utf8"));
611055
+ if (existsSync107(archeFile)) variants = JSON.parse(readFileSync87(archeFile, "utf8"));
610249
611056
  } catch {
610250
611057
  }
610251
611058
  variants.push({
@@ -610265,9 +611072,9 @@ async function runWithTUI(task, config, repoPath, callbacks) {
610265
611072
  }
610266
611073
  }
610267
611074
  try {
610268
- const metaFile = join122(repoRoot, ".oa", "memory", "metabolism", "store.json");
610269
- if (existsSync106(metaFile)) {
610270
- const store2 = JSON.parse(readFileSync86(metaFile, "utf8"));
611075
+ const metaFile = join123(repoRoot, ".oa", "memory", "metabolism", "store.json");
611076
+ if (existsSync107(metaFile)) {
611077
+ const store2 = JSON.parse(readFileSync87(metaFile, "utf8"));
610271
611078
  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);
610272
611079
  let updated = false;
610273
611080
  for (const item of surfaced) {
@@ -610331,9 +611138,9 @@ Rules:
610331
611138
  try {
610332
611139
  const { initDb: initDb2 } = __require("@open-agents/memory");
610333
611140
  const { ProceduralMemoryStore: ProceduralMemoryStore2 } = __require("@open-agents/memory");
610334
- const dbDir = join122(repoRoot, ".oa", "memory");
611141
+ const dbDir = join123(repoRoot, ".oa", "memory");
610335
611142
  mkdirSync64(dbDir, { recursive: true });
610336
- const db = initDb2(join122(dbDir, "structured.db"));
611143
+ const db = initDb2(join123(dbDir, "structured.db"));
610337
611144
  const memStore = new ProceduralMemoryStore2(db);
610338
611145
  memStore.createWithEmbedding({
610339
611146
  content: content.slice(0, 600),
@@ -610348,11 +611155,11 @@ Rules:
610348
611155
  db.close();
610349
611156
  } catch {
610350
611157
  }
610351
- const metaDir = join122(repoRoot, ".oa", "memory", "metabolism");
610352
- const storeFile = join122(metaDir, "store.json");
611158
+ const metaDir = join123(repoRoot, ".oa", "memory", "metabolism");
611159
+ const storeFile = join123(metaDir, "store.json");
610353
611160
  let store2 = [];
610354
611161
  try {
610355
- if (existsSync106(storeFile)) store2 = JSON.parse(readFileSync86(storeFile, "utf8"));
611162
+ if (existsSync107(storeFile)) store2 = JSON.parse(readFileSync87(storeFile, "utf8"));
610356
611163
  } catch {
610357
611164
  }
610358
611165
  store2.push({
@@ -610374,19 +611181,19 @@ Rules:
610374
611181
  } catch {
610375
611182
  }
610376
611183
  try {
610377
- const cohereSettingsFile = join122(repoRoot, ".oa", "settings.json");
611184
+ const cohereSettingsFile = join123(repoRoot, ".oa", "settings.json");
610378
611185
  let cohereActive = false;
610379
611186
  try {
610380
- if (existsSync106(cohereSettingsFile)) {
610381
- const settings = JSON.parse(readFileSync86(cohereSettingsFile, "utf8"));
611187
+ if (existsSync107(cohereSettingsFile)) {
611188
+ const settings = JSON.parse(readFileSync87(cohereSettingsFile, "utf8"));
610382
611189
  cohereActive = settings.cohere === true;
610383
611190
  }
610384
611191
  } catch {
610385
611192
  }
610386
611193
  if (cohereActive) {
610387
- const metaFile = join122(repoRoot, ".oa", "memory", "metabolism", "store.json");
610388
- if (existsSync106(metaFile)) {
610389
- const store2 = JSON.parse(readFileSync86(metaFile, "utf8"));
611194
+ const metaFile = join123(repoRoot, ".oa", "memory", "metabolism", "store.json");
611195
+ if (existsSync107(metaFile)) {
611196
+ const store2 = JSON.parse(readFileSync87(metaFile, "utf8"));
610390
611197
  const latest = store2.filter((m2) => m2.sourceTrace === "trajectory-extraction" || m2.sourceTrace === "llm-trajectory-extraction").slice(-1)[0];
610391
611198
  if (latest && latest.scores?.confidence >= 0.6) {
610392
611199
  try {
@@ -610411,18 +611218,18 @@ Rules:
610411
611218
  }
610412
611219
  } catch (err) {
610413
611220
  try {
610414
- const ikFile = join122(repoRoot, ".oa", "identity", "self-state.json");
610415
- if (existsSync106(ikFile)) {
610416
- const ikState = JSON.parse(readFileSync86(ikFile, "utf8"));
611221
+ const ikFile = join123(repoRoot, ".oa", "identity", "self-state.json");
611222
+ if (existsSync107(ikFile)) {
611223
+ const ikState = JSON.parse(readFileSync87(ikFile, "utf8"));
610417
611224
  ikState.homeostasis.uncertainty = Math.min(1, ikState.homeostasis.uncertainty + 0.1);
610418
611225
  ikState.homeostasis.coherence = Math.max(0, ikState.homeostasis.coherence - 0.05);
610419
611226
  ikState.session_count = (ikState.session_count || 0) + 1;
610420
611227
  ikState.updated_at = (/* @__PURE__ */ new Date()).toISOString();
610421
611228
  writeFileSync56(ikFile, JSON.stringify(ikState, null, 2));
610422
611229
  }
610423
- const metaFile = join122(repoRoot, ".oa", "memory", "metabolism", "store.json");
610424
- if (existsSync106(metaFile)) {
610425
- const store2 = JSON.parse(readFileSync86(metaFile, "utf8"));
611230
+ const metaFile = join123(repoRoot, ".oa", "memory", "metabolism", "store.json");
611231
+ if (existsSync107(metaFile)) {
611232
+ const store2 = JSON.parse(readFileSync87(metaFile, "utf8"));
610426
611233
  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);
610427
611234
  for (const item of surfaced) {
610428
611235
  item.accessCount = (item.accessCount || 0) + 1;
@@ -610433,11 +611240,11 @@ Rules:
610433
611240
  writeFileSync56(metaFile, JSON.stringify(store2, null, 2));
610434
611241
  }
610435
611242
  try {
610436
- const archeDir = join122(repoRoot, ".oa", "arche");
610437
- const archeFile = join122(archeDir, "variants.json");
611243
+ const archeDir = join123(repoRoot, ".oa", "arche");
611244
+ const archeFile = join123(archeDir, "variants.json");
610438
611245
  let variants = [];
610439
611246
  try {
610440
- if (existsSync106(archeFile)) variants = JSON.parse(readFileSync86(archeFile, "utf8"));
611247
+ if (existsSync107(archeFile)) variants = JSON.parse(readFileSync87(archeFile, "utf8"));
610441
611248
  } catch {
610442
611249
  }
610443
611250
  variants.push({
@@ -610541,12 +611348,12 @@ __export(run_exports, {
610541
611348
  });
610542
611349
  import { resolve as resolve40 } from "node:path";
610543
611350
  import { spawn as spawn26 } from "node:child_process";
610544
- import { mkdirSync as mkdirSync65, writeFileSync as writeFileSync57, readFileSync as readFileSync87, readdirSync as readdirSync38, existsSync as existsSync107 } from "node:fs";
611351
+ import { mkdirSync as mkdirSync65, writeFileSync as writeFileSync57, readFileSync as readFileSync88, readdirSync as readdirSync38, existsSync as existsSync108 } from "node:fs";
610545
611352
  import { randomBytes as randomBytes24 } from "node:crypto";
610546
- import { join as join123 } from "node:path";
611353
+ import { join as join124 } from "node:path";
610547
611354
  function jobsDir2(repoPath) {
610548
611355
  const root = resolve40(repoPath ?? process.cwd());
610549
- const dir = join123(root, ".oa", "jobs");
611356
+ const dir = join124(root, ".oa", "jobs");
610550
611357
  mkdirSync65(dir, { recursive: true });
610551
611358
  return dir;
610552
611359
  }
@@ -610669,7 +611476,7 @@ async function runBackground(task, config, opts) {
610669
611476
  }
610670
611477
  });
610671
611478
  job.pid = child.pid ?? 0;
610672
- writeFileSync57(join123(dir, `${id}.json`), JSON.stringify(job, null, 2));
611479
+ writeFileSync57(join124(dir, `${id}.json`), JSON.stringify(job, null, 2));
610673
611480
  let output = "";
610674
611481
  child.stdout?.on("data", (chunk) => {
610675
611482
  output += chunk.toString();
@@ -610685,7 +611492,7 @@ async function runBackground(task, config, opts) {
610685
611492
  job.summary = result.summary;
610686
611493
  job.durationMs = result.durationMs;
610687
611494
  job.error = result.error;
610688
- writeFileSync57(join123(dir, `${id}.json`), JSON.stringify(job, null, 2));
611495
+ writeFileSync57(join124(dir, `${id}.json`), JSON.stringify(job, null, 2));
610689
611496
  } catch {
610690
611497
  }
610691
611498
  });
@@ -610701,13 +611508,13 @@ async function runBackground(task, config, opts) {
610701
611508
  }
610702
611509
  function statusCommand(jobId, repoPath) {
610703
611510
  const dir = jobsDir2(repoPath);
610704
- const file = join123(dir, `${jobId}.json`);
610705
- if (!existsSync107(file)) {
611511
+ const file = join124(dir, `${jobId}.json`);
611512
+ if (!existsSync108(file)) {
610706
611513
  console.error(`Job not found: ${jobId}`);
610707
611514
  console.log(`Available jobs: oa jobs`);
610708
611515
  process.exit(1);
610709
611516
  }
610710
- const job = JSON.parse(readFileSync87(file, "utf-8"));
611517
+ const job = JSON.parse(readFileSync88(file, "utf-8"));
610711
611518
  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`;
610712
611519
  const icon = job.status === "completed" ? "✓" : job.status === "failed" ? "✗" : "●";
610713
611520
  console.log(`${icon} ${job.id} [${job.status}] ${runtime}`);
@@ -610728,7 +611535,7 @@ function jobsCommand(repoPath) {
610728
611535
  console.log("Jobs:");
610729
611536
  for (const file of files) {
610730
611537
  try {
610731
- const job = JSON.parse(readFileSync87(join123(dir, file), "utf-8"));
611538
+ const job = JSON.parse(readFileSync88(join124(dir, file), "utf-8"));
610732
611539
  const icon = job.status === "completed" ? "✓" : job.status === "failed" ? "✗" : "●";
610733
611540
  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`;
610734
611541
  const cleanListTask = cleanForStorage(job.task) || job.task;
@@ -610752,13 +611559,13 @@ __export(index_repo_exports, {
610752
611559
  indexRepoCommand: () => indexRepoCommand
610753
611560
  });
610754
611561
  import { resolve as resolve41 } from "node:path";
610755
- import { existsSync as existsSync108, statSync as statSync35 } from "node:fs";
611562
+ import { existsSync as existsSync109, statSync as statSync35 } from "node:fs";
610756
611563
  import { cwd as cwd2 } from "node:process";
610757
611564
  async function indexRepoCommand(opts, _config3) {
610758
611565
  const repoRoot = resolve41(opts.repoPath ?? cwd2());
610759
611566
  printHeader("Index Repository");
610760
611567
  printInfo(`Indexing: ${repoRoot}`);
610761
- if (!existsSync108(repoRoot)) {
611568
+ if (!existsSync109(repoRoot)) {
610762
611569
  printError(`Path does not exist: ${repoRoot}`);
610763
611570
  process.exit(1);
610764
611571
  }
@@ -611010,8 +611817,8 @@ var config_exports2 = {};
611010
611817
  __export(config_exports2, {
611011
611818
  configCommand: () => configCommand
611012
611819
  });
611013
- import { join as join124, resolve as resolve42 } from "node:path";
611014
- import { homedir as homedir44 } from "node:os";
611820
+ import { join as join125, resolve as resolve42 } from "node:path";
611821
+ import { homedir as homedir45 } from "node:os";
611015
611822
  import { cwd as cwd3 } from "node:process";
611016
611823
  function redactIfSensitive(key, value2) {
611017
611824
  if (SENSITIVE_KEYS.has(key) && typeof value2 === "string" && value2.length > 0) {
@@ -611092,7 +611899,7 @@ function handleShow(opts, config) {
611092
611899
  }
611093
611900
  }
611094
611901
  printSection("Config File");
611095
- printInfo(`~/.open-agents/config.json (${join124(homedir44(), ".open-agents", "config.json")})`);
611902
+ printInfo(`~/.open-agents/config.json (${join125(homedir45(), ".open-agents", "config.json")})`);
611096
611903
  printSection("Priority Chain");
611097
611904
  printInfo(" 1. CLI flags (--model, --backend-url, etc.)");
611098
611905
  printInfo(" 2. Project .oa/settings.json (--local)");
@@ -611131,7 +611938,7 @@ function handleSet(opts, _config3) {
611131
611938
  const coerced = coerceForSettings(key, value2);
611132
611939
  saveProjectSettings(repoRoot, { [key]: coerced });
611133
611940
  printSuccess(`Project override set: ${key} = ${redactIfSensitive(key, value2)}`);
611134
- printInfo(`Saved to ${join124(repoRoot, ".oa", "settings.json")}`);
611941
+ printInfo(`Saved to ${join125(repoRoot, ".oa", "settings.json")}`);
611135
611942
  printInfo("This override applies only when running in this workspace.");
611136
611943
  } catch (err) {
611137
611944
  printError(`Failed to save: ${err instanceof Error ? err.message : String(err)}`);
@@ -611316,7 +612123,7 @@ __export(eval_exports, {
611316
612123
  });
611317
612124
  import { tmpdir as tmpdir21 } from "node:os";
611318
612125
  import { mkdirSync as mkdirSync66, writeFileSync as writeFileSync58 } from "node:fs";
611319
- import { join as join125 } from "node:path";
612126
+ import { join as join126 } from "node:path";
611320
612127
  async function evalCommand(opts, config) {
611321
612128
  const suiteName = opts.suite ?? "basic";
611322
612129
  const suite = SUITES[suiteName];
@@ -611445,10 +612252,10 @@ async function evalCommand(opts, config) {
611445
612252
  process.exit(failed > 0 ? 1 : 0);
611446
612253
  }
611447
612254
  function createTempEvalRepo() {
611448
- const dir = join125(tmpdir21(), `open-agents-eval-${Date.now()}`);
612255
+ const dir = join126(tmpdir21(), `open-agents-eval-${Date.now()}`);
611449
612256
  mkdirSync66(dir, { recursive: true });
611450
612257
  writeFileSync58(
611451
- join125(dir, "package.json"),
612258
+ join126(dir, "package.json"),
611452
612259
  JSON.stringify({ name: "eval-repo", version: "0.0.0" }, null, 2) + "\n",
611453
612260
  "utf8"
611454
612261
  );
@@ -611512,7 +612319,7 @@ init_typed_node_events();
611512
612319
  import { parseArgs as nodeParseArgs2 } from "node:util";
611513
612320
  import { createRequire as createRequire7 } from "node:module";
611514
612321
  import { fileURLToPath as fileURLToPath19 } from "node:url";
611515
- import { dirname as dirname38, join as join126 } from "node:path";
612322
+ import { dirname as dirname38, join as join127 } from "node:path";
611516
612323
 
611517
612324
  // packages/cli/src/cli.ts
611518
612325
  init_typed_node_events();
@@ -611652,7 +612459,7 @@ init_output();
611652
612459
  function getVersion5() {
611653
612460
  try {
611654
612461
  const require4 = createRequire7(import.meta.url);
611655
- const pkgPath = join126(dirname38(fileURLToPath19(import.meta.url)), "..", "package.json");
612462
+ const pkgPath = join127(dirname38(fileURLToPath19(import.meta.url)), "..", "package.json");
611656
612463
  const pkg = require4(pkgPath);
611657
612464
  return pkg.version;
611658
612465
  } catch {
@@ -611967,11 +612774,11 @@ function crashLog(label, err) {
611967
612774
  `;
611968
612775
  try {
611969
612776
  const { appendFileSync: appendFileSync9, mkdirSync: mkdirSync67 } = __require("node:fs");
611970
- const { join: join127 } = __require("node:path");
611971
- const { homedir: homedir45 } = __require("node:os");
611972
- const logDir = join127(homedir45(), ".open-agents");
612777
+ const { join: join128 } = __require("node:path");
612778
+ const { homedir: homedir46 } = __require("node:os");
612779
+ const logDir = join128(homedir46(), ".open-agents");
611973
612780
  mkdirSync67(logDir, { recursive: true });
611974
- appendFileSync9(join127(logDir, "crash.log"), logLine);
612781
+ appendFileSync9(join128(logDir, "crash.log"), logLine);
611975
612782
  } catch {
611976
612783
  }
611977
612784
  try {