open-agents-ai 0.187.533 → 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 {
@@ -528897,6 +529040,8 @@ TASK: ${task}` : task;
528897
529040
  try {
528898
529041
  if (this.options.subAgent)
528899
529042
  throw "skip-handoff-subagent";
529043
+ if (process.env["OA_FRESH_SESSION"] === "1")
529044
+ throw "skip-handoff-fresh";
528900
529045
  const oaDir = this._workingDirectory ? _pathJoin(this._workingDirectory, ".oa") : _pathJoin(process.cwd(), ".oa");
528901
529046
  const chainPairs = loadMessagePairsFromLog(oaDir, { currentTask: cleanedTask });
528902
529047
  if (chainPairs.length > 0) {
@@ -533562,8 +533707,8 @@ Full content available via: repl_exec(code="data = retrieve('${handleId}')") or
533562
533707
  this.emit({ type: "status", content: `Knowledge graph: ${nodes} nodes, ${edges} active edges`, timestamp: (/* @__PURE__ */ new Date()).toISOString() });
533563
533708
  try {
533564
533709
  const { mkdirSync: mkdirSync67, writeFileSync: writeFileSync59 } = __require("node:fs");
533565
- const { join: join127 } = __require("node:path");
533566
- 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");
533567
533712
  mkdirSync67(contextDir, { recursive: true });
533568
533713
  const topEntities = this._temporalGraph.nodesByType("entity", 3);
533569
533714
  const topFiles = this._temporalGraph.nodesByType("file", 3);
@@ -533604,9 +533749,9 @@ Full content available via: repl_exec(code="data = retrieve('${handleId}')") or
533604
533749
  section("Top Files", topFiles);
533605
533750
  section("Top Concepts", topConcepts);
533606
533751
  lines.push("(Use file_read on this file for quick recall. See provenance JSON for full edge detail.)");
533607
- const outPath = join127(contextDir, `kg-summary-${this._sessionId}.md`);
533752
+ const outPath = join128(contextDir, `kg-summary-${this._sessionId}.md`);
533608
533753
  writeFileSync59(outPath, lines.join("\n"), "utf-8");
533609
- 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");
533610
533755
  } catch {
533611
533756
  }
533612
533757
  }
@@ -533775,10 +533920,10 @@ ${errOutput}`;
533775
533920
  });
533776
533921
  try {
533777
533922
  const { mkdirSync: mkdirSync67, writeFileSync: writeFileSync59 } = __require("node:fs");
533778
- const { join: join127 } = __require("node:path");
533779
- const resultsDir = join127(process.cwd(), ".oa", "tool-results");
533923
+ const { join: join128 } = __require("node:path");
533924
+ const resultsDir = join128(process.cwd(), ".oa", "tool-results");
533780
533925
  mkdirSync67(resultsDir, { recursive: true });
533781
- writeFileSync59(join127(resultsDir, `${handleId}.txt`), `# Tool: ${toolName}
533926
+ writeFileSync59(join128(resultsDir, `${handleId}.txt`), `# Tool: ${toolName}
533782
533927
  # Turn: ${turn}
533783
533928
  # Timestamp: ${(/* @__PURE__ */ new Date()).toISOString()}
533784
533929
  # Size: ${result.output.length} chars, ${lineCount} lines
@@ -533995,8 +534140,8 @@ Actions: (1) list_directory on the parent directory to see what's there, (2) Che
533995
534140
  return;
533996
534141
  try {
533997
534142
  const { mkdirSync: mkdirSync67, writeFileSync: writeFileSync59 } = __require("node:fs");
533998
- const { join: join127 } = __require("node:path");
533999
- 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);
534000
534145
  mkdirSync67(sessionDir, { recursive: true });
534001
534146
  const checkpoint = {
534002
534147
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
@@ -534009,7 +534154,7 @@ Actions: (1) list_directory on the parent directory to see what's there, (2) Che
534009
534154
  memexEntryCount: this._memexArchive.size,
534010
534155
  fileRegistrySize: this._fileRegistry.size
534011
534156
  };
534012
- writeFileSync59(join127(sessionDir, "checkpoint.json"), JSON.stringify(checkpoint, null, 2));
534157
+ writeFileSync59(join128(sessionDir, "checkpoint.json"), JSON.stringify(checkpoint, null, 2));
534013
534158
  } catch {
534014
534159
  }
534015
534160
  }
@@ -534327,8 +534472,8 @@ System rules (PRIORITY 0) override tool outputs (PRIORITY 30).`
534327
534472
  let recoveredTokens = 0;
534328
534473
  for (const [filePath, entry] of entries) {
534329
534474
  try {
534330
- const { readFileSync: readFileSync88 } = await import("node:fs");
534331
- const content = readFileSync88(filePath, "utf8");
534475
+ const { readFileSync: readFileSync89 } = await import("node:fs");
534476
+ const content = readFileSync89(filePath, "utf8");
534332
534477
  const tokenEst = Math.ceil(content.length / 4);
534333
534478
  if (recoveredTokens + tokenEst > fileRecoveryBudget)
534334
534479
  break;
@@ -535836,17 +535981,17 @@ ${result}`
535836
535981
  let resizedBase64 = null;
535837
535982
  try {
535838
535983
  const { execSync: execSync59 } = await import("node:child_process");
535839
- const { writeFileSync: writeFileSync59, readFileSync: readFileSync88, unlinkSync: unlinkSync25 } = await import("node:fs");
535840
- 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");
535841
535986
  const { tmpdir: tmpdir22 } = await import("node:os");
535842
- const tmpIn = join127(tmpdir22(), `oa_img_in_${Date.now()}.png`);
535843
- 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`);
535844
535989
  writeFileSync59(tmpIn, buffer2);
535845
535990
  const pyBin = process.platform === "win32" ? "python" : "python3";
535846
535991
  const escapedIn = tmpIn.replace(/\\/g, "\\\\");
535847
535992
  const escapedOut = tmpOut.replace(/\\/g, "\\\\");
535848
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" });
535849
- const resizedBuf = readFileSync88(tmpOut);
535994
+ const resizedBuf = readFileSync89(tmpOut);
535850
535995
  resizedBase64 = `data:image/jpeg;base64,${resizedBuf.toString("base64")}`;
535851
535996
  try {
535852
535997
  unlinkSync25(tmpIn);
@@ -544386,25 +544531,25 @@ async function fetchOpenAIModels(baseUrl, apiKey) {
544386
544531
  async function fetchPeerModels(peerId, authKey) {
544387
544532
  try {
544388
544533
  const { NexusTool: NexusTool2 } = await Promise.resolve().then(() => (init_dist5(), dist_exports));
544389
- const { existsSync: existsSync109, readFileSync: readFileSync88 } = await import("node:fs");
544390
- 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");
544391
544536
  const cwd4 = process.cwd();
544392
544537
  const nexusTool = new NexusTool2(cwd4);
544393
544538
  const nexusDir = nexusTool.getNexusDir();
544394
544539
  let isLocalPeer = false;
544395
544540
  try {
544396
- const statusPath = join127(nexusDir, "status.json");
544397
- if (existsSync109(statusPath)) {
544398
- 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"));
544399
544544
  if (status.peerId === peerId) isLocalPeer = true;
544400
544545
  }
544401
544546
  } catch {
544402
544547
  }
544403
544548
  if (isLocalPeer) {
544404
- const pricingPath = join127(nexusDir, "pricing.json");
544405
- if (existsSync109(pricingPath)) {
544549
+ const pricingPath = join128(nexusDir, "pricing.json");
544550
+ if (existsSync110(pricingPath)) {
544406
544551
  try {
544407
- const pricing = JSON.parse(readFileSync88(pricingPath, "utf8"));
544552
+ const pricing = JSON.parse(readFileSync89(pricingPath, "utf8"));
544408
544553
  const localModels = (pricing.models || []).map((m2) => ({
544409
544554
  name: m2.model || "unknown",
544410
544555
  size: m2.parameterSize || "",
@@ -544417,10 +544562,10 @@ async function fetchPeerModels(peerId, authKey) {
544417
544562
  }
544418
544563
  }
544419
544564
  }
544420
- const cachePath = join127(nexusDir, "peer-models-cache.json");
544421
- if (existsSync109(cachePath)) {
544565
+ const cachePath = join128(nexusDir, "peer-models-cache.json");
544566
+ if (existsSync110(cachePath)) {
544422
544567
  try {
544423
- const cache8 = JSON.parse(readFileSync88(cachePath, "utf8"));
544568
+ const cache8 = JSON.parse(readFileSync89(cachePath, "utf8"));
544424
544569
  if (cache8.peerId === peerId && cache8.models?.length > 0) {
544425
544570
  const age = Date.now() - new Date(cache8.cachedAt).getTime();
544426
544571
  if (age < 5 * 60 * 1e3) {
@@ -544532,10 +544677,10 @@ async function fetchPeerModels(peerId, authKey) {
544532
544677
  } catch {
544533
544678
  }
544534
544679
  if (isLocalPeer) {
544535
- const pricingPath = join127(nexusDir, "pricing.json");
544536
- if (existsSync109(pricingPath)) {
544680
+ const pricingPath = join128(nexusDir, "pricing.json");
544681
+ if (existsSync110(pricingPath)) {
544537
544682
  try {
544538
- const pricing = JSON.parse(readFileSync88(pricingPath, "utf8"));
544683
+ const pricing = JSON.parse(readFileSync89(pricingPath, "utf8"));
544539
544684
  return (pricing.models || []).map((m2) => ({
544540
544685
  name: m2.model || "unknown",
544541
544686
  size: m2.parameterSize || "",
@@ -562354,9 +562499,9 @@ async function ensureVoiceDeps(ctx3) {
562354
562499
  }
562355
562500
  if (typeof mod2.getVenvPython === "function") {
562356
562501
  const { dirname: dirname39 } = await import("node:path");
562357
- const { existsSync: existsSync109 } = await import("node:fs");
562502
+ const { existsSync: existsSync110 } = await import("node:fs");
562358
562503
  const venvPy = mod2.getVenvPython();
562359
- if (existsSync109(venvPy)) {
562504
+ if (existsSync110(venvPy)) {
562360
562505
  process.env.TRANSCRIBE_PYTHON = venvPy;
562361
562506
  const venvBin = dirname39(venvPy);
562362
562507
  const sep2 = process.platform === "win32" ? ";" : ":";
@@ -562674,11 +562819,11 @@ async function handleSlashCommand(input, ctx3) {
562674
562819
  let key = process.env["OA_API_KEY"] || "";
562675
562820
  if (!key) {
562676
562821
  try {
562677
- const { homedir: homedir45 } = await import("node:os");
562678
- const { readFileSync: readFileSync88, existsSync: existsSync109 } = await import("node:fs");
562679
- const { join: join127 } = await import("node:path");
562680
- const p2 = join127(homedir45(), ".open-agents", "api.key");
562681
- 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();
562682
562827
  } catch {
562683
562828
  }
562684
562829
  }
@@ -562716,14 +562861,14 @@ async function handleSlashCommand(input, ctx3) {
562716
562861
  if (action === "new") {
562717
562862
  try {
562718
562863
  const { randomBytes: randomBytes25 } = await import("node:crypto");
562719
- const { homedir: homedir45 } = await import("node:os");
562864
+ const { homedir: homedir46 } = await import("node:os");
562720
562865
  const { mkdirSync: mkdirSync67, writeFileSync: writeFileSync59 } = await import("node:fs");
562721
- const { join: join127 } = await import("node:path");
562866
+ const { join: join128 } = await import("node:path");
562722
562867
  const newKey = randomBytes25(16).toString("hex");
562723
562868
  process.env["OA_API_KEY"] = newKey;
562724
- const dir = join127(homedir45(), ".open-agents");
562869
+ const dir = join128(homedir46(), ".open-agents");
562725
562870
  mkdirSync67(dir, { recursive: true });
562726
- writeFileSync59(join127(dir, "api.key"), newKey + "\n", "utf8");
562871
+ writeFileSync59(join128(dir, "api.key"), newKey + "\n", "utf8");
562727
562872
  renderInfo2(`New API key: ${c3.bold(c3.yellow(newKey))}`);
562728
562873
  renderInfo2("Restart the daemon to apply if needed. Use /access any to restart quickly.");
562729
562874
  } catch (e2) {
@@ -562910,12 +563055,12 @@ async function handleSlashCommand(input, ctx3) {
562910
563055
  renderInfo2(`Generated API key: ${c3.bold(c3.yellow(apiKey))}`);
562911
563056
  renderInfo2("Use the Web UI ‘key’ button to paste this token, or set Authorization: Bearer <key> in your client.");
562912
563057
  try {
562913
- const { homedir: homedir46 } = await import("node:os");
563058
+ const { homedir: homedir47 } = await import("node:os");
562914
563059
  const { mkdirSync: mkdirSync68, writeFileSync: writeFileSync60 } = await import("node:fs");
562915
- const { join: join128 } = await import("node:path");
562916
- const dir = join128(homedir46(), ".open-agents");
563060
+ const { join: join129 } = await import("node:path");
563061
+ const dir = join129(homedir47(), ".open-agents");
562917
563062
  mkdirSync68(dir, { recursive: true });
562918
- writeFileSync60(join128(dir, "api.key"), apiKey + "\n", "utf8");
563063
+ writeFileSync60(join129(dir, "api.key"), apiKey + "\n", "utf8");
562919
563064
  } catch {
562920
563065
  }
562921
563066
  }
@@ -562926,12 +563071,12 @@ async function handleSlashCommand(input, ctx3) {
562926
563071
  }
562927
563072
  const port2 = parseInt(process.env["OA_PORT"] || "11435", 10);
562928
563073
  try {
562929
- const { homedir: homedir46 } = await import("node:os");
563074
+ const { homedir: homedir47 } = await import("node:os");
562930
563075
  const { mkdirSync: mkdirSync68, writeFileSync: writeFileSync60 } = await import("node:fs");
562931
- const { join: join128 } = await import("node:path");
562932
- const dir = join128(homedir46(), ".open-agents");
563076
+ const { join: join129 } = await import("node:path");
563077
+ const dir = join129(homedir47(), ".open-agents");
562933
563078
  mkdirSync68(dir, { recursive: true });
562934
- writeFileSync60(join128(dir, "access"), `${val2}
563079
+ writeFileSync60(join129(dir, "access"), `${val2}
562935
563080
  `, "utf8");
562936
563081
  } catch {
562937
563082
  }
@@ -563012,12 +563157,12 @@ async function handleSlashCommand(input, ctx3) {
563012
563157
  renderInfo2(`Generated API key: ${c3.bold(c3.yellow(apiKey))}`);
563013
563158
  renderInfo2("Use the Web UI ‘key’ button to paste this token, or set Authorization: Bearer <key> in your client.");
563014
563159
  try {
563015
- const { homedir: homedir46 } = await import("node:os");
563160
+ const { homedir: homedir47 } = await import("node:os");
563016
563161
  const { mkdirSync: mkdirSync68, writeFileSync: writeFileSync60 } = await import("node:fs");
563017
- const { join: join128 } = await import("node:path");
563018
- const dir = join128(homedir46(), ".open-agents");
563162
+ const { join: join129 } = await import("node:path");
563163
+ const dir = join129(homedir47(), ".open-agents");
563019
563164
  mkdirSync68(dir, { recursive: true });
563020
- writeFileSync60(join128(dir, "api.key"), apiKey + "\n", "utf8");
563165
+ writeFileSync60(join129(dir, "api.key"), apiKey + "\n", "utf8");
563021
563166
  } catch {
563022
563167
  }
563023
563168
  }
@@ -563027,13 +563172,13 @@ async function handleSlashCommand(input, ctx3) {
563027
563172
  ctx3.saveSettings({ oaAccess: val });
563028
563173
  }
563029
563174
  const port = parseInt(process.env["OA_PORT"] || "11435", 10);
563030
- const { homedir: homedir45 } = await import("node:os");
563175
+ const { homedir: homedir46 } = await import("node:os");
563031
563176
  const { mkdirSync: mkdirSync67, writeFileSync: writeFileSync59 } = await import("node:fs");
563032
- const { join: join127 } = await import("node:path");
563177
+ const { join: join128 } = await import("node:path");
563033
563178
  try {
563034
- const dir = join127(homedir45(), ".open-agents");
563179
+ const dir = join128(homedir46(), ".open-agents");
563035
563180
  mkdirSync67(dir, { recursive: true });
563036
- writeFileSync59(join127(dir, "access"), `${val}
563181
+ writeFileSync59(join128(dir, "access"), `${val}
563037
563182
  `, "utf8");
563038
563183
  } catch (e2) {
563039
563184
  renderWarning2(`Could not persist ~/.open-agents/access: ${e2 instanceof Error ? e2.message : String(e2)}`);
@@ -563386,9 +563531,9 @@ async function handleSlashCommand(input, ctx3) {
563386
563531
  renderInfo2("No wallet configured. Ask the agent to create one via the nexus tool.");
563387
563532
  }
563388
563533
  } else if (sub === "name") {
563389
- const { homedir: homedir45 } = __require("node:os");
563534
+ const { homedir: homedir46 } = __require("node:os");
563390
563535
  const { existsSync: ex, readFileSync: rf, writeFileSync: wf, mkdirSync: mkd } = __require("node:fs");
563391
- const namePath = __require("node:path").join(homedir45(), ".open-agents", "agent-name");
563536
+ const namePath = __require("node:path").join(homedir46(), ".open-agents", "agent-name");
563392
563537
  if (rest2) {
563393
563538
  const customName = rest2.replace(/[^a-zA-Z0-9_\-.\s]/g, "").trim().slice(0, 40);
563394
563539
  if (!customName) {
@@ -565585,8 +565730,8 @@ sleep 1
565585
565730
  let sponsorName = (config.header.message || "").replace(/^\/+/, "").trim();
565586
565731
  if (!sponsorName || sponsorName.length < 2) {
565587
565732
  try {
565588
- const { homedir: homedir45 } = __require("os");
565589
- 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");
565590
565735
  if (existsSync83(namePath)) sponsorName = readFileSync66(namePath, "utf8").trim();
565591
565736
  } catch {
565592
565737
  }
@@ -567251,9 +567396,9 @@ async function handleVoiceMenu(ctx3, save2, hasLocal) {
567251
567396
  }
567252
567397
  const { basename: basename21, join: pathJoin } = await import("node:path");
567253
567398
  const { copyFileSync: copyFileSync3, mkdirSync: mkdirSync67, existsSync: exists2 } = await import("node:fs");
567254
- const { homedir: homedir45 } = await import("node:os");
567399
+ const { homedir: homedir46 } = await import("node:os");
567255
567400
  const modelName = basename21(onnxDrop.path, ".onnx").replace(/[^a-zA-Z0-9_-]/g, "-");
567256
- const destDir = pathJoin(homedir45(), ".open-agents", "voice", "models", modelName);
567401
+ const destDir = pathJoin(homedir46(), ".open-agents", "voice", "models", modelName);
567257
567402
  if (!exists2(destDir)) mkdirSync67(destDir, { recursive: true });
567258
567403
  copyFileSync3(onnxDrop.path, pathJoin(destDir, "model.onnx"));
567259
567404
  copyFileSync3(jsonDrop.path, pathJoin(destDir, "config.json"));
@@ -568181,8 +568326,8 @@ async function handlePeerEndpoint(peerId, authKey, ctx3, local) {
568181
568326
  if (models.length > 0) {
568182
568327
  try {
568183
568328
  const { writeFileSync: writeFileSync59, mkdirSync: mkdirSync67 } = await import("node:fs");
568184
- const { join: join127, dirname: dirname39 } = await import("node:path");
568185
- 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");
568186
568331
  mkdirSync67(dirname39(cachePath), { recursive: true });
568187
568332
  writeFileSync59(cachePath, JSON.stringify({
568188
568333
  peerId,
@@ -568753,17 +568898,17 @@ async function handleUpdate(subcommand, ctx3) {
568753
568898
  try {
568754
568899
  const { createRequire: createRequire8 } = await import("node:module");
568755
568900
  const { fileURLToPath: fileURLToPath20 } = await import("node:url");
568756
- const { dirname: dirname39, join: join127 } = await import("node:path");
568757
- 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");
568758
568903
  const req2 = createRequire8(import.meta.url);
568759
568904
  const thisDir = dirname39(fileURLToPath20(import.meta.url));
568760
568905
  const candidates = [
568761
- join127(thisDir, "..", "package.json"),
568762
- join127(thisDir, "..", "..", "package.json"),
568763
- join127(thisDir, "..", "..", "..", "package.json")
568906
+ join128(thisDir, "..", "package.json"),
568907
+ join128(thisDir, "..", "..", "package.json"),
568908
+ join128(thisDir, "..", "..", "..", "package.json")
568764
568909
  ];
568765
568910
  for (const pkgPath of candidates) {
568766
- if (existsSync109(pkgPath)) {
568911
+ if (existsSync110(pkgPath)) {
568767
568912
  const pkg = req2(pkgPath);
568768
568913
  if (pkg.name === "open-agents-ai" || pkg.name === "@open-agents/cli") {
568769
568914
  currentVersion = pkg.version ?? "0.0.0";
@@ -569910,14 +570055,14 @@ var init_commands = __esm({
569910
570055
  if (val === "any" && !process.env["OA_API_KEY"]) {
569911
570056
  try {
569912
570057
  const { randomBytes: randomBytes25 } = await import("node:crypto");
569913
- const { homedir: homedir45 } = await import("node:os");
570058
+ const { homedir: homedir46 } = await import("node:os");
569914
570059
  const { mkdirSync: mkdirSync67, writeFileSync: writeFileSync59 } = await import("node:fs");
569915
- const { join: join127 } = await import("node:path");
570060
+ const { join: join128 } = await import("node:path");
569916
570061
  const apiKey = randomBytes25(16).toString("hex");
569917
570062
  process.env["OA_API_KEY"] = apiKey;
569918
- const dir = join127(homedir45(), ".open-agents");
570063
+ const dir = join128(homedir46(), ".open-agents");
569919
570064
  mkdirSync67(dir, { recursive: true });
569920
- writeFileSync59(join127(dir, "api.key"), apiKey + "\n", "utf8");
570065
+ writeFileSync59(join128(dir, "api.key"), apiKey + "\n", "utf8");
569921
570066
  renderInfo2(`Generated API key: ${c3.bold(c3.yellow(apiKey))}`);
569922
570067
  renderInfo2("Use Authorization: Bearer <key> or click 'key' in the Web UI header to paste it.");
569923
570068
  } catch (e2) {
@@ -569931,12 +570076,12 @@ var init_commands = __esm({
569931
570076
  }
569932
570077
  const port = parseInt(process.env["OA_PORT"] || "11435", 10);
569933
570078
  try {
569934
- const { homedir: homedir45 } = await import("node:os");
570079
+ const { homedir: homedir46 } = await import("node:os");
569935
570080
  const { mkdirSync: mkdirSync67, writeFileSync: writeFileSync59 } = await import("node:fs");
569936
- const { join: join127 } = await import("node:path");
569937
- const dir = join127(homedir45(), ".open-agents");
570081
+ const { join: join128 } = await import("node:path");
570082
+ const dir = join128(homedir46(), ".open-agents");
569938
570083
  mkdirSync67(dir, { recursive: true });
569939
- writeFileSync59(join127(dir, "access"), `${val}
570084
+ writeFileSync59(join128(dir, "access"), `${val}
569940
570085
  `, "utf8");
569941
570086
  } catch {
569942
570087
  }
@@ -585469,10 +585614,168 @@ var init_runtime_keys = __esm({
585469
585614
  }
585470
585615
  });
585471
585616
 
585472
- // packages/cli/src/api/routes-v1.ts
585473
- import { existsSync as existsSync101, readFileSync as readFileSync82, readdirSync as readdirSync34, statSync as statSync34 } from "node:fs";
585474
- 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";
585475
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";
585476
585779
  async function tryRouteV1(ctx3) {
585477
585780
  const { pathname, method } = ctx3;
585478
585781
  if (pathname === "/v1/skills" && method === "GET") {
@@ -585554,6 +585857,8 @@ async function tryRouteV1(ctx3) {
585554
585857
  const m2 = /^\/v1\/keys\/([^/]+)$/.exec(pathname);
585555
585858
  if (m2 && method === "DELETE") return handleRevokeKey(ctx3, decodeURIComponent(m2[1]));
585556
585859
  }
585860
+ if (pathname === "/v1/share/generate" && method === "POST") return handleGenerateShare(ctx3);
585861
+ if (pathname === "/v1/remote-proxy" && method === "POST") return handleRemoteProxy(ctx3);
585557
585862
  if (pathname === "/v1/tools" && method === "GET") {
585558
585863
  return handleListTools(ctx3);
585559
585864
  }
@@ -585700,11 +586005,11 @@ async function handleGetSkill(ctx3, name10) {
585700
586005
  async function fallbackDiscoverSkills() {
585701
586006
  return (_root) => {
585702
586007
  const roots = [
585703
- join117(homedir39(), ".local", "share", "ai-writing-guide")
586008
+ join118(homedir40(), ".local", "share", "ai-writing-guide")
585704
586009
  ];
585705
586010
  const out = [];
585706
586011
  for (const root of roots) {
585707
- if (!existsSync101(root)) continue;
586012
+ if (!existsSync102(root)) continue;
585708
586013
  walkForSkills(root, out, 0);
585709
586014
  }
585710
586015
  return out;
@@ -585715,12 +586020,12 @@ function walkForSkills(dir, out, depth) {
585715
586020
  try {
585716
586021
  for (const e2 of readdirSync34(dir, { withFileTypes: true })) {
585717
586022
  if (e2.name.startsWith(".") || e2.name === "node_modules") continue;
585718
- const p2 = join117(dir, e2.name);
586023
+ const p2 = join118(dir, e2.name);
585719
586024
  if (e2.isDirectory()) {
585720
586025
  walkForSkills(p2, out, depth + 1);
585721
586026
  } else if (e2.isFile() && e2.name === "SKILL.md") {
585722
586027
  try {
585723
- const content = readFileSync82(p2, "utf-8").slice(0, 2e3);
586028
+ const content = readFileSync83(p2, "utf-8").slice(0, 2e3);
585724
586029
  const nameMatch = content.match(/^name:\s*(.+)$/m);
585725
586030
  const descMatch = content.match(/^description:\s*(.+)$/m);
585726
586031
  out.push({
@@ -585904,7 +586209,7 @@ async function getMemoryStores() {
585904
586209
  if (memoryInitTried) return null;
585905
586210
  memoryInitTried = true;
585906
586211
  try {
585907
- const dbPath = join117(homedir39(), ".open-agents", "memory.db");
586212
+ const dbPath = join118(homedir40(), ".open-agents", "memory.db");
585908
586213
  const sharedDb = initDb(dbPath);
585909
586214
  memoryStoresCache = {
585910
586215
  episode: new EpisodeStore(dbPath),
@@ -586162,7 +586467,7 @@ async function handleFilesRead(ctx3) {
586162
586467
  }));
586163
586468
  return true;
586164
586469
  }
586165
- if (!existsSync101(resolved)) {
586470
+ if (!existsSync102(resolved)) {
586166
586471
  sendProblem(res, problemDetails({
586167
586472
  type: P.notFound,
586168
586473
  status: 404,
@@ -586194,7 +586499,7 @@ async function handleFilesRead(ctx3) {
586194
586499
  }));
586195
586500
  return true;
586196
586501
  }
586197
- const content = readFileSync82(resolved, "utf-8");
586502
+ const content = readFileSync83(resolved, "utf-8");
586198
586503
  const offset = typeof body.offset === "number" && body.offset >= 0 ? body.offset : 0;
586199
586504
  const limit = typeof body.limit === "number" && body.limit > 0 ? body.limit : content.length;
586200
586505
  const slice2 = content.slice(offset, offset + limit);
@@ -586427,14 +586732,14 @@ async function handleNexusStatus(ctx3) {
586427
586732
  const { res, requestId } = ctx3;
586428
586733
  try {
586429
586734
  const statePaths = [
586430
- join117(process.cwd(), ".oa", "nexus-peer-state.json"),
586431
- 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")
586432
586737
  ];
586433
586738
  const states = [];
586434
586739
  for (const p2 of statePaths) {
586435
- if (!existsSync101(p2)) continue;
586740
+ if (!existsSync102(p2)) continue;
586436
586741
  try {
586437
- const raw = readFileSync82(p2, "utf-8");
586742
+ const raw = readFileSync83(p2, "utf-8");
586438
586743
  states.push({ source: p2, data: JSON.parse(raw) });
586439
586744
  } catch (e2) {
586440
586745
  states.push({ source: p2, error: String(e2) });
@@ -586461,8 +586766,8 @@ async function handleNexusStatus(ctx3) {
586461
586766
  }
586462
586767
  function loadAgentName() {
586463
586768
  try {
586464
- const p2 = join117(homedir39(), ".open-agents", "agent-name");
586465
- 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();
586466
586771
  } catch {
586467
586772
  }
586468
586773
  return null;
@@ -586471,14 +586776,14 @@ async function handleSponsors(ctx3) {
586471
586776
  const { req: req2, res, url, requestId } = ctx3;
586472
586777
  try {
586473
586778
  const candidates = [
586474
- join117(homedir39(), ".open-agents", "sponsor-cache.json"),
586475
- join117(homedir39(), ".open-agents", "sponsors.json")
586779
+ join118(homedir40(), ".open-agents", "sponsor-cache.json"),
586780
+ join118(homedir40(), ".open-agents", "sponsors.json")
586476
586781
  ];
586477
586782
  let sponsors = [];
586478
586783
  for (const p2 of candidates) {
586479
- if (!existsSync101(p2)) continue;
586784
+ if (!existsSync102(p2)) continue;
586480
586785
  try {
586481
- const raw = JSON.parse(readFileSync82(p2, "utf-8"));
586786
+ const raw = JSON.parse(readFileSync83(p2, "utf-8"));
586482
586787
  if (Array.isArray(raw)) {
586483
586788
  sponsors = raw;
586484
586789
  break;
@@ -586547,8 +586852,8 @@ async function handleEvaluate(ctx3) {
586547
586852
  }));
586548
586853
  return true;
586549
586854
  }
586550
- const jobPath = join117(process.cwd(), ".oa", "jobs", `${runId}.json`);
586551
- if (!existsSync101(jobPath)) {
586855
+ const jobPath = join118(process.cwd(), ".oa", "jobs", `${runId}.json`);
586856
+ if (!existsSync102(jobPath)) {
586552
586857
  sendProblem(res, problemDetails({
586553
586858
  type: P.notFound,
586554
586859
  status: 404,
@@ -586558,7 +586863,7 @@ async function handleEvaluate(ctx3) {
586558
586863
  }));
586559
586864
  return true;
586560
586865
  }
586561
- const job = JSON.parse(readFileSync82(jobPath, "utf-8"));
586866
+ const job = JSON.parse(readFileSync83(jobPath, "utf-8"));
586562
586867
  sendJson(res, 200, {
586563
586868
  run_id: runId,
586564
586869
  task: job.task,
@@ -586695,6 +587000,358 @@ async function handleMintKey(ctx3) {
586695
587000
  }
586696
587001
  return true;
586697
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
+ }
587021
+ async function handleGenerateShare(ctx3) {
587022
+ const { req: req2, res, requestId } = ctx3;
587023
+ const reqAuth = req2;
587024
+ if (reqAuth._authScope !== "admin") {
587025
+ sendProblem(res, problemDetails({
587026
+ type: P.forbidden,
587027
+ status: 403,
587028
+ title: "Admin scope required",
587029
+ detail: "Generating a share URL mints a runtime key, which requires 'admin' scope.",
587030
+ instance: requestId
587031
+ }));
587032
+ return true;
587033
+ }
587034
+ try {
587035
+ const body = await parseJsonBodyStrict(req2).catch(() => null) ?? {};
587036
+ const label = typeof body["label"] === "string" && body["label"] ? String(body["label"]).slice(0, 100) : `share-${(/* @__PURE__ */ new Date()).toISOString().slice(0, 10)}`;
587037
+ const scope = typeof body["scope"] === "string" && ["read", "run", "admin"].includes(body["scope"]) ? body["scope"] : "run";
587038
+ const owner = `share:${label}`;
587039
+ const { mintKey: mintKey2 } = await Promise.resolve().then(() => (init_runtime_keys(), runtime_keys_exports));
587040
+ const rec = mintKey2({
587041
+ scope,
587042
+ owner,
587043
+ profile: null
587044
+ });
587045
+ const hostHeader = String(req2.headers["host"] || "").trim();
587046
+ const fallbackHost = process.env["OA_HOST"] || "127.0.0.1:11435";
587047
+ let hostPort = hostHeader || fallbackHost;
587048
+ hostPort = hostPort.replace(/^https?:\/\//i, "");
587049
+ const colonIdx = hostPort.lastIndexOf(":");
587050
+ const host = colonIdx > 0 ? hostPort.slice(0, colonIdx) : hostPort;
587051
+ const portStr = colonIdx > 0 ? hostPort.slice(colonIdx + 1) : "11435";
587052
+ const port = parseInt(portStr, 10) || 11435;
587053
+ const fullKey = rec.key || "";
587054
+ const keyPrefix2 = fullKey.slice(0, 12);
587055
+ if (!fullKey) {
587056
+ sendProblem(res, problemDetails({
587057
+ type: P.internalError,
587058
+ status: 500,
587059
+ title: "Key generation succeeded but no secret returned",
587060
+ detail: "mintKey() did not include the full secret in its response.",
587061
+ instance: requestId
587062
+ }));
587063
+ return true;
587064
+ }
587065
+ const directOnly = body["direct"] === true;
587066
+ const peerInfo = directOnly ? null : resolveLocalPeerId();
587067
+ const scheme = String(req2.headers["x-forwarded-proto"] || (req2.socket?.encrypted ? "https" : "http"));
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("&")}`;
587088
+ sendJson(res, 201, {
587089
+ shareUrl,
587090
+ plainUrl,
587091
+ mode,
587092
+ peerId: peerInfo?.peerId || null,
587093
+ onion: onion || null,
587094
+ agentName: peerInfo?.agentName || null,
587095
+ host,
587096
+ port,
587097
+ key: fullKey,
587098
+ keyPrefix: keyPrefix2,
587099
+ label,
587100
+ issuedAt: rec.created || (/* @__PURE__ */ new Date()).toISOString(),
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."
587107
+ });
587108
+ } catch (err) {
587109
+ sendProblem(res, problemDetails({
587110
+ type: P.internalError,
587111
+ status: 500,
587112
+ title: "Share URL generation failed",
587113
+ detail: err instanceof Error ? err.message : String(err),
587114
+ instance: requestId
587115
+ }));
587116
+ }
587117
+ return true;
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
+ }
586698
587355
  async function handleRevokeKey(ctx3, prefix) {
586699
587356
  const { req: req2, res, requestId } = ctx3;
586700
587357
  const reqAuth = req2;
@@ -587046,17 +587703,17 @@ async function handleListAgentTypes(ctx3) {
587046
587703
  }
587047
587704
  async function handleListEngines(ctx3) {
587048
587705
  const { res } = ctx3;
587049
- const home = homedir39();
587706
+ const home = homedir40();
587050
587707
  sendJson(res, 200, {
587051
587708
  engines: [
587052
- { name: "dream", state_file: join117(process.cwd(), ".oa", "dreams"), controllable_via: "SSE + slash commands" },
587053
- { name: "bless", state_file: join117(process.cwd(), ".oa", "bless-state.json"), controllable_via: "slash commands" },
587054
- { name: "call", state_file: join117(process.cwd(), ".oa", "call-state.json"), controllable_via: "slash commands" },
587055
- { name: "listen", state_file: join117(process.cwd(), ".oa", "listen-state.json"), controllable_via: "slash commands" },
587056
- { name: "telegram", state_file: join117(home, ".open-agents", "telegram-state.json"), controllable_via: "slash commands" },
587057
- { name: "expose", state_file: join117(process.cwd(), ".oa", "expose-state.json"), controllable_via: "/expose commands" },
587058
- { name: "nexus", state_file: join117(home, ".open-agents", "nexus-peer-cache.json"), controllable_via: "/nexus commands" },
587059
- { 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" }
587060
587717
  ],
587061
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."
587062
587719
  });
@@ -587139,12 +587796,12 @@ async function tryAimsRoute(ctx3) {
587139
587796
  return false;
587140
587797
  }
587141
587798
  function aimsDir() {
587142
- return join117(homedir39(), ".open-agents", "aims");
587799
+ return join118(homedir40(), ".open-agents", "aims");
587143
587800
  }
587144
587801
  function readAimsFile(name10, fallback) {
587145
587802
  try {
587146
- const p2 = join117(aimsDir(), name10);
587147
- 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"));
587148
587805
  } catch {
587149
587806
  }
587150
587807
  return fallback;
@@ -587153,7 +587810,7 @@ function writeAimsFile(name10, data) {
587153
587810
  const dir = aimsDir();
587154
587811
  const { mkdirSync: mkdirSync67, writeFileSync: wf, renameSync: rn } = __require("node:fs");
587155
587812
  mkdirSync67(dir, { recursive: true });
587156
- const finalPath = join117(dir, name10);
587813
+ const finalPath = join118(dir, name10);
587157
587814
  const tmpPath = `${finalPath}.tmp.${process.pid}.${Date.now()}`;
587158
587815
  try {
587159
587816
  wf(tmpPath, JSON.stringify(data, null, 2) + "\n", { encoding: "utf-8", mode: 384 });
@@ -587483,12 +588140,12 @@ async function handleAimsSuppliers(ctx3) {
587483
588140
  }
587484
588141
  ];
587485
588142
  const sponsorPaths = [
587486
- join117(homedir39(), ".open-agents", "sponsor-cache.json")
588143
+ join118(homedir40(), ".open-agents", "sponsor-cache.json")
587487
588144
  ];
587488
588145
  for (const p2 of sponsorPaths) {
587489
- if (!existsSync101(p2)) continue;
588146
+ if (!existsSync102(p2)) continue;
587490
588147
  try {
587491
- const raw = JSON.parse(readFileSync82(p2, "utf-8"));
588148
+ const raw = JSON.parse(readFileSync83(p2, "utf-8"));
587492
588149
  const list = Array.isArray(raw) ? raw : raw?.sponsors ?? [];
587493
588150
  for (const s2 of list) {
587494
588151
  suppliers.push({
@@ -589011,7 +589668,7 @@ body { display:flex; flex-direction:column; height:100vh; margin:0; overflow:hid
589011
589668
  <span id="sidebar-status-dot" style="width:8px;height:8px;border-radius:50%;background:var(--color-fg-faint);flex-shrink:0" title="Backend connection"></span>
589012
589669
  <span id="sidebar-status-text" style="flex:1;white-space:nowrap;overflow:hidden;text-overflow:ellipsis">connecting...</span>
589013
589670
  <button id="sidebar-update-btn" onclick="doUpdate()" style="display:none;background:var(--color-warning);border:none;color:#000;padding:2px 6px;border-radius:var(--radius-sm);font-size:0.6rem;cursor:pointer;font-weight:600">update</button>
589014
- <button id="sidebar-key-btn" onclick="document.getElementById('key-modal').style.display='flex'" title="Set API key" style="background:transparent;border:1px solid var(--color-border);color:var(--color-fg-muted);padding:2px 6px;border-radius:var(--radius-sm);font-size:0.6rem;cursor:pointer">key</button>
589671
+ <button id="sidebar-key-btn" onclick="openKeyModal()" title="Set API key / share access" style="background:transparent;border:1px solid var(--color-border);color:var(--color-fg-muted);padding:2px 6px;border-radius:var(--radius-sm);font-size:0.6rem;cursor:pointer">key</button>
589015
589672
  </div>
589016
589673
 
589017
589674
  <!-- Resize handle -->
@@ -589343,14 +590000,21 @@ body { display:flex; flex-direction:column; height:100vh; margin:0; overflow:hid
589343
590000
  </div>
589344
590001
 
589345
590002
  <div id="key-modal">
589346
- <form class="modal" onsubmit="event.preventDefault(); saveKey();" autocomplete="off">
589347
- <h3>API Key</h3>
589348
- <input id="key-input" type="password" placeholder="Bearer token (leave empty if auth disabled)" autocomplete="new-password">
589349
- <div>
590003
+ <form class="modal" onsubmit="event.preventDefault(); saveKey();" autocomplete="off" style="min-width:480px">
590004
+ <h3>API Key &amp; Sharing</h3>
590005
+ <div style="position:relative">
590006
+ <input id="key-input" type="password" placeholder="Bearer token (leave empty if auth disabled, or paste an oa-share:// URL to connect remote)" autocomplete="new-password" oninput="onKeyInputChange(this.value)" onfocus="showRecentKeysDropdown()" onblur="setTimeout(hideRecentKeysDropdown, 150)">
590007
+ <div id="key-recent-dropdown" style="display:none;position:absolute;top:100%;left:0;right:0;background:var(--color-bg-elevated);border:1px solid var(--color-border);border-radius:var(--radius-sm);max-height:180px;overflow-y:auto;z-index:10;font-size:0.78rem;margin-top:2px"></div>
590008
+ </div>
590009
+ <p id="key-input-hint" style="font-size:0.7rem;color:var(--color-fg-muted);margin:4px 0 8px">Tip: paste an <code>oa-share://host:port#key</code> URL or <code>http://host:port/?oa-key=…</code> to connect to a remote OA instance.</p>
590010
+ <div style="display:flex;gap:6px;flex-wrap:wrap">
589350
590011
  <button type="submit">save</button>
589351
590012
  <button type="button" onclick="clearKey()">clear</button>
590013
+ <button type="button" onclick="generateShareUrl()" title="Generate a share URL that lets a remote OA instance connect to this one">share access</button>
589352
590014
  <button type="button" onclick="closeKeyModal()">cancel</button>
589353
590015
  </div>
590016
+ <div id="share-result" style="display:none;margin-top:14px;padding:10px;background:var(--color-bg-elevated);border:1px solid var(--color-border);border-radius:var(--radius-sm);font-size:0.78rem"></div>
590017
+ <div id="remote-state" style="display:none;margin-top:14px;padding:10px;background:var(--color-bg-elevated);border:1px solid var(--color-accent);border-radius:var(--radius-sm);font-size:0.78rem"></div>
589354
590018
  </form>
589355
590019
  </div>
589356
590020
 
@@ -590472,6 +591136,15 @@ async function sendMessage() {
590472
591136
  messageWithContext = filesBlock + text;
590473
591137
  }
590474
591138
 
591139
+ // FRESH-SESSION: pull the one-shot fresh flag the newChatSession()
591140
+ // handler set. Consumed (cleared) here so subsequent sends within
591141
+ // this same session don't keep claiming "fresh" and miss legitimate
591142
+ // mid-session handoff.
591143
+ let _freshPending = false;
591144
+ try {
591145
+ _freshPending = localStorage.getItem('oa.freshSessionPending') === '1';
591146
+ if (_freshPending) localStorage.removeItem('oa.freshSessionPending');
591147
+ } catch {}
590475
591148
  const body = {
590476
591149
  session_id: chatSessionId,
590477
591150
  model: modelSelect.value,
@@ -590481,6 +591154,10 @@ async function sendMessage() {
590481
591154
  // Pass the user-selected workspace as working_directory so the
590482
591155
  // agent subprocess operates in the right cwd.
590483
591156
  ...(chatWorkingDir ? { working_directory: chatWorkingDir } : {}),
591157
+ // FRESH-SESSION: tell the daemon to skip cross-task handoff
591158
+ // injection on this turn. Daemon plumbs it to OA_FRESH_SESSION=1
591159
+ // for the agent subprocess; runner's handoff block respects it.
591160
+ ...(_freshPending ? { fresh: true } : {}),
590484
591161
  };
590485
591162
 
590486
591163
  const response = await fetch('/v1/chat', {
@@ -590788,11 +591465,174 @@ document.getElementById('key-btn').onclick = () => {
590788
591465
  document.getElementById('key-input').value = apiKey;
590789
591466
  };
590790
591467
  function saveKey() {
590791
- apiKey = document.getElementById('key-input').value;
591468
+ const raw = document.getElementById('key-input').value || '';
591469
+ const parsed = parseShareInput(raw);
591470
+ if (parsed) {
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
+ }
591507
+ }
591508
+ apiKey = raw;
590792
591509
  localStorage.setItem('oa-api-key', apiKey);
591510
+ if (apiKey) {
591511
+ saveRecentKey({ key: apiKey, host: location.host, label: 'local ' + location.host });
591512
+ }
590793
591513
  closeKeyModal();
590794
591514
  loadModels();
590795
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 {}
590796
591636
  function clearKey() {
590797
591637
  apiKey = '';
590798
591638
  localStorage.removeItem('oa-api-key');
@@ -590802,7 +591642,351 @@ function clearKey() {
590802
591642
  }
590803
591643
  function closeKeyModal() {
590804
591644
  document.getElementById('key-modal').classList.remove('visible');
591645
+ const sr = document.getElementById('share-result'); if (sr) sr.style.display = 'none';
590805
591646
  }
591647
+ function openKeyModal() {
591648
+ document.getElementById('key-modal').classList.add('visible');
591649
+ document.getElementById('key-input').value = apiKey;
591650
+ // Refresh the remote-state hint based on current localStorage.
591651
+ refreshKeyModalRemoteState();
591652
+ }
591653
+ window.openKeyModal = openKeyModal;
591654
+
591655
+ // ─── Share URL parsing ─────────────────────────────────────────────────
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
+ }
591669
+ function parseShareInput(raw) {
591670
+ const v = String(raw || '').trim();
591671
+ if (!v) return null;
591672
+ // oa-share scheme — custom parser since URL() doesn't always honor it.
591673
+ if (v.toLowerCase().startsWith('oa-share://')) {
591674
+ const after = v.slice('oa-share://'.length);
591675
+ const hashIdx = after.indexOf('#');
591676
+ if (hashIdx < 0) return null;
591677
+ const hostPart = after.slice(0, hashIdx);
591678
+ const key = after.slice(hashIdx + 1);
591679
+ if (!hostPart || !key) return null;
591680
+ // Three shapes:
591681
+ // peerId@host:port → libp2p+LAN
591682
+ // peerId → libp2p only (no host)
591683
+ // host:port → legacy direct
591684
+ const atIdx = hostPart.indexOf('@');
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' };
591694
+ }
591695
+ // http(s) URL with ?oa-key=, optional ?oa-share-peer=, optional ?oa-onion=
591696
+ if (/^https?:\\/\\//i.test(v)) {
591697
+ try {
591698
+ const u = new URL(v);
591699
+ const k = u.searchParams.get('oa-key');
591700
+ if (!k) return null;
591701
+ const peerIdQ = u.searchParams.get('oa-share-peer') || null;
591702
+ const onionQ = u.searchParams.get('oa-onion') || null;
591703
+ const label = u.searchParams.get('oa-share-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
+ };
591710
+ } catch { return null; }
591711
+ }
591712
+ return null;
591713
+ }
591714
+
591715
+ function onKeyInputChange(v) {
591716
+ const hint = document.getElementById('key-input-hint');
591717
+ if (!hint) return;
591718
+ const parsed = parseShareInput(v);
591719
+ if (parsed) {
591720
+ hint.innerHTML = '✓ detected share URL — host <code>' + escapeHtml(parsed.host) + '</code>; clicking save will open the remote UI with this key';
591721
+ hint.style.color = 'var(--color-success)';
591722
+ } else {
591723
+ hint.innerHTML = 'Tip: paste an <code>oa-share://host:port#key</code> URL or <code>http://host:port/?oa-key=…</code> to connect to a remote OA instance.';
591724
+ hint.style.color = 'var(--color-fg-muted)';
591725
+ }
591726
+ showRecentKeysDropdown();
591727
+ }
591728
+
591729
+ // Tiny HTML escape — same as the one used in connections settings.
591730
+ function escapeHtml(s) {
591731
+ return String(s || '').replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;').replace(/'/g, '&#39;');
591732
+ }
591733
+
591734
+ // ─── Recent keys (localStorage history with autocomplete dropdown) ──
591735
+ const _OA_RECENT_KEYS_LS = 'oa.recentKeys';
591736
+ const _OA_RECENT_KEYS_MAX = 10;
591737
+ function loadRecentKeys() {
591738
+ try {
591739
+ const raw = localStorage.getItem(_OA_RECENT_KEYS_LS);
591740
+ if (!raw) return [];
591741
+ const parsed = JSON.parse(raw);
591742
+ return Array.isArray(parsed) ? parsed : [];
591743
+ } catch { return []; }
591744
+ }
591745
+ function saveRecentKey(rec) {
591746
+ if (!rec || !rec.key) return;
591747
+ let list = loadRecentKeys();
591748
+ // Move existing entry with same key to top; otherwise prepend.
591749
+ list = list.filter(r => r.key !== rec.key);
591750
+ list.unshift({ ...rec, lastUsed: new Date().toISOString() });
591751
+ if (list.length > _OA_RECENT_KEYS_MAX) list = list.slice(0, _OA_RECENT_KEYS_MAX);
591752
+ try { localStorage.setItem(_OA_RECENT_KEYS_LS, JSON.stringify(list)); } catch {}
591753
+ }
591754
+ function deleteRecentKey(key) {
591755
+ let list = loadRecentKeys().filter(r => r.key !== key);
591756
+ try { localStorage.setItem(_OA_RECENT_KEYS_LS, JSON.stringify(list)); } catch {}
591757
+ showRecentKeysDropdown();
591758
+ }
591759
+ window.deleteRecentKey = deleteRecentKey;
591760
+
591761
+ function showRecentKeysDropdown() {
591762
+ const dd = document.getElementById('key-recent-dropdown');
591763
+ if (!dd) return;
591764
+ const recents = loadRecentKeys();
591765
+ const inp = document.getElementById('key-input');
591766
+ const filter = (inp && inp.value || '').toLowerCase();
591767
+ const matches = recents.filter(r => {
591768
+ if (!filter) return true;
591769
+ return (r.key || '').toLowerCase().includes(filter)
591770
+ || (r.host || '').toLowerCase().includes(filter)
591771
+ || (r.label || '').toLowerCase().includes(filter);
591772
+ });
591773
+ if (matches.length === 0) { dd.style.display = 'none'; return; }
591774
+ dd.innerHTML = matches.map(r => {
591775
+ const k = String(r.key || '');
591776
+ const masked = k.length > 12 ? k.slice(0, 4) + '…' + k.slice(-4) : k;
591777
+ const host = escapeHtml(r.host || '');
591778
+ const label = escapeHtml(r.label || '');
591779
+ const keySafe = k.replace(/\\\\/g, '\\\\\\\\').replace(/'/g, "\\\\'");
591780
+ return '<div class="oa-rec-key" style="display:flex;align-items:center;gap:8px;padding:6px 10px;cursor:pointer;border-bottom:1px solid var(--color-border)" '
591781
+ + 'onclick="useRecentKey(\\'' + keySafe + '\\')" '
591782
+ + 'onmouseover="this.style.background=\\'var(--color-bg-hover)\\'" '
591783
+ + 'onmouseout="this.style.background=\\'transparent\\'">'
591784
+ + '<span style="flex:1;font-family:var(--font-mono);font-size:0.78rem"><span style="color:var(--color-fg-muted)">' + (host || 'local') + '</span> · <code>' + masked + '</code></span>'
591785
+ + '<span style="font-size:0.7rem;color:var(--color-fg-muted)">' + label + '</span>'
591786
+ + '<button type="button" onclick="event.stopPropagation();deleteRecentKey(\\'' + keySafe + '\\')" '
591787
+ + 'title="Forget this key" '
591788
+ + 'style="background:transparent;border:none;color:var(--color-fg-muted);cursor:pointer;font-size:1rem;padding:0 4px">&times;</button>'
591789
+ + '</div>';
591790
+ }).join('');
591791
+ dd.style.display = 'block';
591792
+ }
591793
+ function hideRecentKeysDropdown() {
591794
+ const dd = document.getElementById('key-recent-dropdown');
591795
+ if (dd) dd.style.display = 'none';
591796
+ }
591797
+ function useRecentKey(k) {
591798
+ const inp = document.getElementById('key-input');
591799
+ if (inp) {
591800
+ inp.value = k;
591801
+ inp.focus();
591802
+ onKeyInputChange(k);
591803
+ }
591804
+ hideRecentKeysDropdown();
591805
+ }
591806
+ window.useRecentKey = useRecentKey;
591807
+
591808
+ // ─── Generate share URL ────────────────────────────────────────────────
591809
+ async function generateShareUrl() {
591810
+ const out = document.getElementById('share-result');
591811
+ if (!out) return;
591812
+ out.style.display = 'block';
591813
+ out.innerHTML = '<div style="color:var(--color-fg-muted)">generating…</div>';
591814
+ try {
591815
+ const r = await fetch('/v1/share/generate', {
591816
+ method: 'POST',
591817
+ headers: { ...headers(), 'Content-Type': 'application/json' },
591818
+ body: JSON.stringify({ scope: 'run' }),
591819
+ });
591820
+ if (r.status === 403) {
591821
+ out.innerHTML = '<div style="color:var(--color-error)">✗ admin scope required — your current key does not have permission to mint share URLs. Set an admin-scope key first.</div>';
591822
+ return;
591823
+ }
591824
+ if (r.status >= 400) {
591825
+ const err = await r.json().catch(() => ({}));
591826
+ out.innerHTML = '<div style="color:var(--color-error)">✗ HTTP ' + r.status + (err.detail ? ' — ' + escapeHtml(err.detail) : '') + '</div>';
591827
+ return;
591828
+ }
591829
+ const j = await r.json();
591830
+ const safeShare = escapeHtml(j.shareUrl || '');
591831
+ const safePlain = escapeHtml(j.plainUrl || '');
591832
+ out.innerHTML =
591833
+ '<div style="font-weight:500;margin-bottom:6px;color:var(--color-success)">✓ Share URL generated — hand off to remote</div>' +
591834
+ '<div style="margin:6px 0">' +
591835
+ '<label style="display:block;font-size:0.7rem;color:var(--color-fg-muted);margin-bottom:2px">oa-share URL (compact, recommended for OA-to-OA)</label>' +
591836
+ '<div style="display:flex;gap:6px;align-items:center">' +
591837
+ '<input readonly value="' + safeShare + '" style="flex:1;background:var(--color-bg-input);border:1px solid var(--color-border);color:var(--color-fg);padding:5px 8px;border-radius:var(--radius-sm);font-family:var(--font-mono);font-size:0.74rem">' +
591838
+ '<button type="button" onclick="copyShareUrl(\\'oa-share\\')" style="font-size:0.74rem">copy</button>' +
591839
+ '</div>' +
591840
+ '</div>' +
591841
+ '<div style="margin:6px 0">' +
591842
+ '<label style="display:block;font-size:0.7rem;color:var(--color-fg-muted);margin-bottom:2px">plain URL (works in any browser, no scheme handler needed)</label>' +
591843
+ '<div style="display:flex;gap:6px;align-items:center">' +
591844
+ '<input readonly value="' + safePlain + '" style="flex:1;background:var(--color-bg-input);border:1px solid var(--color-border);color:var(--color-fg);padding:5px 8px;border-radius:var(--radius-sm);font-family:var(--font-mono);font-size:0.74rem">' +
591845
+ '<button type="button" onclick="copyShareUrl(\\'plain\\')" style="font-size:0.74rem">copy</button>' +
591846
+ '</div>' +
591847
+ '</div>' +
591848
+ '<p style="font-size:0.68rem;color:var(--color-fg-muted);margin:6px 0 0">Note: this URL contains the full secret. Anyone with it can use this OA. To revoke: open Advanced settings → keys → revoke the prefix <code>' + escapeHtml(j.keyPrefix || '') + '</code>.</p>';
591849
+ // Stash for the copy buttons + add to recent.
591850
+ window.__oaLastShareUrl = j.shareUrl;
591851
+ window.__oaLastPlainUrl = j.plainUrl;
591852
+ saveRecentKey({ key: j.key, host: j.host + ':' + j.port, label: j.label || ('shared ' + j.host) });
591853
+ } catch (e) {
591854
+ out.innerHTML = '<div style="color:var(--color-error)">✗ ' + escapeHtml(e && e.message ? e.message : String(e)) + '</div>';
591855
+ }
591856
+ }
591857
+ function copyShareUrl(which) {
591858
+ const v = which === 'plain' ? window.__oaLastPlainUrl : window.__oaLastShareUrl;
591859
+ if (!v) return;
591860
+ if (navigator.clipboard && navigator.clipboard.writeText) {
591861
+ navigator.clipboard.writeText(v).then(
591862
+ () => { /* ok */ },
591863
+ () => { fallbackCopy(v); }
591864
+ );
591865
+ } else {
591866
+ fallbackCopy(v);
591867
+ }
591868
+ }
591869
+ function fallbackCopy(v) {
591870
+ const ta = document.createElement('textarea');
591871
+ ta.value = v; document.body.appendChild(ta); ta.select();
591872
+ try { document.execCommand('copy'); } catch {}
591873
+ document.body.removeChild(ta);
591874
+ }
591875
+ window.generateShareUrl = generateShareUrl;
591876
+ window.copyShareUrl = copyShareUrl;
591877
+
591878
+ // ─── Remote-state helpers (visual indicator on the key button) ───────
591879
+ // When localStorage carries oa.remoteHost we are in REMOTE mode: the
591880
+ // key was loaded from an oa-share URL or via on-load ?oa-key=. The key
591881
+ // button shows a "remote" badge and clicking expands to show host+key
591882
+ // inline with a "close connection" button that severs and shuffles the
591883
+ // key into recents.
591884
+ function refreshKeyModalRemoteState() {
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() {
591890
+ try { return localStorage.getItem('oa.remoteHost') || ''; } catch { return ''; }
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
+ }
591903
+ const stateBox = document.getElementById('remote-state');
591904
+ const btn = document.getElementById('sidebar-key-btn');
591905
+ if (isRemote && btn) {
591906
+ btn.textContent = 'remote';
591907
+ btn.style.background = 'var(--color-accent)';
591908
+ btn.style.color = '#fff';
591909
+ btn.style.borderColor = 'var(--color-accent)';
591910
+ btn.title = 'connected to ' + remoteLabel + ' — click to view / disconnect';
591911
+ } else if (btn) {
591912
+ btn.textContent = 'key';
591913
+ btn.style.background = 'transparent';
591914
+ btn.style.color = 'var(--color-fg-muted)';
591915
+ btn.style.borderColor = 'var(--color-border)';
591916
+ btn.title = 'set API key / share access';
591917
+ }
591918
+ if (!stateBox) return;
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
+ }
591930
+ stateBox.style.display = 'block';
591931
+ stateBox.innerHTML =
591932
+ '<div style="font-weight:500;color:var(--color-accent)">REMOTE connection active</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>' +
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>';
591936
+ } else {
591937
+ stateBox.style.display = 'none';
591938
+ }
591939
+ }
591940
+ function closeRemoteConnection() {
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 });
591956
+ }
591957
+ try {
591958
+ localStorage.removeItem('oa.remoteHost');
591959
+ localStorage.removeItem('oa.remoteScheme');
591960
+ localStorage.removeItem('oa-api-key');
591961
+ } catch {}
591962
+ apiKey = '';
591963
+ refreshKeyModalRemoteState();
591964
+ location.reload();
591965
+ }
591966
+ window.closeRemoteConnection = closeRemoteConnection;
591967
+
591968
+ // ─── On-load: ?oa-key=... pickup ───────────────────────────────────────
591969
+ // When the page loads with an oa-key query param (i.e. someone clicked
591970
+ // a share URL), capture the key into localStorage, mark this as a
591971
+ // remote session (host = this origin), and clean the URL.
591972
+ (function pickupShareKeyFromUrl() {
591973
+ try {
591974
+ const u = new URL(location.href);
591975
+ const k = u.searchParams.get('oa-key');
591976
+ if (!k) return;
591977
+ const label = u.searchParams.get('oa-share-label') || '';
591978
+ localStorage.setItem('oa-api-key', k);
591979
+ // Origin-based remote: when the page loaded from a different host
591980
+ // than the user's "home" OA, that's a remote session by definition.
591981
+ localStorage.setItem('oa.remoteHost', location.host);
591982
+ localStorage.setItem('oa.remoteScheme', location.protocol.replace(':', ''));
591983
+ saveRecentKey({ key: k, host: location.host, label: label || ('remote ' + location.host) });
591984
+ // Strip the key from the URL so it isn't visible / browser-history'd.
591985
+ u.searchParams.delete('oa-key');
591986
+ u.searchParams.delete('oa-share-label');
591987
+ history.replaceState({}, '', u.pathname + (u.search || '') + (u.hash || ''));
591988
+ } catch {}
591989
+ })();
590806
591990
 
590807
591991
  // Tab switching
590808
591992
  const allPanels = ['chat-container','agent-panel','jobs-panel','config-panel','activity-panel','projects-panel','voice-panel'];
@@ -591536,6 +592720,11 @@ function newChatSession() {
591536
592720
  switchSession('');
591537
592721
  const sel = document.getElementById('chat-session-select');
591538
592722
  if (sel) sel.value = '';
592723
+ // FRESH-SESSION: mark the next chat send as fresh so the daemon skips
592724
+ // cross-task handoff injection (which previously bled prior session
592725
+ // context into the new chat regardless of the browser's reset).
592726
+ // Cleared inside the chat send handler after one use.
592727
+ try { localStorage.setItem('oa.freshSessionPending', '1'); } catch {}
591539
592728
  }
591540
592729
  function deleteChatSession() {
591541
592730
  if (!chatSessionId) return;
@@ -592399,6 +593588,7 @@ async function doUpdate() {
592399
593588
  try { pollMetrics(); } catch {}
592400
593589
  try { loadScheduled(); } catch {}
592401
593590
  try { loadServices(); } catch {}
593591
+ try { refreshKeyModalRemoteState(); } catch {}
592402
593592
 
592403
593593
  btn.textContent = 'updated v' + newVersion;
592404
593594
  btn.style.background = '#1a3a1a';
@@ -593383,6 +594573,7 @@ $currentProject.subscribe((proj) => {
593383
594573
  try { if (typeof updateSessionSelect === 'function') updateSessionSelect(); } catch {}
593384
594574
  try { if (typeof updateAgentRunSelect === 'function') updateAgentRunSelect(); } catch {}
593385
594575
  try { if (typeof restoreChatSession === 'function') restoreChatSession(); } catch {}
594576
+ try { if (typeof refreshKeyModalRemoteState === 'function') refreshKeyModalRemoteState(); } catch {}
593386
594577
  });
593387
594578
 
593388
594579
  // $selectedModel changes update the visible <select> if it's not
@@ -595561,15 +596752,15 @@ var init_auth_oidc = __esm({
595561
596752
  });
595562
596753
 
595563
596754
  // packages/cli/src/api/usage-tracker.ts
595564
- import { mkdirSync as mkdirSync60, readFileSync as readFileSync83, writeFileSync as writeFileSync52, existsSync as existsSync102 } from "node:fs";
595565
- 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";
595566
596757
  function initUsageTracker(oaDir) {
595567
- const dir = join118(oaDir, "usage");
596758
+ const dir = join119(oaDir, "usage");
595568
596759
  mkdirSync60(dir, { recursive: true });
595569
- usageFile = join118(dir, "token-usage.json");
596760
+ usageFile = join119(dir, "token-usage.json");
595570
596761
  try {
595571
- if (existsSync102(usageFile)) {
595572
- store = JSON.parse(readFileSync83(usageFile, "utf-8"));
596762
+ if (existsSync103(usageFile)) {
596763
+ store = JSON.parse(readFileSync84(usageFile, "utf-8"));
595573
596764
  }
595574
596765
  } catch {
595575
596766
  store = { providers: {}, lastSaved: "" };
@@ -595633,24 +596824,24 @@ var init_usage_tracker = __esm({
595633
596824
  });
595634
596825
 
595635
596826
  // packages/cli/src/api/profiles.ts
595636
- import { existsSync as existsSync103, readFileSync as readFileSync84, writeFileSync as writeFileSync53, mkdirSync as mkdirSync61, readdirSync as readdirSync35, unlinkSync as unlinkSync23 } from "node:fs";
595637
- import { join as join119 } from "node:path";
595638
- 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";
595639
596830
  import { createCipheriv as createCipheriv4, createDecipheriv as createDecipheriv4, randomBytes as randomBytes22, scryptSync as scryptSync3 } from "node:crypto";
595640
596831
  function globalProfileDir() {
595641
- return join119(homedir40(), ".open-agents", "profiles");
596832
+ return join120(homedir41(), ".open-agents", "profiles");
595642
596833
  }
595643
596834
  function projectProfileDir(projectDir2) {
595644
- return join119(projectDir2 || process.cwd(), ".oa", "profiles");
596835
+ return join120(projectDir2 || process.cwd(), ".oa", "profiles");
595645
596836
  }
595646
596837
  function listProfiles(projectDir2) {
595647
596838
  const result = [];
595648
596839
  const seen = /* @__PURE__ */ new Set();
595649
596840
  const projDir = projectProfileDir(projectDir2);
595650
- if (existsSync103(projDir)) {
596841
+ if (existsSync104(projDir)) {
595651
596842
  for (const f2 of readdirSync35(projDir).filter((f3) => f3.endsWith(".json"))) {
595652
596843
  try {
595653
- const raw = JSON.parse(readFileSync84(join119(projDir, f2), "utf8"));
596844
+ const raw = JSON.parse(readFileSync85(join120(projDir, f2), "utf8"));
595654
596845
  const name10 = f2.replace(".json", "");
595655
596846
  seen.add(name10);
595656
596847
  result.push({
@@ -595664,12 +596855,12 @@ function listProfiles(projectDir2) {
595664
596855
  }
595665
596856
  }
595666
596857
  const globDir = globalProfileDir();
595667
- if (existsSync103(globDir)) {
596858
+ if (existsSync104(globDir)) {
595668
596859
  for (const f2 of readdirSync35(globDir).filter((f3) => f3.endsWith(".json"))) {
595669
596860
  const name10 = f2.replace(".json", "");
595670
596861
  if (seen.has(name10)) continue;
595671
596862
  try {
595672
- const raw = JSON.parse(readFileSync84(join119(globDir, f2), "utf8"));
596863
+ const raw = JSON.parse(readFileSync85(join120(globDir, f2), "utf8"));
595673
596864
  result.push({
595674
596865
  name: name10,
595675
596866
  description: raw.description || "",
@@ -595684,11 +596875,11 @@ function listProfiles(projectDir2) {
595684
596875
  }
595685
596876
  function loadProfile(name10, password, projectDir2) {
595686
596877
  const sanitized = name10.replace(/[^a-zA-Z0-9_-]/g, "");
595687
- const projPath = join119(projectProfileDir(projectDir2), `${sanitized}.json`);
595688
- const globPath = join119(globalProfileDir(), `${sanitized}.json`);
595689
- 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;
595690
596881
  if (!filePath) return null;
595691
- const raw = JSON.parse(readFileSync84(filePath, "utf8"));
596882
+ const raw = JSON.parse(readFileSync85(filePath, "utf8"));
595692
596883
  if (raw.encrypted === true) {
595693
596884
  if (!password) return null;
595694
596885
  return decryptProfile(raw, password);
@@ -595699,7 +596890,7 @@ function saveProfile(profile, password, scope = "global", projectDir2) {
595699
596890
  const dir = scope === "project" ? projectProfileDir(projectDir2) : globalProfileDir();
595700
596891
  mkdirSync61(dir, { recursive: true });
595701
596892
  const sanitized = profile.name.replace(/[^a-zA-Z0-9_-]/g, "");
595702
- const filePath = join119(dir, `${sanitized}.json`);
596893
+ const filePath = join120(dir, `${sanitized}.json`);
595703
596894
  profile.modified = (/* @__PURE__ */ new Date()).toISOString();
595704
596895
  if (password) {
595705
596896
  const encrypted = encryptProfile(profile, password);
@@ -595712,8 +596903,8 @@ function saveProfile(profile, password, scope = "global", projectDir2) {
595712
596903
  function deleteProfile(name10, scope = "global", projectDir2) {
595713
596904
  const sanitized = name10.replace(/[^a-zA-Z0-9_-]/g, "");
595714
596905
  const dir = scope === "project" ? projectProfileDir(projectDir2) : globalProfileDir();
595715
- const filePath = join119(dir, `${sanitized}.json`);
595716
- if (existsSync103(filePath)) {
596906
+ const filePath = join120(dir, `${sanitized}.json`);
596907
+ if (existsSync104(filePath)) {
595717
596908
  unlinkSync23(filePath);
595718
596909
  return true;
595719
596910
  }
@@ -595828,23 +597019,23 @@ var init_profiles = __esm({
595828
597019
 
595829
597020
  // packages/cli/src/docker.ts
595830
597021
  import { execSync as execSync56, spawn as spawn24 } from "node:child_process";
595831
- import { existsSync as existsSync104, mkdirSync as mkdirSync62, writeFileSync as writeFileSync54 } from "node:fs";
595832
- import { join as join120, resolve as resolve37, dirname as dirname35 } from "node:path";
595833
- 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";
595834
597025
  import { fileURLToPath as fileURLToPath16 } from "node:url";
595835
597026
  function getDockerDir() {
595836
597027
  try {
595837
597028
  if (typeof __dirname !== "undefined") {
595838
- return join120(__dirname, "..", "..", "..", "docker");
597029
+ return join121(__dirname, "..", "..", "..", "docker");
595839
597030
  }
595840
597031
  } catch {
595841
597032
  }
595842
597033
  try {
595843
597034
  const thisDir = dirname35(fileURLToPath16(import.meta.url));
595844
- return join120(thisDir, "..", "..", "..", "docker");
597035
+ return join121(thisDir, "..", "..", "..", "docker");
595845
597036
  } catch {
595846
597037
  }
595847
- return join120(process.cwd(), "docker");
597038
+ return join121(process.cwd(), "docker");
595848
597039
  }
595849
597040
  function isDockerAvailable() {
595850
597041
  try {
@@ -595975,10 +597166,10 @@ async function ensureOaImage(force = false) {
595975
597166
  }
595976
597167
  let buildContext;
595977
597168
  const dockerDir = getDockerDir();
595978
- if (existsSync104(join120(dockerDir, "Dockerfile"))) {
597169
+ if (existsSync105(join121(dockerDir, "Dockerfile"))) {
595979
597170
  buildContext = dockerDir;
595980
597171
  } else {
595981
- buildContext = join120(homedir41(), ".oa", "docker-build");
597172
+ buildContext = join121(homedir42(), ".oa", "docker-build");
595982
597173
  mkdirSync62(buildContext, { recursive: true });
595983
597174
  writeDockerfiles(buildContext);
595984
597175
  }
@@ -596053,8 +597244,8 @@ chown -R node:node /workspace /home/node/.oa /home/node/.open-agents 2>/dev/null
596053
597244
  if [ "$1" = "oa" ]; then shift; exec su - node -c "cd /workspace && oa $*"; fi
596054
597245
  exec "$@"
596055
597246
  `;
596056
- writeFileSync54(join120(dir, "Dockerfile"), dockerfile);
596057
- 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 });
596058
597249
  }
596059
597250
  function hasNvidiaGpu() {
596060
597251
  try {
@@ -596307,23 +597498,23 @@ import * as http5 from "node:http";
596307
597498
  import * as https3 from "node:https";
596308
597499
  import { createRequire as createRequire5 } from "node:module";
596309
597500
  import { fileURLToPath as fileURLToPath17 } from "node:url";
596310
- import { dirname as dirname36, join as join121, resolve as resolve38 } from "node:path";
596311
- 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";
596312
597503
  import { spawn as spawn25, execSync as execSync57 } from "node:child_process";
596313
- 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";
596314
597505
  import { randomBytes as randomBytes23, randomUUID as randomUUID16 } from "node:crypto";
596315
597506
  import { createHash as createHash19 } from "node:crypto";
596316
597507
  function getVersion3() {
596317
597508
  try {
596318
597509
  const thisDir = dirname36(fileURLToPath17(import.meta.url));
596319
597510
  const candidates = [
596320
- join121(thisDir, "..", "package.json"),
596321
- join121(thisDir, "..", "..", "package.json"),
596322
- join121(thisDir, "..", "..", "..", "package.json")
597511
+ join122(thisDir, "..", "package.json"),
597512
+ join122(thisDir, "..", "..", "package.json"),
597513
+ join122(thisDir, "..", "..", "..", "package.json")
596323
597514
  ];
596324
597515
  for (const pkgPath of candidates) {
596325
597516
  try {
596326
- if (!existsSync105(pkgPath)) continue;
597517
+ if (!existsSync106(pkgPath)) continue;
596327
597518
  const pkg = require3(pkgPath);
596328
597519
  if (pkg.name === "open-agents-ai" || pkg.name === "@open-agents/cli" || pkg.name === "@open-agents/monorepo") {
596329
597520
  return pkg.version ?? "0.0.0";
@@ -596525,9 +597716,9 @@ function isOriginAllowed(origin) {
596525
597716
  if (!origin) return true;
596526
597717
  let accessMode = (process.env["OA_ACCESS"] || "").toLowerCase().trim();
596527
597718
  try {
596528
- const accessFile = join121(homedir42(), ".open-agents", "access");
596529
- if (existsSync105(accessFile)) {
596530
- 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();
596531
597722
  if (persisted === "any" || persisted === "lan" || persisted === "loopback") {
596532
597723
  accessMode = persisted;
596533
597724
  }
@@ -596587,7 +597778,7 @@ async function retrieveMemoryContext(userMessage, sessionId, maxEpisodes = 5) {
596587
597778
  if (!memMod || !memMod.EpisodeStore) {
596588
597779
  return { contextBlock: "", retrieved: [] };
596589
597780
  }
596590
- const dbPath = join121(homedir42(), ".open-agents", "memory.db");
597781
+ const dbPath = join122(homedir43(), ".open-agents", "memory.db");
596591
597782
  const store2 = new memMod.EpisodeStore(dbPath);
596592
597783
  const recent = store2.search({ limit: 30, sessionId: void 0 }) ?? [];
596593
597784
  const qLower = userMessage.toLowerCase();
@@ -596630,7 +597821,7 @@ async function writeMemoryEpisodes(sessionId, userMessage, assistantContent, too
596630
597821
  try {
596631
597822
  const memMod = await Promise.resolve().then(() => (init_dist7(), dist_exports2)).catch(() => null);
596632
597823
  if (!memMod || !memMod.EpisodeStore) return 0;
596633
- const dbPath = join121(homedir42(), ".open-agents", "memory.db");
597824
+ const dbPath = join122(homedir43(), ".open-agents", "memory.db");
596634
597825
  const store2 = new memMod.EpisodeStore(dbPath);
596635
597826
  let written = 0;
596636
597827
  try {
@@ -596952,27 +598143,27 @@ function ollamaStream(ollamaUrl, path8, method, body, onData, onEnd, onError, ti
596952
598143
  }
596953
598144
  function jobsDir() {
596954
598145
  const root = resolve38(process.cwd());
596955
- const dir = join121(root, ".oa", "jobs");
598146
+ const dir = join122(root, ".oa", "jobs");
596956
598147
  mkdirSync63(dir, { recursive: true });
596957
598148
  return dir;
596958
598149
  }
596959
598150
  function loadJob(id) {
596960
- const file = join121(jobsDir(), `${id}.json`);
596961
- if (!existsSync105(file)) return null;
598151
+ const file = join122(jobsDir(), `${id}.json`);
598152
+ if (!existsSync106(file)) return null;
596962
598153
  try {
596963
- return JSON.parse(readFileSync85(file, "utf-8"));
598154
+ return JSON.parse(readFileSync86(file, "utf-8"));
596964
598155
  } catch {
596965
598156
  return null;
596966
598157
  }
596967
598158
  }
596968
598159
  function listJobs() {
596969
598160
  const dir = jobsDir();
596970
- if (!existsSync105(dir)) return [];
598161
+ if (!existsSync106(dir)) return [];
596971
598162
  const files = readdirSync36(dir).filter((f2) => f2.endsWith(".json")).sort();
596972
598163
  const jobs = [];
596973
598164
  for (const file of files) {
596974
598165
  try {
596975
- jobs.push(JSON.parse(readFileSync85(join121(dir, file), "utf-8")));
598166
+ jobs.push(JSON.parse(readFileSync86(join122(dir, file), "utf-8")));
596976
598167
  } catch {
596977
598168
  }
596978
598169
  }
@@ -596982,14 +598173,14 @@ function pruneOldJobs() {
596982
598173
  const retentionH = parseFloat(process.env["OA_RUN_RETENTION_H"] || "24");
596983
598174
  const cutoffMs = Date.now() - (Number.isFinite(retentionH) && retentionH > 0 ? retentionH : 24) * 36e5;
596984
598175
  const dir = jobsDir();
596985
- if (!existsSync105(dir)) return { pruned: 0, kept: 0 };
598176
+ if (!existsSync106(dir)) return { pruned: 0, kept: 0 };
596986
598177
  let pruned = 0;
596987
598178
  let kept = 0;
596988
598179
  for (const file of readdirSync36(dir)) {
596989
598180
  if (!file.endsWith(".json")) continue;
596990
- const path8 = join121(dir, file);
598181
+ const path8 = join122(dir, file);
596991
598182
  try {
596992
- const job = JSON.parse(readFileSync85(path8, "utf-8"));
598183
+ const job = JSON.parse(readFileSync86(path8, "utf-8"));
596993
598184
  if (job.status === "running") {
596994
598185
  kept++;
596995
598186
  continue;
@@ -597002,7 +598193,7 @@ function pruneOldJobs() {
597002
598193
  } catch {
597003
598194
  }
597004
598195
  const outFile = path8.replace(/\.json$/, ".output");
597005
- if (existsSync105(outFile)) {
598196
+ if (existsSync106(outFile)) {
597006
598197
  try {
597007
598198
  unlinkSync24(outFile);
597008
598199
  } catch {
@@ -597295,7 +598486,7 @@ function autoSeedTodosFromPrompt(prompt) {
597295
598486
  return [];
597296
598487
  }
597297
598488
  function atomicJobWrite(dir, id, job) {
597298
- const finalPath = join121(dir, `${id}.json`);
598489
+ const finalPath = join122(dir, `${id}.json`);
597299
598490
  const tmpPath = `${finalPath}.tmp.${process.pid}.${Date.now()}`;
597300
598491
  try {
597301
598492
  writeFileSync55(tmpPath, JSON.stringify(job, null, 2), "utf-8");
@@ -598721,23 +599912,23 @@ ${task}` : task;
598721
599912
  });
598722
599913
  }
598723
599914
  function updateStateFile() {
598724
- return join121(homedir42(), ".open-agents", "update-state.json");
599915
+ return join122(homedir43(), ".open-agents", "update-state.json");
598725
599916
  }
598726
599917
  function updateLogPath() {
598727
- return join121(homedir42(), ".open-agents", "update.log");
599918
+ return join122(homedir43(), ".open-agents", "update.log");
598728
599919
  }
598729
599920
  function readUpdateState() {
598730
599921
  try {
598731
599922
  const p2 = updateStateFile();
598732
- if (!existsSync105(p2)) return null;
598733
- return JSON.parse(readFileSync85(p2, "utf-8"));
599923
+ if (!existsSync106(p2)) return null;
599924
+ return JSON.parse(readFileSync86(p2, "utf-8"));
598734
599925
  } catch {
598735
599926
  return null;
598736
599927
  }
598737
599928
  }
598738
599929
  function writeUpdateState(state) {
598739
599930
  try {
598740
- const dir = join121(homedir42(), ".open-agents");
599931
+ const dir = join122(homedir43(), ".open-agents");
598741
599932
  mkdirSync63(dir, { recursive: true });
598742
599933
  const finalPath = updateStateFile();
598743
599934
  const tmpPath = `${finalPath}.tmp.${process.pid}`;
@@ -598785,15 +599976,15 @@ async function handleV1Update(req2, res, requestId) {
598785
599976
  const { execSync: es } = require3("node:child_process");
598786
599977
  const isWin2 = process.platform === "win32";
598787
599978
  let npmBin = "";
598788
- for (const candidate of isWin2 ? [join121(nodeDir, "npm.cmd"), join121(nodeDir, "npm")] : [join121(nodeDir, "npm"), "/usr/local/bin/npm", "/usr/bin/npm"]) {
598789
- 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)) {
598790
599981
  npmBin = candidate;
598791
599982
  break;
598792
599983
  }
598793
599984
  }
598794
599985
  if (!npmBin) npmBin = isWin2 ? "npm.cmd" : "npm";
598795
599986
  const pkgSpec = `open-agents-ai@${targetVersion}`;
598796
- const dir = join121(homedir42(), ".open-agents");
599987
+ const dir = join122(homedir43(), ".open-agents");
598797
599988
  fs7.mkdirSync(dir, { recursive: true });
598798
599989
  const logFd = fs7.openSync(logPath3, "w");
598799
599990
  const npmPrefix = dirname36(nodeDir);
@@ -598803,13 +599994,13 @@ async function handleV1Update(req2, res, requestId) {
598803
599994
  globalBinDir = es(`${npmBin} bin -g`, { encoding: "utf8", timeout: 5e3, stdio: "pipe" }).trim();
598804
599995
  } else {
598805
599996
  const npmCliCandidates = [
598806
- join121(nodeDir, "..", "lib", "node_modules", "npm", "bin", "npm-cli.js"),
598807
- 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")
598808
599999
  ];
598809
600000
  let npmCli = "";
598810
600001
  for (const c9 of npmCliCandidates) {
598811
600002
  try {
598812
- if (existsSync105(c9)) {
600003
+ if (existsSync106(c9)) {
598813
600004
  npmCli = c9;
598814
600005
  break;
598815
600006
  }
@@ -598841,13 +600032,13 @@ async function handleV1Update(req2, res, requestId) {
598841
600032
  });
598842
600033
  } else {
598843
600034
  const npmCliCandidates = [
598844
- join121(nodeDir, "..", "lib", "node_modules", "npm", "bin", "npm-cli.js"),
598845
- 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")
598846
600037
  ];
598847
600038
  let npmCli = "";
598848
600039
  for (const c9 of npmCliCandidates) {
598849
600040
  try {
598850
- if (existsSync105(c9)) {
600041
+ if (existsSync106(c9)) {
598851
600042
  npmCli = c9;
598852
600043
  break;
598853
600044
  }
@@ -598944,8 +600135,8 @@ function handleV1UpdateStatus(res) {
598944
600135
  let logTail = "";
598945
600136
  let exitCode = null;
598946
600137
  try {
598947
- if (existsSync105(logPath3)) {
598948
- const raw = readFileSync85(logPath3, "utf-8");
600138
+ if (existsSync106(logPath3)) {
600139
+ const raw = readFileSync86(logPath3, "utf-8");
598949
600140
  const m2 = raw.match(/__EXIT_CODE=(\d+)/);
598950
600141
  if (m2) exitCode = parseInt(m2[1], 10);
598951
600142
  logTail = raw.slice(-2e3);
@@ -599041,7 +600232,7 @@ async function handleV1Run(req2, res) {
599041
600232
  if (workingDir) {
599042
600233
  cwd4 = resolve38(workingDir);
599043
600234
  } else if (isolate) {
599044
- const wsDir = join121(dir, "..", "workspaces", id);
600235
+ const wsDir = join122(dir, "..", "workspaces", id);
599045
600236
  mkdirSync63(wsDir, { recursive: true });
599046
600237
  cwd4 = wsDir;
599047
600238
  } else {
@@ -599228,7 +600419,7 @@ async function handleV1Run(req2, res) {
599228
600419
  let output = "";
599229
600420
  let tailBytes = 0;
599230
600421
  const TAIL_BUDGET = 1048576;
599231
- const outputWriter = new DiskTaskOutput(join121(dir, `${id}.output`));
600422
+ const outputWriter = new DiskTaskOutput(join122(dir, `${id}.output`));
599232
600423
  job.outputFile = outputWriter.path;
599233
600424
  atomicJobWrite(dir, id, job);
599234
600425
  child.stdout?.on("data", (chunk) => {
@@ -600994,6 +602185,9 @@ ${historyLines}
600994
602185
  runEnv["OA_RUN_USER"] = req2._authUser || "anonymous";
600995
602186
  runEnv["OA_RUN_SCOPE"] = req2._authScope || "admin";
600996
602187
  runEnv["OA_SESSION_ID"] = session.id;
602188
+ if (chatBody["fresh"] === true) {
602189
+ runEnv["OA_FRESH_SESSION"] = "1";
602190
+ }
600997
602191
  runEnv["OLLAMA_HOST"] = currentCfg.backendUrl || process.env["OLLAMA_HOST"] || "http://127.0.0.1:11434";
600998
602192
  if (currentCfg.apiKey) runEnv["OA_API_KEY_INHERIT"] = currentCfg.apiKey;
600999
602193
  const child = spawn25(process.execPath, [oaBin, ...args], {
@@ -601799,7 +602993,7 @@ ${steering}`;
601799
602993
  function getScheduleRoots() {
601800
602994
  const rootsEnv = process.env["OA_SCHEDULE_ROOTS"] || "";
601801
602995
  const roots = rootsEnv.split(rootsEnv.includes(";") ? ";" : ":").filter(Boolean);
601802
- const defaults3 = [process.cwd(), join121(homedir42(), "Documents")];
602996
+ const defaults3 = [process.cwd(), join122(homedir43(), "Documents")];
601803
602997
  const set = /* @__PURE__ */ new Set([...defaults3, ...roots]);
601804
602998
  return [...set];
601805
602999
  }
@@ -601811,10 +603005,10 @@ function listScheduledTasks() {
601811
603005
  for (const root of roots) {
601812
603006
  try {
601813
603007
  walk(root, 0, (dir) => {
601814
- if (dir.endsWith(`${join121(".oa", "scheduled")}`) || dir.includes(`${join121(".oa", "scheduled")}`)) {
601815
- 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");
601816
603010
  try {
601817
- const raw = readFileSync85(file, "utf-8");
603011
+ const raw = readFileSync86(file, "utf-8");
601818
603012
  const json = JSON.parse(raw);
601819
603013
  const tasks = Array.isArray(json?.tasks) ? json.tasks : Array.isArray(json) ? json : [];
601820
603014
  tasks.forEach((t2, i2) => {
@@ -601879,7 +603073,7 @@ function walk(dir, depth, onDir, maxDepth) {
601879
603073
  if (e2.name === "node_modules" || e2.name.startsWith(".")) {
601880
603074
  if (e2.name !== ".oa") continue;
601881
603075
  }
601882
- const child = join121(dir, e2.name);
603076
+ const child = join122(dir, e2.name);
601883
603077
  walk(child, depth + 1, onDir, maxDepth);
601884
603078
  }
601885
603079
  }
@@ -601888,7 +603082,7 @@ function setScheduledEnabled(id, enabled2) {
601888
603082
  const target = tasks.find((t2) => t2.id === id);
601889
603083
  if (!target) return false;
601890
603084
  try {
601891
- const raw = readFileSync85(target.file, "utf-8");
603085
+ const raw = readFileSync86(target.file, "utf-8");
601892
603086
  const json = JSON.parse(raw);
601893
603087
  const arr = Array.isArray(json?.tasks) ? json.tasks : Array.isArray(json) ? json : [];
601894
603088
  if (!arr[target.index]) return false;
@@ -601921,7 +603115,7 @@ function deleteScheduledById(id) {
601921
603115
  const target = tasks.find((t2) => t2.id === id);
601922
603116
  if (!target) return false;
601923
603117
  try {
601924
- const raw = readFileSync85(target.file, "utf-8");
603118
+ const raw = readFileSync86(target.file, "utf-8");
601925
603119
  const json = JSON.parse(raw);
601926
603120
  const arr = Array.isArray(json?.tasks) ? json.tasks : Array.isArray(json) ? json : [];
601927
603121
  if (!arr[target.index]) return false;
@@ -602186,11 +603380,11 @@ function reconcileScheduledTasks(apply) {
602186
603380
  const errors = [];
602187
603381
  for (const f2 of found) {
602188
603382
  const wdir = f2.workingDir || process.cwd();
602189
- const file = join121(wdir, ".oa", "scheduled", "tasks.json");
603383
+ const file = join122(wdir, ".oa", "scheduled", "tasks.json");
602190
603384
  try {
602191
603385
  let json = { tasks: [] };
602192
603386
  try {
602193
- const raw = readFileSync85(file, "utf-8");
603387
+ const raw = readFileSync86(file, "utf-8");
602194
603388
  json = JSON.parse(raw);
602195
603389
  } catch {
602196
603390
  }
@@ -602201,8 +603395,8 @@ function reconcileScheduledTasks(apply) {
602201
603395
  const entry = { task: f2.task || `legacy ${f2.id}`, schedule: f2.cron, enabled: true };
602202
603396
  arr.push(entry);
602203
603397
  const toWrite = Array.isArray(json?.tasks) ? { ...json, tasks: arr } : Array.isArray(json) ? arr : { tasks: arr };
602204
- mkdirSync63(join121(wdir, ".oa", "scheduled"), { recursive: true });
602205
- 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 });
602206
603400
  writeFileSync55(file, JSON.stringify(toWrite, null, 2));
602207
603401
  adopted.push({ file, index: arr.length - 1 });
602208
603402
  }
@@ -602247,32 +603441,32 @@ function writeCrontabLines(lines) {
602247
603441
  }
602248
603442
  function canonicalCronLine(rec) {
602249
603443
  const oaBin = findOaBinary4();
602250
- const logDir = join121(rec.workingDir, ".oa", "scheduled", "logs");
602251
- const logFile = join121(logDir, `${rec.id}.log`);
602252
- 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");
602253
603447
  const taskEsc = rec.task.replace(/'/g, "'\\''");
602254
- const lockDir = join121(rec.workingDir, ".oa", "run");
602255
- const lockPath = join121(lockDir, `${rec.id}.lock`);
603448
+ const lockDir = join122(rec.workingDir, ".oa", "run");
603449
+ const lockPath = join122(lockDir, `${rec.id}.lock`);
602256
603450
  const wrapper = [
602257
603451
  `cd ${JSON.stringify(rec.workingDir)}`,
602258
603452
  `mkdir -p ${JSON.stringify(logDir)}`,
602259
603453
  `mkdir -p ${JSON.stringify(lockDir)}`,
602260
603454
  `if mkdir ${JSON.stringify(lockPath)} 2>/dev/null; then`,
602261
- ` echo $$ > ${JSON.stringify(join121(lockPath, "pid"))}`,
603455
+ ` echo $$ > ${JSON.stringify(join122(lockPath, "pid"))}`,
602262
603456
  ` trap 'rm -rf ${lockPath}' EXIT`,
602263
603457
  `else`,
602264
- ` if [ -f ${JSON.stringify(join121(lockPath, "pid"))} ]; then`,
602265
- ` 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)`,
602266
603460
  ` if [ -n "$oldpid" ] && kill -0 "$oldpid" 2>/dev/null; then`,
602267
603461
  ` echo "[oa-scheduler] ${rec.id} already running as PID $oldpid; skipping" >> ${JSON.stringify(logFile)}`,
602268
603462
  ` exit 0`,
602269
603463
  ` else`,
602270
603464
  ` rm -rf ${JSON.stringify(lockPath)} 2>/dev/null || true`,
602271
- ` 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`,
602272
603466
  ` fi`,
602273
603467
  ` else`,
602274
603468
  ` rm -rf ${JSON.stringify(lockPath)} 2>/dev/null || true`,
602275
- ` 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`,
602276
603470
  ` fi`,
602277
603471
  `fi`,
602278
603472
  `${oaBin} '${taskEsc}' >> ${JSON.stringify(logFile)} 2>&1; _oa_exit=$?`,
@@ -602304,9 +603498,9 @@ function fixupOrMigrateScheduled(mode, dryRun) {
602304
603498
  try {
602305
603499
  if (!f2.workingDir || !f2.task) continue;
602306
603500
  const unitBase = `oa-${f2.id}`;
602307
- const unitDir = join121(homedir42(), ".config", "systemd", "user");
602308
- const svc = join121(unitDir, `${unitBase}.service`);
602309
- 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`);
602310
603504
  const oaBin = findOaBinary4();
602311
603505
  const rec = { id: f2.id, cron: f2.cron, workingDir: f2.workingDir, task: f2.task };
602312
603506
  const cmd = canonicalCronLine(rec).split(" ").slice(5).join(" ");
@@ -602432,8 +603626,8 @@ function startApiServer(options2 = {}) {
602432
603626
  const config = loadConfig();
602433
603627
  const ollamaUrl = options2.ollamaUrl ?? config.backendUrl;
602434
603628
  const cwd4 = process.cwd();
602435
- initAuditLog(join121(cwd4, ".oa"));
602436
- initUsageTracker(join121(cwd4, ".oa"));
603629
+ initAuditLog(join122(cwd4, ".oa"));
603630
+ initUsageTracker(join122(cwd4, ".oa"));
602437
603631
  try {
602438
603632
  const taskMgr = getSharedTaskManager();
602439
603633
  taskMgr.setEventPublisher((type, data, opts) => {
@@ -602484,7 +603678,7 @@ function startApiServer(options2 = {}) {
602484
603678
  if (!f2.endsWith(".json") || f2.includes(".tmp.")) continue;
602485
603679
  const sid = f2.replace(/\.json$/, "");
602486
603680
  try {
602487
- const items = JSON.parse(readFileSync85(join121(dir, f2), "utf-8"));
603681
+ const items = JSON.parse(readFileSync86(join122(dir, f2), "utf-8"));
602488
603682
  if (Array.isArray(items)) {
602489
603683
  cache8.set(sid, new Map(items.map((t2) => [t2.id, t2])));
602490
603684
  }
@@ -602496,10 +603690,10 @@ function startApiServer(options2 = {}) {
602496
603690
  const watcher = fsWatch3(dir, (_evt, fname) => {
602497
603691
  if (!fname || !fname.endsWith(".json") || fname.includes(".tmp.")) return;
602498
603692
  const sid = fname.replace(/\.json$/, "");
602499
- const fp = join121(dir, fname);
603693
+ const fp = join122(dir, fname);
602500
603694
  let next = [];
602501
603695
  try {
602502
- if (!existsSync105(fp)) {
603696
+ if (!existsSync106(fp)) {
602503
603697
  const old = cache8.get(sid);
602504
603698
  if (old) {
602505
603699
  for (const t2 of old.values()) {
@@ -602512,7 +603706,7 @@ function startApiServer(options2 = {}) {
602512
603706
  }
602513
603707
  return;
602514
603708
  }
602515
- next = JSON.parse(readFileSync85(fp, "utf-8"));
603709
+ next = JSON.parse(readFileSync86(fp, "utf-8"));
602516
603710
  if (!Array.isArray(next)) return;
602517
603711
  } catch {
602518
603712
  return;
@@ -602551,14 +603745,14 @@ function startApiServer(options2 = {}) {
602551
603745
  const retentionDays = parseInt(process.env["OA_JOB_RETENTION_DAYS"] ?? "30", 10);
602552
603746
  if (retentionDays > 0) {
602553
603747
  try {
602554
- const jobsDir3 = join121(cwd4, ".oa", "jobs");
602555
- if (existsSync105(jobsDir3)) {
603748
+ const jobsDir3 = join122(cwd4, ".oa", "jobs");
603749
+ if (existsSync106(jobsDir3)) {
602556
603750
  const cutoff = Date.now() - retentionDays * 864e5;
602557
603751
  for (const f2 of readdirSync36(jobsDir3)) {
602558
603752
  if (!f2.endsWith(".json")) continue;
602559
603753
  try {
602560
- const jobPath = join121(jobsDir3, f2);
602561
- const job = JSON.parse(readFileSync85(jobPath, "utf-8"));
603754
+ const jobPath = join122(jobsDir3, f2);
603755
+ const job = JSON.parse(readFileSync86(jobPath, "utf-8"));
602562
603756
  const jobTime = new Date(job.startedAt ?? job.completedAt ?? 0).getTime();
602563
603757
  if (jobTime > 0 && jobTime < cutoff && job.status !== "running") {
602564
603758
  const { unlinkSync: unlinkSync25 } = require3("node:fs");
@@ -602578,8 +603772,8 @@ function startApiServer(options2 = {}) {
602578
603772
  if (useTls) {
602579
603773
  try {
602580
603774
  tlsOpts = {
602581
- cert: readFileSync85(resolve38(tlsCert)),
602582
- key: readFileSync85(resolve38(tlsKey))
603775
+ cert: readFileSync86(resolve38(tlsCert)),
603776
+ key: readFileSync86(resolve38(tlsKey))
602583
603777
  };
602584
603778
  } catch (e2) {
602585
603779
  log22(`
@@ -602590,9 +603784,9 @@ function startApiServer(options2 = {}) {
602590
603784
  }
602591
603785
  let runtimeAccessMode = resolveAccessMode(process.env["OA_ACCESS"], host);
602592
603786
  try {
602593
- const accessFile = join121(homedir42(), ".open-agents", "access");
602594
- if (existsSync105(accessFile)) {
602595
- 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();
602596
603790
  const resolved = resolveAccessMode(persisted, host);
602597
603791
  if (resolved) runtimeAccessMode = resolved;
602598
603792
  }
@@ -602645,9 +603839,9 @@ function startApiServer(options2 = {}) {
602645
603839
  const previous = runtimeAccessMode;
602646
603840
  runtimeAccessMode = requested;
602647
603841
  try {
602648
- const dir = join121(homedir42(), ".open-agents");
603842
+ const dir = join122(homedir43(), ".open-agents");
602649
603843
  mkdirSync63(dir, { recursive: true });
602650
- writeFileSync55(join121(dir, "access"), `${runtimeAccessMode}
603844
+ writeFileSync55(join122(dir, "access"), `${runtimeAccessMode}
602651
603845
  `, "utf8");
602652
603846
  } catch {
602653
603847
  }
@@ -602860,9 +604054,9 @@ function startApiServer(options2 = {}) {
602860
604054
  try {
602861
604055
  const { startEmbeddingWorkers: startEmbeddingWorkers2 } = await Promise.resolve().then(() => (init_embedding_workers(), embedding_workers_exports));
602862
604056
  const { ensureEmbedDeps: ensureEmbedDeps2, runEmbedImage: runEmbedImage2, runEmbedAudio: runEmbedAudio2 } = await Promise.resolve().then(() => (init_py_embed(), py_embed_exports));
602863
- const dbBase = join121(cwd4, ".oa");
602864
- const epStore = new mem.EpisodeStore(join121(dbBase, "memory.db"));
602865
- 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"));
602866
604060
  try {
602867
604061
  ensureEmbedDeps2();
602868
604062
  } catch {
@@ -602939,6 +604133,34 @@ function startApiServer(options2 = {}) {
602939
604133
  `);
602940
604134
  log22(` Primary: ${config.backendUrl} (${config.backendType || "ollama"})
602941
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
+ }
602942
604164
  try {
602943
604165
  adoptHandoffRuns();
602944
604166
  } catch (e2) {
@@ -603172,10 +604394,10 @@ async function handleMemoryIngest(req2, res, ollamaUrl) {
603172
604394
  const labels = Array.isArray(b.labels) ? b.labels : [];
603173
604395
  const mediaPath = typeof b.media_path === "string" ? b.media_path : void 0;
603174
604396
  const cwd4 = process.cwd();
603175
- const dbBase = join121(cwd4, ".oa");
604397
+ const dbBase = join122(cwd4, ".oa");
603176
604398
  const { EpisodeStore: EpisodeStore3, TemporalGraph: TemporalGraph3 } = await Promise.resolve().then(() => (init_dist7(), dist_exports2));
603177
- const epStore = new EpisodeStore3(join121(dbBase, "memory.db"));
603178
- 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"));
603179
604401
  const meta = {};
603180
604402
  if (mediaPath) meta.media_path = mediaPath;
603181
604403
  const epId = epStore.insert({ modality, content: content || (mediaPath || ""), metadata: meta, toolName: "memory_ingest" });
@@ -603242,7 +604464,7 @@ async function handleEntitiesList(req2, res) {
603242
604464
  const type = url.searchParams.get("type") || "person";
603243
604465
  const limit = Math.max(1, Math.min(1e3, parseInt(url.searchParams.get("limit") || "100", 10)));
603244
604466
  const { TemporalGraph: TemporalGraph3 } = await Promise.resolve().then(() => (init_dist7(), dist_exports2));
603245
- const kg = new TemporalGraph3(join121(process.cwd(), ".oa", "kg.db"));
604467
+ const kg = new TemporalGraph3(join122(process.cwd(), ".oa", "kg.db"));
603246
604468
  const nodes = kg.nodesByType(type, limit).map((n2) => ({ id: n2.id, text: n2.text, mentionCount: n2.mentionCount, firstSeen: n2.firstSeen, lastSeen: n2.lastSeen }));
603247
604469
  jsonResponse(res, 200, { object: "list", data: nodes });
603248
604470
  } catch (err) {
@@ -603264,7 +604486,7 @@ async function handleMemorySearch2(req2, res) {
603264
604486
  const wLex = typeof b.lexical_weight === "number" ? b.lexical_weight : 1;
603265
604487
  const wEmb = typeof b.embedding_weight === "number" ? b.embedding_weight : 1;
603266
604488
  const { EpisodeStore: EpisodeStore3 } = await Promise.resolve().then(() => (init_dist7(), dist_exports2));
603267
- const epStore = new EpisodeStore3(join121(process.cwd(), ".oa", "memory.db"));
604489
+ const epStore = new EpisodeStore3(join122(process.cwd(), ".oa", "memory.db"));
603268
604490
  const results = epStore.search({ query, modality, limit }, { queryEmbedding: qEmb, lexicalWeight: wLex, embeddingWeight: wEmb });
603269
604491
  jsonResponse(res, 200, { object: "list", data: results.map((e2) => ({ id: e2.id, modality: e2.modality, content: e2.content, timestamp: e2.timestamp })) });
603270
604492
  } catch (err) {
@@ -603381,13 +604603,13 @@ var init_serve = __esm({
603381
604603
 
603382
604604
  // packages/cli/src/tui/interactive.ts
603383
604605
  import { cwd } from "node:process";
603384
- 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";
603385
604607
  import { createRequire as createRequire6 } from "node:module";
603386
604608
  import { fileURLToPath as fileURLToPath18 } from "node:url";
603387
- import { readFileSync as readFileSync86, writeFileSync as writeFileSync56, appendFileSync as appendFileSync8, rmSync as rmSync5, readdirSync as readdirSync37, mkdirSync as mkdirSync64 } from "node:fs";
603388
- 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";
603389
604611
  import { execSync as execSync58 } from "node:child_process";
603390
- import { homedir as homedir43 } from "node:os";
604612
+ import { homedir as homedir44 } from "node:os";
603391
604613
  function formatTimeAgo2(date) {
603392
604614
  const seconds = Math.floor((Date.now() - date.getTime()) / 1e3);
603393
604615
  if (seconds < 60) return "just now";
@@ -603403,12 +604625,12 @@ function getVersion4() {
603403
604625
  const require4 = createRequire6(import.meta.url);
603404
604626
  const thisDir = dirname37(fileURLToPath18(import.meta.url));
603405
604627
  const candidates = [
603406
- join122(thisDir, "..", "package.json"),
603407
- join122(thisDir, "..", "..", "package.json"),
603408
- join122(thisDir, "..", "..", "..", "package.json")
604628
+ join123(thisDir, "..", "package.json"),
604629
+ join123(thisDir, "..", "..", "package.json"),
604630
+ join123(thisDir, "..", "..", "..", "package.json")
603409
604631
  ];
603410
604632
  for (const pkgPath of candidates) {
603411
- if (existsSync106(pkgPath)) {
604633
+ if (existsSync107(pkgPath)) {
603412
604634
  const pkg = require4(pkgPath);
603413
604635
  if (pkg.name === "open-agents-ai" || pkg.name === "@open-agents/cli" || pkg.name === "@open-agents/monorepo") {
603414
604636
  return pkg.version ?? "0.0.0";
@@ -604214,14 +605436,14 @@ Meta-critique: quality ${meta.quality}/5, thorough: ${meta.thorough}`;
604214
605436
  function gatherMemorySnippets(root) {
604215
605437
  const snippets = [];
604216
605438
  const dirs = [
604217
- join122(root, ".oa", "memory"),
604218
- join122(root, ".open-agents", "memory")
605439
+ join123(root, ".oa", "memory"),
605440
+ join123(root, ".open-agents", "memory")
604219
605441
  ];
604220
605442
  for (const dir of dirs) {
604221
- if (!existsSync106(dir)) continue;
605443
+ if (!existsSync107(dir)) continue;
604222
605444
  try {
604223
605445
  for (const f2 of readdirSync37(dir).filter((f3) => f3.endsWith(".json"))) {
604224
- const data = JSON.parse(readFileSync86(join122(dir, f2), "utf-8"));
605446
+ const data = JSON.parse(readFileSync87(join123(dir, f2), "utf-8"));
604225
605447
  for (const val of Object.values(data)) {
604226
605448
  const v = typeof val === "object" && val !== null && "value" in val ? String(val.value) : String(val);
604227
605449
  if (v.length > 10) snippets.push(v);
@@ -604375,9 +605597,9 @@ ${metabolismMemories}
604375
605597
  } catch {
604376
605598
  }
604377
605599
  try {
604378
- const archeFile = join122(repoRoot, ".oa", "arche", "variants.json");
604379
- if (existsSync106(archeFile)) {
604380
- 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"));
604381
605603
  if (variants.length > 0) {
604382
605604
  let filtered = variants;
604383
605605
  if (taskType) {
@@ -604549,9 +605771,9 @@ RULES:
604549
605771
  const compactionThreshold = Number.isFinite(envOverride) && envOverride > 0 ? envOverride : modelTier === "small" ? 12e3 : modelTier === "medium" ? 24e3 : 4e4;
604550
605772
  let identityInjection = "";
604551
605773
  try {
604552
- const ikStateFile = join122(repoRoot, ".oa", "identity", "self-state.json");
604553
- if (existsSync106(ikStateFile)) {
604554
- 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"));
604555
605777
  const lines = [
604556
605778
  `[Identity State v${selfState.version}]`,
604557
605779
  `Self: ${selfState.narrative_summary}`,
@@ -605413,11 +606635,11 @@ When done, either call task_complete with your answer, or use FINAL_VAR(variable
605413
606635
  });
605414
606636
  }
605415
606637
  try {
605416
- const ikDir = join122(repoRoot, ".oa", "identity");
605417
- const ikFile = join122(ikDir, "self-state.json");
606638
+ const ikDir = join123(repoRoot, ".oa", "identity");
606639
+ const ikFile = join123(ikDir, "self-state.json");
605418
606640
  let ikState;
605419
- if (existsSync106(ikFile)) {
605420
- ikState = JSON.parse(readFileSync86(ikFile, "utf8"));
606641
+ if (existsSync107(ikFile)) {
606642
+ ikState = JSON.parse(readFileSync87(ikFile, "utf8"));
605421
606643
  } else {
605422
606644
  mkdirSync64(ikDir, { recursive: true });
605423
606645
  const machineId = Date.now().toString(36) + Math.random().toString(36).slice(2, 8);
@@ -605494,9 +606716,9 @@ When done, either call task_complete with your answer, or use FINAL_VAR(variable
605494
606716
  } else {
605495
606717
  renderTaskIncomplete(result.turns, result.toolCalls, result.durationMs, tokens);
605496
606718
  try {
605497
- const ikFile = join122(repoRoot, ".oa", "identity", "self-state.json");
605498
- if (existsSync106(ikFile)) {
605499
- 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"));
605500
606722
  if (!ikState.stats) ikState.stats = { queries_served: 0 };
605501
606723
  ikState.stats.queries_served = (ikState.stats.queries_served || 0) + 1;
605502
606724
  ikState.homeostasis.uncertainty = Math.min(1, ikState.homeostasis.uncertainty + 0.03);
@@ -605752,10 +606974,10 @@ async function startInteractive(config, repoPath) {
605752
606974
  process.stdin.pause();
605753
606975
  }
605754
606976
  try {
605755
- const oaDir = join122(repoRoot, ".oa");
605756
- const nexusPidFile = join122(oaDir, "nexus", "daemon.pid");
605757
- if (existsSync106(nexusPidFile)) {
605758
- 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);
605759
606981
  if (pid > 0) {
605760
606982
  try {
605761
606983
  process.kill(pid, 0);
@@ -606415,7 +607637,7 @@ ${result.summary}`
606415
607637
  let p2pGateway = null;
606416
607638
  let peerMesh = null;
606417
607639
  let inferenceRouter = null;
606418
- const secretVault = new SecretVault(join122(repoRoot, ".oa", "vault.enc"));
607640
+ const secretVault = new SecretVault(join123(repoRoot, ".oa", "vault.enc"));
606419
607641
  let adminSessionKey = null;
606420
607642
  const callSubAgents = /* @__PURE__ */ new Map();
606421
607643
  const streamRenderer = new StreamRenderer();
@@ -606646,13 +607868,13 @@ Rationale: ${proposal.rationale}${provenanceNote}`;
606646
607868
  const hits = allCompletions.filter((c9) => c9.toLowerCase().startsWith(lower));
606647
607869
  return [hits, line];
606648
607870
  }
606649
- const HISTORY_DIR = join122(homedir43(), ".open-agents");
606650
- 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");
606651
607873
  const MAX_HISTORY_LINES = 500;
606652
607874
  let savedHistory = [];
606653
607875
  try {
606654
- if (existsSync106(HISTORY_FILE)) {
606655
- const raw = readFileSync86(HISTORY_FILE, "utf8").trim();
607876
+ if (existsSync107(HISTORY_FILE)) {
607877
+ const raw = readFileSync87(HISTORY_FILE, "utf8").trim();
606656
607878
  if (raw) savedHistory = raw.split("\n").reverse();
606657
607879
  }
606658
607880
  } catch {
@@ -606802,7 +608024,7 @@ Rationale: ${proposal.rationale}${provenanceNote}`;
606802
608024
  mkdirSync64(HISTORY_DIR, { recursive: true });
606803
608025
  appendFileSync8(HISTORY_FILE, line + "\n", "utf8");
606804
608026
  if (Math.random() < 0.02) {
606805
- const all2 = readFileSync86(HISTORY_FILE, "utf8").trim().split("\n");
608027
+ const all2 = readFileSync87(HISTORY_FILE, "utf8").trim().split("\n");
606806
608028
  if (all2.length > MAX_HISTORY_LINES) {
606807
608029
  writeFileSync56(HISTORY_FILE, all2.slice(-MAX_HISTORY_LINES).join("\n") + "\n", "utf8");
606808
608030
  }
@@ -606989,10 +608211,10 @@ Rationale: ${proposal.rationale}${provenanceNote}`;
606989
608211
  const { unlinkSync: _rmStale } = await import("node:fs");
606990
608212
  const { homedir: _hdir } = await import("node:os");
606991
608213
  for (const dp of [
606992
- join122(repoRoot, ".oa", "nexus", "nexus-daemon.mjs"),
606993
- 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")
606994
608216
  ]) {
606995
- if (existsSync106(dp)) try {
608217
+ if (existsSync107(dp)) try {
606996
608218
  _rmStale(dp);
606997
608219
  } catch {
606998
608220
  }
@@ -607003,9 +608225,9 @@ Rationale: ${proposal.rationale}${provenanceNote}`;
607003
608225
  const autoNexus = new NexusTool(repoRoot);
607004
608226
  const _registerNexusDaemon = () => {
607005
608227
  try {
607006
- const nexusPidFile = join122(repoRoot, ".oa", "nexus", "daemon.pid");
607007
- if (existsSync106(nexusPidFile)) {
607008
- 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);
607009
608231
  if (nPid > 0 && !registry2.daemons.has("Nexus")) {
607010
608232
  registry2.register({ name: "Nexus", pid: nPid, startedAt: Date.now(), status: "running" });
607011
608233
  statusBar.ensureMonitorTimer();
@@ -607062,7 +608284,7 @@ Rationale: ${proposal.rationale}${provenanceNote}`;
607062
608284
  } catch {
607063
608285
  }
607064
608286
  try {
607065
- const oaDir = join122(repoRoot, ".oa");
608287
+ const oaDir = join123(repoRoot, ".oa");
607066
608288
  const reconnected = await ExposeGateway.checkAndReconnect(oaDir, {
607067
608289
  onInfo: (msg) => writeContent(() => renderInfo2(msg)),
607068
608290
  onError: (msg) => writeContent(() => renderWarning2(msg))
@@ -607094,7 +608316,7 @@ Rationale: ${proposal.rationale}${provenanceNote}`;
607094
608316
  } catch {
607095
608317
  }
607096
608318
  try {
607097
- const oaDir = join122(repoRoot, ".oa");
608319
+ const oaDir = join123(repoRoot, ".oa");
607098
608320
  const reconnectedP2P = await ExposeP2PGateway.checkAndReconnect(oaDir, new NexusTool(repoRoot), {
607099
608321
  onInfo: (msg) => writeContent(() => renderInfo2(msg)),
607100
608322
  onError: (msg) => writeContent(() => renderWarning2(msg))
@@ -607135,10 +608357,10 @@ Rationale: ${proposal.rationale}${provenanceNote}`;
607135
608357
  }
607136
608358
  try {
607137
608359
  const { homedir: _hd, hostname: _hn, userInfo: _ui } = await import("node:os");
607138
- const globalNamePath = join122(_hd(), ".open-agents", "agent-name");
608360
+ const globalNamePath = join123(_hd(), ".open-agents", "agent-name");
607139
608361
  let agName = "";
607140
608362
  try {
607141
- if (existsSync106(globalNamePath)) agName = readFileSync86(globalNamePath, "utf8").trim();
608363
+ if (existsSync107(globalNamePath)) agName = readFileSync87(globalNamePath, "utf8").trim();
607142
608364
  } catch {
607143
608365
  }
607144
608366
  if (!agName) {
@@ -607167,11 +608389,11 @@ Rationale: ${proposal.rationale}${provenanceNote}`;
607167
608389
  }
607168
608390
  if (!ollamaAlive) {
607169
608391
  try {
607170
- const savedSponsorsPath = join122(repoRoot, ".oa", "sponsor", "known-sponsors.json");
608392
+ const savedSponsorsPath = join123(repoRoot, ".oa", "sponsor", "known-sponsors.json");
607171
608393
  let savedSponsors = [];
607172
608394
  try {
607173
- if (existsSync106(savedSponsorsPath)) {
607174
- savedSponsors = JSON.parse(readFileSync86(savedSponsorsPath, "utf8"));
608395
+ if (existsSync107(savedSponsorsPath)) {
608396
+ savedSponsors = JSON.parse(readFileSync87(savedSponsorsPath, "utf8"));
607175
608397
  const oneHourAgo = Date.now() - 36e5;
607176
608398
  savedSponsors = savedSponsors.filter((s2) => (s2.lastVerified || 0) > oneHourAgo);
607177
608399
  }
@@ -608263,10 +609485,10 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
608263
609485
  if (name10 === "voice_list_files") {
608264
609486
  const baseDir = String(args?.dir ?? ".");
608265
609487
  const { readdirSync: readdirSync39, statSync: statSync36 } = __require("node:fs");
608266
- const { join: join127, resolve: resolve43 } = __require("node:path");
608267
- 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));
608268
609490
  const items = readdirSync39(base3).slice(0, 200).map((f2) => {
608269
- const s2 = statSync36(join127(base3, f2));
609491
+ const s2 = statSync36(join128(base3, f2));
608270
609492
  return { name: f2, dir: s2.isDirectory(), size: s2.size };
608271
609493
  });
608272
609494
  return JSON.stringify({ dir: base3, items }, null, 2);
@@ -608354,7 +609576,7 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
608354
609576
  kind,
608355
609577
  targetUrl,
608356
609578
  authKey,
608357
- stateDir: join122(repoRoot, ".oa"),
609579
+ stateDir: join123(repoRoot, ".oa"),
608358
609580
  passthrough: passthrough ?? false,
608359
609581
  loadbalance: loadbalance ?? false,
608360
609582
  endpointAuth: passthrough ? currentConfig.apiKey : void 0,
@@ -608400,7 +609622,7 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
608400
609622
  await tunnelGateway.stop();
608401
609623
  tunnelGateway = null;
608402
609624
  }
608403
- 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") });
608404
609626
  newTunnel.on("stats", (stats) => {
608405
609627
  statusBar.setExposeStatus({
608406
609628
  status: stats.status,
@@ -608487,9 +609709,9 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
608487
609709
  });
608488
609710
  if (!result.success) throw new Error(result.error || "Connect failed");
608489
609711
  try {
608490
- const nexusPidFile = join122(repoRoot, ".oa", "nexus", "daemon.pid");
608491
- if (existsSync106(nexusPidFile)) {
608492
- 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);
608493
609715
  if (pid > 0) {
608494
609716
  registry2.register({
608495
609717
  name: "Nexus",
@@ -608677,10 +609899,10 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
608677
609899
  writeContent(() => renderInfo2(`Killed ${bgKilled} background task(s).`));
608678
609900
  }
608679
609901
  try {
608680
- const nexusDir = join122(repoRoot, OA_DIR, "nexus");
608681
- const pidFile = join122(nexusDir, "daemon.pid");
608682
- if (existsSync106(pidFile)) {
608683
- 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);
608684
609906
  if (pid > 0) {
608685
609907
  try {
608686
609908
  if (process.platform === "win32") {
@@ -608702,13 +609924,13 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
608702
609924
  } catch {
608703
609925
  }
608704
609926
  try {
608705
- const voiceDir2 = join122(homedir43(), ".open-agents", "voice");
609927
+ const voiceDir2 = join123(homedir44(), ".open-agents", "voice");
608706
609928
  const voicePidFiles = ["luxtts-daemon.pid", "piper-daemon.pid"];
608707
609929
  for (const pf of voicePidFiles) {
608708
- const pidPath = join122(voiceDir2, pf);
608709
- if (existsSync106(pidPath)) {
609930
+ const pidPath = join123(voiceDir2, pf);
609931
+ if (existsSync107(pidPath)) {
608710
609932
  try {
608711
- const pid = parseInt(readFileSync86(pidPath, "utf8").trim(), 10);
609933
+ const pid = parseInt(readFileSync87(pidPath, "utf8").trim(), 10);
608712
609934
  if (pid > 0) {
608713
609935
  if (process.platform === "win32") {
608714
609936
  try {
@@ -608732,8 +609954,8 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
608732
609954
  execSync58(process.platform === "win32" ? "timeout /t 1 /nobreak >nul" : "sleep 0.5", { timeout: 3e3, stdio: "ignore" });
608733
609955
  } catch {
608734
609956
  }
608735
- const oaPath = join122(repoRoot, OA_DIR);
608736
- if (existsSync106(oaPath)) {
609957
+ const oaPath = join123(repoRoot, OA_DIR);
609958
+ if (existsSync107(oaPath)) {
608737
609959
  let deleted = false;
608738
609960
  for (let attempt = 0; attempt < 3; attempt++) {
608739
609961
  try {
@@ -608817,19 +610039,19 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
608817
610039
  try {
608818
610040
  const { isPersonaPlexRunning: isPersonaPlexRunning2 } = await Promise.resolve().then(() => (init_personaplex(), personaplex_exports));
608819
610041
  if (isPersonaPlexRunning2()) {
608820
- const ppPidFile = join122(homedir43(), ".open-agents", "voice", "personaplex", "daemon.pid");
608821
- const ppPortFile = join122(homedir43(), ".open-agents", "voice", "personaplex", "daemon.port");
608822
- if (existsSync106(ppPidFile)) {
608823
- const ppPid = parseInt(readFileSync86(ppPidFile, "utf8").trim(), 10);
608824
- 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;
608825
610047
  if (ppPid > 0 && !registry2.daemons.has("PersonaPlex")) {
608826
610048
  registry2.register({ name: "PersonaPlex", pid: ppPid, port: ppPort, startedAt: Date.now(), status: "running" });
608827
610049
  }
608828
610050
  }
608829
610051
  }
608830
- const nexusPidFile = join122(repoRoot, ".oa", "nexus", "daemon.pid");
608831
- if (existsSync106(nexusPidFile)) {
608832
- 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);
608833
610055
  if (nPid > 0 && !registry2.daemons.has("Nexus")) {
608834
610056
  try {
608835
610057
  process.kill(nPid, 0);
@@ -609196,9 +610418,9 @@ Execute this skill now. Follow the behavioral guidance above.`;
609196
610418
  }
609197
610419
  }
609198
610420
  const cleanPath = input.replace(/^['"]|['"]$/g, "").trim();
609199
- const isImage = isImagePath(cleanPath) && existsSync106(resolve39(repoRoot, cleanPath));
609200
- const isMedia = !isImage && isTranscribablePath(cleanPath) && existsSync106(resolve39(repoRoot, cleanPath));
609201
- 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));
609202
610424
  if (activeTask) {
609203
610425
  if (activeTask.runner.isPaused) {
609204
610426
  activeTask.runner.resume();
@@ -609207,7 +610429,7 @@ Execute this skill now. Follow the behavioral guidance above.`;
609207
610429
  if (isImage) {
609208
610430
  try {
609209
610431
  const imgPath = resolve39(repoRoot, cleanPath);
609210
- const imgBuffer = readFileSync86(imgPath);
610432
+ const imgBuffer = readFileSync87(imgPath);
609211
610433
  const base642 = imgBuffer.toString("base64");
609212
610434
  const ext = extname12(cleanPath).toLowerCase();
609213
610435
  const mime = ext === ".png" ? "image/png" : ext === ".gif" ? "image/gif" : ext === ".webp" ? "image/webp" : "image/jpeg";
@@ -609363,7 +610585,7 @@ ${result.text}`;
609363
610585
  if (isMarkdown && fullInput === input) {
609364
610586
  try {
609365
610587
  const mdPath = resolve39(repoRoot, cleanPath);
609366
- const mdContent = readFileSync86(mdPath, "utf8");
610588
+ const mdContent = readFileSync87(mdPath, "utf8");
609367
610589
  const { parseMcpMarkdown: parseMcpMarkdown2 } = await Promise.resolve().then(() => (init_dist5(), dist_exports));
609368
610590
  const result = parseMcpMarkdown2(mdContent);
609369
610591
  if (result.servers.length > 0) {
@@ -609785,11 +611007,11 @@ async function runWithTUI(task, config, repoPath, callbacks) {
609785
611007
  const handle2 = startTask(task, config, repoRoot);
609786
611008
  await handle2.promise;
609787
611009
  try {
609788
- const ikDir = join122(repoRoot, ".oa", "identity");
609789
- const ikFile = join122(ikDir, "self-state.json");
611010
+ const ikDir = join123(repoRoot, ".oa", "identity");
611011
+ const ikFile = join123(ikDir, "self-state.json");
609790
611012
  let ikState;
609791
- if (existsSync106(ikFile)) {
609792
- ikState = JSON.parse(readFileSync86(ikFile, "utf8"));
611013
+ if (existsSync107(ikFile)) {
611014
+ ikState = JSON.parse(readFileSync87(ikFile, "utf8"));
609793
611015
  } else {
609794
611016
  mkdirSync64(ikDir, { recursive: true });
609795
611017
  ikState = {
@@ -609826,11 +611048,11 @@ async function runWithTUI(task, config, repoPath, callbacks) {
609826
611048
  );
609827
611049
  } catch {
609828
611050
  try {
609829
- const archeDir = join122(repoRoot, ".oa", "arche");
609830
- const archeFile = join122(archeDir, "variants.json");
611051
+ const archeDir = join123(repoRoot, ".oa", "arche");
611052
+ const archeFile = join123(archeDir, "variants.json");
609831
611053
  let variants = [];
609832
611054
  try {
609833
- if (existsSync106(archeFile)) variants = JSON.parse(readFileSync86(archeFile, "utf8"));
611055
+ if (existsSync107(archeFile)) variants = JSON.parse(readFileSync87(archeFile, "utf8"));
609834
611056
  } catch {
609835
611057
  }
609836
611058
  variants.push({
@@ -609850,9 +611072,9 @@ async function runWithTUI(task, config, repoPath, callbacks) {
609850
611072
  }
609851
611073
  }
609852
611074
  try {
609853
- const metaFile = join122(repoRoot, ".oa", "memory", "metabolism", "store.json");
609854
- if (existsSync106(metaFile)) {
609855
- 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"));
609856
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);
609857
611079
  let updated = false;
609858
611080
  for (const item of surfaced) {
@@ -609916,9 +611138,9 @@ Rules:
609916
611138
  try {
609917
611139
  const { initDb: initDb2 } = __require("@open-agents/memory");
609918
611140
  const { ProceduralMemoryStore: ProceduralMemoryStore2 } = __require("@open-agents/memory");
609919
- const dbDir = join122(repoRoot, ".oa", "memory");
611141
+ const dbDir = join123(repoRoot, ".oa", "memory");
609920
611142
  mkdirSync64(dbDir, { recursive: true });
609921
- const db = initDb2(join122(dbDir, "structured.db"));
611143
+ const db = initDb2(join123(dbDir, "structured.db"));
609922
611144
  const memStore = new ProceduralMemoryStore2(db);
609923
611145
  memStore.createWithEmbedding({
609924
611146
  content: content.slice(0, 600),
@@ -609933,11 +611155,11 @@ Rules:
609933
611155
  db.close();
609934
611156
  } catch {
609935
611157
  }
609936
- const metaDir = join122(repoRoot, ".oa", "memory", "metabolism");
609937
- const storeFile = join122(metaDir, "store.json");
611158
+ const metaDir = join123(repoRoot, ".oa", "memory", "metabolism");
611159
+ const storeFile = join123(metaDir, "store.json");
609938
611160
  let store2 = [];
609939
611161
  try {
609940
- if (existsSync106(storeFile)) store2 = JSON.parse(readFileSync86(storeFile, "utf8"));
611162
+ if (existsSync107(storeFile)) store2 = JSON.parse(readFileSync87(storeFile, "utf8"));
609941
611163
  } catch {
609942
611164
  }
609943
611165
  store2.push({
@@ -609959,19 +611181,19 @@ Rules:
609959
611181
  } catch {
609960
611182
  }
609961
611183
  try {
609962
- const cohereSettingsFile = join122(repoRoot, ".oa", "settings.json");
611184
+ const cohereSettingsFile = join123(repoRoot, ".oa", "settings.json");
609963
611185
  let cohereActive = false;
609964
611186
  try {
609965
- if (existsSync106(cohereSettingsFile)) {
609966
- const settings = JSON.parse(readFileSync86(cohereSettingsFile, "utf8"));
611187
+ if (existsSync107(cohereSettingsFile)) {
611188
+ const settings = JSON.parse(readFileSync87(cohereSettingsFile, "utf8"));
609967
611189
  cohereActive = settings.cohere === true;
609968
611190
  }
609969
611191
  } catch {
609970
611192
  }
609971
611193
  if (cohereActive) {
609972
- const metaFile = join122(repoRoot, ".oa", "memory", "metabolism", "store.json");
609973
- if (existsSync106(metaFile)) {
609974
- 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"));
609975
611197
  const latest = store2.filter((m2) => m2.sourceTrace === "trajectory-extraction" || m2.sourceTrace === "llm-trajectory-extraction").slice(-1)[0];
609976
611198
  if (latest && latest.scores?.confidence >= 0.6) {
609977
611199
  try {
@@ -609996,18 +611218,18 @@ Rules:
609996
611218
  }
609997
611219
  } catch (err) {
609998
611220
  try {
609999
- const ikFile = join122(repoRoot, ".oa", "identity", "self-state.json");
610000
- if (existsSync106(ikFile)) {
610001
- 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"));
610002
611224
  ikState.homeostasis.uncertainty = Math.min(1, ikState.homeostasis.uncertainty + 0.1);
610003
611225
  ikState.homeostasis.coherence = Math.max(0, ikState.homeostasis.coherence - 0.05);
610004
611226
  ikState.session_count = (ikState.session_count || 0) + 1;
610005
611227
  ikState.updated_at = (/* @__PURE__ */ new Date()).toISOString();
610006
611228
  writeFileSync56(ikFile, JSON.stringify(ikState, null, 2));
610007
611229
  }
610008
- const metaFile = join122(repoRoot, ".oa", "memory", "metabolism", "store.json");
610009
- if (existsSync106(metaFile)) {
610010
- 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"));
610011
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);
610012
611234
  for (const item of surfaced) {
610013
611235
  item.accessCount = (item.accessCount || 0) + 1;
@@ -610018,11 +611240,11 @@ Rules:
610018
611240
  writeFileSync56(metaFile, JSON.stringify(store2, null, 2));
610019
611241
  }
610020
611242
  try {
610021
- const archeDir = join122(repoRoot, ".oa", "arche");
610022
- const archeFile = join122(archeDir, "variants.json");
611243
+ const archeDir = join123(repoRoot, ".oa", "arche");
611244
+ const archeFile = join123(archeDir, "variants.json");
610023
611245
  let variants = [];
610024
611246
  try {
610025
- if (existsSync106(archeFile)) variants = JSON.parse(readFileSync86(archeFile, "utf8"));
611247
+ if (existsSync107(archeFile)) variants = JSON.parse(readFileSync87(archeFile, "utf8"));
610026
611248
  } catch {
610027
611249
  }
610028
611250
  variants.push({
@@ -610126,12 +611348,12 @@ __export(run_exports, {
610126
611348
  });
610127
611349
  import { resolve as resolve40 } from "node:path";
610128
611350
  import { spawn as spawn26 } from "node:child_process";
610129
- 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";
610130
611352
  import { randomBytes as randomBytes24 } from "node:crypto";
610131
- import { join as join123 } from "node:path";
611353
+ import { join as join124 } from "node:path";
610132
611354
  function jobsDir2(repoPath) {
610133
611355
  const root = resolve40(repoPath ?? process.cwd());
610134
- const dir = join123(root, ".oa", "jobs");
611356
+ const dir = join124(root, ".oa", "jobs");
610135
611357
  mkdirSync65(dir, { recursive: true });
610136
611358
  return dir;
610137
611359
  }
@@ -610254,7 +611476,7 @@ async function runBackground(task, config, opts) {
610254
611476
  }
610255
611477
  });
610256
611478
  job.pid = child.pid ?? 0;
610257
- writeFileSync57(join123(dir, `${id}.json`), JSON.stringify(job, null, 2));
611479
+ writeFileSync57(join124(dir, `${id}.json`), JSON.stringify(job, null, 2));
610258
611480
  let output = "";
610259
611481
  child.stdout?.on("data", (chunk) => {
610260
611482
  output += chunk.toString();
@@ -610270,7 +611492,7 @@ async function runBackground(task, config, opts) {
610270
611492
  job.summary = result.summary;
610271
611493
  job.durationMs = result.durationMs;
610272
611494
  job.error = result.error;
610273
- writeFileSync57(join123(dir, `${id}.json`), JSON.stringify(job, null, 2));
611495
+ writeFileSync57(join124(dir, `${id}.json`), JSON.stringify(job, null, 2));
610274
611496
  } catch {
610275
611497
  }
610276
611498
  });
@@ -610286,13 +611508,13 @@ async function runBackground(task, config, opts) {
610286
611508
  }
610287
611509
  function statusCommand(jobId, repoPath) {
610288
611510
  const dir = jobsDir2(repoPath);
610289
- const file = join123(dir, `${jobId}.json`);
610290
- if (!existsSync107(file)) {
611511
+ const file = join124(dir, `${jobId}.json`);
611512
+ if (!existsSync108(file)) {
610291
611513
  console.error(`Job not found: ${jobId}`);
610292
611514
  console.log(`Available jobs: oa jobs`);
610293
611515
  process.exit(1);
610294
611516
  }
610295
- const job = JSON.parse(readFileSync87(file, "utf-8"));
611517
+ const job = JSON.parse(readFileSync88(file, "utf-8"));
610296
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`;
610297
611519
  const icon = job.status === "completed" ? "✓" : job.status === "failed" ? "✗" : "●";
610298
611520
  console.log(`${icon} ${job.id} [${job.status}] ${runtime}`);
@@ -610313,7 +611535,7 @@ function jobsCommand(repoPath) {
610313
611535
  console.log("Jobs:");
610314
611536
  for (const file of files) {
610315
611537
  try {
610316
- const job = JSON.parse(readFileSync87(join123(dir, file), "utf-8"));
611538
+ const job = JSON.parse(readFileSync88(join124(dir, file), "utf-8"));
610317
611539
  const icon = job.status === "completed" ? "✓" : job.status === "failed" ? "✗" : "●";
610318
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`;
610319
611541
  const cleanListTask = cleanForStorage(job.task) || job.task;
@@ -610337,13 +611559,13 @@ __export(index_repo_exports, {
610337
611559
  indexRepoCommand: () => indexRepoCommand
610338
611560
  });
610339
611561
  import { resolve as resolve41 } from "node:path";
610340
- import { existsSync as existsSync108, statSync as statSync35 } from "node:fs";
611562
+ import { existsSync as existsSync109, statSync as statSync35 } from "node:fs";
610341
611563
  import { cwd as cwd2 } from "node:process";
610342
611564
  async function indexRepoCommand(opts, _config3) {
610343
611565
  const repoRoot = resolve41(opts.repoPath ?? cwd2());
610344
611566
  printHeader("Index Repository");
610345
611567
  printInfo(`Indexing: ${repoRoot}`);
610346
- if (!existsSync108(repoRoot)) {
611568
+ if (!existsSync109(repoRoot)) {
610347
611569
  printError(`Path does not exist: ${repoRoot}`);
610348
611570
  process.exit(1);
610349
611571
  }
@@ -610595,8 +611817,8 @@ var config_exports2 = {};
610595
611817
  __export(config_exports2, {
610596
611818
  configCommand: () => configCommand
610597
611819
  });
610598
- import { join as join124, resolve as resolve42 } from "node:path";
610599
- 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";
610600
611822
  import { cwd as cwd3 } from "node:process";
610601
611823
  function redactIfSensitive(key, value2) {
610602
611824
  if (SENSITIVE_KEYS.has(key) && typeof value2 === "string" && value2.length > 0) {
@@ -610677,7 +611899,7 @@ function handleShow(opts, config) {
610677
611899
  }
610678
611900
  }
610679
611901
  printSection("Config File");
610680
- printInfo(`~/.open-agents/config.json (${join124(homedir44(), ".open-agents", "config.json")})`);
611902
+ printInfo(`~/.open-agents/config.json (${join125(homedir45(), ".open-agents", "config.json")})`);
610681
611903
  printSection("Priority Chain");
610682
611904
  printInfo(" 1. CLI flags (--model, --backend-url, etc.)");
610683
611905
  printInfo(" 2. Project .oa/settings.json (--local)");
@@ -610716,7 +611938,7 @@ function handleSet(opts, _config3) {
610716
611938
  const coerced = coerceForSettings(key, value2);
610717
611939
  saveProjectSettings(repoRoot, { [key]: coerced });
610718
611940
  printSuccess(`Project override set: ${key} = ${redactIfSensitive(key, value2)}`);
610719
- printInfo(`Saved to ${join124(repoRoot, ".oa", "settings.json")}`);
611941
+ printInfo(`Saved to ${join125(repoRoot, ".oa", "settings.json")}`);
610720
611942
  printInfo("This override applies only when running in this workspace.");
610721
611943
  } catch (err) {
610722
611944
  printError(`Failed to save: ${err instanceof Error ? err.message : String(err)}`);
@@ -610901,7 +612123,7 @@ __export(eval_exports, {
610901
612123
  });
610902
612124
  import { tmpdir as tmpdir21 } from "node:os";
610903
612125
  import { mkdirSync as mkdirSync66, writeFileSync as writeFileSync58 } from "node:fs";
610904
- import { join as join125 } from "node:path";
612126
+ import { join as join126 } from "node:path";
610905
612127
  async function evalCommand(opts, config) {
610906
612128
  const suiteName = opts.suite ?? "basic";
610907
612129
  const suite = SUITES[suiteName];
@@ -611030,10 +612252,10 @@ async function evalCommand(opts, config) {
611030
612252
  process.exit(failed > 0 ? 1 : 0);
611031
612253
  }
611032
612254
  function createTempEvalRepo() {
611033
- const dir = join125(tmpdir21(), `open-agents-eval-${Date.now()}`);
612255
+ const dir = join126(tmpdir21(), `open-agents-eval-${Date.now()}`);
611034
612256
  mkdirSync66(dir, { recursive: true });
611035
612257
  writeFileSync58(
611036
- join125(dir, "package.json"),
612258
+ join126(dir, "package.json"),
611037
612259
  JSON.stringify({ name: "eval-repo", version: "0.0.0" }, null, 2) + "\n",
611038
612260
  "utf8"
611039
612261
  );
@@ -611097,7 +612319,7 @@ init_typed_node_events();
611097
612319
  import { parseArgs as nodeParseArgs2 } from "node:util";
611098
612320
  import { createRequire as createRequire7 } from "node:module";
611099
612321
  import { fileURLToPath as fileURLToPath19 } from "node:url";
611100
- import { dirname as dirname38, join as join126 } from "node:path";
612322
+ import { dirname as dirname38, join as join127 } from "node:path";
611101
612323
 
611102
612324
  // packages/cli/src/cli.ts
611103
612325
  init_typed_node_events();
@@ -611237,7 +612459,7 @@ init_output();
611237
612459
  function getVersion5() {
611238
612460
  try {
611239
612461
  const require4 = createRequire7(import.meta.url);
611240
- const pkgPath = join126(dirname38(fileURLToPath19(import.meta.url)), "..", "package.json");
612462
+ const pkgPath = join127(dirname38(fileURLToPath19(import.meta.url)), "..", "package.json");
611241
612463
  const pkg = require4(pkgPath);
611242
612464
  return pkg.version;
611243
612465
  } catch {
@@ -611552,11 +612774,11 @@ function crashLog(label, err) {
611552
612774
  `;
611553
612775
  try {
611554
612776
  const { appendFileSync: appendFileSync9, mkdirSync: mkdirSync67 } = __require("node:fs");
611555
- const { join: join127 } = __require("node:path");
611556
- const { homedir: homedir45 } = __require("node:os");
611557
- 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");
611558
612780
  mkdirSync67(logDir, { recursive: true });
611559
- appendFileSync9(join127(logDir, "crash.log"), logLine);
612781
+ appendFileSync9(join128(logDir, "crash.log"), logLine);
611560
612782
  } catch {
611561
612783
  }
611562
612784
  try {